Repository: fossasia/open-event-server

Issue:
Allow to search attendees via email or name
https://test-api.eventyay.com/#attendees-list-attendees-under-an-event-get


Discussion:

Resolving patch:
diff --git a/app/api/custom/events.py b/app/api/custom/events.py
--- a/app/api/custom/events.py
+++ b/app/api/custom/events.py
@@ -15,6 +15,7 @@
 from app.models.exhibitor import Exhibitor
 from app.models.session import Session
 from app.models.speaker import Speaker
+from app.models.ticket_holder import TicketHolder
 
 events_routes = Blueprint('events_routes', __name__, url_prefix='/v1/events')
 
@@ -193,3 +194,23 @@ def delete_unused_discount_codes(event_id):
     db.session.commit()
 
     return jsonify({'success': True, 'deletes': result})
+
+
+@events_routes.route('/<string:event_identifier>/attendees/search', methods=['GET'])
+@to_event_id
+@jwt_required
+def search_attendees(event_id):
+    """Search attendees by name or email."""
+    query = TicketHolder.query.filter(TicketHolder.event_id == event_id)
+    args = request.args
+    if args.get('name'):
+        query = query.filter(
+            (TicketHolder.firstname.ilike('%' + args.get('name') + '%'))
+            | (TicketHolder.lastname.ilike('%' + args.get('name') + '%'))
+        )
+    if args.get('email'):
+        query = query.filter(TicketHolder.email.ilike('%' + args.get('email') + '%'))
+
+    attendees = query.order_by(TicketHolder.id.desc()).all()
+
+    return jsonify({'attendees': attendees})


Test patch:
diff --git a/tests/all/integration/api/event/test_event_attendee_search.py b/tests/all/integration/api/event/test_event_attendee_search.py
new file mode 100644
--- /dev/null
+++ b/tests/all/integration/api/event/test_event_attendee_search.py
@@ -0,0 +1,27 @@
+from tests.factories.attendee import AttendeeFactoryBase
+from tests.factories.event import EventFactoryBasic
+from tests.factories.order import OrderFactory
+from tests.factories.ticket import TicketFactory
+
+
+def test_event_search_attendee(db, jwt, client):
+    """Method to test the count query of sold tickets"""
+    event = EventFactoryBasic()
+    ticket = TicketFactory()
+    completed_order = OrderFactory(status='completed')
+
+    db.session.commit()
+
+    # will be counted as attendee have valid orders
+    AttendeeFactoryBase.create_batch(
+        1, order_id=completed_order.id, ticket_id=ticket.id, event_id=event.id
+    )
+
+    response = client.get(
+        f'/v1/events/{event.id}/attendees/search',
+        content_type='application/vnd.api+json',
+        headers=jwt,
+    )
+
+    assert response.status_code == 200
+    assert len(response.json['attendees']) == 1
diff --git a/tests/hook_main.py b/tests/hook_main.py
--- a/tests/hook_main.py
+++ b/tests/hook_main.py
@@ -51,7 +51,10 @@
 from tests.factories.session import SessionFactory, SessionFactoryBasic
 from tests.factories.speaker import SpeakerFactory
 from tests.factories.ticket import TicketFactory
-from tests.factories.attendee import AttendeeFactory, AttendeeOrderSubFactory
+from tests.factories.attendee import (
+    AttendeeFactory,
+    AttendeeOrderSubFactory,
+)
 from tests.factories.session_type import SessionTypeFactory
 from tests.factories.track import TrackFactory
 from tests.factories.ticket_tag import TicketTagFactory
@@ -815,7 +818,7 @@ def group_get_list(transaction):
 
 
 @hooks.before("Group > Groups under an User > List All Groups under an User")
-def group_get_list(transaction):
+def group_get_list_from_user(transaction):
     """
     GET /users/1/groups
     :param transaction:
@@ -3579,7 +3582,6 @@ def image_upload_post(transaction):
     :param transaction:
     :return:
     """
-    pass
 
 
 @hooks.before("Upload > File Upload > Upload a File")
@@ -4957,3 +4959,18 @@ def exhibitor_delete(transaction):
     with stash['app'].app_context():
         ExhibitorFactory()
         db.session.commit()
+
+
+@hooks.before(
+    "Attendees > Search Attendees under an event > Search All Attendees under an event"
+)
+def search_attendees_from_event(transaction):
+    """
+    GET /v1/events/{event_id}/attendees/search
+    :param transaction:
+    :return:
+    """
+    with stash['app'].app_context():
+        event = EventFactoryBasic()
+        db.session.add(event)
+        db.session.commit()