We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Export of Documents leads to 404
When Trying to export the event as Documents (json files) the page reports that my request was completed successfully.  
However, when clicking on the download link I get a 404.   
In this case, the exact link visited was:  
https://eventyay.com/notifications/opev/open_eventhttps://storage.googleapis.com/eventyay.com/exports/69/aVllSlAvdk/event69.zip


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `app/api/helpers/export_helpers.py`
Content:
```
1 import json
2 import os
3 import shutil
4 from collections import OrderedDict
5 from datetime import datetime
6 
7 import requests
8 from flask import current_app as app
9 from flask import request, g, url_for
10 from flask_restplus import marshal
11 
12 from app.helpers.data import save_to_db
13 from app.helpers.data_getter import DataGetter
14 from app.helpers.helpers import send_email_after_export, send_notif_after_export
15 from app.models.event import Event as EventModel
16 from app.models.export_jobs import ExportJob
17 from import_helpers import is_downloadable, get_filename_from_cd
18 from .non_apis import CustomFormDAO, CUSTOM_FORM
19 from ..events import DAO as EventDAO, EVENT as EVENT_MODEL
20 from ..microlocations import DAO as MicrolocationDAO, MICROLOCATION
21 from ..sessions import DAO as SessionDAO, SESSION, \
22     TypeDAO as SessionTypeDAO, SESSION_TYPE
23 from ..speakers import DAO as SpeakerDAO, SPEAKER
24 from ..sponsors import DAO as SponsorDAO, SPONSOR
25 from ..tracks import DAO as TrackDAO, TRACK
26 from app.helpers.storage import upload, UPLOAD_PATHS, UploadedFile
27 from app.settings import get_settings
28 
29 # DELETE FIELDS
30 # All fields to be deleted go here
31 EVENT = EVENT_MODEL.clone('EventExport')
32 
33 EXPORTS = [
34     ('event', EventDAO, EVENT),
35     ('microlocations', MicrolocationDAO, MICROLOCATION),
36     ('sessions', SessionDAO, SESSION),
37     ('speakers', SpeakerDAO, SPEAKER),
38     ('sponsors', SponsorDAO, SPONSOR),
39     ('tracks', TrackDAO, TRACK),
40     ('session_types', SessionTypeDAO, SESSION_TYPE),
41     ('forms', CustomFormDAO, CUSTOM_FORM)
42 ]
43 
44 # order of keys in export json
45 FIELD_ORDER = {
46     'event': [
47         'id', 'name', 'latitude', 'longitude', 'location_name', 'start_time', 'end_time',
48         'timezone', 'description', 'background_image', 'logo', 'organizer_name',
49         'organizer_description', 'event_url', 'social_links', 'ticket_url', 'privacy', 'type',
50         'topic', 'sub_topic', 'code_of_conduct', 'copyright'
51     ],
52     'microlocations': ['id', 'name', 'floor'],
53     'sessions': [
54         'id', 'title', 'subtitle', 'short_abstract', 'long_abstract', 'start_time', 'end_time',
55         'session_type', 'track', 'comments', 'language', 'slides', 'audio', 'video'
56     ],
57     'speakers': [
58         'id', 'name', 'email', 'mobile', 'photo', 'organisation', 'position', 'country',
59         'short_biography', 'long_biography', 'website', 'twitter', 'facebook', 'github', 'linkedin'
60     ],
61     'sponsors': ['id', 'name', 'logo', 'level', 'sponsor_type', 'url', 'description'],
62     'tracks': ['id', 'name', 'color', 'font_color'],
63     'session_types': ['id', 'name', 'length'],
64     'forms': []
65 }
66 
67 # keep sync with storage.UPLOAD_PATHS
68 DOWNLOAD_FIEDLS = {
69     'sessions': {
70         'video': ['video', '/videos/session_%d'],
71         'audio': ['audio', '/audios/session_%d'],
72         'slides': ['document', '/slides/session_%d']
73     },
74     'speakers': {
75         'photo': ['image', '/images/speakers/%s_%d']
76     },
77     'event': {
78         'logo': ['image', '/images/logo'],
79         'background_image': ['image', '/images/background']
80     },
81     'sponsors': {
82         'logo': ['image', '/images/sponsors/%s_%d']
83     },
84     'tracks': {
85         'track_image_url': ['image', '/images/tracks/image_%d']
86     }
87 }
88 
89 # strings to remove in a filename
90 FILENAME_EXCLUDE = '<>:"/\|?*;'
91 
92 
93 # FUNCTIONS
94 
95 def sorted_dict(data):
96     """
97     sorts a json (dict/list->dict) and returns OrderedDict
98     """
99     if type(data) == OrderedDict:
100         data = dict(data)
101     if type(data) == dict:
102         data = OrderedDict(sorted(data.items(), key=lambda t: t[0]))
103     elif type(data) == list:
104         for count in range(len(data)):
105             data[count] = OrderedDict(sorted(data[count].items(), key=lambda t: t[0]))
106     return data
107 
108 
109 def _order_json(data, srv):
110     """
111     sorts the data a/c FIELD_ORDER and returns.
112     If some keys are not included in FIELD_ORDER, they go at last, sorted alphabetically
113     """
114     new_data = OrderedDict()
115     for field in FIELD_ORDER[srv[0]]:
116         new_data[field] = sorted_dict(data[field])
117         data.pop(field, None)
118     # remaining fields, sort and add
119     # https://docs.python.org/2/library/collections.html#collections.OrderedDict
120     data = OrderedDict(sorted(data.items(), key=lambda t: t[0]))
121     for key in data:
122         new_data[key] = sorted_dict(data[key])
123     return new_data
124 
125 
126 def _download_media(data, srv, dir_path, settings):
127     """
128     Downloads the media and saves it
129     """
130     if srv not in DOWNLOAD_FIEDLS:
131         return
132     for i in DOWNLOAD_FIEDLS[srv]:
133         if not data[i]:
134             continue
135         if not settings[DOWNLOAD_FIEDLS[srv][i][0]]:
136             continue
137         path = DOWNLOAD_FIEDLS[srv][i][1]
138         if srv == 'speakers':
139             path %= make_filename(data['name']), data['id']
140         elif srv == 'sponsors':
141             path %= make_filename(data['name']), data['id']
142         elif srv != 'event':
143             path = path % (data['id'])
144         if data[i].find('.') > -1:  # add extension
145             ext = data[i].rsplit('.', 1)[1]
146             if ext.find('/') == -1:
147                 path += '.' + ext
148         full_path = dir_path + path
149         # make dir
150         cdir = full_path.rsplit('/', 1)[0]
151         if not os.path.isdir(cdir):
152             os.makedirs(cdir)
153         # download and set
154         url = data[i]
155         if not is_downloadable(url):
156             continue
157         try:
158             r = requests.get(url, allow_redirects=True)
159             ext = get_filename_from_cd(r.headers.get('content-disposition'))[1]
160             full_path += ext
161             path += ext
162             open(full_path, 'wb').write(r.content)
163             data[i] = path
164         except Exception:
165             pass
166 
167 
168 def _generate_meta():
169     """
170     Generate Meta information for export
171     """
172     d = {'root_url': request.url_root}
173     return d
174 
175 
176 def export_event_json(event_id, settings):
177     """
178     Exports the event as a zip on the server and return its path
179     """
180     # make directory
181     exports_dir = app.config['BASE_DIR'] + '/static/uploads/exports/'
182     if not os.path.isdir(exports_dir):
183         os.mkdir(exports_dir)
184     dir_path = exports_dir + 'event%d' % event_id
185     if os.path.isdir(dir_path):
186         shutil.rmtree(dir_path, ignore_errors=True)
187     os.mkdir(dir_path)
188     # save to directory
189     for e in EXPORTS:
190         if e[0] == 'event':
191             data = _order_json(marshal(e[1].get(event_id), e[2]), e)
192             _download_media(data, 'event', dir_path, settings)
193         else:
194             data = marshal(e[1].list(event_id), e[2])
195             for count in range(len(data)):
196                 data[count] = _order_json(data[count], e)
197                 _download_media(data[count], e[0], dir_path, settings)
198         data_str = json.dumps(data, indent=4, ensure_ascii=False).encode('utf-8')
199         fp = open(dir_path + '/' + e[0], 'w')
200         fp.write(data_str)
201         fp.close()
202     # add meta
203     data_str = json.dumps(
204         _generate_meta(), sort_keys=True,
205         indent=4, ensure_ascii=False
206     ).encode('utf-8')
207     fp = open(dir_path + '/meta', 'w')
208     fp.write(data_str)
209     fp.close()
210     # make zip
211     shutil.make_archive(dir_path, 'zip', dir_path)
212     dir_path = dir_path + ".zip"
213 
214     storage_path = UPLOAD_PATHS['exports']['zip'].format(
215         event_id = event_id
216     )
217     uploaded_file = UploadedFile(dir_path, dir_path.rsplit('/', 1)[1])
218     storage_url = upload(uploaded_file, storage_path)
219 
220     if get_settings()['storage_place'] != "s3" or get_settings()['storage_place'] != 'gs':
221         storage_url = app.config['BASE_DIR'] + storage_url.replace("/serve_","/")
222     return storage_url
223 
224 
225 # HELPERS
226 
227 def create_export_job(task_id, event_id):
228     """
229     Create export job for an export that is going to start
230     """
231     export_job = ExportJob.query.filter_by(event_id=event_id).first()
232     task_url = url_for('api.extras_celery_task', task_id=task_id)
233     if export_job:
234         export_job.task = task_url
235         export_job.user_email = g.user.email
236         export_job.event = EventModel.query.get(event_id)
237         export_job.start_time = datetime.now()
238     else:
239         export_job = ExportJob(
240             task=task_url, user_email=g.user.email,
241             event=EventModel.query.get(event_id)
242         )
243     save_to_db(export_job, 'ExportJob saved')
244 
245 
246 def send_export_mail(event_id, result):
247     """
248     send export event mail after the process is complete
249     """
250     job = DataGetter.get_export_jobs(event_id)
251     if not job:  # job not stored, happens in case of CELERY_ALWAYS_EAGER
252         return
253     event = EventModel.query.get(event_id)
254     if not event:
255         event_name = '(Undefined)'
256     else:
257         event_name = event.name
258     send_email_after_export(job.user_email, event_name, result)
259     user = DataGetter.get_user_by_email(job.user_email)
260     send_notif_after_export(user, event_name, result)
261 
262 
263 # FIELD DATA FORMATTERS
264 def make_filename(name):
265     """Make speaker image filename for export"""
266     for _ in FILENAME_EXCLUDE:
267         name = name.replace(_, ' ')
268     return ''.join(s.title() for s in name.split() if s)
269
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/app/api/helpers/export_helpers.py b/app/api/helpers/export_helpers.py
--- a/app/api/helpers/export_helpers.py
+++ b/app/api/helpers/export_helpers.py
@@ -217,7 +217,7 @@
     uploaded_file = UploadedFile(dir_path, dir_path.rsplit('/', 1)[1])
     storage_url = upload(uploaded_file, storage_path)
 
-    if get_settings()['storage_place'] != "s3" or get_settings()['storage_place'] != 'gs':
+    if get_settings()['storage_place'] != "s3" and get_settings()['storage_place'] != 'gs':
         storage_url = app.config['BASE_DIR'] + storage_url.replace("/serve_","/")
     return storage_url

{"golden_diff": "diff --git a/app/api/helpers/export_helpers.py b/app/api/helpers/export_helpers.py\n--- a/app/api/helpers/export_helpers.py\n+++ b/app/api/helpers/export_helpers.py\n@@ -217,7 +217,7 @@\n     uploaded_file = UploadedFile(dir_path, dir_path.rsplit('/', 1)[1])\n     storage_url = upload(uploaded_file, storage_path)\n \n-    if get_settings()['storage_place'] != \"s3\" or get_settings()['storage_place'] != 'gs':\n+    if get_settings()['storage_place'] != \"s3\" and get_settings()['storage_place'] != 'gs':\n         storage_url = app.config['BASE_DIR'] + storage_url.replace(\"/serve_\",\"/\")\n     return storage_url\n", "issue": "Export of Documents leads to 404\nWhen Trying to export the event as Documents (json files) the page reports that my request was completed successfully.  \r\nHowever, when clicking on the download link I get a 404.   \r\nIn this case, the exact link visited was:  \r\nhttps://eventyay.com/notifications/opev/open_eventhttps://storage.googleapis.com/eventyay.com/exports/69/aVllSlAvdk/event69.zip\r\n\n", "before_files": [{"content": "import json\nimport os\nimport shutil\nfrom collections import OrderedDict\nfrom datetime import datetime\n\nimport requests\nfrom flask import current_app as app\nfrom flask import request, g, url_for\nfrom flask_restplus import marshal\n\nfrom app.helpers.data import save_to_db\nfrom app.helpers.data_getter import DataGetter\nfrom app.helpers.helpers import send_email_after_export, send_notif_after_export\nfrom app.models.event import Event as EventModel\nfrom app.models.export_jobs import ExportJob\nfrom import_helpers import is_downloadable, get_filename_from_cd\nfrom .non_apis import CustomFormDAO, CUSTOM_FORM\nfrom ..events import DAO as EventDAO, EVENT as EVENT_MODEL\nfrom ..microlocations import DAO as MicrolocationDAO, MICROLOCATION\nfrom ..sessions import DAO as SessionDAO, SESSION, \\\n    TypeDAO as SessionTypeDAO, SESSION_TYPE\nfrom ..speakers import DAO as SpeakerDAO, SPEAKER\nfrom ..sponsors import DAO as SponsorDAO, SPONSOR\nfrom ..tracks import DAO as TrackDAO, TRACK\nfrom app.helpers.storage import upload, UPLOAD_PATHS, UploadedFile\nfrom app.settings import get_settings\n\n# DELETE FIELDS\n# All fields to be deleted go here\nEVENT = EVENT_MODEL.clone('EventExport')\n\nEXPORTS = [\n    ('event', EventDAO, EVENT),\n    ('microlocations', MicrolocationDAO, MICROLOCATION),\n    ('sessions', SessionDAO, SESSION),\n    ('speakers', SpeakerDAO, SPEAKER),\n    ('sponsors', SponsorDAO, SPONSOR),\n    ('tracks', TrackDAO, TRACK),\n    ('session_types', SessionTypeDAO, SESSION_TYPE),\n    ('forms', CustomFormDAO, CUSTOM_FORM)\n]\n\n# order of keys in export json\nFIELD_ORDER = {\n    'event': [\n        'id', 'name', 'latitude', 'longitude', 'location_name', 'start_time', 'end_time',\n        'timezone', 'description', 'background_image', 'logo', 'organizer_name',\n        'organizer_description', 'event_url', 'social_links', 'ticket_url', 'privacy', 'type',\n        'topic', 'sub_topic', 'code_of_conduct', 'copyright'\n    ],\n    'microlocations': ['id', 'name', 'floor'],\n    'sessions': [\n        'id', 'title', 'subtitle', 'short_abstract', 'long_abstract', 'start_time', 'end_time',\n        'session_type', 'track', 'comments', 'language', 'slides', 'audio', 'video'\n    ],\n    'speakers': [\n        'id', 'name', 'email', 'mobile', 'photo', 'organisation', 'position', 'country',\n        'short_biography', 'long_biography', 'website', 'twitter', 'facebook', 'github', 'linkedin'\n    ],\n    'sponsors': ['id', 'name', 'logo', 'level', 'sponsor_type', 'url', 'description'],\n    'tracks': ['id', 'name', 'color', 'font_color'],\n    'session_types': ['id', 'name', 'length'],\n    'forms': []\n}\n\n# keep sync with storage.UPLOAD_PATHS\nDOWNLOAD_FIEDLS = {\n    'sessions': {\n        'video': ['video', '/videos/session_%d'],\n        'audio': ['audio', '/audios/session_%d'],\n        'slides': ['document', '/slides/session_%d']\n    },\n    'speakers': {\n        'photo': ['image', '/images/speakers/%s_%d']\n    },\n    'event': {\n        'logo': ['image', '/images/logo'],\n        'background_image': ['image', '/images/background']\n    },\n    'sponsors': {\n        'logo': ['image', '/images/sponsors/%s_%d']\n    },\n    'tracks': {\n        'track_image_url': ['image', '/images/tracks/image_%d']\n    }\n}\n\n# strings to remove in a filename\nFILENAME_EXCLUDE = '<>:\"/\\|?*;'\n\n\n# FUNCTIONS\n\ndef sorted_dict(data):\n    \"\"\"\n    sorts a json (dict/list->dict) and returns OrderedDict\n    \"\"\"\n    if type(data) == OrderedDict:\n        data = dict(data)\n    if type(data) == dict:\n        data = OrderedDict(sorted(data.items(), key=lambda t: t[0]))\n    elif type(data) == list:\n        for count in range(len(data)):\n            data[count] = OrderedDict(sorted(data[count].items(), key=lambda t: t[0]))\n    return data\n\n\ndef _order_json(data, srv):\n    \"\"\"\n    sorts the data a/c FIELD_ORDER and returns.\n    If some keys are not included in FIELD_ORDER, they go at last, sorted alphabetically\n    \"\"\"\n    new_data = OrderedDict()\n    for field in FIELD_ORDER[srv[0]]:\n        new_data[field] = sorted_dict(data[field])\n        data.pop(field, None)\n    # remaining fields, sort and add\n    # https://docs.python.org/2/library/collections.html#collections.OrderedDict\n    data = OrderedDict(sorted(data.items(), key=lambda t: t[0]))\n    for key in data:\n        new_data[key] = sorted_dict(data[key])\n    return new_data\n\n\ndef _download_media(data, srv, dir_path, settings):\n    \"\"\"\n    Downloads the media and saves it\n    \"\"\"\n    if srv not in DOWNLOAD_FIEDLS:\n        return\n    for i in DOWNLOAD_FIEDLS[srv]:\n        if not data[i]:\n            continue\n        if not settings[DOWNLOAD_FIEDLS[srv][i][0]]:\n            continue\n        path = DOWNLOAD_FIEDLS[srv][i][1]\n        if srv == 'speakers':\n            path %= make_filename(data['name']), data['id']\n        elif srv == 'sponsors':\n            path %= make_filename(data['name']), data['id']\n        elif srv != 'event':\n            path = path % (data['id'])\n        if data[i].find('.') > -1:  # add extension\n            ext = data[i].rsplit('.', 1)[1]\n            if ext.find('/') == -1:\n                path += '.' + ext\n        full_path = dir_path + path\n        # make dir\n        cdir = full_path.rsplit('/', 1)[0]\n        if not os.path.isdir(cdir):\n            os.makedirs(cdir)\n        # download and set\n        url = data[i]\n        if not is_downloadable(url):\n            continue\n        try:\n            r = requests.get(url, allow_redirects=True)\n            ext = get_filename_from_cd(r.headers.get('content-disposition'))[1]\n            full_path += ext\n            path += ext\n            open(full_path, 'wb').write(r.content)\n            data[i] = path\n        except Exception:\n            pass\n\n\ndef _generate_meta():\n    \"\"\"\n    Generate Meta information for export\n    \"\"\"\n    d = {'root_url': request.url_root}\n    return d\n\n\ndef export_event_json(event_id, settings):\n    \"\"\"\n    Exports the event as a zip on the server and return its path\n    \"\"\"\n    # make directory\n    exports_dir = app.config['BASE_DIR'] + '/static/uploads/exports/'\n    if not os.path.isdir(exports_dir):\n        os.mkdir(exports_dir)\n    dir_path = exports_dir + 'event%d' % event_id\n    if os.path.isdir(dir_path):\n        shutil.rmtree(dir_path, ignore_errors=True)\n    os.mkdir(dir_path)\n    # save to directory\n    for e in EXPORTS:\n        if e[0] == 'event':\n            data = _order_json(marshal(e[1].get(event_id), e[2]), e)\n            _download_media(data, 'event', dir_path, settings)\n        else:\n            data = marshal(e[1].list(event_id), e[2])\n            for count in range(len(data)):\n                data[count] = _order_json(data[count], e)\n                _download_media(data[count], e[0], dir_path, settings)\n        data_str = json.dumps(data, indent=4, ensure_ascii=False).encode('utf-8')\n        fp = open(dir_path + '/' + e[0], 'w')\n        fp.write(data_str)\n        fp.close()\n    # add meta\n    data_str = json.dumps(\n        _generate_meta(), sort_keys=True,\n        indent=4, ensure_ascii=False\n    ).encode('utf-8')\n    fp = open(dir_path + '/meta', 'w')\n    fp.write(data_str)\n    fp.close()\n    # make zip\n    shutil.make_archive(dir_path, 'zip', dir_path)\n    dir_path = dir_path + \".zip\"\n\n    storage_path = UPLOAD_PATHS['exports']['zip'].format(\n        event_id = event_id\n    )\n    uploaded_file = UploadedFile(dir_path, dir_path.rsplit('/', 1)[1])\n    storage_url = upload(uploaded_file, storage_path)\n\n    if get_settings()['storage_place'] != \"s3\" or get_settings()['storage_place'] != 'gs':\n        storage_url = app.config['BASE_DIR'] + storage_url.replace(\"/serve_\",\"/\")\n    return storage_url\n\n\n# HELPERS\n\ndef create_export_job(task_id, event_id):\n    \"\"\"\n    Create export job for an export that is going to start\n    \"\"\"\n    export_job = ExportJob.query.filter_by(event_id=event_id).first()\n    task_url = url_for('api.extras_celery_task', task_id=task_id)\n    if export_job:\n        export_job.task = task_url\n        export_job.user_email = g.user.email\n        export_job.event = EventModel.query.get(event_id)\n        export_job.start_time = datetime.now()\n    else:\n        export_job = ExportJob(\n            task=task_url, user_email=g.user.email,\n            event=EventModel.query.get(event_id)\n        )\n    save_to_db(export_job, 'ExportJob saved')\n\n\ndef send_export_mail(event_id, result):\n    \"\"\"\n    send export event mail after the process is complete\n    \"\"\"\n    job = DataGetter.get_export_jobs(event_id)\n    if not job:  # job not stored, happens in case of CELERY_ALWAYS_EAGER\n        return\n    event = EventModel.query.get(event_id)\n    if not event:\n        event_name = '(Undefined)'\n    else:\n        event_name = event.name\n    send_email_after_export(job.user_email, event_name, result)\n    user = DataGetter.get_user_by_email(job.user_email)\n    send_notif_after_export(user, event_name, result)\n\n\n# FIELD DATA FORMATTERS\ndef make_filename(name):\n    \"\"\"Make speaker image filename for export\"\"\"\n    for _ in FILENAME_EXCLUDE:\n        name = name.replace(_, ' ')\n    return ''.join(s.title() for s in name.split() if s)\n", "path": "app/api/helpers/export_helpers.py"}], "after_files": [{"content": "import json\nimport os\nimport shutil\nfrom collections import OrderedDict\nfrom datetime import datetime\n\nimport requests\nfrom flask import current_app as app\nfrom flask import request, g, url_for\nfrom flask_restplus import marshal\n\nfrom app.helpers.data import save_to_db\nfrom app.helpers.data_getter import DataGetter\nfrom app.helpers.helpers import send_email_after_export, send_notif_after_export\nfrom app.models.event import Event as EventModel\nfrom app.models.export_jobs import ExportJob\nfrom import_helpers import is_downloadable, get_filename_from_cd\nfrom .non_apis import CustomFormDAO, CUSTOM_FORM\nfrom ..events import DAO as EventDAO, EVENT as EVENT_MODEL\nfrom ..microlocations import DAO as MicrolocationDAO, MICROLOCATION\nfrom ..sessions import DAO as SessionDAO, SESSION, \\\n    TypeDAO as SessionTypeDAO, SESSION_TYPE\nfrom ..speakers import DAO as SpeakerDAO, SPEAKER\nfrom ..sponsors import DAO as SponsorDAO, SPONSOR\nfrom ..tracks import DAO as TrackDAO, TRACK\nfrom app.helpers.storage import upload, UPLOAD_PATHS, UploadedFile\nfrom app.settings import get_settings\n\n# DELETE FIELDS\n# All fields to be deleted go here\nEVENT = EVENT_MODEL.clone('EventExport')\n\nEXPORTS = [\n    ('event', EventDAO, EVENT),\n    ('microlocations', MicrolocationDAO, MICROLOCATION),\n    ('sessions', SessionDAO, SESSION),\n    ('speakers', SpeakerDAO, SPEAKER),\n    ('sponsors', SponsorDAO, SPONSOR),\n    ('tracks', TrackDAO, TRACK),\n    ('session_types', SessionTypeDAO, SESSION_TYPE),\n    ('forms', CustomFormDAO, CUSTOM_FORM)\n]\n\n# order of keys in export json\nFIELD_ORDER = {\n    'event': [\n        'id', 'name', 'latitude', 'longitude', 'location_name', 'start_time', 'end_time',\n        'timezone', 'description', 'background_image', 'logo', 'organizer_name',\n        'organizer_description', 'event_url', 'social_links', 'ticket_url', 'privacy', 'type',\n        'topic', 'sub_topic', 'code_of_conduct', 'copyright'\n    ],\n    'microlocations': ['id', 'name', 'floor'],\n    'sessions': [\n        'id', 'title', 'subtitle', 'short_abstract', 'long_abstract', 'start_time', 'end_time',\n        'session_type', 'track', 'comments', 'language', 'slides', 'audio', 'video'\n    ],\n    'speakers': [\n        'id', 'name', 'email', 'mobile', 'photo', 'organisation', 'position', 'country',\n        'short_biography', 'long_biography', 'website', 'twitter', 'facebook', 'github', 'linkedin'\n    ],\n    'sponsors': ['id', 'name', 'logo', 'level', 'sponsor_type', 'url', 'description'],\n    'tracks': ['id', 'name', 'color', 'font_color'],\n    'session_types': ['id', 'name', 'length'],\n    'forms': []\n}\n\n# keep sync with storage.UPLOAD_PATHS\nDOWNLOAD_FIEDLS = {\n    'sessions': {\n        'video': ['video', '/videos/session_%d'],\n        'audio': ['audio', '/audios/session_%d'],\n        'slides': ['document', '/slides/session_%d']\n    },\n    'speakers': {\n        'photo': ['image', '/images/speakers/%s_%d']\n    },\n    'event': {\n        'logo': ['image', '/images/logo'],\n        'background_image': ['image', '/images/background']\n    },\n    'sponsors': {\n        'logo': ['image', '/images/sponsors/%s_%d']\n    },\n    'tracks': {\n        'track_image_url': ['image', '/images/tracks/image_%d']\n    }\n}\n\n# strings to remove in a filename\nFILENAME_EXCLUDE = '<>:\"/\\|?*;'\n\n\n# FUNCTIONS\n\ndef sorted_dict(data):\n    \"\"\"\n    sorts a json (dict/list->dict) and returns OrderedDict\n    \"\"\"\n    if type(data) == OrderedDict:\n        data = dict(data)\n    if type(data) == dict:\n        data = OrderedDict(sorted(data.items(), key=lambda t: t[0]))\n    elif type(data) == list:\n        for count in range(len(data)):\n            data[count] = OrderedDict(sorted(data[count].items(), key=lambda t: t[0]))\n    return data\n\n\ndef _order_json(data, srv):\n    \"\"\"\n    sorts the data a/c FIELD_ORDER and returns.\n    If some keys are not included in FIELD_ORDER, they go at last, sorted alphabetically\n    \"\"\"\n    new_data = OrderedDict()\n    for field in FIELD_ORDER[srv[0]]:\n        new_data[field] = sorted_dict(data[field])\n        data.pop(field, None)\n    # remaining fields, sort and add\n    # https://docs.python.org/2/library/collections.html#collections.OrderedDict\n    data = OrderedDict(sorted(data.items(), key=lambda t: t[0]))\n    for key in data:\n        new_data[key] = sorted_dict(data[key])\n    return new_data\n\n\ndef _download_media(data, srv, dir_path, settings):\n    \"\"\"\n    Downloads the media and saves it\n    \"\"\"\n    if srv not in DOWNLOAD_FIEDLS:\n        return\n    for i in DOWNLOAD_FIEDLS[srv]:\n        if not data[i]:\n            continue\n        if not settings[DOWNLOAD_FIEDLS[srv][i][0]]:\n            continue\n        path = DOWNLOAD_FIEDLS[srv][i][1]\n        if srv == 'speakers':\n            path %= make_filename(data['name']), data['id']\n        elif srv == 'sponsors':\n            path %= make_filename(data['name']), data['id']\n        elif srv != 'event':\n            path = path % (data['id'])\n        if data[i].find('.') > -1:  # add extension\n            ext = data[i].rsplit('.', 1)[1]\n            if ext.find('/') == -1:\n                path += '.' + ext\n        full_path = dir_path + path\n        # make dir\n        cdir = full_path.rsplit('/', 1)[0]\n        if not os.path.isdir(cdir):\n            os.makedirs(cdir)\n        # download and set\n        url = data[i]\n        if not is_downloadable(url):\n            continue\n        try:\n            r = requests.get(url, allow_redirects=True)\n            ext = get_filename_from_cd(r.headers.get('content-disposition'))[1]\n            full_path += ext\n            path += ext\n            open(full_path, 'wb').write(r.content)\n            data[i] = path\n        except Exception:\n            pass\n\n\ndef _generate_meta():\n    \"\"\"\n    Generate Meta information for export\n    \"\"\"\n    d = {'root_url': request.url_root}\n    return d\n\n\ndef export_event_json(event_id, settings):\n    \"\"\"\n    Exports the event as a zip on the server and return its path\n    \"\"\"\n    # make directory\n    exports_dir = app.config['BASE_DIR'] + '/static/uploads/exports/'\n    if not os.path.isdir(exports_dir):\n        os.mkdir(exports_dir)\n    dir_path = exports_dir + 'event%d' % event_id\n    if os.path.isdir(dir_path):\n        shutil.rmtree(dir_path, ignore_errors=True)\n    os.mkdir(dir_path)\n    # save to directory\n    for e in EXPORTS:\n        if e[0] == 'event':\n            data = _order_json(marshal(e[1].get(event_id), e[2]), e)\n            _download_media(data, 'event', dir_path, settings)\n        else:\n            data = marshal(e[1].list(event_id), e[2])\n            for count in range(len(data)):\n                data[count] = _order_json(data[count], e)\n                _download_media(data[count], e[0], dir_path, settings)\n        data_str = json.dumps(data, indent=4, ensure_ascii=False).encode('utf-8')\n        fp = open(dir_path + '/' + e[0], 'w')\n        fp.write(data_str)\n        fp.close()\n    # add meta\n    data_str = json.dumps(\n        _generate_meta(), sort_keys=True,\n        indent=4, ensure_ascii=False\n    ).encode('utf-8')\n    fp = open(dir_path + '/meta', 'w')\n    fp.write(data_str)\n    fp.close()\n    # make zip\n    shutil.make_archive(dir_path, 'zip', dir_path)\n    dir_path = dir_path + \".zip\"\n\n    storage_path = UPLOAD_PATHS['exports']['zip'].format(\n        event_id = event_id\n    )\n    uploaded_file = UploadedFile(dir_path, dir_path.rsplit('/', 1)[1])\n    storage_url = upload(uploaded_file, storage_path)\n\n    if get_settings()['storage_place'] != \"s3\" and get_settings()['storage_place'] != 'gs':\n        storage_url = app.config['BASE_DIR'] + storage_url.replace(\"/serve_\",\"/\")\n    return storage_url\n\n\n# HELPERS\n\ndef create_export_job(task_id, event_id):\n    \"\"\"\n    Create export job for an export that is going to start\n    \"\"\"\n    export_job = ExportJob.query.filter_by(event_id=event_id).first()\n    task_url = url_for('api.extras_celery_task', task_id=task_id)\n    if export_job:\n        export_job.task = task_url\n        export_job.user_email = g.user.email\n        export_job.event = EventModel.query.get(event_id)\n        export_job.start_time = datetime.now()\n    else:\n        export_job = ExportJob(\n            task=task_url, user_email=g.user.email,\n            event=EventModel.query.get(event_id)\n        )\n    save_to_db(export_job, 'ExportJob saved')\n\n\ndef send_export_mail(event_id, result):\n    \"\"\"\n    send export event mail after the process is complete\n    \"\"\"\n    job = DataGetter.get_export_jobs(event_id)\n    if not job:  # job not stored, happens in case of CELERY_ALWAYS_EAGER\n        return\n    event = EventModel.query.get(event_id)\n    if not event:\n        event_name = '(Undefined)'\n    else:\n        event_name = event.name\n    send_email_after_export(job.user_email, event_name, result)\n    user = DataGetter.get_user_by_email(job.user_email)\n    send_notif_after_export(user, event_name, result)\n\n\n# FIELD DATA FORMATTERS\ndef make_filename(name):\n    \"\"\"Make speaker image filename for export\"\"\"\n    for _ in FILENAME_EXCLUDE:\n        name = name.replace(_, ' ')\n    return ''.join(s.title() for s in name.split() if s)\n", "path": "app/api/helpers/export_helpers.py"}]}