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
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
):
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', []),
49 contribution_ids
= set(filter_data
.get('contributions', []) +
50 self
._get
_contributions
_by
_schedule
_date
(filter_data
.get('contributions_schedule_dates',
52 attachments
.extend(self
._filter
_protected
(self
._filter
_by
_contributions
(contribution_ids
, added_since
)))
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
)
62 query
= self
._filter
_by
_date
(query
, added_since
)
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
)
74 query
= query
.filter(AttachmentFolder
.session_id
.in_(session_ids
))
77 query
= self
._filter
_by
_date
(query
, added_since
)
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
]))
89 query
= query
.filter(AttachmentFolder
.contribution_id
.in_(contribution_ids
))
91 query
= query
.filter(AttachmentFolder
.contribution_id
is not None)
94 query
= self
._filter
_by
_date
(query
, added_since
)
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
124 while obj
!= self
._conf
:
125 owner
= obj
.getOwner()
126 if isinstance(obj
, SubContribution
):
127 start_date
= owner
.getAdjustedStartDate()
129 start_date
= obj
.getAdjustedStartDate()
131 paths
.append(secure_filename(start_date
.strftime('%H%M_{}').format(obj
.getTitle()), ''))
134 if isinstance(linked_object
, SubContribution
):
135 linked_obj_start_date
= linked_object
.getOwner().getAdjustedStartDate()
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
):
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
()
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))