Repository: fossasia/open-event-server

Issue:
Add api endpoint to get dates for the event.
@shivamMg @niranjan94 We need an endpoint to get all the dates of an event. Processing it while downloading sessions takes a massive hit on the performance of the app. We should do it on the server itself. 
When showing the schedule in the app, we need sessions sorted by dates, so I first tried processing dates and getting sessions by these dates, app was freezing due to the db operations. Then I tried with this dates endpoint, it seems to work.

But the best way would be to have an endpoint that gives sessions according to date as well or else something like https://github.com/fossasia/open-event/blob/master/testapi/event/1/eventDates should be okay as well.

Let me know what do you think @mariobehling @shivamMg @niranjan94 



Discussion:
> When showing the schedule in the app, we need sessions sorted by dates, ...........
> But the best way would be to have an endpoint that gives sessions according to date as well 

Do you mean that you need an endpoint that returns sessions sorted by their datetime values (like in ascending order or descending order). If that's the case, we can just add some query params to the sessions GET API to do so. Example - 

```
/api/v2/events/1/sessions?order_by=start_time&sort_direction=desc
```

So I don't think this will work. What can work is if you add an endpoint which shows all the dates of an event like done [here](https://github.com/fossasia/open-event/blob/master/testapi/event/1/eventDates) and add query parameters to query sessions by date.

> What can work is if you add an endpoint which shows all the dates of an event like done here 

Won't the dates of an event be equal to all dates between "event start time" to "event end time" ? Or am I missing something ?

> add query parameters to query sessions by date.

Yes we can add query params like start_time_gt , start_time_lt , end_time_gt , end_time_lt for that. The same has been done in Event GET API. That will suffice your need. 

> Won't the dates of an event be equal to all dates between "event start time" to "event end time" ?Or am I missing something ?

Didn't think about this. This should work.

> Yes we can add query params like start_time_gt , start_time_lt , end_time_gt , end_time_lt for that.The same has been done in Event GET API. That will suffice your need.

Awesome 👍 

@niranjan94 Is this implemented now?

@mananwason Sorry ... that was accidental ... On mobile :sweat_smile: ... 

Np 😄 

I am implementing it.


Resolving patch:
diff --git a/app/api/helpers/query_filters.py b/app/api/helpers/query_filters.py
--- a/app/api/helpers/query_filters.py
+++ b/app/api/helpers/query_filters.py
@@ -4,6 +4,7 @@
 
 from app.helpers.helpers import get_date_range
 from app.models.event import Event
+from app.models.session import Session
 from custom_fields import DateTime
 
 
@@ -116,6 +117,22 @@ def event_time_period(value, query):
     return query
 
 
+def sessions_start_time_gt(value, query):
+    return query.filter(Session.start_time >= DateTime().from_str(value))
+
+
+def sessions_start_time_lt(value, query):
+    return query.filter(Session.start_time <= DateTime().from_str(value))
+
+
+def sessions_end_time_gt(value, query):
+    return query.filter(Session.end_time >= DateTime().from_str(value))
+
+
+def sessions_end_time_lt(value, query):
+    return query.filter(Session.end_time <= DateTime().from_str(value))
+
+
 #######
 # ADD CUSTOM FILTERS TO LIST
 #######
@@ -130,4 +147,9 @@ def event_time_period(value, query):
     '__event_end_time_gt': event_end_time_gt,
     '__event_end_time_lt': event_end_time_lt,
     '__event_time_period': event_time_period,
+    # sessions
+    '__sessions_start_time_gt': sessions_start_time_gt,
+    '__sessions_start_time_lt': sessions_start_time_lt,
+    '__sessions_end_time_gt': sessions_end_time_gt,
+    '__sessions_end_time_lt': sessions_end_time_lt,
 }
diff --git a/app/api/sessions.py b/app/api/sessions.py
--- a/app/api/sessions.py
+++ b/app/api/sessions.py
@@ -1,4 +1,4 @@
-from flask.ext.restplus import Namespace
+from flask.ext.restplus import Namespace, reqparse
 from sqlalchemy.orm.collections import InstrumentedList
 
 from app.helpers.notification_email_triggers import trigger_new_session_notifications, \
@@ -13,7 +13,7 @@
 from app.helpers.data_getter import DataGetter
 
 from .helpers.helpers import save_db_model, get_object_in_event, \
-    model_custom_form, requires_auth
+    model_custom_form, requires_auth, parse_args
 from .helpers.helpers import (
     can_create,
     can_update,
@@ -193,8 +193,30 @@ def validate(self, data, event_id, check_required=True):
 DAO = SessionDAO(SessionModel, SESSION_POST)
 TypeDAO = SessionTypeDAO(SessionTypeModel, SESSION_TYPE_POST)
 
+# Define Params
+
+SESSIONS_PARAMS = {
+    'start_time_gt': {},
+    'start_time_lt': {},
+    'end_time_gt': {},
+    'end_time_lt': {},
+}
+
+# #########
+# Resources
+# #########
+
+
+class SessionResource():
+    """
+    Session Resource Base class
+    """
+    session_parser = reqparse.RequestParser()
+    session_parser.add_argument('start_time_gt', dest='__sessions_start_time_gt')
+    session_parser.add_argument('start_time_lt', dest='__sessions_start_time_lt')
+    session_parser.add_argument('end_time_gt', dest='__sessions_end_time_gt')
+    session_parser.add_argument('end_time_lt', dest='__sessions_end_time_lt')
 
-# Create resources
 
 @api.route('/events/<int:event_id>/sessions/<int:session_id>')
 @api.doc(responses=SERVICE_RESPONSES)
@@ -225,13 +247,13 @@ def put(self, event_id, session_id):
 
 
 @api.route('/events/<int:event_id>/sessions')
-class SessionList(Resource):
-    @api.doc('list_sessions')
+class SessionList(Resource, SessionResource):
+    @api.doc('list_sessions', params=SESSIONS_PARAMS)
     @api.header(*ETAG_HEADER_DEFN)
     @api.marshal_list_with(SESSION)
     def get(self, event_id):
         """List all sessions"""
-        return DAO.list(event_id)
+        return DAO.list(event_id, **parse_args(self.session_parser))
 
     @requires_auth
     @can_create(DAO)
@@ -250,14 +272,17 @@ def post(self, event_id):
 
 
 @api.route('/events/<int:event_id>/sessions/page')
-class SessionListPaginated(Resource, PaginatedResourceBase):
+class SessionListPaginated(Resource, PaginatedResourceBase, SessionResource):
     @api.doc('list_sessions_paginated', params=PAGE_PARAMS)
+    @api.doc(params=SESSIONS_PARAMS)
     @api.header(*ETAG_HEADER_DEFN)
     @api.marshal_with(SESSION_PAGINATED)
     def get(self, event_id):
         """List sessions in a paginated manner"""
         args = self.parser.parse_args()
-        return DAO.paginated_list(args=args, event_id=event_id)
+        return DAO.paginated_list(
+            args=args, event_id=event_id, **parse_args(self.session_parser)
+        )
 
 
 # Use Session DAO to check for permission


Test patch:
diff --git a/tests/api/test_get_list_queries.py b/tests/api/test_get_list_queries.py
--- a/tests/api/test_get_list_queries.py
+++ b/tests/api/test_get_list_queries.py
@@ -87,6 +87,26 @@ def test_event_location_queries(self):
             self.assertIn('SomeBuilding', resp.data)
             self.assertIn('Berlin', resp.data)
 
+    def test_session_time_queries(self):
+        with app.test_request_context():
+            path = get_path(1, 'sessions')
+            resp = self._post(path, POST_SESSION_DATA)
+            # 2016-05-30 8:47 to 9:47
+            # normal
+            resp = self.app.get(path)
+            self.assertIn('TestSession', resp.data)
+            # check results when query exists
+            for _ in ['start_time_lt', 'end_time_lt']:
+                resp = self.app.get(path + '?%s=2014-05-12T00:00:00' % _)
+                self.assertNotIn('TestSession', resp.data)
+                resp = self.app.get(path + '?%s=2017-05-12T00:00:00' % _)
+                self.assertIn('TestSession', resp.data)
+            for _ in ['start_time_gt', 'end_time_gt']:
+                resp = self.app.get(path + '?%s=2018-05-12T00:00:00' % _)
+                self.assertNotIn('TestSession', resp.data)
+                resp = self.app.get(path + '?%s=2015-05-12T00:00:00' % _)
+                self.assertIn('TestSession', resp.data)
+
 
 if __name__ == '__main__':
     unittest.main()