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 AttachmentPackageMixin
:
44 form
= self
._prepare
_form
()
45 if form
.validate_on_submit():
46 return self
._generate
_zip
_file
(self
._filter
_attachments
(form
))
48 return self
.wp
.render_template('generate_package.html', self
._conf
, form
=form
)
50 def _prepare_form(self
):
51 form
= AttachmentPackageForm()
52 form
.sessions
.choices
= self
._load
_session
_data
()
53 form
.contributions
.choices
= self
._load
_contribution
_data
()
54 form
.contributions_schedule_dates
.choices
= self
._load
_schedule
_data
()
57 def _load_session_data(self
):
58 return [(session
.getId(), to_unicode(session
.getTitle())) for session
in self
._conf
.getSessionList()]
60 def _load_contribution_data(self
):
61 return [(contrib
.getId(), to_unicode(contrib
.getTitle()))
62 for contrib
in self
._conf
.getContributionList() if contrib
.getOwner() == self
._conf
63 and contrib
.getStartDate()]
65 def _load_schedule_data(self
):
66 dates
= {contrib
.getStartDate().date() for contrib
in self
._conf
.getContributionList() if contrib
.getStartDate()}
67 return sorted([(unicode(d
), format_date(d
, 'short')) for d
in dates
], key
=itemgetter(1))
69 def _filter_attachments(self
, form
):
71 added_since
= form
.added_since
.data
72 attachments
.extend(self
._filter
_protected
(self
._filter
_top
_level
_attachments
(added_since
)))
73 attachments
.extend(self
._filter
_protected
(self
._filter
_by
_sessions
(form
.sessions
.data
, added_since
)))
75 contribution_ids
= set(form
.contributions
.data
+
76 self
._get
_contributions
_by
_schedule
_date
(form
.contributions_schedule_dates
.data
))
77 attachments
.extend(self
._filter
_protected
(self
._filter
_by
_contributions
(contribution_ids
, added_since
)))
80 def _filter_protected(self
, attachments
):
81 return [attachment
for attachment
in attachments
if attachment
.can_access(session
.user
)]
83 def _filter_top_level_attachments(self
, added_since
):
84 query
= self
._build
_base
_query
().filter(AttachmentFolder
.linked_object
== self
._conf
)
87 query
= self
._filter
_by
_date
(query
, added_since
)
91 def _build_base_query(self
):
92 return Attachment
.find(Attachment
.type == AttachmentType
.file, ~AttachmentFolder
.is_deleted
,
93 ~Attachment
.is_deleted
, AttachmentFolder
.event_id
== int(self
._conf
.getId()),
94 _join
=AttachmentFolder
)
96 def _filter_by_sessions(self
, session_ids
, added_since
):
97 query
= self
._build
_base
_query
().filter(AttachmentFolder
.link_type
== LinkType
.session
)
99 query
= query
.filter(AttachmentFolder
.session_id
.in_(session_ids
))
102 query
= self
._filter
_by
_date
(query
, added_since
)
106 def _get_contributions_by_schedule_date(self
, dates
):
107 return [contribution
.getId() for contribution
in self
._conf
.getContributionList()
108 if contribution
.getStartDate() and str(contribution
.getStartDate().date()) in dates
]
110 def _filter_by_contributions(self
, contribution_ids
, added_since
):
111 query
= self
._build
_base
_query
().filter(AttachmentFolder
.link_type
.in_([LinkType
.contribution
,
112 LinkType
.subcontribution
]))
114 query
= query
.filter(AttachmentFolder
.contribution_id
.in_(contribution_ids
))
116 query
= query
.filter(AttachmentFolder
.contribution_id
is not None)
119 query
= self
._filter
_by
_date
(query
, added_since
)
123 def _filter_by_date(self
, query
, added_since
):
124 return query
.filter(cast(AttachmentFile
.created_dt
, Date
) >= added_since
)
126 def _generate_zip_file(self
, attachments
):
127 # XXX: could use a celery task to delay the temporary file after a day or so.
128 # right now this relies on an external cronjob to do so...
129 temp_file
= NamedTemporaryFile(suffix
='indico.tmp', dir=Config
.getInstance().getTempDir(), delete
=False)
130 with
ZipFile(temp_file
.name
, 'w', allowZip64
=True) as zip_handler
:
131 for attachment
in attachments
:
132 name
= self
._prepare
_folder
_structure
(attachment
)
133 with attachment
.file.storage
.get_local_path(attachment
.file.storage_file_id
) as filepath
:
134 zip_handler
.write(filepath
, name
)
136 return send_file('material-{}.zip'.format(self
._conf
.id), temp_file
.name
, 'application/zip', inline
=False)
138 def _prepare_folder_structure(self
, attachment
):
139 event_dir
= secure_filename(self
._conf
.getTitle(), None)
140 segments
= [event_dir
] if event_dir
else []
141 segments
.extend(self
._get
_base
_path
(attachment
))
142 segments
.extend([secure_filename(attachment
.folder
.title
, unicode(attachment
.folder
.id)),
143 attachment
.file.filename
])
144 return os
.path
.join(*segments
)
146 def _get_base_path(self
, attachment
):
147 obj
= linked_object
= attachment
.folder
.linked_object
149 while obj
!= self
._conf
:
150 owner
= obj
.getOwner()
151 if isinstance(obj
, SubContribution
):
152 start_date
= owner
.getAdjustedStartDate()
154 start_date
= obj
.getAdjustedStartDate()
156 paths
.append(secure_filename(start_date
.strftime('%H%M_{}').format(obj
.getTitle()), ''))
159 if isinstance(linked_object
, SubContribution
):
160 linked_obj_start_date
= linked_object
.getOwner().getAdjustedStartDate()
162 linked_obj_start_date
= linked_object
.getAdjustedStartDate()
164 if attachment
.folder
.linked_object
!= self
._conf
:
165 paths
.append(secure_filename(linked_obj_start_date
.strftime('%Y%m%d_%A'), ''))
167 return reversed(paths
)