]> git.zarvox.org Git - shortlog.git/commitdiff
Import of running code as of 2011-01-05.
authorDrew Fisher <drew.m.fisher@gmail.com>
Wed, 5 Jan 2011 19:49:16 +0000 (13:49 -0600)
committerDrew Fisher <drew.m.fisher@gmail.com>
Wed, 5 Jan 2011 19:49:16 +0000 (13:49 -0600)
It seemed a really bad idea to NOT have this in version control,
and it also was hindering deployment.

comments/.gitignore [new file with mode: 0644]
entries/.gitignore [new file with mode: 0644]
pending/.gitignore [new file with mode: 0644]
shortlog.py [new file with mode: 0644]
static/.gitignore [new file with mode: 0644]
templates/day.html [new file with mode: 0644]
templates/feed.xml [new file with mode: 0644]
templates/multiday.html [new file with mode: 0644]
templates/noentry.html [new file with mode: 0644]

diff --git a/comments/.gitignore b/comments/.gitignore
new file mode 100644 (file)
index 0000000..2211df6
--- /dev/null
@@ -0,0 +1 @@
+*.txt
diff --git a/entries/.gitignore b/entries/.gitignore
new file mode 100644 (file)
index 0000000..2211df6
--- /dev/null
@@ -0,0 +1 @@
+*.txt
diff --git a/pending/.gitignore b/pending/.gitignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/shortlog.py b/shortlog.py
new file mode 100644 (file)
index 0000000..a341b66
--- /dev/null
@@ -0,0 +1,259 @@
+#!/usr/bin/env python
+
+import web
+import os
+import datetime
+import re
+import smtplib
+import md5
+
+pwd = os.path.dirname( os.path.realpath( __file__ ) )
+
+web.config.debug = True
+
+monthnames = {"01": "January",
+               "02": "February",
+               "03": "March",
+               "04": "April",
+               "05": "May",
+               "06": "June",
+               "07": "July",
+               "08": "August",
+               "09": "September",
+               "10": "October",
+               "11": "November",
+               "12": "December"}
+
+urls = (
+       '/', 'pastweek',
+       '/week', 'pastweek' ,
+       '/month', 'pastmonth' ,
+       '/year', 'pastyear' ,
+       '/feed', 'feed' ,
+       '/(\d\d\d\d)/(\d\d)/(\d\d)', 'day' ,
+       '/(\d\d\d\d)-(\d\d)-(\d\d)', 'day' ,
+       '/(\d\d\d\d)/(\d\d)', 'month' ,
+       '/(\d\d\d\d)', 'year',
+       '', 'redir',
+       )
+
+## This is the way to do it in web.py 0.3+
+#app = web.application(urls, globals())
+
+## Workaround for autoreloading and sessions when using web.config.debug = True
+## Note: I don't actually use sessions
+#if web.config.get("_session") is None:
+#      session = web.session.Session(app, web.session.DiskStore('sessions'), initializer={'count': 0})
+#      web.config._session = session
+#else:
+#      session = web.config._session
+
+# read the paragraphs from a file
+def readFile(filepath):
+       f = open(filepath)
+       lines = map(str.strip, f.readlines())
+       return makeParas(lines)
+
+def loadComment(filepath):
+       f = open(filepath)
+       nameline = f.readline().strip()
+       emailline = f.readline().strip()
+       websiteline = f.readline().strip()
+       timestamp = f.readline().strip()
+       commentbody = makeParas(map(str.strip, f.readlines()))
+       m = md5.new()
+       m.update(emailline.lower())
+       gravatar = "http://www.gravatar.com/avatar/%s?s=48&d=identicon" % str(m.hexdigest())
+       #timestamp = datetime.datetime.fromtimestamp(os.stat(filepath).st_mtime).isoformat()
+       return (nameline, emailline, websiteline, commentbody, timestamp, gravatar)
+
+def getCommentFiles(date): # Returns a list of paths to comment files associated with date
+       filelist = filter( lambda x: re.match(date, x), os.listdir(pwd + "/comments"))
+       return filelist
+
+# Collect lines into paragraph blocks.  Returns a list of strings, each containing a well-formed HTML block.
+def makeParas(lines):
+       paragraphs = []
+       thispara = []
+       for line in lines:
+               if len(line) != 0:
+                       thispara.append(line)
+               else:
+                       paragraphs.append("\n".join(thispara))
+                       thispara = []
+       if len(thispara) > 0:
+               paragraphs.append("\n".join(thispara))
+
+       # wrap paragraphs in <p></p> if they aren't other HTML tags that don't want to be wrapped in <p> tags
+       return map( lambda x: x if "<ol>" in x or "<ul>" in x or "<pre>" in x else "<p>" + x + "</p>", paragraphs)
+
+
+def getFileList(numdays=7):
+       files = os.listdir(pwd + "/entries")
+       today = datetime.date.today()
+       then = today - datetime.timedelta(days=numdays)
+       return filter( lambda x: x > then.isoformat() , sorted(files))
+
+class year:
+       def GET(self, theyear):
+               files = sorted(os.listdir(pwd + "/entries"))
+               filelist = filter( lambda x: re.match(theyear, x), files)
+               filelist = map( lambda x: pwd + "/entries/" + x, filelist)
+               # It makes more sense to sort fixed chunks of time in chronological order, rather than feed-style
+               #filelist.reverse()
+               dates = map( lambda filename: re.sub("(.*)(\d\d\d\d-\d\d-\d\d).txt", "\g<2>", filename), filelist )
+               entries = map( readFile, filelist)
+               commentcounts = map(len, map( getCommentFiles, dates))
+               render = web.template.render(pwd + "/templates")
+               # This mess is because web.py 0.2 doesn't include passing common useful functions to the template engine
+               web.template.Template.globals['xrange'] = xrange
+               web.template.Template.globals['len'] = len
+               print render.multiday(zip(dates, entries, commentcounts), "Entries from %s" % theyear)
+               # This is the web.py 0.3+ way to do it.
+               #return render.multiday(dates, entries, "Entries from %s" % theyear)
+
+class month:
+       def GET(self, theyear, themonth):
+               files = sorted(os.listdir(pwd + "/entries"))
+               filelist = filter( lambda x: re.match("%s-%s" % (theyear, themonth), x), files)
+               filelist = map( lambda x: pwd + "/entries/" + x, filelist)
+               #filelist.reverse()
+               dates = map( lambda filename: re.sub("(.*)(\d\d\d\d-\d\d-\d\d).txt", "\g<2>", filename), filelist )
+               entries = map( readFile, filelist)
+               commentcounts = map(len, map( getCommentFiles, dates))
+               render = web.template.render(pwd + "/templates")
+               web.template.Template.globals['xrange'] = xrange
+               web.template.Template.globals['len'] = len
+               print render.multiday(zip(dates, entries, commentcounts), "Entries from %s, %s" % (monthnames[themonth], theyear))
+
+class day:
+       def GET(self, year, month, day):
+               filename = pwd + "/entries/" + year + "-" + month + "-" + day + ".txt"
+               if os.path.isfile(filename):
+                       paras = readFile(filename)
+                       commentfilelist = os.listdir(pwd + "/comments")
+                       files = filter(lambda x: re.match("(.*)" + year + "-" + month + "-" + day + "_(\d\d\d\d\d\d).txt$", x), commentfilelist)
+                       files = sorted([pwd + "/comments/" + x for x in files])
+                       comments = []
+                       for f in files:
+                               comments.append(loadComment(f))
+                       render = web.template.render(pwd + "/templates")
+                       web.template.Template.globals['xrange'] = xrange
+                       web.template.Template.globals['len'] = len
+                       print render.day("%s-%s-%s" % (year, month, day), paras, comments)
+               else:
+                       render = web.template.render(pwd + "/templates")
+                       web.template.Template.globals['xrange'] = xrange
+                       web.template.Template.globals['len'] = len
+                       print render.noentry("%s-%s-%s" % (year, month, day))
+
+
+       def POST(self, year, month, day):
+               i = web.input()
+               name = i.name.strip()
+               email = i.email.strip()
+               website = i.website.strip()
+               if website != "" and not website.startswith("http"):
+                       website = "http://" + website
+               comment = i.comment.strip()
+               timestamp = datetime.datetime.today().isoformat()
+               # find a file name that isn't taken
+               fname = ""
+               for i in xrange(0, 1000000):
+                       fname = pwd + "/pending/" + year + "-" + month + "-" + day + "_" + ("%06d" % i) + ".txt"
+                       if not os.path.exists(fname):
+                               break
+               f = open(fname, "a+")
+               f.write(name + "\n")
+               f.write(email + "\n")
+               f.write(website + "\n")
+               f.write(timestamp + "\n")
+               f.write(comment + "\n")
+               f.close()
+
+               s = smtplib.SMTP("127.0.0.1")
+               s.sendmail("shortlog@zarvox.org", ["drew.m.fisher@gmail.com"], "To: Drew Fisher <drew.m.fisher@gmail.com>\nFrom: Shortlog <shortlog@zarvox.org>\nSubject: [shortlog] new comment on %s-%s-%s\n\nName: %s\nEmail: %s\nWebsite: %s\nComment: %s" % (year, month, day, name, email, website, comment ))
+               s.quit()
+               web.header('Content-Type', 'text/html')
+               print "<html><body>Thanks! Your comment has been submitted and will be posted pending review.</body></html>"
+#              paras = readFile(pwd + "/entries/" + year + "-" + month + "-" + day + ".txt")
+#              render = web.template.render(pwd + "/templates")
+#              web.template.Template.globals['xrange'] = xrange
+#              web.template.Template.globals['len'] = len
+#              print render.day("%s-%s-%s" % (year, month, day), paras)
+
+
+class pastyear:
+       def GET(self):
+               filelist = map( lambda x: pwd + "/entries/" + x, getFileList(365) )
+               filelist.reverse()
+               dates = map( lambda filename: re.sub("(.*)(\d\d\d\d-\d\d-\d\d).txt", "\g<2>", filename), filelist )
+               entries = map( readFile, filelist)
+               commentcounts = map(len, map( getCommentFiles, dates))
+               render = web.template.render(pwd + "/templates")
+               web.template.Template.globals['xrange'] = xrange
+               web.template.Template.globals['len'] = len
+               print render.multiday(zip(dates, entries, commentcounts))
+
+class pastmonth:
+       def GET(self):
+               filelist = map( lambda x: pwd + "/entries/" + x, getFileList(30) )
+               filelist.reverse()
+               dates = map( lambda filename: re.sub("(.*)(\d\d\d\d-\d\d-\d\d).txt", "\g<2>", filename), filelist )
+               entries = map( readFile, filelist)
+               commentcounts = map(len, map( getCommentFiles, dates))
+               render = web.template.render(pwd + "/templates")
+               web.template.Template.globals['xrange'] = xrange
+               web.template.Template.globals['len'] = len
+               print render.multiday(zip(dates, entries, commentcounts))
+
+class pastweek:
+       def GET(self):
+               filelist = map( lambda x: pwd + "/entries/" + x, getFileList(7) )
+               filelist.reverse()
+               dates = map( lambda filename: re.sub("(.*)(\d\d\d\d-\d\d-\d\d).txt", "\g<2>", filename), filelist )
+               entries = map( readFile, filelist)
+               commentcounts = map(len, map( getCommentFiles, dates))
+               render = web.template.render(pwd + "/templates")
+               web.template.Template.globals['xrange'] = xrange
+               web.template.Template.globals['len'] = len
+               print render.multiday(zip(dates, entries, commentcounts))
+
+class redir:
+       def GET(self): 
+               web.redirect("/")
+
+class feed:
+       def GET(self):
+               web.header('Content-Type', 'application/atom+xml')
+               filelist = os.listdir(pwd + "/entries")
+               files = filter(lambda x: re.match("(.*)(\d\d\d\d-\d\d-\d\d).txt$", x), filelist)
+               files = [pwd + "/entries/" + x for x in files]
+               stats = [os.stat(f) for f in files]
+               decorated = [(stat.st_mtime, filename) for (filename, stat) in zip(files, stats)]
+               decorated.sort(reverse=True)
+               if len(decorated) == 0:
+                       return render.feed(datetime.datetime.today(), [] )
+               group_timestamp = datetime.datetime.fromtimestamp(decorated[0][0]).isoformat() + "-06:00"
+               entries = []
+               for d in decorated[:10]:
+                       mtime = datetime.datetime.fromtimestamp(d[0])
+                       second = {"date" : re.sub("(.*)(\d\d\d\d-\d\d-\d\d).txt", "\g<2>", d[1]),
+                                       "content": "".join(readFile(d[1])),
+                                       "timestamp": (mtime.isoformat() + "-06:00") }
+                       entries.append(second)
+               render = web.template.render(pwd +"/templates")
+               print render.feed(group_timestamp, entries)
+
+# web.py 0.3+ way
+#application = web.application(urls, globals()).wsgifunc()
+
+# web.py 0.2 way
+application = web.wsgifunc(web.webpyfunc(urls, globals()))
+
+if __name__ == "__main__":
+       ## The web.py 0.3+ way
+       #app.run()
+       ## The web.py 0.2 way
+       web.run(urls, globals())
+
diff --git a/static/.gitignore b/static/.gitignore
new file mode 100644 (file)
index 0000000..88cc107
--- /dev/null
@@ -0,0 +1,2 @@
+*.jpg
+*.png
diff --git a/templates/day.html b/templates/day.html
new file mode 100644 (file)
index 0000000..df4b4fe
--- /dev/null
@@ -0,0 +1,42 @@
+$def with (date, paras, comments)
+<!DOCTYPE HTML> 
+<html> 
+<head> 
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" > 
+<title>Shortlog - a log of everyday things</title> 
+</head> 
+<body> 
+<h1>Shortlog - a log of everyday things</h1> 
+<h3><a href="/shortlog/">Home</a></h3>
+<div>
+<h3><a href="/shortlog/$date">$date</a></h3>
+$for p in paras:
+       $:p
+</div>
+<div id="comments">
+$if len(comments) > 0:
+       <hr><h3>Comments:</h3>
+$for c in comments:
+       <div class="comment">
+       <p><span style="float: right;"><img src="$c[5]"></span>
+       $if c[2]:
+               <p><a href="$c[2]">$c[0]</a> | $c[4]</p>
+       $else:
+               <p>$c[0] | $c[4]</p>
+       $for paragraph in c[3]:
+               $:paragraph
+       </div>
+       <hr>
+</div>
+<div id="writecomment">
+<h3>Write a comment:</h3>
+<form action="/shortlog/$date" method="post" id="commentform">
+       <p><input name="name" type="text" size="30" aria-required="true" />Name</p>
+       <p><input name="email" type="text" size="30" aria-required="true" />Email (required, but won't be posted)</p>
+       <p><input name="website" type="text" size="30" aria-required="false" />Website (optional)</p>
+       <p><textarea name="comment" cols="45" rows="8" aria-required="true"></textarea></p>
+       <input name="submit" type="submit" id="submit" value="Post Comment" />
+</form>
+</div>
+</body>
+</html>
diff --git a/templates/feed.xml b/templates/feed.xml
new file mode 100644 (file)
index 0000000..86c7721
--- /dev/null
@@ -0,0 +1,22 @@
+$def with (timestamp, entries)
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
+       <title>Shortlog</title>
+       <subtitle>A log of everyday things</subtitle>
+       <link href="http://zarvox.org/shortlog/feed" rel="self"></link>
+       <link href="http://zarvox.org/shortlog/" rel="alternate"></link>
+       <id>http://zarvox.org/shortlog/</id>
+       <updated>$timestamp</updated>
+       <author>
+               <name>Drew Fisher</name>
+               <email>zarvox@zarvox.org</email>
+       </author>
+$for entry in entries:
+       <entry>
+               <title>$entry["date"]</title>
+               <link href="http://zarvox.org/shortlog/$entry['date']" rel="alternate"></link>
+               <id>http://zarvox.org/shortlog/$entry['date']</id>
+               <updated>$entry["timestamp"]</updated>
+               <summary type="html">$entry["content"]</summary>
+       </entry>
+</feed>
diff --git a/templates/multiday.html b/templates/multiday.html
new file mode 100644 (file)
index 0000000..b9fc9c7
--- /dev/null
@@ -0,0 +1,24 @@
+$def with (entries, notes="")
+$# entries is a list of tuples as follows:
+$# (date of entry,
+$#  [paragraphs],
+$#  comment count)
+<!DOCTYPE HTML> 
+<html> 
+<head> 
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" > 
+<title>Shortlog - a log of everyday things</title> 
+</head> 
+<body> 
+<h1>Shortlog - a log of everyday things</h1> 
+<h3>View the past: <a href="/shortlog/week">Week</a> <a href="/shortlog/month">Month</a> <a href="/shortlog/year">Year</a></h3>
+<h4><a href="/shortlog/feed">Atom Feed</a></h4>
+$notes
+$for i in xrange(len(entries)):
+       <div>
+       <h3><a href="/shortlog/$entries[i][0]">$entries[i][0]</a> | $entries[i][2] comments</h3>
+       $for sec in entries[i][1]:
+               $:sec
+       </div>
+</body>
+</html>
diff --git a/templates/noentry.html b/templates/noentry.html
new file mode 100644 (file)
index 0000000..1402f10
--- /dev/null
@@ -0,0 +1,13 @@
+$def with (date)
+<!DOCTYPE HTML> 
+<html> 
+<head> 
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" > 
+<title>Shortlog - a log of everyday things</title> 
+</head> 
+<body> 
+<h1>Shortlog - a log of everyday things</h1> 
+<h3><a href="/shortlog/">Home</a></h3>
+<p>There's no entry for $date.  Perhaps you reached this page by mistake?</p>
+</body>
+</html>