Permalink
Browse files

Add following model, lots of other stuff.

  • Loading branch information...
1 parent 18ada37 commit 539b8fce57f878c7300f9031028536d8a0c03214 @kushal committed Jan 10, 2011
Showing with 174 additions and 29 deletions.
  1. +3 −1 app.yaml
  2. +7 −4 emails.py
  3. +87 −15 main.py
  4. +14 −1 model.py
  5. +3 −3 templates/base.html
  6. +13 −4 templates/index.html
  7. +23 −0 templates/tag.html
  8. +24 −1 templates/user.html
View
@@ -6,9 +6,11 @@ api_version: 1
handlers:
- url: /_ah/mail/snippets@.*fssnippets\.appspotmail\.com
script: receive_email.py
-
+ login: admin
+
- url: .*
script: main.py
+ secure: always
inbound_services:
- mail
View
@@ -40,9 +40,12 @@ def __snippet_to_text(self, snippet):
def get(self):
all_users = User.all().filter("enabled =", True).fetch(500)
- d = date_for_retrieval()
+ d = date_for_new_snippet()
all_snippets = Snippet.all().filter("date =", d).fetch(500)
- # TODO: Build custom emails
- body = '\n\n\n'.join([self.__snippet_to_text(s) for s in all_snippets])
for user in all_users:
- self.__send_mail(user.email, body)
+ following = compute_following(user, all_users)
+ body = '\n\n\n'.join([self.__snippet_to_text(s) for s in all_snippets if s.user.email in following])
+ if body:
+ self.__send_mail(user.email, body)
+ else:
+ logging.info(user.email + ' not following anybody.')
View
102 main.py
@@ -38,6 +38,7 @@ def wrapper(self, *args, **kwargs):
return method(self, *args, **kwargs)
return wrapper
+
class BaseHandler(webapp.RequestHandler):
def get_user(self):
'''Returns the user object on authenticated requests'''
@@ -51,7 +52,12 @@ def get_user(self):
else:
userObj = userObj[0]
return userObj
-
+
+ def render(self, template_name, template_values):
+ self.response.headers['Content-Type'] = 'text/html'
+ path = os.path.join(os.path.dirname(__file__), 'templates/%s.html' % template_name)
+ self.response.out.write(template.render(path, template_values))
+
class UserHandler(BaseHandler):
"""Show a given user's snippets."""
@@ -60,23 +66,80 @@ class UserHandler(BaseHandler):
def get(self, email):
user = self.get_user()
email = urllib.unquote_plus(email)
-
desired_user = user_from_email(email)
-
- snippets = user.snippet_set
+ snippets = desired_user.snippet_set
snippets = sorted(snippets, key=lambda s: s.date, reverse=True)
-
- self.response.headers['Content-Type'] = 'text/html'
+ following = email in user.following
+ tags = [(t, t in user.tags_following) for t in desired_user.tags]
+
template_values = {
'current_user' : user,
'user': desired_user,
- 'snippets': snippets
+ 'snippets': snippets,
+ 'following': following,
+ 'tags': tags
}
+ self.render('user', template_values)
- path = os.path.join(os.path.dirname(__file__), 'templates/user.html')
- self.response.out.write(template.render(path, template_values))
+
+class FollowHandler(BaseHandler):
+ """Follow a user or tag."""
+ @authenticated
+ def get(self):
@dolapo

dolapo Jan 10, 2011

Contributor

this should be a post no?

@kushal

kushal Jan 10, 2011

Owner

theoretically! but i have links to it all over the place. :)

@dolapo

dolapo Jan 10, 2011

Contributor

i reserve all rights to i told you so in the future!

+ user = self.get_user()
+ tag = self.request.get('tag')
@dolapo

dolapo Jan 10, 2011

Contributor

name this desired_tag for consistency with desired_user?

+ desired_user = self.request.get('user')
+ continue_url = self.request.get('continue')
+
+ if tag and (tag not in user.tags_following):
+ user.tags_following.append(tag)
+ user.put()
+ if desired_user and (desired_user not in user.following):
+ user.following.append(desired_user)
+ user.put()
+
+ self.redirect(continue_url)
+class UnfollowHandler(BaseHandler):
+ """Unfollow a user or tag."""
+ @authenticated
+ def get(self):
+ user = self.get_user()
+ tag = self.request.get('tag')
+ desired_user = self.request.get('user')
+ continue_url = self.request.get('continue')
+
+ if tag and (tag in user.tags_following):
+ user.tags_following.remove(tag)
+ user.put()
+ if desired_user and (desired_user in user.following):
+ user.following.remove(desired_user)
+ user.put()
+
+ self.redirect(continue_url)
+
+
+class TagHandler(BaseHandler):
+ """View this week's snippets in a given tag."""
+ @authenticated
+ def get(self, tag):
+ user = self.get_user()
+ d = date_for_new_snippet()
+ all_snippets = Snippet.all().filter("date =", d).fetch(500)
+ if (tag != 'all'):
+ all_snippets = [s for s in all_snippets if tag in s.user.tags]
+ following = tag in user.tags_following
+
+ template_values = {
+ 'current_user' : user,
+ 'snippets': all_snippets,
+ 'following': following,
+ 'tag': tag
+ }
+ self.render('tag', template_values)
+
+
class MainHandler(BaseHandler):
"""Show list of all users and acting user's settings."""
@@ -92,22 +155,31 @@ def get(self):
user.enabled = False
user.put()
+ # Update tags if sent
+ tags = self.request.get('tags')
+ if tags:
+ user.tags = [s.strip() for s in tags.split(',')]
+ user.put()
+
# Fetch user list and display
- all_users = User.all().fetch(500)
- self.response.headers['Content-Type'] = 'text/html'
+ raw_users = User.all().order('email').fetch(500)
+ following = compute_following(user, raw_users)
+ all_users = [(u, u.email in following) for u in raw_users]
+
template_values = {
'current_user' : user,
- 'all_users': all_users
+ 'all_users': all_users
}
-
- path = os.path.join(os.path.dirname(__file__), 'templates/index.html')
- self.response.out.write(template.render(path, template_values))
+ self.render('index', template_values)
def main():
application = webapp.WSGIApplication(
[('/', MainHandler),
('/user/(.*)', UserHandler),
+ ('/tag/(.*)', TagHandler),
+ ('/follow', FollowHandler),
+ ('/unfollow', UnfollowHandler),
('/reminderemail', ReminderEmail),
('/digestemail', DigestEmail)],
debug=True)
View
@@ -6,14 +6,27 @@
class User(db.Model):
# Just store email address, because GAFYD seems to be buggy (omits domain in stored email or something...)
email = db.StringProperty()
- following = db.ListProperty(db.Key)
+ following = db.StringListProperty()
enabled = db.BooleanProperty(default=True)
+ tags = db.StringListProperty()
+ tags_following = db.StringListProperty()
class Snippet(db.Model):
user = db.ReferenceProperty(User)
text = db.TextProperty()
date = db.DateProperty()
+def compute_following(current_user, users):
+ """Return set of email addresses being followed by this user."""
+ email_set = set(current_user.following)
+ tag_set = set(current_user.tags_following)
+ following = set()
+ for u in users:
+ if ((u.email in email_set) or
+ (len(tag_set.intersection(u.tags)) > 0)):
+ following.add(u.email)
+ return following
+
def user_from_email(email):
return User.all().filter("email =", email).fetch(1)[0]
View
@@ -3,14 +3,14 @@
<head>
<style>
body { font-family: Courier New, monospaced; font-size: 83% }
- a.title { color: #CCC; text-decoration: none }
- a.title:hover { text-decoration: underline }
+ a.light { color: #AAA; text-decoration: none }
+ a.light:hover { text-decoration: underline }
</style>
<title>{% block title %}snippets{% endblock %}</title>
</head>
<body>
- <h1><a class="title" href="/">snippets</a></h2>
+ <h1><a class="light" href="/">snippets</a></h2>
{% block body %}
{% endblock %}
</body>
View
@@ -4,14 +4,23 @@
<h2><a href="/user/{{ current_user.email }}">{{ current_user.email }}</a></h2>
currently
{% if current_user.enabled %}
- <b>enabled</b> <a href="?setenabled=0">disable</a>
+ enabled (<a class="light" href="?setenabled=0">disable</a>)
{% else %}
- <b>disabled</b> <a href="?setenabled=1">enable</a>
+ disabled (<a class="light" href="?setenabled=1">enable</a>)
{% endif %}
-
+
+
+ <br/><br/>
+ my tags <form action="/" method="get"><input name="tags" type="text" value="{{ current_user.tags|join:"," }}"/></form>
+
<h3>Users</h3>
{% for user in all_users %}
- <a href="/user/{{ user.email }}">{{ user.email }}</a>
+ <a href="/user/{{ user.0.email }}">{{ user.0.email }}</a>
+ {% if user.1 %}
+ <a class="light" href="/unfollow?user={{ user.0.email }}&continue=/">unfollow</a>
+ {% else %}
+ <a class="light" href="/follow?user={{ user.0.email }}&continue=/">follow</a>
+ {% endif %}
<br/>
{% endfor %}
View
@@ -0,0 +1,23 @@
+{% extends "base.html" %}
+
+{% block body %}
+
+ <div style="float: left; width: 200px">
+ <h2>{{ tag }}</h2>
+
+ {% if following %}
+ <a class="light" href="/unfollow?tag={{ tag }}&continue=/tag/{{ tag }}">unfollow</a>
+ {% else %}
+ <a class="light" href="/follow?tag={{ tag }}&continue=/tag/{{ tag }}">follow</a>
+ {% endif %}
+
+ </div>
+ <div style="float: left; margin-left: 30px">
+ {% for snippet in snippets %}
+ <b>{{ snippet.user.email }}</b><br/>
+ <pre>{{ snippet.text }}</pre>
+ <br/></br>
+ {% endfor %}
+ </div>
+
+{% endblock %}
View
@@ -1,12 +1,35 @@
{% extends "base.html" %}
{% block body %}
+
+ <div style="float: left; width: 200px">
<h2>{{ user.email }}</h2>
+ {% if following %}
+ <a class="light" href="/unfollow?user={{ user.email }}&continue=/user/{{ user.email }}">unfollow</a>
+ {% else %}
+ <a class="light" href="/follow?user={{ user.email }}&continue=/user/{{ user.email }}">follow</a>
+ {% endif %}
+
+ <br/></br>
+ <b>Tags</b><br/>
+ {% for tag in tags %}
+ {{ tag.0 }}
+ {% if tag.1 %}
+ <a class="light" href="/unfollow?tag={{ tag.0 }}&continue=/user/{{ user.email }}">unfollow</a>
+ {% else %}
+ <a class="light" href="/follow?tag={{ tag.0 }}&continue=/user/{{ user.email }}">follow</a>
+ {% endif %}
+ <br/>
+ {% endfor %}
+
+ </div>
+ <div style="float: left; margin-left: 30px">
{% for snippet in snippets %}
<b>{{ snippet.date }}</b><br/>
- {{ snippet.text }}
+ <pre>{{ snippet.text }}</pre>
<br/></br>
{% endfor %}
+ </div>
{% endblock %}

0 comments on commit 539b8fc

Please sign in to comment.