Repository: fossasia/open-event-server

Issue:
Please add possibility to get events from locations and filter it by phase
#846

location='Berlin'&phase='Python'

TODO
Filtering
- [x] Location
- [x] Phase
- [x] state
- [x] start_time
- [x] end_time
- [x] privacy
- [x] type
- [x] topic

above example should returns all events from Berlin which have Python word in title or description



Discussion:
Filter by location has been added. See https://github.com/fossasia/open-event-orga-server/pull/964
I will add filter by phrase. 
You can see and play with available options here - http://open-event.herokuapp.com/api/v2/#!/events/get_event_list

@aviaryan how can i filter only events which are published?

No, not now but I will add filtering for `state` now that you mention it. 

@aviaryan I've added above todo list what i need


Resolving patch:
diff --git a/open_event/api/events.py b/open_event/api/events.py
--- a/open_event/api/events.py
+++ b/open_event/api/events.py
@@ -104,7 +104,22 @@ def update(self, event_id, data):
 
 EVENT_PARAMS = {
     'location_name': {
-        'description': 'Filter by location_name',
+        'type': str
+    },
+    'contains': {
+        'description': 'Contains the string in name and description',
+        'type': str
+    },
+    'state': {
+        'type': str
+    },
+    'privacy': {
+        'type': str
+    },
+    'type': {
+        'type': str
+    },
+    'topic': {
         'type': str
     }
 }
@@ -118,6 +133,11 @@ class EventResource():
     """
     event_parser = reqparse.RequestParser()
     event_parser.add_argument('location_name', type=str)
+    event_parser.add_argument('contains', type=str, dest='__event_contains')
+    event_parser.add_argument('state', type=str)
+    event_parser.add_argument('privacy', type=str)
+    event_parser.add_argument('type', type=str)
+    event_parser.add_argument('topic', type=str)
 
 
 @api.route('/<int:event_id>')
diff --git a/open_event/api/helpers/helpers.py b/open_event/api/helpers/helpers.py
--- a/open_event/api/helpers/helpers.py
+++ b/open_event/api/helpers/helpers.py
@@ -1,4 +1,5 @@
 from datetime import datetime
+from sqlalchemy import func
 from functools import wraps, update_wrapper
 from flask import request, g, make_response
 from flask.ext.restplus import fields
@@ -14,6 +15,8 @@
 from open_event.models.user import User as UserModel
 from open_event.helpers.data import save_to_db, update_version, delete_from_db
 
+from query_filters import extract_special_queries, apply_special_queries
+
 
 def _get_queryset(klass):
     """Returns the queryset for `klass` model"""
@@ -33,8 +36,18 @@ def get_object_list(klass, **kwargs):
     `klass` can be a model such as a Track, Event, Session, etc.
     """
     queryset = _get_queryset(klass)
-    obj_list = list(queryset.filter_by(**kwargs))
-    return obj_list
+    kwargs, specials = extract_special_queries(kwargs)
+    # case insenstive filter
+    for i in kwargs:
+        if type(kwargs[i]) == str:
+            queryset = queryset.filter(
+                func.lower(getattr(klass, i)) == kwargs[i].lower())
+        else:
+            queryset = queryset.filter(getattr(klass, i) == kwargs[i])
+    # special filters
+    obj_list = apply_special_queries(queryset, specials)
+    # return as list
+    return list(obj_list)
 
 
 def get_list_or_404(klass, **kwargs):
diff --git a/open_event/api/helpers/query_filters.py b/open_event/api/helpers/query_filters.py
new file mode 100644
--- /dev/null
+++ b/open_event/api/helpers/query_filters.py
@@ -0,0 +1,50 @@
+from sqlalchemy import or_, func
+
+from open_event.models.event import Event
+
+
+def extract_special_queries(queries):
+    """
+    Separate special queries from normal queries
+    """
+    specials = {}
+    dc = queries.copy()
+    for i in queries:
+        if i.startswith('__') and i in FILTERS_LIST:
+            specials[i] = queries[i]
+            del dc[i]
+    return (dc, specials)
+
+
+def apply_special_queries(query, specials):
+    """
+    Apply all special queries on the current
+    existing :query (set)
+    """
+    for i in specials:
+        query = FILTERS_LIST[i](specials[i], query)
+    return query
+
+
+#######
+# DEFINE CUSTOM FILTERS BELOW
+#######
+
+
+def event_contains(value, query):
+    value = value.lower()
+    q = query.filter(or_(
+        func.lower(Event.name).contains(value),
+        func.lower(Event.description).contains(value)
+    ))
+    return q
+
+
+#######
+# ADD CUSTOM FILTERS TO LIST
+#######
+
+
+FILTERS_LIST = {
+    '__event_contains': event_contains
+}


Test patch:
diff --git a/tests/api/test_get_list_queries.py b/tests/api/test_get_list_queries.py
new file mode 100644
--- /dev/null
+++ b/tests/api/test_get_list_queries.py
@@ -0,0 +1,50 @@
+import unittest
+import json
+
+from tests.auth_helper import register
+from tests.setup_database import Setup
+from tests.utils import OpenEventTestCase
+from tests.api.utils import get_path, create_event
+from utils_post_data import *
+
+from open_event import current_app as app
+
+
+class TestGetListQueries(OpenEventTestCase):
+    """
+    Test Get List Queries
+    """
+    def setUp(self):
+        self.app = Setup.create_app()
+        with app.test_request_context():
+            register(self.app, u'test@example.com', u'test')
+            create_event(creator_email='test@example.com')
+
+    def _post(self, path, data):
+        """
+        send a post request to a url
+        """
+        return self.app.post(
+            path,
+            data=json.dumps(data),
+            headers={'content-type': 'application/json'}
+        )
+
+    def test_event_queries(self):
+        path = get_path()
+        resp = self._post(path, POST_EVENT_DATA)
+        self.assertEqual(resp.status_code, 201)
+        # check no return
+        resp = self.app.get(path + '?state=r@nd0m')
+        self.assertEqual(len(resp.data), 3, msg=resp.data)
+        # check case-insensitive search
+        resp = self.app.get(path + '?contains=test')
+        self.assertEqual(resp.status_code, 200)
+        self.assertEqual(len(json.loads(resp.data)), 2)
+        # check queryset forwarding
+        resp = self.app.get(path + '?contains=test&state=r@nd0m')
+        self.assertEqual(len(resp.data), 3)
+
+
+if __name__ == '__main__':
+    unittest.main()