Repository: fossasia/open-event-server

Issue:
Export attendees as CSV
**Is your feature request related to a problem? Please describe.**
Create an endpoint to export attendees as CSV

**Describe the solution you'd like**
Use the method of version 1

**Describe alternatives you've considered**
\-



Discussion:

Resolving patch:
diff --git a/app/api/exports.py b/app/api/exports.py
--- a/app/api/exports.py
+++ b/app/api/exports.py
@@ -172,3 +172,23 @@ def export_orders_pdf(event_identifier):
     return jsonify(
         task_url=url_for('tasks.celery_task', task_id=task.id)
     )
+
+
+@export_routes.route('/events/<string:event_identifier>/export/attendees/csv', methods=['GET'])
+@jwt_required()
+def export_attendees_csv(event_identifier):
+    if not event_identifier.isdigit():
+        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
+        event_id = str(event.id)
+    else:
+        event_id = event_identifier
+
+    from .helpers.tasks import export_attendees_csv_task
+
+    task = export_attendees_csv_task.delay(event_id)
+
+    create_export_job(task.id, event_id)
+
+    return jsonify(
+        task_url=url_for('tasks.celery_task', task_id=task.id)
+    )
diff --git a/app/api/helpers/csv_jobs_util.py b/app/api/helpers/csv_jobs_util.py
--- a/app/api/helpers/csv_jobs_util.py
+++ b/app/api/helpers/csv_jobs_util.py
@@ -17,3 +17,26 @@ def export_orders_csv(orders):
             rows.append(column)
 
     return rows
+
+
+def export_attendees_csv(attendees):
+    headers = ['Order#', 'Order Date', 'Status', 'First Name', 'Last Name', 'Email',
+               'Country', 'Payment Type', 'Ticket Name', 'Ticket Price', 'Ticket Type']
+
+    rows = [headers]
+    for attendee in attendees:
+        column = [str(attendee.order.get_invoice_number()) if attendee.order else '-',
+                  str(attendee.order.created_at) if attendee.order and attendee.order.created_at else '-',
+                  str(attendee.order.status) if attendee.order and attendee.order.status else '-',
+                  str(attendee.firstname) if attendee.firstname else '',
+                  str(attendee.lastname) if attendee.lastname else '',
+                  str(attendee.email) if attendee.email else '',
+                  str(attendee.country) if attendee.country else '',
+                  str(attendee.order.payment_mode) if attendee.order and attendee.order.payment_mode else '',
+                  str(attendee.ticket.name) if attendee.ticket and attendee.ticket.name else '',
+                  str(attendee.ticket.price) if attendee.ticket and attendee.ticket.price else '0',
+                  str(attendee.ticket.type) if attendee.ticket and attendee.ticket.type else '']
+
+        rows.append(column)
+
+    return rows
diff --git a/app/api/helpers/tasks.py b/app/api/helpers/tasks.py
--- a/app/api/helpers/tasks.py
+++ b/app/api/helpers/tasks.py
@@ -27,6 +27,7 @@
 from app.models.event import Event
 from app.models.order import Order
 from app.models.discount_code import DiscountCode
+from app.models.ticket_holder import TicketHolder
 from app.api.helpers.ICalExporter import ICalExporter
 from app.api.helpers.xcal import XCalExporter
 from app.api.helpers.pentabarfxml import PentabarfExporter
@@ -124,14 +125,14 @@ def export_ical_task(self, event_id, temp=True):
 
     try:
         if temp:
-            filedir = current_app.config.get('BASE_DIR') + '/static/uploads/temp/' + event_id + '/'
+            filedir = os.path.join(current_app.config.get('BASE_DIR'), 'static/uploads/temp/' + event_id + '/')
         else:
-            filedir = current_app.config.get('BASE_DIR') + '/static/uploads/' + event_id + '/'
+            filedir = os.path.join(current_app.config.get('BASE_DIR'), 'static/uploads/' + event_id + '/')
 
         if not os.path.isdir(filedir):
             os.makedirs(filedir)
         filename = "ical.ics"
-        file_path = filedir + filename
+        file_path = os.path.join(filedir, filename)
         with open(file_path, "w") as temp_file:
             temp_file.write(str(ICalExporter.export(event_id), 'utf-8'))
         ical_file = UploadedFile(file_path=file_path, filename=filename)
@@ -159,14 +160,14 @@ def export_xcal_task(self, event_id, temp=True):
 
     try:
         if temp:
-            filedir = current_app.config.get('BASE_DIR') + '/static/uploads/temp/' + event_id + '/'
+            filedir = os.path.join(current_app.config.get('BASE_DIR'), 'static/uploads/temp/' + event_id + '/')
         else:
-            filedir = current_app.config.get('BASE_DIR') + '/static/uploads/' + event_id + '/'
+            filedir = os.path.join(current_app.config.get('BASE_DIR'), 'static/uploads/' + event_id + '/')
 
         if not os.path.isdir(filedir):
             os.makedirs(filedir)
         filename = "xcal.xcs"
-        file_path = filedir + filename
+        file_path = os.path.join(filedir, filename)
         with open(file_path, "w") as temp_file:
             temp_file.write(str(XCalExporter.export(event_id), 'utf-8'))
         xcal_file = UploadedFile(file_path=file_path, filename=filename)
@@ -194,14 +195,14 @@ def export_pentabarf_task(self, event_id, temp=True):
 
     try:
         if temp:
-            filedir = current_app.config.get('BASE_DIR') + '/static/uploads/temp/' + event_id + '/'
+            filedir = os.path.join(current_app.config.get('BASE_DIR'), 'static/uploads/temp/' + event_id + '/')
         else:
-            filedir = current_app.config.get('BASE_DIR') + '/static/uploads/' + event_id + '/'
+            filedir = os.path.join(current_app.config.get('BASE_DIR'), 'static/uploads/' + event_id + '/')
 
         if not os.path.isdir(filedir):
             os.makedirs(filedir)
         filename = "pentabarf.xml"
-        file_path = filedir + filename
+        file_path = os.path.join(filedir, filename)
         with open(file_path, "w") as temp_file:
             temp_file.write(str(PentabarfExporter.export(event_id), 'utf-8'))
         pentabarf_file = UploadedFile(file_path=file_path, filename=filename)
@@ -228,11 +229,11 @@ def export_order_csv_task(self, event_id):
     orders = db.session.query(Order).filter_by(event_id=event_id)
 
     try:
-        filedir = current_app.config.get('BASE_DIR') + '/static/uploads/temp/'
+        filedir = os.path.join(current_app.config.get('BASE_DIR'), 'static/uploads/temp/')
         if not os.path.isdir(filedir):
             os.makedirs(filedir)
         filename = "order-{}.csv".format(uuid.uuid1().hex)
-        file_path = filedir + filename
+        file_path = os.path.join(filedir, filename)
 
         with open(file_path, "w") as temp_file:
             writer = csv.writer(temp_file)
@@ -269,3 +270,31 @@ def export_order_pdf_task(self, event_id):
         result = {'__error': True, 'result': str(e)}
 
     return result
+
+
+@celery.task(base=RequestContextTask, name='export.attendees.csv', bind=True)
+def export_attendees_csv_task(self, event_id):
+    attendees = db.session.query(TicketHolder).filter_by(event_id=event_id)
+    try:
+        filedir = os.path.join(current_app.config.get('BASE_DIR'), 'static/uploads/temp/')
+        if not os.path.isdir(filedir):
+            os.makedirs(filedir)
+        filename = "attendees-{}.csv".format(uuid.uuid1().hex)
+        file_path = os.path.join(filedir, filename)
+
+        with open(file_path, "w") as temp_file:
+            writer = csv.writer(temp_file)
+            from app.api.helpers.csv_jobs_util import export_attendees_csv
+            content = export_attendees_csv(attendees)
+            for row in content:
+                writer.writerow(row)
+        attendees_csv_file = UploadedFile(file_path=file_path, filename=filename)
+        attendees_csv_url = upload(attendees_csv_file, UPLOAD_PATHS['exports']['csv'].format(event_id=event_id))
+        result = {
+            'download_url': attendees_csv_url
+        }
+    except Exception as e:
+        print(traceback.format_exc())
+        result = {'__error': True, 'result': str(e)}
+
+    return result


Test patch:
diff --git a/tests/hook_main.py b/tests/hook_main.py
--- a/tests/hook_main.py
+++ b/tests/hook_main.py
@@ -3603,6 +3603,19 @@ def event_orders_export_pdf_get(transaction):
         db.session.add(event)
         db.session.commit()
 
+
+@hooks.before(
+    "Event Export > Start Attendees Export as CSV > Start a Task to Export Attendees of an Event as CSV")
+def event_attendees_export_csv_get(transaction):
+    """
+    :param transaction:
+    :return:
+    """
+    with stash['app'].app_context():
+        event = EventFactoryBasic()
+        db.session.add(event)
+        db.session.commit()
+
 # ------------------------- Import -------------------------
 @hooks.before(
     "Event Import > Start Event Import > Start a Task to Import an Event")