Use new material package module for contributions
[cds-indico.git] / indico / modules / attachments / controllers / event_package.py
blob9158e806dc081c03070c66059a7a0b1da0d17d40
1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
17 from __future__ import unicode_literals
19 import os
20 from operator import itemgetter
21 from tempfile import NamedTemporaryFile
22 from zipfile import ZipFile
24 from flask import session
25 from sqlalchemy import cast, Date
27 from indico.core.config import Config
28 from indico.core.db.sqlalchemy.links import LinkType
29 from indico.util.fs import secure_filename
30 from indico.util.date_time import format_date
31 from indico.util.string import to_unicode
32 from indico.web.flask.util import send_file
33 from indico.modules.attachments.forms import AttachmentPackageForm
34 from indico.modules.attachments.models.attachments import Attachment, AttachmentFile, AttachmentType
35 from indico.modules.attachments.models.folders import AttachmentFolder
37 from MaKaC.conference import SubContribution
40 class AttachmentPackageGeneratorMixin:
42 def _filter_attachments(self, filter_data):
43 attachments = []
44 added_since = filter_data.get('added_since', None)
45 attachments.extend(self._filter_protected(self._filter_top_level_attachments(added_since)))
46 attachments.extend(self._filter_protected(self._filter_by_sessions(filter_data.get('sessions', []),
47 added_since)))
49 contribution_ids = set(filter_data.get('contributions', []) +
50 self._get_contributions_by_schedule_date(filter_data.get('contributions_schedule_dates',
51 [])))
52 attachments.extend(self._filter_protected(self._filter_by_contributions(contribution_ids, added_since)))
53 return attachments
55 def _filter_protected(self, attachments):
56 return [attachment for attachment in attachments if attachment.can_access(session.user)]
58 def _filter_top_level_attachments(self, added_since):
59 query = self._build_base_query().filter(AttachmentFolder.linked_object == self._conf)
61 if added_since:
62 query = self._filter_by_date(query, added_since)
64 return query.all()
66 def _build_base_query(self):
67 return Attachment.find(Attachment.type == AttachmentType.file, ~AttachmentFolder.is_deleted,
68 ~Attachment.is_deleted, AttachmentFolder.event_id == int(self._conf.getId()),
69 _join=AttachmentFolder)
71 def _filter_by_sessions(self, session_ids, added_since):
72 query = self._build_base_query().filter(AttachmentFolder.link_type == LinkType.session)
73 if session_ids:
74 query = query.filter(AttachmentFolder.session_id.in_(session_ids))
76 if added_since:
77 query = self._filter_by_date(query, added_since)
79 return query.all()
81 def _get_contributions_by_schedule_date(self, dates):
82 return [contribution.getId() for contribution in self._conf.getContributionList()
83 if contribution.getStartDate() and str(contribution.getStartDate().date()) in dates]
85 def _filter_by_contributions(self, contribution_ids, added_since):
86 query = self._build_base_query().filter(AttachmentFolder.link_type.in_([LinkType.contribution,
87 LinkType.subcontribution]))
88 if contribution_ids:
89 query = query.filter(AttachmentFolder.contribution_id.in_(contribution_ids))
90 else:
91 query = query.filter(AttachmentFolder.contribution_id is not None)
93 if added_since:
94 query = self._filter_by_date(query, added_since)
96 return query.all()
98 def _filter_by_date(self, query, added_since):
99 return query.filter(cast(AttachmentFile.created_dt, Date) >= added_since)
101 def _generate_zip_file(self, attachments):
102 # XXX: could use a celery task to delay the temporary file after a day or so.
103 # right now this relies on an external cronjob to do so...
104 temp_file = NamedTemporaryFile(suffix='indico.tmp', dir=Config.getInstance().getTempDir(), delete=False)
105 with ZipFile(temp_file.name, 'w', allowZip64=True) as zip_handler:
106 for attachment in attachments:
107 name = self._prepare_folder_structure(attachment)
108 with attachment.file.storage.get_local_path(attachment.file.storage_file_id) as filepath:
109 zip_handler.write(filepath, name)
111 return send_file('material-{}.zip'.format(self._conf.id), temp_file.name, 'application/zip', inline=False)
113 def _prepare_folder_structure(self, attachment):
114 event_dir = secure_filename(self._conf.getTitle(), None)
115 segments = [event_dir] if event_dir else []
116 segments.extend(self._get_base_path(attachment))
117 segments.extend([secure_filename(attachment.folder.title, unicode(attachment.folder.id)),
118 attachment.file.filename])
119 return os.path.join(*segments)
121 def _get_base_path(self, attachment):
122 obj = linked_object = attachment.folder.linked_object
123 paths = []
124 while obj != self._conf:
125 owner = obj.getOwner()
126 if isinstance(obj, SubContribution):
127 start_date = owner.getAdjustedStartDate()
128 else:
129 start_date = obj.getAdjustedStartDate()
131 paths.append(secure_filename(start_date.strftime('%H%M_{}').format(obj.getTitle()), ''))
132 obj = owner
134 if isinstance(linked_object, SubContribution):
135 linked_obj_start_date = linked_object.getOwner().getAdjustedStartDate()
136 else:
137 linked_obj_start_date = linked_object.getAdjustedStartDate()
139 if attachment.folder.linked_object != self._conf:
140 paths.append(secure_filename(linked_obj_start_date.strftime('%Y%m%d_%A'), ''))
142 return reversed(paths)
145 class AttachmentPackageMixin(AttachmentPackageGeneratorMixin):
146 wp = None
148 def _process(self):
149 form = self._prepare_form()
150 if form.validate_on_submit():
151 return self._generate_zip_file(self._filter_attachments(form.data))
153 return self.wp.render_template('generate_package.html', self._conf, form=form)
155 def _prepare_form(self):
156 form = AttachmentPackageForm()
157 form.sessions.choices = self._load_session_data()
158 form.contributions.choices = self._load_contribution_data()
159 form.contributions_schedule_dates.choices = self._load_schedule_data()
160 return form
162 def _load_session_data(self):
163 return [(session.getId(), to_unicode(session.getTitle())) for session in self._conf.getSessionList()]
165 def _load_contribution_data(self):
166 return [(contrib.getId(), to_unicode(contrib.getTitle()))
167 for contrib in self._conf.getContributionList() if contrib.getOwner() == self._conf
168 and contrib.getStartDate()]
170 def _load_schedule_data(self):
171 dates = {contrib.getStartDate().date() for contrib in self._conf.getContributionList() if contrib.getStartDate()}
172 return sorted([(unicode(d), format_date(d, 'short')) for d in dates], key=itemgetter(1))