Repository: fossasia/open-event-server

Issue:
TODO: Make resizing of event images a celery task
Earlier, the functionality to use the original image to generate images of various sizes was temporarily commented out as it was a blocking task, and the server timed out. It should be handled as a celery task. 


Discussion:

Resolving patch:
diff --git a/app/api/events.py b/app/api/events.py
--- a/app/api/events.py
+++ b/app/api/events.py
@@ -8,14 +8,11 @@
 from sqlalchemy.orm.exc import NoResultFound
 import pytz
 from datetime import datetime
-import urllib.error
-
 from app.api.bootstrap import api
 from app.api.data_layers.EventCopyLayer import EventCopyLayer
 from app.api.helpers.db import save_to_db, safe_query
 from app.api.helpers.events import create_custom_forms_for_attendees
-from app.api.helpers.exceptions import ForbiddenException, ConflictException, UnprocessableEntity
-from app.api.helpers.files import create_save_image_sizes
+from app.api.helpers.exceptions import ForbiddenException, ConflictException
 from app.api.helpers.permission_manager import has_access
 from app.api.helpers.utilities import dasherize
 from app.api.schema.events import EventSchemaPublic, EventSchema
@@ -173,16 +170,9 @@ def after_create_object(self, event, data, view_kwargs):
 
         if event.state == 'published' and event.schedule_published_on:
             start_export_tasks(event)
-        # TODO: Create an asynchronous celery task for this
-        # if data.get('original_image_url'):
-        #     try:
-        #         uploaded_images = create_save_image_sizes(data['original_image_url'], 'event-image', event.id)
-        #     except (urllib.error.HTTPError, urllib.error.URLError):
-        #         raise UnprocessableEntity(
-        #             {'source': 'attributes/original-image-url'}, 'Invalid Image URL'
-        #         )
-        #     self.session.query(Event).filter_by(id=event.id).update(uploaded_images)
-        #     self.session.commit()
+
+        if data.get('original_image_url'):
+            start_image_resizing_tasks(event, data['original_image_url'])
 
     # This permission decorator ensures, you are logged in to create an event
     # and have filter ?withRole to get events associated with logged in user
@@ -500,18 +490,6 @@ def before_update_object(self, event, data, view_kwargs):
         :param view_kwargs:
         :return:
         """
-        # TODO: Create an asynchronous celery task for this
-        # if data.get('original_image_url') and data['original_image_url'] != event.original_image_url:
-        #     try:
-        #         uploaded_images = create_save_image_sizes(data['original_image_url'], 'event-image', event.id)
-        #     except (urllib.error.HTTPError, urllib.error.URLError):
-        #         raise UnprocessableEntity(
-        #             {'source': 'attributes/original-image-url'}, 'Invalid Image URL'
-        #         )
-        #     data['original_image_url'] = uploaded_images['original_image_url']
-        #     data['large_image_url'] = uploaded_images['large_image_url']
-        #     data['thumbnail_image_url'] = uploaded_images['thumbnail_image_url']
-        #     data['icon_image_url'] = uploaded_images['icon_image_url']
 
         if has_access('is_admin') and data.get('deleted_at') != event.deleted_at:
             event.deleted_at = data.get('deleted_at')
@@ -521,6 +499,8 @@ def before_update_object(self, event, data, view_kwargs):
             if data.get('state', None) == 'published' and not data.get('location_name', None):
                 raise ConflictException({'pointer': '/data/attributes/location-name'},
                                         "Location is required to publish the event")
+        if data.get('original_image_url') and data['original_image_url'] != event.original_image_url:
+            start_image_resizing_tasks(event, data['original_image_url'])
 
     def after_update_object(self, event, data, view_kwargs):
         if event.state == 'published' and event.schedule_published_on:
@@ -605,6 +585,12 @@ def start_export_tasks(event):
     create_export_job(task_pentabarf.id, event_id)
 
 
+def start_image_resizing_tasks(event, original_image_url):
+    event_id = str(event.id)
+    from .helpers.tasks import resize_event_images_task
+    resize_event_images_task.delay(event_id, original_image_url)
+
+
 def clear_export_urls(event):
     event.ical_url = None
     event.xcal_url = None
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
@@ -19,6 +19,7 @@
 import logging
 import traceback
 
+from app.api.helpers.files import create_save_image_sizes
 from app.api.helpers.request_context_task import RequestContextTask
 from app.api.helpers.mail import send_export_mail, send_import_mail
 from app.api.helpers.notification import send_notif_after_import, send_notif_after_export
@@ -38,6 +39,7 @@
 from app.api.helpers.storage import UploadedFile, upload, UPLOAD_PATHS
 from app.api.helpers.db import save_to_db
 from app.api.helpers.files import create_save_pdf
+import urllib.error
 
 celery = make_celery()
 
@@ -80,6 +82,21 @@ def send_mail_via_smtp_task(config, payload):
     mailer.stop()
 
 
+@celery.task(base=RequestContextTask, name='resize.event.images', bind=True)
+def resize_event_images_task(self, event_id, original_image_url):
+    event = safe_query(db, Event, 'id', event_id, 'event_id')
+    try:
+        logging.info('Event image resizing tasks started {}'.format(original_image_url))
+        uploaded_images = create_save_image_sizes(original_image_url, 'event-image', event.id)
+        event.large_image_url = uploaded_images['large_image_url']
+        event.thumbnail_image_url = uploaded_images['thumbnail_image_url']
+        event.icon_image_url = uploaded_images['icon_image_url']
+        save_to_db(event)
+        logging.info('Resized images saved successfully for event with id: {}'.format(event_id))
+    except (urllib.error.HTTPError, urllib.error.URLError):
+        logging.exception('Error encountered while generating resized images for event with id: {}'.format(event_id))
+
+
 @celery.task(base=RequestContextTask, name='export.event', bind=True)
 def export_event_task(self, email, event_id, settings):
     event = safe_query(db, Event, 'id', event_id, 'event_id')


Test patch: