Skip to content

Commit 3650203

Browse files
Adds event owner role to the system
adds migration file Updates migration to add owner and update role Updates docs fix hound errors
1 parent 1a837b2 commit 3650203

27 files changed

+391
-192
lines changed

app/api/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
'/event-invoices/<int:event_invoice_id>/user', '/speakers/<int:speaker_id>/user',
9393
'/access-codes/<int:access_code_id>/marketer', '/email-notifications/<int:email_notification_id>/user',
9494
'/discount-codes/<int:discount_code_id>/marketer', '/sessions/<int:session_id>/creator',
95-
'/attendees/<int:attendee_id>/user', '/feedbacks/<int:feedback_id>/user',
95+
'/attendees/<int:attendee_id>/user', '/feedbacks/<int:feedback_id>/user', '/events/<int:event_id>/owner',
9696
'/alternate-emails/<int:user_email_id>/user', '/favourite-events/<int:user_favourite_event_id>/user')
9797
api.route(UserRelationship, 'user_notification', '/users/<int:id>/relationships/notifications')
9898
api.route(UserRelationship, 'user_feedback', '/users/<int:id>/relationships/feedbacks')
@@ -102,6 +102,7 @@
102102
api.route(UserRelationship, 'user_access_codes', '/users/<int:id>/relationships/access-codes')
103103
api.route(UserRelationship, 'user_discount_codes', '/users/<int:id>/relationships/discount-codes')
104104
api.route(UserRelationship, 'user_email_notifications', '/users/<int:id>/relationships/email-notifications')
105+
api.route(UserRelationship, 'user_owner_event', '/users/<int:id>/relationships/owner-events')
105106
api.route(UserRelationship, 'user_organizer_event', '/users/<int:id>/relationships/organizer-events')
106107
api.route(UserRelationship, 'user_coorganizer_event', '/users/<int:id>/relationships/coorganizer-events')
107108
api.route(UserRelationship, 'user_track_organizer_event', '/users/<int:id>/relationships/track-organizer-events')
@@ -315,6 +316,8 @@
315316
api.route(EventRelationship, 'event_stripe_authorization', '/events/<int:id>/relationships/stripe-authorization',
316317
'/events/<identifier>/relationships/stripe-authorization')
317318
# Events -> roles:
319+
api.route(EventRelationship, 'event_owner', '/events/<int:id>/relationships/owner',
320+
'/events/<identifier>/relationships/owner')
318321
api.route(EventRelationship, 'event_organizers', '/events/<int:id>/relationships/organizers',
319322
'/events/<identifier>/relationships/organizers')
320323
api.route(EventRelationship, 'event_coorganizers', '/events/<int:id>/relationships/coorganizers',

app/api/admin_sales/organizer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from marshmallow_jsonapi import fields
22
from marshmallow_jsonapi.flask import Schema
3+
from sqlalchemy import or_
34
from flask_rest_jsonapi import ResourceList
45

56
from app.api.helpers.utilities import dasherize
@@ -53,7 +54,7 @@ class AdminSalesByOrganizersList(ResourceList):
5354

5455
def query(self, _):
5556
query_ = self.session.query(User)
56-
query_ = query_.join(UsersEventsRoles).filter(Role.name == 'organizer')
57+
query_ = query_.join(UsersEventsRoles).filter(or_(Role.name == 'organizer', Role.name == 'owner'))
5758
query_ = query_.join(Event).outerjoin(Order).outerjoin(OrderTicket)
5859

5960
return query_

app/api/events.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
from app.models.ticket_holder import TicketHolder
4747
from app.models.track import Track
4848
from app.models.user_favourite_event import UserFavouriteEvent
49-
from app.models.user import User, ATTENDEE, ORGANIZER, COORGANIZER
49+
from app.models.user import User, ATTENDEE, OWNER, ORGANIZER, COORGANIZER
5050
from app.models.users_events_role import UsersEventsRoles
5151
from app.models.stripe_authorization import StripeAuthorization
5252

@@ -108,7 +108,7 @@ def query(self, view_kwargs):
108108
_jwt_required(current_app.config['JWT_DEFAULT_REALM'])
109109
query2 = self.session.query(Event)
110110
query2 = query2.join(Event.roles).filter_by(user_id=current_identity.id).join(UsersEventsRoles.role). \
111-
filter(or_(Role.name == COORGANIZER, Role.name == ORGANIZER))
111+
filter(or_(Role.name == COORGANIZER, Role.name == ORGANIZER, Role.name == OWNER))
112112
query_ = query_.union(query2)
113113

114114
if view_kwargs.get('user_id') and 'GET' in request.method:
@@ -154,19 +154,19 @@ def before_post(self, args, kwargs, data=None):
154154

155155
def after_create_object(self, event, data, view_kwargs):
156156
"""
157-
after create method to save roles for users and add the user as an accepted role(organizer)
157+
after create method to save roles for users and add the user as an accepted role(owner and organizer)
158158
:param event:
159159
:param data:
160160
:param view_kwargs:
161161
:return:
162162
"""
163-
role = Role.query.filter_by(name=ORGANIZER).first()
164163
user = User.query.filter_by(id=view_kwargs['user_id']).first()
164+
role = Role.query.filter_by(name=OWNER).first()
165165
uer = UsersEventsRoles(user, event, role)
166166
save_to_db(uer, 'Event Saved')
167167
role_invite = RoleInvite(user.email, role.title_name, event.id, role.id, datetime.now(pytz.utc),
168168
status='accepted')
169-
save_to_db(role_invite, 'Organiser Role Invite Added')
169+
save_to_db(role_invite, 'Owner Role Invite Added')
170170

171171
# create custom forms for compulsory fields of attendee form.
172172
create_custom_forms_for_attendees(event)

app/api/full_text_search/events.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ def search(self, args, es_client=client):
4444
search = search.query('fuzzy', location_name=args['location_name'])
4545
search = search.highlight('location_name')
4646

47-
if args.get('organizer-name'):
47+
if args.get('owner-name'):
4848
search = search.query(
49-
'fuzzy', organizer_name=args['organizer_name'])
50-
search = search.highlight('organizer_name')
49+
'fuzzy', owner_name=args['owner_name'])
50+
search = search.highlight('owner_name')
5151

52-
if args.get('organizer-description'):
52+
if args.get('owner-description'):
5353
search = search.query(
54-
'fuzzy', organizer_description=args['organizer_description'])
55-
search = search.highlight('organizer_description')
54+
'fuzzy', owner_description=args['owner_description'])
55+
search = search.highlight('owner_description')
5656

5757
return [to_dict(r) for r in search.execute()]
5858

app/api/helpers/export_helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
FIELD_ORDER = {
3030
'event': [
3131
'id', 'name', 'latitude', 'longitude', 'location_name', 'starts_at', 'ends_at',
32-
'timezone', 'description', 'original_image_url', 'logo_url', 'organizer_name',
33-
'organizer_description', 'external_event_url', 'ticket_url', 'privacy', 'event_type_id',
32+
'timezone', 'description', 'original_image_url', 'logo_url', 'owner_name',
33+
'owner_description', 'external_event_url', 'ticket_url', 'privacy', 'event_type_id',
3434
'event_topic_id', 'event_sub_topic_id', 'code_of_conduct'
3535
],
3636
'microlocations': ['id', 'name', 'floor'],

app/api/helpers/import_helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from app.models.speaker import Speaker
3030
from app.models.sponsor import Sponsor
3131
from app.models.track import Track
32-
from app.models.user import User, ORGANIZER
32+
from app.models.user import User, OWNER
3333

3434
IMPORT_SERIES = [
3535
('social_links', SocialLink),
@@ -356,7 +356,7 @@ def import_event_json(task_handle, zip_path, creator_id):
356356
data = _delete_fields(srv, data)
357357
new_event = Event(**data)
358358
save_to_db(new_event)
359-
role = Role.query.filter_by(name=ORGANIZER).first()
359+
role = Role.query.filter_by(name=OWNER).first()
360360
user = User.query.filter_by(id=creator_id).first()
361361
uer = UsersEventsRoles(user_id=user.id, event_id=new_event.id, role_id=role.id)
362362
save_to_db(uer, 'Event Saved')

app/api/helpers/notification.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,16 @@ def send_notif_ticket_cancel(order):
301301
.format(identifier=order.event.identifier))
302302
)
303303
)
304+
send_notification(
305+
user=order.event.owner,
306+
title=NOTIFS[TICKET_CANCELLED_ORGANIZER]['title'].format(
307+
invoice_id=order.invoice_number
308+
),
309+
message=NOTIFS[TICKET_CANCELLED_ORGANIZER]['message'].format(
310+
cancel_note=order.cancel_note,
311+
invoice_id=order.invoice_number
312+
)
313+
)
304314

305315

306316
def send_notification_with_action(user, action, **kwargs):

app/api/helpers/permission_manager.py

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,39 @@ def is_admin(view, view_args, view_kwargs, *args, **kwargs):
4040

4141

4242
@jwt_required
43-
def is_organizer(view, view_args, view_kwargs, *args, **kwargs):
43+
def is_owner(view, view_args, view_kwargs, *args, **kwargs):
4444
user = current_identity
4545

4646
if user.is_staff:
4747
return view(*view_args, **view_kwargs)
4848

49-
if not user.is_organizer(kwargs['event_id']):
50-
return ForbiddenError({'source': ''}, 'Organizer access is required').respond()
49+
if not user.is_owner(kwargs['event_id']):
50+
return ForbiddenError({'source': ''}, 'Owner access is required').respond()
5151

5252
return view(*view_args, **view_kwargs)
5353

5454

55+
@jwt_required
56+
def is_organizer(view, view_args, view_kwargs, *args, **kwargs):
57+
user = current_identity
58+
59+
if user.is_staff:
60+
return view(*view_args, **view_kwargs)
61+
62+
if user.is_owner(kwargs['event_id']) or user.is_organizer(kwargs['event_id']):
63+
return view(*view_args, **view_kwargs)
64+
65+
return ForbiddenError({'source': ''}, 'Organizer access is required').respond()
66+
67+
5568
@jwt_required
5669
def is_coorganizer(view, view_args, view_kwargs, *args, **kwargs):
5770
user = current_identity
5871

5972
if user.is_staff:
6073
return view(*view_args, **view_kwargs)
6174

62-
if user.is_organizer(kwargs['event_id']) or user.is_coorganizer(kwargs['event_id']):
75+
if user.has_event_access(kwargs['event_id']):
6376
return view(*view_args, **view_kwargs)
6477

6578
return ForbiddenError({'source': ''}, 'Co-organizer access is required.').respond()
@@ -69,7 +82,7 @@ def is_coorganizer(view, view_args, view_kwargs, *args, **kwargs):
6982
def is_coorganizer_but_not_admin(view, view_args, view_kwargs, *args, **kwargs):
7083
user = current_identity
7184

72-
if user.is_organizer(kwargs['event_id']) or user.is_coorganizer(kwargs['event_id']):
85+
if user.has_event_access(kwargs['event_id']):
7386
return view(*view_args, **view_kwargs)
7487

7588
return ForbiddenError({'source': ''}, 'Co-organizer access is required.').respond()
@@ -94,7 +107,7 @@ def is_coorganizer_endpoint_related_to_event(view, view_args, view_kwargs, *args
94107
_jwt_required(app.config['JWT_DEFAULT_REALM'])
95108
return view(*view_args, **view_kwargs)
96109

97-
if user.is_organizer(kwargs['event_id']) or user.is_coorganizer(kwargs['event_id']):
110+
if user.has_event_access(kwargs['event_id']):
98111
_jwt_required(app.config['JWT_DEFAULT_REALM'])
99112
return view(*view_args, **view_kwargs)
100113

@@ -127,7 +140,7 @@ def is_coorganizer_or_user_itself(view, view_args, view_kwargs, *args, **kwargs)
127140
if user.is_staff:
128141
return view(*view_args, **view_kwargs)
129142

130-
if user.is_organizer(kwargs['event_id']) or user.is_coorganizer(kwargs['event_id']):
143+
if user.has_event_access(kwargs['event_id']):
131144
return view(*view_args, **view_kwargs)
132145

133146
return ForbiddenError({'source': ''}, 'Co-organizer access is required.').respond()
@@ -151,7 +164,7 @@ def is_speaker_for_session(view, view_args, view_kwargs, *args, **kwargs):
151164
except NoResultFound:
152165
return NotFoundError({'parameter': 'id'}, 'Session not found.').respond()
153166

154-
if user.is_organizer(session.event_id) or user.is_coorganizer(session.event_id):
167+
if user.has_event_access(session.event_id):
155168
return view(*view_args, **view_kwargs)
156169

157170
if session.speakers:
@@ -176,7 +189,7 @@ def is_speaker_itself_or_admin(view, view_args, view_kwargs, *args, **kwargs):
176189
if user.is_admin or user.is_super_admin:
177190
return view(*view_args, **view_kwargs)
178191

179-
if user.is_organizer(kwargs['event_id']) or user.is_coorganizer(kwargs['event_id']):
192+
if user.has_event_access(kwargs['event_id']):
180193
return view(*view_args, **view_kwargs)
181194

182195
if ('model' in kwargs) and (kwargs['model'] == Speaker):
@@ -205,7 +218,7 @@ def is_session_self_submitted(view, view_args, view_kwargs, *args, **kwargs):
205218
except NoResultFound:
206219
return NotFoundError({'parameter': 'session_id'}, 'Session not found.').respond()
207220

208-
if user.is_organizer(session.event_id) or user.is_coorganizer(session.event_id):
221+
if user.has_event_access(session.event_id):
209222
return view(*view_args, **view_kwargs)
210223

211224
if session.creator_id == user.id:
@@ -224,7 +237,7 @@ def is_registrar(view, view_args, view_kwargs, *args, **kwargs):
224237

225238
if user.is_staff:
226239
return view(*view_args, **view_kwargs)
227-
if user.is_registrar(event_id) or user.is_organizer(event_id) or user.is_coorganizer(event_id):
240+
if user.is_registrar(event_id) or user.has_event_access(event_id):
228241
return view(*view_args, **view_kwargs)
229242
return ForbiddenError({'source': ''}, 'Registrar Access is Required.').respond()
230243

@@ -243,7 +256,7 @@ def is_registrar_or_user_itself(view, view_args, view_kwargs, *args, **kwargs):
243256
return view(*view_args, **view_kwargs)
244257

245258
event_id = kwargs['event_id']
246-
if user.is_registrar(event_id) or user.is_organizer(event_id) or user.is_coorganizer(event_id):
259+
if user.is_registrar(event_id) or user.has_event_access(event_id):
247260
return view(*view_args, **view_kwargs)
248261

249262
return ForbiddenError({'source': ''}, 'Registrar access is required.').respond()
@@ -259,7 +272,7 @@ def is_track_organizer(view, view_args, view_kwargs, *args, **kwargs):
259272

260273
if user.is_staff:
261274
return view(*view_args, **view_kwargs)
262-
if user.is_track_organizer(event_id) or user.is_organizer(event_id) or user.is_coorganizer(event_id):
275+
if user.is_track_organizer(event_id) or user.has_event_access(event_id):
263276
return view(*view_args, **view_kwargs)
264277
return ForbiddenError({'source': ''}, 'Track Organizer access is Required.').respond()
265278

@@ -273,7 +286,7 @@ def is_moderator(view, view_args, view_kwargs, *args, **kwargs):
273286
event_id = kwargs['event_id']
274287
if user.is_staff:
275288
return view(*view_args, **view_kwargs)
276-
if user.is_moderator(event_id) or user.is_organizer(event_id) or user.is_coorganizer(event_id):
289+
if user.is_moderator(event_id) or user.has_event_access(event_id):
277290
return view_kwargs(*view_args, **view_kwargs)
278291
return ForbiddenError({'source': ''}, 'Moderator Access is Required.').respond()
279292

@@ -315,6 +328,7 @@ def create_event(view, view_args, view_kwargs, *args, **kwargs):
315328
permissions = {
316329
'is_super_admin': is_super_admin,
317330
'is_admin': is_admin,
331+
'is_owner': is_owner,
318332
'is_organizer': is_organizer,
319333
'is_coorganizer': is_coorganizer,
320334
'is_track_organizer': is_track_organizer,

app/api/helpers/permissions.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,27 @@ def decorated_function(*args, **kwargs):
103103
return decorated_function
104104

105105

106+
@second_order_decorator(jwt_required)
107+
def is_owner(f):
108+
"""
109+
Allows only Owner to access the event resources.
110+
:param f:
111+
:return:
112+
"""
113+
114+
@wraps(f)
115+
def decorated_function(*args, **kwargs):
116+
user = current_identity
117+
118+
if user.is_staff:
119+
return f(*args, **kwargs)
120+
if 'event_id' in kwargs and user.is_owner(kwargs['event_id']):
121+
return f(*args, **kwargs)
122+
return ForbiddenError({'source': ''}, 'Owner access is required').respond()
123+
124+
return decorated_function
125+
126+
106127
@second_order_decorator(jwt_required)
107128
def is_organizer(f):
108129
"""
@@ -138,9 +159,7 @@ def decorated_function(*args, **kwargs):
138159

139160
if user.is_staff:
140161
return f(*args, **kwargs)
141-
if 'event_id' in kwargs and (
142-
user.is_coorganizer(kwargs['event_id']) or
143-
user.is_organizer(kwargs['event_id'])):
162+
if 'event_id' in kwargs and user.has_event_access(kwargs['event_id']):
144163
return f(*args, **kwargs)
145164
return ForbiddenError({'source': ''}, 'Co-organizer access is required.').respond()
146165

@@ -163,8 +182,7 @@ def decorated_function(*args, **kwargs):
163182
return f(*args, **kwargs)
164183
if 'event_id' in kwargs and (
165184
user.is_registrar(kwargs['event_id']) or
166-
user.is_organizer(kwargs['event_id']) or
167-
user.is_coorganizer(kwargs['event_id'])):
185+
user.has_event_access(kwargs['event_id'])):
168186
return f(*args, **kwargs)
169187
return ForbiddenError({'source': ''}, 'Registrar Access is Required.').respond()
170188

@@ -187,8 +205,7 @@ def decorated_function(*args, **kwargs):
187205
return f(*args, **kwargs)
188206
if 'event_id' in kwargs and (
189207
user.is_track_organizer(kwargs['event_id']) or
190-
user.is_organizer(kwargs['event_id']) or
191-
user.is_coorganizer(kwargs['event_id'])):
208+
user.has_event_access(kwargs['event_id'])):
192209
return f(*args, **kwargs)
193210
return ForbiddenError({'source': ''}, 'Track Organizer access is Required.').respond()
194211

@@ -211,8 +228,7 @@ def decorated_function(*args, **kwargs):
211228
return f(*args, **kwargs)
212229
if 'event_id' in kwargs and (
213230
user.is_moderator(kwargs['event_id']) or
214-
user.is_organizer(kwargs['event_id']) or
215-
user.is_coorganizer(kwargs['event_id'])):
231+
user.has_event_access(kwargs['event_id'])):
216232
return f(*args, **kwargs)
217233
return ForbiddenError({'source': ''}, 'Moderator Access is Required.').respond()
218234

app/api/helpers/scheduled_jobs.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def send_after_event_mail():
3535
for event in events:
3636
organizers = get_user_event_roles_by_role_name(event.id, 'organizer')
3737
speakers = Speaker.query.filter_by(event_id=event.id, deleted_at=None).all()
38+
owner = get_user_event_roles_by_role_name(event.id, 'owner').first()
3839
current_time = datetime.datetime.now(pytz.timezone(event.timezone))
3940
time_difference = current_time - event.ends_at
4041
time_difference_minutes = (time_difference.days * 24 * 60) + \
@@ -46,6 +47,9 @@ def send_after_event_mail():
4647
for organizer in organizers:
4748
send_email_after_event(organizer.user.email, event.name, upcoming_event_links)
4849
send_notif_after_event(organizer.user, event.name)
50+
if owner:
51+
send_email_after_event(owner.user.email, event.name, upcoming_event_links)
52+
send_notif_after_event(owner.user, event.name)
4953

5054

5155
def change_session_state_on_event_completion():
@@ -84,9 +88,9 @@ def send_event_fee_notification():
8488
fee_total += fee
8589

8690
if fee_total > 0:
87-
organizer = get_user_event_roles_by_role_name(event.id, 'organizer').first()
91+
owner = get_user_event_roles_by_role_name(event.id, 'owner').first()
8892
new_invoice = EventInvoice(
89-
amount=fee_total, event_id=event.id, user_id=organizer.user.id)
93+
amount=fee_total, event_id=event.id, user_id=owner.user.id)
9094

9195
if event.discount_code_id and event.discount_code:
9296
r = relativedelta(datetime.utcnow(), event.created_at)

0 commit comments

Comments
 (0)