Repository: fossasia/open-event-server

Issue:
Implement call-for-speaker api with proper permissions and relationships 



Discussion:
Please note that this api should exist only as a relationship to an event. 

Resolving patch:
diff --git a/app/api/__init__.py b/app/api/__init__.py
--- a/app/api/__init__.py
+++ b/app/api/__init__.py
@@ -9,6 +9,7 @@
 from app.api.social_links import SocialLinkList, SocialLinkDetail, SocialLinkRelationship
 from app.api.sponsors import SponsorList, SponsorDetail, SponsorRelationship
 from app.api.tracks import TrackList, TrackDetail, TrackRelationship
+from app.api.call_for_papers import CallForPaperList, CallForPaperDetail, CallForPaperRelationship
 
 
 api_v1 = Blueprint('v1', __name__, url_prefix='/v1')
@@ -27,12 +28,13 @@
 api.route(EventList, 'event_list', '/events')
 api.route(EventDetail, 'event_detail', '/events/<int:id>', '/tickets/<int:ticket_id>/event',
           '/microlocations/<int:microlocation_id>/event', '/social_links/<int:social_link_id>/event',
-          '/sponsors/<int:sponsor_id>/event', '/tracks/<int:track_id>/event')
+          '/sponsors/<int:sponsor_id>/event', '/tracks/<int:track_id>/event', '/call_for_papers/<int:call_for_paper_id>/event')
 api.route(EventRelationship, 'event_ticket', '/events/<int:id>/relationships/ticket')
 api.route(EventRelationship, 'event_microlocation', '/events/<int:id>/relationships/microlocation')
 api.route(EventRelationship, 'event_social_link', '/events/<int:id>/relationships/social_link')
 api.route(EventRelationship, 'event_sponsor', '/events/<int:id>/relationships/sponsor')
 api.route(EventRelationship, 'event_tracks', '/events/<int:id>/relationships/tracks')
+api.route(EventRelationship, 'event_call_for_paper', '/events/<int:id>/relationships/call_for_paper')
 
 # microlocations
 api.route(MicrolocationList, 'microlocation_list', '/microlocations',
@@ -69,3 +71,8 @@
 api.route(TrackDetail, 'track_detail', '/tracks/<int:id>', '/sessions/<int:session_id>/track')
 api.route(TrackRelationship, 'track_sessions', '/tracks/<int:id>/relationships/sessions')
 api.route(TrackRelationship, 'track_event', '/tracks/<int:id>/relationships/event')
+
+# call_for_papers
+api.route(CallForPaperList, 'call_for_paper_list', '/events/<int:event_id>/call_for_papers')
+api.route(CallForPaperDetail, 'call_for_paper_detail', '/call_for_papers/<int:id>', '/events/<int:event_id>/call_for_papers')
+api.route(CallForPaperRelationship, 'call_for_paper_event', '/call_for_papers/<int:id>/relationships/event')
diff --git a/app/api/call_for_papers.py b/app/api/call_for_papers.py
new file mode 100644
--- /dev/null
+++ b/app/api/call_for_papers.py
@@ -0,0 +1,110 @@
+from flask_rest_jsonapi import ResourceDetail, ResourceList, ResourceRelationship
+from marshmallow_jsonapi.flask import Schema, Relationship
+from marshmallow_jsonapi import fields
+from app.models import db
+from app.api.helpers.permissions import jwt_required
+from sqlalchemy.orm.exc import NoResultFound
+from flask_rest_jsonapi.exceptions import ObjectNotFound
+from app.models.event import Event
+from app.models.call_for_paper import CallForPaper
+import pytz
+from datetime import datetime
+import dateutil.parser
+
+
+class CallForPaperSchema(Schema):
+    """
+    Api Schema for cfp model
+    """
+    class Meta:
+        """
+        Meta class for cfp Api Schema
+        """
+        type_ = 'call_for_paper'
+        self_view = 'v1.call_for_paper_detail'
+        self_view_kwargs = {'id': '<id>'}
+
+    id = fields.Str(dump_only=True)
+    announcement = fields.Str(required=True)
+    start_date = fields.DateTime(required=True)
+    end_date = fields.DateTime(required=True)
+    timezone = fields.Str(default="UTC")
+    hash = fields.Str()
+    privacy = fields.String()
+    event = Relationship(attribute='event',
+                         self_view='v1.call_for_paper_event',
+                         self_view_kwargs={'id': '<id>'},
+                         related_view='v1.event_detail',
+                         related_view_kwargs={'call_for_paper_id': '<id>'},
+                         schema='EventSchema',
+                         type_='event')
+
+
+class CallForPaperList(ResourceList):
+    """
+    List and create cfp
+    """
+
+    def query(self, view_kwargs):
+        query_ = self.session.query(CallForPaper)
+        if view_kwargs.get('event_id') is not None:
+            query_ = query_.join(Event).filter(Event.id == view_kwargs['event_id'])
+        return query_
+
+    def before_create_object(self, data, view_kwargs):
+        if view_kwargs.get('event_id') is not None:
+            event = self.session.query(Event).filter_by(id=view_kwargs['event_id']).one()
+            data['event_id'] = event.id
+
+    def after_get(self, result):
+        tz = pytz.timezone(result['data'][0]['attributes']['timezone'])
+        start_date_str = result['data'][0]['attributes']['start_date']
+        start_date_datetime = dateutil.parser.parse(start_date_str)
+        start_date_datetime = tz.localize(start_date_datetime.replace(tzinfo=None))
+        result['data'][0]['attributes']['start_date'] = start_date_datetime.isoformat()
+        end_date_str = result['data'][0]['attributes']['end_date']
+        end_date_datetime = dateutil.parser.parse(end_date_str)
+        end_date_datetime = tz.localize(end_date_datetime.replace(tzinfo=None))
+        result['data'][0]['attributes']['end_date'] = end_date_datetime.isoformat()
+
+    view_kwargs = True
+    decorators = (jwt_required, )
+    schema = CallForPaperSchema
+    data_layer = {'session': db.session,
+                  'model': CallForPaper,
+                  'methods': {
+                      'query': query,
+                      'before_create_object': before_create_object
+                  }}
+
+
+class CallForPaperDetail(ResourceDetail):
+    """
+    cfs detail by id
+    """
+
+    def after_get(self, result):
+        tz = pytz.timezone(result['data']['attributes']['timezone'])
+        start_date_str = result['data']['attributes']['start_date']
+        start_date_datetime = dateutil.parser.parse(start_date_str)
+        start_date_datetime = tz.localize(start_date_datetime.replace(tzinfo=None))
+        result['data']['attributes']['start_date'] = start_date_datetime.isoformat()
+        end_date_str = result['data']['attributes']['end_date']
+        end_date_datetime = dateutil.parser.parse(end_date_str)
+        end_date_datetime = tz.localize(end_date_datetime.replace(tzinfo=None))
+        result['data']['attributes']['end_date'] = end_date_datetime.isoformat()
+
+    decorators = (jwt_required,)
+    schema = CallForPaperSchema
+    data_layer = {'session': db.session,
+                  'model': CallForPaper}
+
+
+class CallForPaperRelationship(ResourceRelationship):
+    """
+    cfs Relationship
+    """
+    decorators = (jwt_required,)
+    schema = CallForPaperSchema
+    data_layer = {'session': db.session,
+                  'model': CallForPaper}
diff --git a/app/api/events.py b/app/api/events.py
--- a/app/api/events.py
+++ b/app/api/events.py
@@ -54,7 +54,6 @@ class Meta:
                           schema='TrackSchema',
                           many=True,
                           type_='track')
-
     sponsor = Relationship(attribute='sponsor',
                            self_view='v1.event_sponsor',
                            self_view_kwargs={'id': '<id>'},
@@ -63,6 +62,13 @@ class Meta:
                            schema='SponsorSchema',
                            many=True,
                            type_='sponsor')
+    call_for_speaker = Relationship(attribute='call_for_paper',
+                                    self_view='v1.event_call_for_paper',
+                                    self_view_kwargs={'id': '<id>'},
+                                    related_view='v1.call_for_paper_detail',
+                                    related_view_kwargs={'event_id': '<id>'},
+                                    schema='CallForPaperSchema',
+                                    type_='sponsor')
 
 
 class EventList(ResourceList):


Test patch: