Merge pull request #158 from alberanid/master

maximum number of available tickets
This commit is contained in:
Davide Alberani 2016-07-31 23:26:49 +02:00 committed by GitHub
commit 92e8aab55e
3 changed files with 58 additions and 28 deletions

View file

@ -25,15 +25,15 @@
</button>
</div>
<div class="input-group input-group-lg">
<div class="input-group input-group">
<span class="input-group-addon min100">{{'Title' | translate}}</span>
<input type="text" class="form-control" placeholder="{{'Title' | translate}}" ng-model="event.title" ng-required="true">
</div>
<div class="input-group input-group-lg top5">
<div class="input-group input-group top5">
<span class="input-group-addon min100">{{'Tagline' | translate}}</span>
<input type="text" class="form-control" placeholder="{{'Tagline' | translate}}" ng-model="event.tagline">
</div>
<div class="input-group input-group-lg top5">
<div class="input-group input-group top5">
<span class="input-group-addon min100">{{'Short summary' | translate}}</span>
<input type="text" class="form-control" placeholder="{{'Short summary' | translate}}" ng-model="event.summary">
</div>
@ -74,25 +74,35 @@
</div>
</div>
<div class="input-group input-group-lg top5">
<div class="input-group input-group top5">
<span class="input-group-addon min100">{{'Where' | translate}}</span>
<input type="text" class="form-control" placeholder="{{'Where' | translate}}" ng-model="event.where">
</div>
<div class="input-group input-group-lg top5">
<div class="input-group input-group top5">
<span class="input-group-addon min100">{{'Group ID' | translate}}</span>
<input type="text" class="form-control" placeholder="{{'Used to share persons amongst multiple events. Must be hard to guess (if empty, will be autogenerated)' | translate}}" ng-model="event.group_id">
</div>
<div class="panel panel-default table-striped top5">
<div class="panel-heading">
<h1>{{'Ticket limits'}}</h1>
</div>
<div class="panel-body">
<div class="input-group input-group top5">
<span class="input-group-addon min100">{{'Number of tickets' | translate}}</span>
<input type="number" min="0" class="form-control" placeholder="{{'Number of tickets (0 or empty means unlimited)' | translate}}" ng-model="event.number_of_tickets">
</div>
</div>
</div>
<input type="submit" class="outside-screen" />
<div ng-if="!eventFormDisabled" ng-class="{clearfix: true, alert: true, 'alert-success': !eventForm.$dirty, 'alert-danger': eventForm.$dirty}">
<button type="button" class="btn btn-default pull-right" ng-click="save($event)" ng-disabled="!eventForm.$dirty">
<span class="fa fa-floppy-o vcenter"></span>
{{'save' | translate}}
</button>
</div>
</fieldset>
<label></label>
<input type="submit" class="outside-screen" />
<div ng-if="!eventFormDisabled" ng-class="{clearfix: true, alert: true, 'alert-success': !eventForm.$dirty, 'alert-danger': eventForm.$dirty}">
<button type="button" class="btn btn-default pull-right" ng-click="save($event)" ng-disabled="!eventForm.$dirty">
<span class="fa fa-floppy-o vcenter"></span>
{{'save' | translate}}
</button>
</div>
</form>
</div>
</div>

View file

@ -32,7 +32,8 @@
<tr>
<th><strong>{{'Event' | translate}}</strong></th>
<th ng-if="hasPermission('event|update')" class="hcenter"><strong>{{'Attendees / Registered' | translate}}</strong></th>
<th class="hcenter"><strong>{{'Actions' | translate}}</strong></th>
<th class="hcenter"><strong>{{'Tickets' | translate}}</strong></th>
<th ng-if="hasPermission('event|update') || hasPermission('event|delete')" class="hcenter"><strong>{{'Actions' | translate}}</strong></th>
</tr>
</thead>
<tbody>
@ -53,11 +54,14 @@
</div>
</td>
<td ng-if="hasPermission('event:tickets-all|read')" class="hcenter">
<p><span ng-init="attendeesNr = ((event.tickets || []) | attendeesFilter).length">{{attendeesNr}}</span> / {{((event.tickets || []) | registeredFilter).length}} ({{((attendeesNr / ((event.tickets || []) | registeredFilter).length * 100) || 0).toFixed()}}%)</p>
<p><span ng-init="attendeesNr = ((event.tickets || []) | attendeesFilter).length">{{attendeesNr}}</span> / {{event.tickets_sold || 0}} ({{((attendeesNr / (event.tickets_sold || 0) * 100) || 0).toFixed()}}%)</p>
</td>
<td>
<div ng-if="hasPermission('event:tickets-all|create')" class="top5 hcenter"><button ng-click="$state.go('event.ticket.new', {id: event._id})" class="min150 btn btn-success" type="button" title="{{'Join this event' | translate}}"><span class="fa fa-user-plus vcenter"></span> {{'Join this event' | translate}}</button></div>
<div ng-if="hasPermission('ticket|update')" class="top5 hcenter"><button ng-click="$state.go('event.tickets', {id: event._id})" class="min150 btn btn-primary" type="button" title="{{'Manage tickets' | translate}}"><span class="fa fa-ticket"></span> {{'Manage tickets' | translate}}</button></div>
<div class="top5 hcenter" ng-if="event.number_of_tickets">{{event.number_of_tickets}} {{'tickets' | translate}}, {{event.number_of_tickets - (event.tickets_sold || 0)}} {{'still available' | translate}}</div>
</td>
<td ng-if="hasPermission('event|update') || hasPermission('event|delete')">
<div ng-if="hasPermission('event|update')" class="top5 hcenter"><button ng-click="$state.go('event.edit', {id: event._id})" type="button" class="min150 btn btn-warning" title="{{'Edit event' | translate}}"><span class="fa fa-cog"></span> {{'Edit event' | translate}}</button></div>
<div ng-if="hasPermission('event|delete')" class="top5 hcenter bottom5"><button ng-click="deleteEvent(event._id)" type="button" class="min150 btn btn-danger" title="{{'Delete event' | translate}}"><span class="fa fa-trash"></span> {{'Delete event' | translate}}</button></div>
</td>

View file

@ -522,8 +522,9 @@ class CollectionHandler(BaseHandler):
:type env: dict
"""
self.ioloop = tornado.ioloop.IOLoop.instance()
processed_env = self._dict2env(env)
p = process.Subprocess(cmd, close_fds=True, stdin=process.Subprocess.STREAM,
stdout=process.Subprocess.STREAM, stderr=process.Subprocess.STREAM, env=env)
stdout=process.Subprocess.STREAM, stderr=process.Subprocess.STREAM, env=processed_env)
p.set_exit_callback(lambda returncode: self.on_exit(returncode, cmd, p))
self.timeout = self.ioloop.add_timeout(datetime.timedelta(seconds=PROCESS_TIMEOUT),
lambda: self.on_timeout(cmd, p))
@ -589,15 +590,17 @@ class EventsHandler(CollectionHandler):
collection = 'events'
def filter_get(self, output):
if not self.has_permission('tickets-all|read'):
if 'tickets' in output:
if 'tickets' in output:
output['tickets_sold'] = len([t for t in output['tickets'] if not t.get('cancelled')])
if not self.has_permission('tickets-all|read'):
output['tickets'] = []
return output
def filter_get_all(self, output):
if not self.has_permission('tickets-all|read'):
for event in output.get('events') or []:
if 'tickets' in event:
for event in output.get('events') or []:
if 'tickets' in event:
event['tickets_sold'] = len([t for t in event['tickets'] if not t.get('cancelled')])
if not self.has_permission('tickets-all|read'):
event['tickets'] = []
return output
@ -659,6 +662,12 @@ class EventsHandler(CollectionHandler):
return {'tickets': tickets}
def handle_post_tickets(self, id_, resource_id, data):
event = self.db.query('events', {'_id': id_})[0]
if 'number_of_tickets' in event:
tickets = event.get('tickets') or []
tickets = [t for t in tickets if not t.get('cancelled')]
if len(tickets) >= event['number_of_tickets']:
raise InputException('no more tickets available')
uuid, arguments = self.uuid_arguments
self._clean_dict(data)
data['seq'] = self.get_next_seq('event_%s_tickets' % id_)
@ -673,7 +682,7 @@ class EventsHandler(CollectionHandler):
if doc:
self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(ret))
ticket = self._get_ticket_data(ticket_id, doc.get('tickets') or [])
env = self._dict2env(ticket)
env = dict(ticket)
env.update({'PERSON_ID': ticket_id, 'TICKED_ID': ticket_id, 'EVENT_ID': id_,
'EVENT_TITLE': doc.get('title', ''), 'WEB_USER': self.current_user,
'WEB_REMOTE_IP': self.request.remote_ip})
@ -701,13 +710,20 @@ class EventsHandler(CollectionHandler):
current_event = current_event[0]
else:
current_event = {}
old_ticket_data = self._get_ticket_data(ticket_query,
current_event.get('tickets') or [])
tickets = current_event.get('tickets') or []
old_ticket_data = self._get_ticket_data(ticket_query, tickets)
# We updating the "cancelled" status of a ticket; check if we still have a ticket available
if 'number_of_tickets' in current_event and old_ticket_data.get('cancelled') and not data.get('cancelled'):
active_tickets = [t for t in tickets if not t.get('cancelled')]
if len(active_tickets) >= current_event['number_of_tickets']:
raise InputException('no more tickets available')
merged, doc = self.db.update('events', query,
data, updateList='tickets', create=False)
new_ticket_data = self._get_ticket_data(ticket_query,
doc.get('tickets') or [])
env = self._dict2env(new_ticket_data)
env = dict(new_ticket_data)
# always takes the ticket_id from the new ticket
ticket_id = str(new_ticket_data.get('_id'))
env.update({'PERSON_ID': ticket_id, 'TICKED_ID': ticket_id, 'EVENT_ID': id_,
@ -742,7 +758,7 @@ class EventsHandler(CollectionHandler):
operation='delete',
create=False)
self.send_ws_message('event/%s/tickets/updates' % id_, json.dumps(ret))
env = self._dict2env(ticket)
env = dict(ticket)
env.update({'PERSON_ID': ticket_id, 'TICKED_ID': ticket_id, 'EVENT_ID': id_,
'EVENT_TITLE': rdoc.get('title', ''), 'WEB_USER': self.current_user,
'WEB_REMOTE_IP': self.request.remote_ip})