From 428f8872d15aff319602697f16ffb51868c86c12 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Tue, 2 Apr 2019 17:52:19 -0700 Subject: file basic editing; several cleanups - use logging when appropriate - refactor out editgroup form fetching code - handle "editgroup doesn't exist" error (and display) --- python/fatcat_web/editing_routes.py | 166 ++++++++++++++++++++++++------------ python/fatcat_web/forms.py | 61 ++++++++----- python/fatcat_web/routes.py | 9 +- 3 files changed, 159 insertions(+), 77 deletions(-) diff --git a/python/fatcat_web/editing_routes.py b/python/fatcat_web/editing_routes.py index fe2c0722..f45c5379 100644 --- a/python/fatcat_web/editing_routes.py +++ b/python/fatcat_web/editing_routes.py @@ -25,15 +25,19 @@ def form_editgroup_get_or_create(api, edit_form): try: eg = api.get_editgroup(edit_form.editgroup_id.data) except ApiException as ae: - app.logging.warn(ae) + if ae.status == 404: + edit_form.editgroup_id.errors.append("Editgroup does not exist") + return None + app.logger.warn(ae) abort(ae.status) + # TODO: check here that editgroup hasn't been merged already else: # if no editgroup, create one from description try: eg = api.create_editgroup( Editgroup(description=edit_form.editgroup_description.data or None)) except ApiException as ae: - app.logging.warn(ae) + app.logger.warn(ae) abort(ae.status) # set this session editgroup_id session['active_editgroup_id'] = eg.editgroup_id @@ -52,22 +56,22 @@ def container_create(): # API on behalf of user user_api = auth_api(session['api_token']) eg = form_editgroup_get_or_create(user_api, form) - # no merge or anything hard to do; just create the entity - entity = form.to_entity() - try: - edit = user_api.create_container(entity, editgroup_id=eg.editgroup_id) - except ApiException as ae: - app.logging.warn(ae) - abort(ae.status) - # redirect to new entity - return redirect('/container/{}'.format(edit.ident)) + if eg: + # no merge or anything hard to do; just create the entity + entity = form.to_entity() + try: + edit = user_api.create_container(entity, editgroup_id=eg.editgroup_id) + except ApiException as ae: + app.logger.warn(ae) + abort(ae.status) + # redirect to new entity + return redirect('/container/{}'.format(edit.ident)) elif form.errors: app.logger.info("form errors (did not validate): {}".format(form.errors)) else: editgroup_id = session.get('active_editgroup_id', None) form.editgroup_id.data = editgroup_id - return render_template('container_create.html', - form=form, editgroup_id=editgroup_id) + return render_template('container_create.html', form=form) @login_required @app.route('/container//edit', methods=['GET', 'POST']) @@ -83,17 +87,18 @@ def container_edit(ident): # API on behalf of user user_api = auth_api(session['api_token']) eg = form_editgroup_get_or_create(user_api, form) - # all the tricky logic is in the update method - form.update_entity(entity) - try: - edit = user_api.update_container(entity.ident, entity, - editgroup_id=eg.editgroup_id) - except ApiException as ae: - app.logging.warn(ae) - abort(ae.status) - # redirect to entity revision - # TODO: container_rev_view - return redirect('/container/{}'.format(edit.ident)) + if eg: + # all the tricky logic is in the update method + form.update_entity(entity) + try: + edit = user_api.update_container(entity.ident, entity, + editgroup_id=eg.editgroup_id) + except ApiException as ae: + app.logger.warn(ae) + abort(ae.status) + # redirect to entity revision + # TODO: container_rev_view + return redirect('/container/{}'.format(edit.ident)) elif form.errors: app.logger.info("form errors (did not validate): {}".format(form.errors)) else: @@ -101,8 +106,7 @@ def container_edit(ident): if not form.is_submitted(): editgroup_id = session.get('active_editgroup_id', None) form.editgroup_id.data = editgroup_id - return render_template('container_edit.html', - form=form, editgroup_id=editgroup_id, entity=entity) + return render_template('container_edit.html', form=form, entity=entity) @app.route('/creator//edit', methods=['GET']) def creator_edit(ident): @@ -112,13 +116,68 @@ def creator_edit(ident): abort(ae.status) return render_template('entity_edit.html') -@app.route('/file//edit', methods=['GET']) +@app.route('/file/create', methods=['GET', 'POST']) +@login_required +def file_create(): + form = FileEntityForm() + if form.is_submitted(): + if form.validate_on_submit(): + # API on behalf of user + user_api = auth_api(session['api_token']) + eg = form_editgroup_get_or_create(user_api, form) + if eg: + # no merge or anything hard to do; just create the entity + entity = form.to_entity() + try: + edit = user_api.create_file(entity, editgroup_id=eg.editgroup_id) + except ApiException as ae: + app.logger.warn(ae) + abort(ae.status) + # redirect to new entity + return redirect('/file/{}'.format(edit.ident)) + elif form.errors: + app.logger.info("form errors (did not validate): {}".format(form.errors)) + else: + editgroup_id = session.get('active_editgroup_id', None) + form.editgroup_id.data = editgroup_id + form.urls.append_entry() + form.release_ids.append_entry() + return render_template('file_create.html', + form=form) + +@login_required +@app.route('/file//edit', methods=['GET', 'POST']) def file_edit(ident): + # TODO: prev_rev interlock try: entity = api.get_file(ident) except ApiException as ae: abort(ae.status) - return render_template('entity_edit.html') + form = FileEntityForm() + if form.is_submitted(): + if form.validate_on_submit(): + # API on behalf of user + user_api = auth_api(session['api_token']) + eg = form_editgroup_get_or_create(user_api, form) + if eg: + # all the tricky logic is in the update method + form.update_entity(entity) + try: + edit = user_api.update_file(entity.ident, entity, + editgroup_id=eg.editgroup_id) + except ApiException as ae: + app.logger.warn(ae) + abort(ae.status) + # redirect to entity revision + # TODO: file_rev_view + return redirect('/file/{}'.format(edit.ident)) + elif form.errors: + app.logger.info("form errors (did not validate): {}".format(form.errors)) + else: # not submitted + form = FileEntityForm.from_entity(entity) + editgroup_id = session.get('active_editgroup_id', None) + form.editgroup_id.data = editgroup_id + return render_template('file_edit.html', form=form, entity=entity) @app.route('/fileset//edit', methods=['GET']) def fileset_edit(ident): @@ -145,24 +204,23 @@ def release_create(): # API on behalf of user user_api = auth_api(session['api_token']) eg = form_editgroup_get_or_create(user_api, form) - # no merge or anything hard to do; just create the entity - entity = form.to_entity() - try: - edit = user_api.create_release(entity, editgroup_id=eg.editgroup_id) - except ApiException as ae: - app.logging.warn(ae) - abort(ae.status) - # redirect to new release - return redirect('/release/{}'.format(edit.ident)) + if eg: + # no merge or anything hard to do; just create the entity + entity = form.to_entity() + try: + edit = user_api.create_release(entity, editgroup_id=eg.editgroup_id) + except ApiException as ae: + app.logger.warn(ae) + abort(ae.status) + # redirect to new release + return redirect('/release/{}'.format(edit.ident)) elif form.errors: app.logger.info("form errors (did not validate): {}".format(form.errors)) - elif len(form.contribs) == 0: + else: # not submitted form.contribs.append_entry() - if not form.is_submitted(): editgroup_id = session.get('active_editgroup_id', None) form.editgroup_id.data = editgroup_id - return render_template('release_create.html', - form=form, editgroup_id=editgroup_id) + return render_template('release_create.html', form=form) @login_required @app.route('/release//edit', methods=['GET', 'POST']) @@ -178,25 +236,25 @@ def release_edit(ident): # API on behalf of user user_api = auth_api(session['api_token']) eg = form_editgroup_get_or_create(user_api, form) - # all the tricky logic is in the update method - form.update_entity(entity) - try: - edit = user_api.update_release(entity.ident, entity, - editgroup_id=eg.editgroup_id) - except ApiException as ae: - app.logging.warn(ae) - abort(ae.status) - # redirect to entity revision - # TODO: release_rev_view - return redirect('/release/{}'.format(edit.ident)) + if eg: + # all the tricky logic is in the update method + form.update_entity(entity) + try: + edit = user_api.update_release(entity.ident, entity, + editgroup_id=eg.editgroup_id) + except ApiException as ae: + app.logger.warn(ae) + abort(ae.status) + # redirect to entity revision + # TODO: release_rev_view + return redirect('/release/{}'.format(edit.ident)) elif form.errors: app.logger.info("form errors (did not validate): {}".format(form.errors)) else: # not submitted form = ReleaseEntityForm.from_entity(entity) editgroup_id = session.get('active_editgroup_id', None) form.editgroup_id.data = editgroup_id - return render_template('release_edit.html', - form=form, editgroup_id=editgroup_id, entity=entity) + return render_template('release_edit.html', form=form, entity=entity) @app.route('/work/create', methods=['GET']) def work_create_view(): diff --git a/python/fatcat_web/forms.py b/python/fatcat_web/forms.py index ebe6fafe..08c401b7 100644 --- a/python/fatcat_web/forms.py +++ b/python/fatcat_web/forms.py @@ -9,7 +9,7 @@ from wtforms import SelectField, DateField, StringField, IntegerField, \ HiddenField, FormField, FieldList, validators from fatcat_client import ContainerEntity, CreatorEntity, FileEntity, \ - ReleaseEntity, ReleaseContrib + ReleaseEntity, ReleaseContrib, FileEntityUrls release_type_options = [ ('', 'Unknown'), @@ -37,7 +37,8 @@ role_type_options = [ class EntityEditForm(FlaskForm): editgroup_id = StringField('Editgroup ID', - [validators.Optional(True)]) + [validators.Optional(True), + validators.Length(min=26, max=26)]) editgroup_description = StringField('Editgroup Description', [validators.Optional(True)]) edit_description = StringField('Description of Changes', @@ -47,6 +48,7 @@ class ReleaseContribForm(FlaskForm): class Meta: # this is a sub-form, so disable CSRF csrf = False + #surname #given_name #creator_id (?) @@ -71,10 +73,14 @@ class ReleaseEntityForm(EntityEditForm): - date """ title = StringField('Title', - [validators.InputRequired()]) + [validators.DataRequired()]) original_title = StringField('Original Title') - work_id = StringField('Work FCID') - container_id = StringField('Container FCID') + work_id = StringField('Work FCID', + [validators.Optional(True), + validators.Length(min=26, max=26)]) + container_id = StringField('Container FCID', + [validators.Optional(True), + validators.Length(min=26, max=26)]) release_type = SelectField('Release Type', [validators.DataRequired()], choices=release_type_options, @@ -172,7 +178,7 @@ CONTAINER_SIMPLE_ATTRS = ['name', 'container_type', 'publisher', 'issnl', class ContainerEntityForm(EntityEditForm): name = StringField('Name/Title', - [validators.InputRequired()]) + [validators.DataRequired()]) container_type = SelectField('Container Type', [validators.Optional(True)], choices=container_type_options, @@ -230,7 +236,8 @@ class FileUrlForm(FlaskForm): csrf = False url = StringField('Display Name', - [validators.DataRequired()]) + [validators.DataRequired(), + validators.URL(require_tld=False)]) rel = SelectField( [validators.DataRequired()], choices=url_rel_options, @@ -240,25 +247,41 @@ class FileEntityForm(EntityEditForm): size = IntegerField('Size (bytes)', [validators.DataRequired()]) # TODO: positive definite - md5 = StringField("MD5") - sha1 = StringField("SHA-1") - sha256 = StringField("SHA-256") + md5 = StringField("MD5", + [validators.Optional(True), + validators.Length(min=32, max=32)]) + sha1 = StringField("SHA-1", + [validators.DataRequired(), + validators.Length(min=40, max=40)]) + sha256 = StringField("SHA-256", + [validators.Optional(True), + validators.Length(min=64, max=64)]) urls = FieldList(FormField(FileUrlForm)) mimetype = StringField("Mimetype") - release_ids = FieldList(StringField("Release FCID")) + release_ids = FieldList( + StringField("Release FCID", + [validators.DataRequired(), + validators.Length(min=26, max=26)])) - def from_entity(re): + def from_entity(fe): """ Initializes form with values from an existing file entity. """ ref = FileEntityForm() for simple_attr in FILE_SIMPLE_ATTRS: a = getattr(ref, simple_attr) - a.data = getattr(re, simple_attr) + a.data = getattr(fe, simple_attr) + for i, c in enumerate(fe.urls): + ruf = FileUrlForm() + ruf.rel = c.rel + ruf.url = c.url + ref.urls.append_entry(ruf) + for r in fe.release_ids: + ref.release_ids.append_entry(r) return ref def to_entity(self): - assert(self.name.data) + assert(self.sha1.data) entity = FileEntity() self.update_entity(entity) return entity @@ -275,15 +298,15 @@ class FileEntityForm(EntityEditForm): if a == '': a = None setattr(fe, simple_attr, a) - re.urls = [] + fe.urls = [] for u in self.urls: - re.contribs.append(FileUrl( - rel=u.role.data or None, + fe.urls.append(FileEntityUrls( + rel=u.rel.data or None, url=u.url.data or None, )) - re.release_ids = [] + fe.release_ids = [] for ri in self.release_ids: - re.release_ids.append(ri.data) + fe.release_ids.append(ri.data) if self.edit_description.data: fe.edit_extra = dict(description=self.edit_description.data) diff --git a/python/fatcat_web/routes.py b/python/fatcat_web/routes.py index ed9abf0b..c4152188 100644 --- a/python/fatcat_web/routes.py +++ b/python/fatcat_web/routes.py @@ -22,6 +22,7 @@ def container_history(ident): entity = api.get_container(ident) history = api.get_container_history(ident) except ApiException as ae: + app.logger.info(ae) abort(ae.status) #print(history) return render_template('entity_history.html', @@ -57,7 +58,7 @@ def container_view(ident): stats = get_elastic_container_stats(entity.issnl) except Exception as e: stats = None - print(e) + app.logger.error(e) else: stats = None @@ -397,7 +398,7 @@ def stats_page(): stats = get_elastic_entity_stats() stats.update(get_changelog_stats()) except Exception as ae: - print(ae) + app.logger.error(e) abort(503) return render_template('stats.html', stats=stats) @@ -410,7 +411,7 @@ def stats_json(): stats = get_elastic_entity_stats() stats.update(get_changelog_stats()) except Exception as ae: - print(ae) + app.logger.error(e) abort(503) return jsonify(stats) @@ -420,7 +421,7 @@ def container_issnl_stats(issnl): try: stats = get_elastic_container_stats(issnl) except Exception as ae: - print(ae) + app.logger.error(e) abort(503) return jsonify(stats) -- cgit v1.2.3