Permalink
Browse files

adding errors for form validation, full notifications, and minor edit…

…s to the event page
  • Loading branch information...
1 parent 63e5c31 commit 81a81e143033f6f2cacb0ea456f01dd65e736f13 @progrium progrium committed Apr 18, 2010
Showing with 164 additions and 63 deletions.
  1. +11 −54 main.py
  2. +7 −2 models.py
  3. +94 −0 notices.py
  4. +4 −3 templates/event.html
  5. +18 −4 templates/new.html
  6. +30 −0 utils.py
View
65 main.py
@@ -10,60 +10,10 @@
from datetime import datetime, timedelta, time, date
from pytz import timezone
import pytz
-from models import Event, Feedback, ROOM_OPTIONS, GUESTS_PER_STAFF, PENDING_LIFETIME, FROM_ADDRESS
-# Hacker Dojo Domain API helper with caching
-def dojo(path):
- base_url = 'http://hackerdojo-domain.appspot.com'
- cache_ttl = 3600
- resp = memcache.get(path)
- if not resp:
- resp = urlfetch.fetch(base_url + path, deadline=10)
- try:
- resp = simplejson.loads(resp.content)
- except Exception, e:
- resp = []
- cache_ttl = 10
- memcache.set(path, resp, cache_ttl)
- return resp
-
-def username(user):
- return user.nickname().split('@')[0] if user else None
-
-def human_username(user):
- if user:
- nick = user.nickname().split('@')[0]
- first = nick.split('.')[0]
- last = nick.split('.')[1]
- return first + ' ' + last
- else:
- return None
-
-def notify_owner_confirmation(event):
- mail.send_mail(sender=FROM_ADDRESS, to=event.member.email(),
- subject="Event application submitted",
- body="""This is a confirmation that your event:\n\n%s\n\n
-has been submitted for approval. If staff is needed for your event, they
-will be notified of your request. You will be notified as soon as it's
-approved and on the calendar.""" % event.name)
-
-def notify_staff_needed(event):
- pass
-
-def notify_new_event(event):
- pass
-
-def notify_owner_approved(event):
- pass
-
-def notify_owner_expiring(event):
- pass
-
-def notify_owner_expired(event):
- pass
-
-def set_cookie(headers, name, value):
- headers.add_header('Set-Cookie', '%s=%s;' % (name, simplejson.dumps(value)))
+from models import Event, Feedback, ROOM_OPTIONS, GUESTS_PER_STAFF, PENDING_LIFETIME
+from utils import dojo, username, human_username, set_cookie
+from notices import *
class ExpireCron(webapp.RequestHandler):
def post(self):
@@ -253,8 +203,15 @@ def post(self):
if not event.is_staffed():
notify_staff_needed(event)
notify_new_event(event)
+ set_cookie(self.response.headers, 'formvalues', None)
self.redirect('/event/%s-%s' % (event.key().id(), slugify(event.name)))
- except Exception:
+ except Exception, e:
+ message = str(e)
+ if 'match format' in message:
+ message = "Date is required."
+ if message.startswith('Property'):
+ message = message[9:].replace('_', ' ').capitalize()
+ set_cookie(self.response.headers, 'formerror', message)
set_cookie(self.response.headers, 'formvalues', dict(self.request.POST))
self.redirect('/new')
View
@@ -1,11 +1,13 @@
from google.appengine.ext import db
from google.appengine.api import urlfetch, memcache, users, mail
from datetime import datetime, timedelta, time, date
+from icalendar import Calendar, Event as CalendarEvent
+from pytz import timezone
+from utils import human_username
ROOM_OPTIONS = ['cave', 'deck', 'savanna', 'frontarea', '140b']
GUESTS_PER_STAFF = 25
PENDING_LIFETIME = 30 # days
-FROM_ADDRESS = "Dojo Events <no-reply@hackerdojo-events.appspotmail.com>"
def to_sentence(aList):
sentence = ', '.join([e for e in aList if aList.index(e) != len(aList) -1])
@@ -62,7 +64,10 @@ def roomlist(self):
return to_sentence(self.rooms)
def is_staffed(self):
- return len(self.staff) >= int(self.estimated_size) / GUESTS_PER_STAFF
+ return len(self.staff) >= self.staff_needed()
+
+ def staff_needed(self):
+ return int(self.estimated_size) / GUESTS_PER_STAFF
def is_canceled(self):
return self.status == 'canceled'
View
@@ -0,0 +1,94 @@
+from google.appengine.api import mail
+from django.template.defaultfilters import slugify
+
+FROM_ADDRESS = "Dojo Events <no-reply@hackerdojo-events.appspotmail.com>"
+NEW_EVENT_ADDRESS = "events@hackerdojo.com"
+STAFF_ADDRESS = "staff@hackerdojo.com"
+
+def notify_owner_confirmation(event):
+ mail.send_mail(sender=FROM_ADDRESS, to=event.member.email(),
+ subject="[New Event] Submitted but **not yet approved**",
+ body="""This is a confirmation that your event:
+
+%s
+on %s
+
+has been submitted to be approved. If staff is needed for your event, they
+will be notified of your request. You will be notified as soon as it's
+approved and on the calendar. Here is a link to the event page:
+
+http://events.hackerdojo.com/event/%s-%s
+
+Again, your event is NOT YET APPROVED and not on the calendar.""" % (
+ event.name,
+ event.start_time.strftime('%A, %B %d'),
+ event.key().id(),
+ slugify(event.name),))
+
+def notify_staff_needed(event):
+ mail.send_mail(sender=FROM_ADDRESS, to=STAFF_ADDRESS,
+ subject="[Event Staffing] %s on %s" % (event.name, event.start_time.strftime('%a %b %d')),
+ body="""Hello staff!
+
+Fellow member %s is sponsoring a ~%s person event:
+
+%s
+at %s to %s on %s
+
+At %s people expected, %s staff members need to opt in to support this event.
+
+Without your help, this event won't happen. If you can staff this event, click
+the Staff button once logged in on this page:
+
+http://events.hackerdojo.com/event/%s-%s
+""" % (
+ event.member.email(),
+ event.estimated_size,
+ event.name,
+ event.start_time.strftime('%I:%M%p'),
+ event.end_time.strftime('%I:%M%p'),
+ event.start_time.strftime('%A, %B %d'),
+ event.estimated_size,
+ event.staff_needed(),
+ event.key().id(),
+ slugify(event.name),))
+
+def notify_new_event(event):
+ mail.send_mail(sender=FROM_ADDRESS, to=NEW_EVENT_ADDRESS,
+ subject="[New Event] %s on %s" % (event.name, event.start_time.strftime('%a %b %d')),
+ body="""Member: %s
+When: %s to %s
+Type: %s
+Size: %s
+Contact: %s (%s)
+Notes: %s
+
+http://events.hackerdojo.com/event/%s-%s
+""" % (
+ event.member.email(),
+ event.start_time.strftime('%I:%M%p'),
+ event.end_time.strftime('%I:%M%p'),
+ event.type,
+ event.estimated_size,
+ event.contact_name,
+ event.contact_phone,
+ event.notes,
+ event.key().id(),
+ slugify(event.name),))
+
+def notify_owner_approved(event):
+ mail.send_mail(sender=FROM_ADDRESS, to=event.member.email(),
+ subject="[Event Approved] %s" % event.name,
+ body="""Your event is approved and on the calendar!
+
+Please notify the event organizer if that is not you. You still need to be
+present at the event! And remember your duties as a Hacker Dojo event sponsor.
+
+http://events.hackerdojo.com/event/%s-%s
+""" % (event.key().id(), slugify(event.name)))
+
+def notify_owner_expiring(event):
+ pass
+
+def notify_owner_expired(event):
+ pass
View
@@ -15,19 +15,20 @@
</p>{% endif %}
<div class='b-block'><div class='b-label'>Status:</div><div class='b-data'>{{event.status|title}}</div></div>
- <div class='b-block'><div class='b-label'>Start:</div><div class='b-data'>{{event.start_time|date:"l, F j"}} @ {{event.start_time|date:"g:iA"|lower}}</div></div>
+ <div class='b-block'><div class='b-label'>Date:</div><div class='b-data'>{{event.start_time|date:"l, F j Y"}}</div></div>
+ <div class='b-block'><div class='b-label'>Time:</div><div class='b-data'>{{event.start_time|date:"g:iA"|lower}} to {{event.end_time|date:"g:iA"|lower}}</div></div>
<div class='b-block'><div class='b-label'>Member:</div><div class='b-data'>{{event.member}}</div></div>
<div class='b-block'><div class='b-label'>Type:</div><div class='b-data'>{{event.type}}</div></div>
<div class='b-block'><div class='b-label'>Estimated size:</div><div class='b-data'>{{event.estimated_size}}</div></div>
- <div class='b-block'><div class='b-label'>Contact:</div><div class='b-data'>{{event.contact_name}}, {{event.contact_phone}}</div></div>
+ <div class='b-block'><div class='b-label'>Contact:</div><div class='b-data'>{{event.contact_name}}{% if is_staff %}, {{event.contact_phone}}{% endif %}</div></div>
<div class='b-block'><div class='b-label'>URL:</div><div class='b-data'><a href='{{event.url}}'>{{event.url}}</a></div></div>
<div class='b-block'><div class='b-label'>Fee:</div><div class='b-data'>{{event.fee}}</div></div>
<div class='b-block'><div class='b-label'>Rooms:</div><div class='b-data'>{{event.roomlist}}</div></div>
<div class='b-block'><div class='b-label'>Staff:</div><div class='b-data'>{{event.stafflist}}</div></div>
<div class='thin-border'></div>
<div class='b-block'><div class='b-label'>Details:</div><div class='b-data'>{{event.details}}</div></div>
<br />
- <div class='b-block'><div class='b-label'>Notes:</div><div class='b-data'>{{event.notes}}</div></div>
+ {% if is_staff %}<div class='b-block'><div class='b-label'>Notes:</div><div class='b-data'>{{event.notes}}</div></div>{% endif %}
{% if feedbacks.count %}
<div class='thin-border'></div>
<div id='secondary'>
View
@@ -6,13 +6,26 @@
var formvalues = $.cookie('formvalues');
if (formvalues) {
formvalues = JSON.parse(formvalues);
+ $('#type-select').append('<option>'+formvalues['type']+'</option>');
for (var key in formvalues) {
$('[name='+key+']').val(formvalues[key]);
}
- $('select[name=type]').val(formvalues['type']);
}
- //$.cookie('formvalues', null);
+ var formerror = $.cookie('formerror');
+ if (formerror) {
+ formerror = JSON.parse(formerror);
+ $('#error').append(formerror);
+ $.cookie('formerror', null);
+ }
+
});
+
+function showOther() {
+ $('#type-select').css('display','none');$('#type-text').css('display','inline');
+}
+function hideOther() {
+ $('#type-select').css('display','inline');$('#type-text').css('display','none');
+}
</script>
<style type="text/css">
/* located in demo.css and creates a little calendar icon
@@ -85,6 +98,7 @@ <h3 style="margin-top: 0px;">Member Terms as Event Host:</h3>
</ol>
</div>
+ <span style="color: red; font-size: smaller;" id="error"></span>
<form method="post" style="width: 49%;">
<h4>Event Name</h4>
<input type="text" name="name" style="width: 300px; margin-left: 4px;" />
@@ -138,15 +152,15 @@ <h3 style="margin-top: 0px;">Member Terms as Event Host:</h3>
<h4>Type of Event</h4>
<table style="margin: 0px;"><tr>
<td class="first"><label for="type">Category:</label>
- <select name="type" id="type-select" onchange="if(this.value=='Other...'){$('#type-select').css('display','none');$('#type-text').css('display','inline');$('#type-text').focus()}">
+ <select name="type" id="type-select" onchange="if(this.value=='Other...'){showOther();$('#type-text').focus()}">
<option>Meetup</option>
<option>Hackathon</option>
<option>Conference</option>
<option>Workshop</option>
<option>Lecture</option>
<option>Social</option>
<option>Other...</option>
- </select><input type="text" name="type" id="type-text" style="display: none;" size="15"></td>
+ </select><input type="text" id="type-text" onblur="$('#type-select').append('<option>'+this.value+'</option>');$('#type-select').val(this.value);hideOther();" style="display: none;" size="15"></td>
<td><label for="estimated_size">Expected Size:</label>
<input type="text" name="estimated_size" size="4" style="text-align: right;" /> <span>people</span></td>
</tr></table>
View
@@ -0,0 +1,30 @@
+from google.appengine.api import urlfetch, memcache
+from django.utils import simplejson
+
+# Hacker Dojo Domain API helper with caching
+def dojo(path):
+ base_url = 'http://hackerdojo-domain.appspot.com'
+ cache_ttl = 3600
+ resp = memcache.get(path)
+ if not resp:
+ resp = urlfetch.fetch(base_url + path, deadline=10)
+ try:
+ resp = simplejson.loads(resp.content)
+ except Exception, e:
+ resp = []
+ cache_ttl = 10
+ memcache.set(path, resp, cache_ttl)
+ return resp
+
+def username(user):
+ return user.nickname().split('@')[0] if user else None
+
+def human_username(user):
+ if user:
+ nick = user.nickname().split('@')[0]
+ return nick.replace('.', ' ').capitalize()
+ else:
+ return None
+
+def set_cookie(headers, name, value):
+ headers.add_header('Set-Cookie', '%s=%s;' % (name, simplejson.dumps(value)))

0 comments on commit 81a81e1

Please sign in to comment.