Skip to content

Commit

Permalink
Add bulk export for editing data to JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
ThiefMaster committed Mar 5, 2021
1 parent 5031dd0 commit 05e9bc4
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Expand Up @@ -33,6 +33,7 @@ Improvements
- Use the new token-based URLs instead of API keys for persistent ical links and replace
the calendar link widgets in category, event, session and contribution views with the
more modern ones used in dashboard (:issue:`4776`, :pr:`4801`)
- Add an option to export editables to JSON (:issue:`4767`, :pr:`4810`)

Internal Changes
^^^^^^^^^^^^^^^^
Expand Down
6 changes: 3 additions & 3 deletions indico/modules/events/editing/blueprint.py
Expand Up @@ -71,10 +71,10 @@
_bp.add_url_rule('/editing/api/service/disconnect', 'api_service_disconnect', service.RHDisconnectService,
methods=('POST',))
_bp.add_url_rule('/editing/api/service/status', 'api_service_status', service.RHServiceStatus)
_bp.add_url_rule('/editing/api/<any(paper,slides,poster):type>/editables/prepare-archive',
_bp.add_url_rule('/editing/api/<any(paper,slides,poster):type>/editables/prepare-<any(archive,json):archive_type>',
'api_prepare_editables_archive', editable_list.RHPrepareEditablesArchive, methods=('POST',))
_bp.add_url_rule('/editing/<any(paper,slides,poster):type>/editables/archive/<uuid:uuid>.zip', 'download_archive',
editable_list.RHDownloadArchive)
_bp.add_url_rule('/editing/<any(paper,slides,poster):type>/editables/<any(archive,json):archive_type>/<uuid:uuid>.zip',
'download_archive', editable_list.RHDownloadArchive)
_bp.add_url_rule('/editing/api/<any(paper,slides,poster):type>/editables/assign', 'api_assign_editor',
editable_list.RHAssignEditor, methods=('POST',))
_bp.add_url_rule('/editing/api/<any(paper,slides,poster):type>/editables/assign/me', 'api_assign_myself',
Expand Down
Expand Up @@ -298,13 +298,16 @@ function EditableListDisplay({
}
};

const checkedEditablesRequest = async (urlFunc, data = {}) => {
const checkedEditablesRequest = async (urlFunc, data = {}, urlData = {}) => {
let response;
try {
response = await indicoAxios.post(urlFunc({event_id: eventId, type: editableType}), {
editables: checked,
...data,
});
response = await indicoAxios.post(
urlFunc({event_id: eventId, type: editableType, ...urlData}),
{
editables: checked,
...data,
}
);
} catch (error) {
handleAxiosError(error);
return null;
Expand All @@ -317,7 +320,15 @@ function EditableListDisplay({

const downloadAllFiles = async () => {
setActiveRequest('download');
const rv = await checkedEditablesRequest(editablesArchiveURL);
const rv = await checkedEditablesRequest(editablesArchiveURL, {}, {archive_type: 'archive'});
if (rv) {
location.href = rv.downloadURL;
}
};

const exportJSON = async () => {
setActiveRequest('json');
const rv = await checkedEditablesRequest(editablesArchiveURL, {}, {archive_type: 'json'});
if (rv) {
location.href = rv.downloadURL;
}
Expand Down Expand Up @@ -400,12 +411,20 @@ function EditableListDisplay({
loading={activeRequest === 'unassign'}
/>
</Button.Group>{' '}
<Button
disabled={!hasCheckedContribs || !!activeRequest}
content={Translate.string('Download all files')}
onClick={downloadAllFiles}
loading={activeRequest === 'download'}
/>
<Button.Group>
<Button
disabled={!hasCheckedContribs || !!activeRequest}
content={Translate.string('Download all files')}
onClick={downloadAllFiles}
loading={activeRequest === 'download'}
/>
<Button
disabled={!hasCheckedContribs || !!activeRequest}
content={Translate.string('Export as JSON')}
onClick={exportJSON}
loading={activeRequest === 'json'}
/>
</Button.Group>
</>
)}
</div>
Expand Down
26 changes: 21 additions & 5 deletions indico/modules/events/editing/controllers/backend/editable_list.py
Expand Up @@ -9,7 +9,7 @@

from flask import jsonify, request, session
from marshmallow import fields
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import joinedload, selectinload
from sqlalchemy.sql import and_, func, over
from werkzeug.exceptions import Forbidden

Expand All @@ -21,7 +21,8 @@
from indico.modules.events.editing.models.editable import Editable
from indico.modules.events.editing.models.revision_files import EditingRevisionFile
from indico.modules.events.editing.models.revisions import EditingRevision
from indico.modules.events.editing.operations import assign_editor, generate_editables_zip, unassign_editor
from indico.modules.events.editing.operations import (assign_editor, generate_editables_json, generate_editables_zip,
unassign_editor)
from indico.modules.events.editing.schemas import EditableBasicSchema, EditingEditableListSchema, FilteredEditableSchema
from indico.modules.files.models.files import File
from indico.util.i18n import _
Expand Down Expand Up @@ -53,15 +54,30 @@ def _process(self):
key = str(uuid.uuid4())
data = [editable.id for editable in self.editables]
archive_cache.set(key, data, timeout=1800)
download_url = url_for('.download_archive', self.event, type=self.editable_type.name, uuid=key)
archive_type = request.view_args['archive_type']
download_url = url_for('.download_archive', self.event, type=self.editable_type.name,
archive_type=archive_type, uuid=key)
return jsonify(download_url=download_url)


class RHDownloadArchive(RHEditableTypeManagementBase):
def _process(self):
editable_ids = archive_cache.get(str(request.view_args['uuid']), [])
editables = Editable.query.filter(Editable.id.in_(editable_ids)).all()
return generate_editables_zip(editables)
revisions_strategy = selectinload('revisions')
revisions_strategy.subqueryload('comments').joinedload('user')
revisions_strategy.subqueryload('files').joinedload('file_type')
revisions_strategy.subqueryload('tags')
revisions_strategy.joinedload('submitter')
revisions_strategy.joinedload('editor')
editables = (Editable.query
.filter(Editable.id.in_(editable_ids))
.options(joinedload('editor'), joinedload('contribution'), revisions_strategy)
.all())
fn = {
'archive': generate_editables_zip,
'json': generate_editables_json,
}[request.view_args['archive_type']]
return fn(self.event, self.editable_type, editables)


class RHAssignEditor(RHEditablesBase):
Expand Down
16 changes: 13 additions & 3 deletions indico/modules/events/editing/operations.py
Expand Up @@ -9,7 +9,7 @@
from io import BytesIO
from zipfile import ZipFile

from flask import session
from flask import jsonify, session
from werkzeug.exceptions import BadRequest

from indico.core.db import db
Expand All @@ -24,7 +24,8 @@
from indico.modules.events.editing.models.tags import EditingTag
from indico.modules.events.editing.notifications import (notify_comment, notify_editor_judgment,
notify_submitter_confirmation, notify_submitter_upload)
from indico.modules.events.editing.schemas import EditingConfirmationAction, EditingReviewAction
from indico.modules.events.editing.schemas import (EditableDumpSchema, EditingConfirmationAction, EditingFileTypeSchema,
EditingReviewAction)
from indico.modules.events.logs import EventLogKind, EventLogRealm
from indico.util.date_time import now_utc
from indico.util.fs import secure_filename
Expand Down Expand Up @@ -346,7 +347,7 @@ def unassign_editor(editable):
db.session.flush()


def generate_editables_zip(editables):
def generate_editables_zip(event, editable_type, editables):
buf = BytesIO()
with ZipFile(buf, 'w', allowZip64=True) as zip_file:
for editable in editables:
Expand All @@ -359,6 +360,15 @@ def generate_editables_zip(editables):
return send_file('files.zip', buf, 'application/zip', inline=False)


def generate_editables_json(event, editable_type, editables):
file_types = EditingFileType.query.with_parent(event).filter_by(type=editable_type).all()
file_types_dump = EditingFileTypeSchema(many=True).dump(file_types)
editables_dump = EditableDumpSchema(many=True).dump(editables)
response = jsonify(version=1, file_types=file_types_dump, editables=editables_dump)
response.headers['Content-Disposition'] = 'attachment; filename="editables.json"'
return response


def _compose_filepath(editable, revision_file):
file_obj = revision_file.file
contrib = editable.contribution
Expand Down
5 changes: 5 additions & 0 deletions indico/modules/events/editing/schemas.py
Expand Up @@ -191,6 +191,11 @@ class Meta:
state = fields.Nested(EditableStateSchema)


class EditableDumpSchema(EditableSchema):
class Meta(EditableSchema.Meta):
fields = [f for f in EditableSchema.Meta.fields if not f.startswith('can_')]


class EditableBasicSchema(mm.SQLAlchemyAutoSchema):
class Meta:
model = Editable
Expand Down

0 comments on commit 05e9bc4

Please sign in to comment.