Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Adding Not Approved status for events #42

Merged
merged 1 commit into from

2 participants

@billsaysthis
  • Adds NotApproved button on Edit view for event if status isn't already not approved and viewer is an Admin
  • Adds new Not Approved list view at /not_approved (not linked anywhere, like /past, but these events are shown on All list view)
@geoelectric

Looks fine, automerging.

@geoelectric geoelectric merged commit 135d2c7 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 26, 2011
  1. @billsaysthis
This page is out of date. Refresh to see the latest.
View
85 main.py
@@ -24,12 +24,12 @@ def event_path(event):
return '/event/%s-%s' % (event.key().id(), slugify(event.name))
class DomainCacheCron(webapp.RequestHandler):
- def post(self):
+ def post(self):
noop = dojo('/groups/events',force=True)
class ReminderCron(webapp.RequestHandler):
- def post(self):
+ def post(self):
self.response.out.write("REMINDERS")
today = local_today()
# remind everyone 3 days in advance they need to show up
@@ -37,7 +37,7 @@ def post(self):
.filter('status IN', ['approved']) \
.filter('reminded =', False) \
.filter('start_time <', today + timedelta(days=3))
- for event in events:
+ for event in events:
self.response.out.write(event.name)
# only mail them if they created the event 2+ days ago
if event.created < today - timedelta(days=2):
@@ -57,7 +57,7 @@ def post(self):
for event in events:
event.expire()
notify_owner_expired(event)
-
+
class ExpireReminderCron(webapp.RequestHandler):
def post(self):
@@ -75,7 +75,7 @@ def get(self, format):
content_type, body = getattr(self, 'export_%s' % format)()
self.response.headers['content-type'] = content_type
self.response.out.write(body)
-
+
def export_json(self):
events = Event.get_recent_past_and_future()
for k in self.request.GET:
@@ -86,7 +86,7 @@ def export_json(self):
events = events.filter('%s =' % k, value)
events = map(lambda x: x.to_dict(summarize=True), events)
return 'application/json', simplejson.dumps(events)
-
+
def export_ics(self):
events = Event.get_recent_past_and_future()
url_base = 'http://' + self.request.headers.get('host', 'events.hackerdojo.com')
@@ -96,16 +96,16 @@ def export_ics(self):
iev.add('summary', event.name if event.status == 'approved' else event.name + ' (%s)' % event.status.upper())
# make verbose description with empty fields where information is missing
ev_desc = '__Status: %s\n__Member: %s\n__Type: %s\n__Estimated size: %s\n__Info URL: %s\n__Fee: %s\n__Contact: %s, %s\n__Rooms: %s\n\n__Details: %s\n\n__Notes: %s' % (
- event.status,
- event.owner(),
- event.type,
- event.estimated_size,
- event.url,
- event.fee,
- event.contact_name,
- event.contact_phone,
- event.roomlist(),
- event.details,
+ event.status,
+ event.owner(),
+ event.type,
+ event.estimated_size,
+ event.url,
+ event.fee,
+ event.contact_name,
+ event.contact_phone,
+ event.roomlist(),
+ event.details,
event.notes)
# then delete the empty fields with a regex
ev_desc = re.sub(re.compile(r'^__.*?:[ ,]*$\n*',re.M),'',ev_desc)
@@ -129,16 +129,16 @@ def export_large_ics(self):
iev.add('summary', event.name + ' (%s)' % event.estimated_size)
# make verbose description with empty fields where information is missing
ev_desc = '__Status: %s\n__Member: %s\n__Type: %s\n__Estimated size: %s\n__Info URL: %s\n__Fee: %s\n__Contact: %s, %s\n__Rooms: %s\n\n__Details: %s\n\n__Notes: %s' % (
- event.status,
- event.owner(),
- event.type,
- event.estimated_size,
- event.url,
- event.fee,
- event.contact_name,
- event.contact_phone,
- event.roomlist(),
- event.details,
+ event.status,
+ event.owner(),
+ event.type,
+ event.estimated_size,
+ event.url,
+ event.fee,
+ event.contact_name,
+ event.contact_phone,
+ event.roomlist(),
+ event.details,
event.notes)
# then delete the empty fields with a regex
ev_desc = re.sub(re.compile(r'^__.*?:[ ,]*$\n*',re.M),'',ev_desc)
@@ -152,7 +152,7 @@ def export_large_ics(self):
iev.add('dtend', event.end_time.replace(tzinfo=pytz.timezone('US/Pacific')))
cal.add_component(iev)
return 'text/calendar', cal.as_string()
-
+
def export_rss(self):
url_base = 'http://' + self.request.headers.get('host', 'events.hackerdojo.com')
events = Event.get_recent_past_and_future()
@@ -179,7 +179,7 @@ def get(self, id):
show_all_nav = user
access_rights = UserRights(user, event)
if access_rights.can_edit:
- logout_url = users.create_logout_url('/')
+ logout_url = users.create_logout_url('/')
rooms = ROOM_OPTIONS
hours = [1,2,3,4,5,6,7,8,9,10,11,12]
self.response.out.write(template.render('templates/edit.html', locals()))
@@ -265,7 +265,7 @@ def post(self, id):
class EventHandler(webapp.RequestHandler):
def get(self, id):
-
+
event = Event.get_by_id(int(id))
if self.request.path.endswith('json'):
self.response.headers['content-type'] = 'application/json'
@@ -275,7 +275,7 @@ def get(self, id):
if user:
access_rights = UserRights(user, event)
logout_url = users.create_logout_url('/')
-
+
else:
login_url = users.create_login_url('/')
event.details = db.Text(event.details.replace('\n','<br/>'))
@@ -294,6 +294,9 @@ def post(self, id):
if state.lower() == 'approve' and access_rights.can_approve:
event.approve()
desc = 'Approved event'
+ if state.lower() == 'notapproved' and access_rights.can_not_approve:
+ event.not_approved()
+ desc = 'Event marked not approved'
if state.lower() == 'rsvp' and user:
event.rsvp()
notify_owner_rsvp(event,user)
@@ -371,6 +374,19 @@ def get(self):
self.response.out.write(template.render('templates/past.html', locals()))
+class NotApprovedHandler(webapp.RequestHandler):
+ def get(self):
+ user = users.get_current_user()
+ if user:
+ logout_url = users.create_logout_url('/')
+ else:
+ login_url = users.create_login_url('/')
+ today = local_today()
+ show_all_nav = user
+ events = Event.get_recent_not_approved_list()
+ self.response.out.write(template.render('templates/not_approved.html', locals()))
+
+
class CronBugOwnersHandler(webapp.RequestHandler):
def get(self):
events = Event.get_pending_list()
@@ -431,7 +447,7 @@ def get(self):
rooms = ROOM_OPTIONS
rules = memcache.get("rules")
if(rules is None):
- rules = urlfetch.fetch("http://wiki.hackerdojo.com/api_v2/op/GetPage/page/Event+Policies/_type/html", "GET").content
+ rules = urlfetch.fetch("http://wiki.hackerdojo.com/api_v2/op/GetPage/page/Event+Policies/_type/html", "GET").content
memcache.add("rules", rules, 86400)
self.response.out.write(template.render('templates/new.html', locals()))
@@ -487,7 +503,7 @@ def post(self):
set_cookie(self.response.headers, 'formvalues', None)
#self.redirect('/event/%s-%s' % (event.key().id(), slugify(event.name)))
self.redirect('/confirm/%s-%s' % (event.key().id(), slugify(event.name)))
-
+
except Exception, e:
message = str(e)
if 'match format' in message:
@@ -506,7 +522,7 @@ def get(self, id):
event = Event.get_by_id(int(id))
rules = memcache.get("rules")
if(rules is None):
- rules = urlfetch.fetch("http://wiki.hackerdojo.com/api_v2/op/GetPage/page/Event+Policies/_type/html", "GET").content
+ rules = urlfetch.fetch("http://wiki.hackerdojo.com/api_v2/op/GetPage/page/Event+Policies/_type/html", "GET").content
memcache.add("rules", rules, 86400)
self.response.out.write(template.render('templates/confirmation.html', locals()))
@@ -561,6 +577,7 @@ def main():
('/past', PastHandler),
('/cronbugowners', CronBugOwnersHandler),
('/myevents', MyEventsHandler),
+ ('/not_approved', NotApprovedHandler),
('/new', NewHandler),
('/confirm/(\d+).*', ConfirmationHandler),
('/edit/(\d+).*', EditHandler),
@@ -573,7 +590,7 @@ def main():
# CRON tasks
('/expire', ExpireCron),
('/expiring', ExpireReminderCron),
- ('/domaincache', DomainCacheCron),
+ ('/domaincache', DomainCacheCron),
('/reminder', ReminderCron),
#
('/logs', LogsHandler),
View
31 models.py
@@ -19,7 +19,7 @@
class Event(db.Model):
status = db.StringProperty(required=True, default='pending', choices=set(
- ['pending', 'understaffed', 'approved', 'canceled', 'onhold', 'expired', 'deleted']))
+ ['pending', 'understaffed', 'approved', 'not_approved', 'canceled', 'onhold', 'expired', 'deleted']))
member = db.UserProperty(auto_current_user_add=True)
name = db.StringProperty(required=True)
start_time = db.DateTimeProperty(required=True)
@@ -66,7 +66,7 @@ def check_conflict(cls,proposed_start_time,proposed_end_time,proposed_rooms,opti
def get_all_future_list(cls):
return cls.all() \
.filter('start_time >', local_today()) \
- .filter('status IN', ['approved', 'canceled', 'pending', 'onhold']) \
+ .filter('status IN', ['approved', 'not_approved', 'canceled', 'pending', 'onhold']) \
.order('start_time')
@classmethod
@@ -77,7 +77,7 @@ def get_large_list(cls):
if int(e.estimated_size) >= 50:
large_list.append(e)
return large_list
-
+
@classmethod
def get_approved_list(cls):
return cls.all() \
@@ -99,9 +99,17 @@ def get_pending_list(cls):
.filter('status IN', ['pending', 'understaffed', 'onhold', 'expired']) \
.order('start_time')
+ @classmethod
+ # show last 60 days and all future not approved events
+ def get_recent_not_approved_list(cls):
+ return cls.all() \
+ .filter('start_time >', local_today() - timedelta(days=60)) \
+ .filter('status IN', ['not_approved']) \
+ .order('start_time')
+
def owner(self):
return human_username(self.member)
-
+
def stafflist(self):
return to_sentence_list(map(human_username, self.staff))
@@ -113,7 +121,7 @@ def roomlist_as_phrase(self):
return "in " + self.roomlist()
else:
return ""
-
+
def is_staffed(self):
return len(self.staff) >= self.staff_needed()
@@ -142,6 +150,9 @@ def is_deleted(self):
def is_past(self):
return self.end_time < local_today()
+ def is_not_approved(self):
+ return self.status == 'not_approved'
+
def start_date(self):
return self.start_time.date()
@@ -175,10 +186,10 @@ def has_rsvped(self):
def can_rsvp(self):
if self.has_rsvped():
return False
- time_till_event = self.start_time.replace(tzinfo=pytz.timezone('US/Pacific')) - datetime.now(pytz.timezone('US/Pacific'))
+ time_till_event = self.start_time.replace(tzinfo=pytz.timezone('US/Pacific')) - datetime.now(pytz.timezone('US/Pacific'))
hours = time_till_event.seconds/3600+time_till_event.days*24
return (hours > 48)
-
+
def cancel(self):
user = users.get_current_user()
self.status = 'canceled'
@@ -191,6 +202,12 @@ def on_hold(self):
self.put()
logging.info('%s put %s on hold' % (user.nickname, self.name))
+ def not_approved(self):
+ user = users.get_current_user()
+ self.status = 'not_approved'
+ self.put()
+ logging.info('%s not_approved %s' % (user.nickname, self.name))
+
def delete(self):
user = users.get_current_user()
self.status = 'deleted'
View
21 templates/event.html
@@ -9,10 +9,11 @@
{% if event.is_deleted %}
{% if access_rights.is_admin %}<input type="submit" name="state" value="Undelete" />{% endif %}
(This will return the event to the &ldquo;pending&rdquo; status.)
- {% else %}
+ {% else %}
<div id="edit-approve-btns">
{% if access_rights.can_cancel and not event.is_onhold %}<input type="submit" name="state" value="OnHold" />{% endif %}
{% if access_rights.can_approve %}<input type="submit" name="state" value="Approve" />{% endif %}
+ {% if access_rights.can_not_approve %}<input type="submit" name="state" value="NotApproved" />{% endif %}
{% if access_rights.can_edit %}<input type="button" value="Edit" onclick="document.location.href='/edit/{{event.key.id}}';" />{% endif %}
</div>
<div id="cancel-delete-btns">
@@ -32,7 +33,7 @@
<span style="color: red; font-weight: bold">This event is not yet visible.</span><br/>
This event will not appear on the public calendar until it has been approved. If this does not happen soon, contact <a href="email:events@hackerdojo.com">events@hackerdojo.com</a> for assistance.
</p>{% endif %}{% 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'>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>
@@ -41,7 +42,7 @@
<div class='b-block'><div class='b-label'>Estimated size:</div><div class='b-data'>{{event.estimated_size}}</div></div>
{% if event.contact_name %}
<div class='b-block'><div class='b-label'>Contact:</div><div class='b-data'>{{event.contact_name}}{% if user %}, {{event.contact_phone}}{% endif %}</div></div>
- {% endif %}
+ {% endif %}
<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>
@@ -64,28 +65,28 @@
{% endif %}
{% ifequal event.status 'approved' %}
<br/>
- <div class='thin-border'></div>
+ <div class='thin-border'></div>
<form method="post" style="display: inline;">
<h4 style="margin-top:0; margin-bottom:1em">Member RSVP</h4>
{% if user and event.can_rsvp %}
<input type="submit" name="state" value="RSVP" />
{% endif %}
-
+
</form>
-
+
<p>Hacker Dojo members may {% if not user %} <a href="{{login_url}}">login</a> to {% endif %} reserve space in the event room up to 48 hours before the event.</p>
<p>Member RSVP does not imply event registration if applicable.</p>
-
+
{% if user and event.rsvps.count %}
<hr size=1>
<p>The following members have RSVPed:</p>
<ol>
{% for rsvp in event.rsvps %}
<li>{{ rsvp.user }}</li>
- {% endfor %}
+ {% endfor %}
</ol>
- {% endif %}
-
+ {% endif %}
+
{% endifequal %}
</div>
View
20 templates/not_approved.html
@@ -0,0 +1,20 @@
+{% extends 'base.html' %}
+{% block content %}
+
+<div id="primary">
+ <h3>Not Approved Events</h3>
+ <a href="/" style="font-size: smaller; margin-top: -20px; display: block;">&larr; Upcoming Events</a>
+
+ {% regroup events by start_date as grouped_events %}
+ {% for events in grouped_events %}
+ <h4>{% ifequal events.grouper today.date %}<span style="text-decoration:underline;">Today</span> - {% endifequal %}{{events.grouper|date:"l, F j"}}</h4>
+ <table>
+ {% for event in events.list %}
+ {% include 'list_event.html' %}
+ {% endfor %}
+ </table>
+ {% endfor %}
+</div>
+
+
+{% endblock %}
View
9 utils.py
@@ -16,7 +16,7 @@ def to_sentence_list(lst):
count = len(lst)
if count == 0:
return ''
- elif count == 1:
+ elif count == 1:
return lst[0]
else:
if count > 2:
@@ -97,16 +97,17 @@ def __init__(self, user=None, event=None):
self.can_edit = False
self.can_staff = False
self.can_unstaff = False
-
+
if self.user:
self.is_admin = username(self.user) in dojo('/groups/events',force=False)
if self.event:
self.is_owner = (self.user == self.event.member)
- self.can_approve = ((self.event.status in ['pending'] or self.event.status in ['onhold']) and self.is_admin
+ self.can_approve = ((self.event.status in ['pending'] or self.event.status in ['onhold'] or self.event.status in ['not_approved'] ) and self.is_admin
and not self.is_owner)
+ self.can_not_approve = self.event.status not in ['not_approved'] and self.is_admin
self.can_cancel = self.is_admin or self.is_owner
self.can_edit = self.is_admin or self.is_owner
self.can_staff = (self.event.status in ['pending', 'understaffed', 'approved']
and self.user not in self.event.staff)
- self.can_unstaff = (self.event.status not in ['canceled', 'deleted']
+ self.can_unstaff = (self.event.status not in ['canceled', 'deleted']
and self.user in self.event.staff)
Something went wrong with that request. Please try again.