From 90b06f6f6db1a40946ba280f7324b15fec2f667e Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Thu, 30 Jul 2020 17:28:11 -0700 Subject: generic HTML views for TOML editing --- python/fatcat_web/templates/base.html | 4 +++ python/fatcat_web/templates/edit_macros.html | 17 ++++++++++ .../fatcat_web/templates/entity_create_toml.html | 20 +++++++++++ python/fatcat_web/templates/entity_edit_toml.html | 39 ++++++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 python/fatcat_web/templates/entity_create_toml.html create mode 100644 python/fatcat_web/templates/entity_edit_toml.html (limited to 'python/fatcat_web/templates') diff --git a/python/fatcat_web/templates/base.html b/python/fatcat_web/templates/base.html index 18c66077..2c18ec44 100644 --- a/python/fatcat_web/templates/base.html +++ b/python/fatcat_web/templates/base.html @@ -25,6 +25,10 @@ @media only screen and (max-width: 479px) { .mobile-hide{ display: none !important; } } + + .field textarea#toml { + font-family: monospace; + } {% block extra_head %}{% endblock %} diff --git a/python/fatcat_web/templates/edit_macros.html b/python/fatcat_web/templates/edit_macros.html index a207e51e..60c17aa9 100644 --- a/python/fatcat_web/templates/edit_macros.html +++ b/python/fatcat_web/templates/edit_macros.html @@ -17,6 +17,23 @@ {%- endmacro %} +{% macro form_toml_field(field, div_classes="") -%} +

Raw Metadata (TOML)

+
+

TOML is a markup language, similar to + YAML or Wikitext. The schema here is the same as the Fatcat API JSON schema, but + with a syntax that is easier to read and edit by hand. All nested metadata + fields are available here; refer to the fatcat guide for metadata + documentation and style guide, or TOML documentation for syntax notes (eg, + what those double brackets mean, and how to represent lists of authors or + references). +
+
+ {{ field(rows=24) }} + {{ form_field_errors(field) }} +

+{%- endmacro %} + {% macro form_field_inline(field, div_classes="") -%}
diff --git a/python/fatcat_web/templates/entity_create_toml.html b/python/fatcat_web/templates/entity_create_toml.html new file mode 100644 index 00000000..ec5bc4a2 --- /dev/null +++ b/python/fatcat_web/templates/entity_create_toml.html @@ -0,0 +1,20 @@ +{% extends "entity_edit_toml.html" %} + +{% block edit_form_prefix %} +
+

Create New {{ entity_type }} Entity (TOML mode)

+ +
+{% endblock %} + +{% block edit_form_suffix %} +

+ +

+ New {{ entity_type }} entity will be part of the current editgroup, which + needs to be submited and approved before the entity will formally be included + in the catalog. +

+
+{% endblock %} + diff --git a/python/fatcat_web/templates/entity_edit_toml.html b/python/fatcat_web/templates/entity_edit_toml.html new file mode 100644 index 00000000..4b6e7b6d --- /dev/null +++ b/python/fatcat_web/templates/entity_edit_toml.html @@ -0,0 +1,39 @@ +{% import "edit_macros.html" as edit_macros %} +{% extends "base.html" %} + +{% block body %} +{% block edit_form_prefix %} +
+

Edit Entity (TOML mode)

+ +
+{% endblock %} + +

See the catalog + style guide for schema notes, and the editing + tutorial if this is your first time making an edit. + + {{ form.hidden_tag() }} + +

Editgroup Metadata

+ {{ edit_macros.editgroup_dropdown(form, editgroup, potential_editgroups) }} + + {{ edit_macros.form_toml_field(form.toml, "required") }} + +

Submit

+ {{ edit_macros.form_field_basic(form.edit_description) }} + This description will be attached to the individual edit, not to the + editgroup as a whole. + +{% block edit_form_suffix %} +

+ +

+ Edit will be part of the current editgroup, which needs to be submited and + approved before the change is included in the catalog. +

+
+{% endblock %} +{% endblock %} + -- cgit v1.2.3 From b14040ecb359d1575280b24eaab9bd0e4964e3f7 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Thu, 30 Jul 2020 17:31:17 -0700 Subject: wire up new TOML views --- python/fatcat_web/editing_routes.py | 193 +++++++++++++++++----- python/fatcat_web/templates/container_create.html | 4 +- python/fatcat_web/templates/container_edit.html | 5 + python/fatcat_web/templates/edit_macros.html | 2 +- python/fatcat_web/templates/editgroup_view.html | 2 +- python/fatcat_web/templates/entity_edit.html | 8 - python/fatcat_web/templates/entity_edit_toml.html | 12 ++ python/fatcat_web/templates/file_create.html | 4 +- python/fatcat_web/templates/file_edit.html | 5 + python/fatcat_web/templates/home.html | 12 +- python/fatcat_web/templates/release_create.html | 4 +- python/fatcat_web/templates/release_edit.html | 6 + python/tests/web_editing.py | 66 ++++++-- python/tests/web_entity_views.py | 16 +- 14 files changed, 256 insertions(+), 83 deletions(-) delete mode 100644 python/fatcat_web/templates/entity_edit.html (limited to 'python/fatcat_web/templates') diff --git a/python/fatcat_web/editing_routes.py b/python/fatcat_web/editing_routes.py index f3c6fdd0..e84b14f7 100644 --- a/python/fatcat_web/editing_routes.py +++ b/python/fatcat_web/editing_routes.py @@ -366,27 +366,18 @@ def generic_edit_delete(editgroup_id, entity_type, edit_id): try: editgroup = api.get_editgroup(editgroup_id) except ApiException as ae: - raise ae + abort(ae.status) # check that editgroup is edit-able if editgroup.changelog_index != None: - abort(400, "Editgroup already merged") + flash("Editgroup already merged") + abort(400) # API on behalf of user user_api = auth_api(session['api_token']) # do the deletion - try: - if entity_type == 'container': - user_api.delete_container_edit(editgroup.editgroup_id, edit_id) - elif entity_type == 'file': - user_api.delete_file_edit(editgroup.editgroup_id, edit_id) - elif entity_type == 'release': - user_api.delete_release_edit(editgroup.editgroup_id, edit_id) - else: - raise NotImplementedError - except ApiException as ae: - raise ae + generic_entity_delete_edit(user_api, entity_type, editgroup.editgroup_id, edit_id) return redirect("/editgroup/{}".format(editgroup_id)) @@ -452,69 +443,187 @@ def release_editgroup_edit(editgroup_id, ident): def release_edit_delete(editgroup_id, edit_id): return generic_edit_delete(editgroup_id, 'release', edit_id) +@app.route('/editgroup//creator/edit//delete', methods=['POST']) +def creator_edit_delete(editgroup_id, edit_id): + return generic_edit_delete(editgroup_id, 'creator', edit_id) + +@app.route('/editgroup//fileset/edit//delete', methods=['POST']) +def fileset_edit_delete(editgroup_id, edit_id): + return generic_edit_delete(editgroup_id, 'fileset', edit_id) -### Not-Implemented Views ################################################### +@app.route('/editgroup//webcapture/edit//delete', methods=['POST']) +def webcapture_edit_delete(editgroup_id, edit_id): + return generic_edit_delete(editgroup_id, 'webcapture', edit_id) + +@app.route('/editgroup//work/edit//delete', methods=['POST']) +def work_edit_delete(editgroup_id, edit_id): + return generic_edit_delete(editgroup_id, 'work', edit_id) + +### TOML Views ############################################################## + +@app.route('/container/create/toml', methods=['GET', 'POST']) +@login_required +def container_create_toml_view(): + return generic_entity_toml_edit(None, 'container', None, 'entity_create_toml.html') + +@app.route('/container//edit/toml', methods=['GET', 'POST']) +@login_required +def container_edit_toml(ident): + return generic_entity_toml_edit(None, 'container', ident, 'entity_edit_toml.html') + +@app.route('/editgroup//container//edit/toml', methods=['GET', 'POST']) +@login_required +def container_editgroup_edit_toml(editgroup_id, ident): + return generic_entity_toml_edit(editgroup_id, 'container', ident, 'entity_edit_toml.html') + +@app.route('/creator/create/toml', methods=['GET', 'POST']) +@login_required +def creator_create_toml_view(): + return generic_entity_toml_edit(None, 'creator', None, 'entity_create_toml.html') + +@app.route('/creator//edit/toml', methods=['GET', 'POST']) +@login_required +def creator_edit_toml(ident): + return generic_entity_toml_edit(None, 'creator', ident, 'entity_edit_toml.html') + +@app.route('/editgroup//creator//edit/toml', methods=['GET', 'POST']) +@login_required +def creator_editgroup_edit_toml(editgroup_id, ident): + return generic_entity_toml_edit(editgroup_id, 'creator', ident, 'entity_edit_toml.html') + +@app.route('/file/create/toml', methods=['GET', 'POST']) +@login_required +def file_create_toml_view(): + return generic_entity_toml_edit(None, 'file', None, 'entity_create_toml.html') + +@app.route('/file//edit/toml', methods=['GET', 'POST']) +@login_required +def file_edit_toml(ident): + return generic_entity_toml_edit(None, 'file', ident, 'entity_edit_toml.html') + +@app.route('/editgroup//file//edit/toml', methods=['GET', 'POST']) +@login_required +def file_editgroup_edit_toml(editgroup_id, ident): + return generic_entity_toml_edit(editgroup_id, 'file', ident, 'entity_edit_toml.html') + +@app.route('/fileset/create/toml', methods=['GET', 'POST']) +@login_required +def fileset_create_toml_view(): + return generic_entity_toml_edit(None, 'fileset', None, 'entity_create_toml.html') + +@app.route('/fileset//edit/toml', methods=['GET', 'POST']) +@login_required +def fileset_edit_toml(ident): + return generic_entity_toml_edit(None, 'fileset', ident, 'entity_edit_toml.html') + +@app.route('/editgroup//fileset//edit/toml', methods=['GET', 'POST']) +@login_required +def fileset_editgroup_edit_toml(editgroup_id, ident): + return generic_entity_toml_edit(editgroup_id, 'fileset', ident, 'entity_edit_toml.html') + +@app.route('/webcapture/create/toml', methods=['GET', 'POST']) +@login_required +def webcapture_create_toml_view(): + return generic_entity_toml_edit(None, 'webcapture', None, 'entity_create_toml.html') + +@app.route('/webcapture//edit/toml', methods=['GET', 'POST']) +@login_required +def webcapture_edit_toml(ident): + return generic_entity_toml_edit(None, 'webcapture', ident, 'entity_edit_toml.html') + +@app.route('/editgroup//webcapture//edit/toml', methods=['GET', 'POST']) +@login_required +def webcapture_editgroup_edit_toml(editgroup_id, ident): + return generic_entity_toml_edit(editgroup_id, 'webcapture', ident, 'entity_edit_toml.html') + +@app.route('/release/create/toml', methods=['GET', 'POST']) +@login_required +def release_create_toml_view(): + return generic_entity_toml_edit(None, 'release', None, 'entity_create_toml.html') + +@app.route('/release//edit/toml', methods=['GET', 'POST']) +@login_required +def release_edit_toml(ident): + return generic_entity_toml_edit(None, 'release', ident, 'entity_edit_toml.html') + +@app.route('/editgroup//release//edit/toml', methods=['GET', 'POST']) +@login_required +def release_editgroup_edit_toml(editgroup_id, ident): + return generic_entity_toml_edit(editgroup_id, 'release', ident, 'entity_edit_toml.html') + +@app.route('/work/create/toml', methods=['GET', 'POST']) +@login_required +def work_create_toml_view(): + return generic_entity_toml_edit(None, 'work', None, 'entity_create_toml.html') + +@app.route('/work//edit/toml', methods=['GET', 'POST']) +@login_required +def work_edit_toml(ident): + return generic_entity_toml_edit(None, 'work', ident, 'entity_edit_toml.html') + +@app.route('/editgroup//work//edit/toml', methods=['GET', 'POST']) +@login_required +def work_editgroup_edit_toml(editgroup_id, ident): + return generic_entity_toml_edit(editgroup_id, 'work', ident, 'entity_edit_toml.html') + +### TOML-Only Editing Redirects ################################################ @app.route('/creator/create', methods=['GET']) +@login_required def creator_create_view(): - return abort(404) + return redirect('/creator/create/toml') @app.route('/creator//edit', methods=['GET']) +@login_required def creator_edit(ident): - return render_template('entity_edit.html'), 404 + return redirect(f'/creator/{ident}/edit/toml') @app.route('/editgroup//creator//edit', methods=['GET', 'POST']) +@login_required def creator_editgroup_edit(editgroup_id, ident): - return abort(404) - -@app.route('/editgroup//creator/edit//delete', methods=['POST']) -def creator_edit_delete(editgroup_id, edit_id): - return abort(404) + return redirect(f'/editgroup/{editgroup_id}/creator/{ident}/edit/toml') @app.route('/fileset/create', methods=['GET']) +@login_required def fileset_create_view(): - return abort(404) + return redirect('/fileset/create/toml') @app.route('/fileset//edit', methods=['GET']) +@login_required def fileset_edit(ident): - return render_template('entity_edit.html'), 404 + return redirect(f'/fileset/{ident}/edit/toml') @app.route('/editgroup//fileset//edit', methods=['GET', 'POST']) +@login_required def fileset_editgroup_edit(editgroup_id, ident): - return abort(404) - -@app.route('/editgroup//fileset/edit//delete', methods=['POST']) -def fileset_edit_delete(editgroup_id, edit_id): - return abort(404) + return redirect(f'/editgroup/{editgroup_id}/fileset/{ident}/edit/toml') @app.route('/webcapture/create', methods=['GET']) +@login_required def webcapture_create_view(): - return abort(404) + return redirect('/webcapture/create/toml') @app.route('/webcapture//edit', methods=['GET']) +@login_required def webcapture_edit(ident): - return render_template('entity_edit.html'), 404 + return redirect(f'/webcapture/{ident}/edit/toml') @app.route('/editgroup//webcapture//edit', methods=['GET', 'POST']) +@login_required def webcapture_editgroup_edit(editgroup_id, ident): - return abort(404) - -@app.route('/editgroup//webcapture/edit//delete', methods=['POST']) -def webcapture_edit_delete(editgroup_id, edit_id): - return abort(404) + return redirect(f'/editgroup/{editgroup_id}/webcapture/{ident}/edit/toml') @app.route('/work/create', methods=['GET']) +@login_required def work_create_view(): - return abort(404) + return redirect('/work/create/toml') @app.route('/work//edit', methods=['GET']) +@login_required def work_edit(ident): - return render_template('entity_edit.html'), 404 + return redirect(f'/work/{ident}/edit/toml') @app.route('/editgroup//work//edit', methods=['GET', 'POST']) +@login_required def work_editgroup_edit(editgroup_id, ident): - return abort(404) - -@app.route('/editgroup//work/edit//delete', methods=['POST']) -def work_edit_delete(editgroup_id, edit_id): - return abort(404) + return redirect(f'/editgroup/{editgroup_id}/work/{ident}/edit/toml') diff --git a/python/fatcat_web/templates/container_create.html b/python/fatcat_web/templates/container_create.html index 5786d05d..be8c5671 100644 --- a/python/fatcat_web/templates/container_create.html +++ b/python/fatcat_web/templates/container_create.html @@ -9,13 +9,15 @@ a journal (eg, "New England Journal of Medicine"), conference proceedings, a book series, or a blog. Not all publications are in a container.
+

Experienced users can also use the TOML + creation form to access all metadata fields in a raw format. {% endblock %} {% block edit_form_suffix %}

- New entity will be part of the current editgroup, which needs to be + New container entity will be part of the current editgroup, which needs to be submited and approved before the entity will formally be included in the catalog. diff --git a/python/fatcat_web/templates/container_edit.html b/python/fatcat_web/templates/container_edit.html index 5188ce0d..fd07b3da 100644 --- a/python/fatcat_web/templates/container_edit.html +++ b/python/fatcat_web/templates/container_edit.html @@ -7,6 +7,11 @@

Edit Container Entity

+ +

Experienced users can also use the TOML editing form to access all metadata + fields in a raw format. {% endblock %}

See the catalog diff --git a/python/fatcat_web/templates/edit_macros.html b/python/fatcat_web/templates/edit_macros.html index 60c17aa9..d4839373 100644 --- a/python/fatcat_web/templates/edit_macros.html +++ b/python/fatcat_web/templates/edit_macros.html @@ -55,7 +55,7 @@ {% macro editgroup_dropdown(form, editgroup=None, potential_editgroups=None) -%} {% if editgroup %}

You are updating an existing un-merged editgroup: {{ editgroup.editgroup_id }}. -

Description: {{ editgroup.description }} +

Description: {{ editgroup.description or "" }} {% else %} {% if not potential_editgroups %}

You have no un-submitted editgroups in progress; a new one will be diff --git a/python/fatcat_web/templates/editgroup_view.html b/python/fatcat_web/templates/editgroup_view.html index e8146d19..a36dc3e5 100644 --- a/python/fatcat_web/templates/editgroup_view.html +++ b/python/fatcat_web/templates/editgroup_view.html @@ -25,7 +25,7 @@ updated {% endif %} [view edit] - {% if auth_to.edit and not editgroup.changelog_index and not editgroup.submitted and entity_type in ('release', 'file', 'container') %} + {% if auth_to.edit and not editgroup.changelog_index and not editgroup.submitted %} [re-edit] diff --git a/python/fatcat_web/templates/entity_edit.html b/python/fatcat_web/templates/entity_edit.html deleted file mode 100644 index 97f7bf46..00000000 --- a/python/fatcat_web/templates/entity_edit.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "base.html" %} -{% block body %} - -

Not Implemented

- -

Entity editing via the web interface isn't implemented yet. Sorry! - -{% endblock %} diff --git a/python/fatcat_web/templates/entity_edit_toml.html b/python/fatcat_web/templates/entity_edit_toml.html index 4b6e7b6d..807e4d2b 100644 --- a/python/fatcat_web/templates/entity_edit_toml.html +++ b/python/fatcat_web/templates/entity_edit_toml.html @@ -37,3 +37,15 @@ {% endblock %} {% endblock %} +{% block postscript %} + + +{% endblock %} diff --git a/python/fatcat_web/templates/file_create.html b/python/fatcat_web/templates/file_create.html index a7c99b96..affcfb6e 100644 --- a/python/fatcat_web/templates/file_create.html +++ b/python/fatcat_web/templates/file_create.html @@ -5,13 +5,15 @@

Create New File Entity

+

Experienced users can also use the TOML + creation form to access all metadata fields in a raw format. {% endblock %} {% block edit_form_suffix %}

- New entity will be part of the current editgroup, which needs to be + New file entity will be part of the current editgroup, which needs to be submited and approved before the entity will formally be included in the catalog. diff --git a/python/fatcat_web/templates/file_edit.html b/python/fatcat_web/templates/file_edit.html index e8a421b3..b7876fc5 100644 --- a/python/fatcat_web/templates/file_edit.html +++ b/python/fatcat_web/templates/file_edit.html @@ -7,6 +7,11 @@

Edit File Entity

+ +

Experienced users can also use the TOML editing form to access all metadata + fields in a raw format. {% endblock %}

See the catalog diff --git a/python/fatcat_web/templates/home.html b/python/fatcat_web/templates/home.html index 4557e212..de32d6a4 100644 --- a/python/fatcat_web/templates/home.html +++ b/python/fatcat_web/templates/home.html @@ -171,11 +171,10 @@ Creator
authors, editors, translators +
Create {% if config.FATCAT_DOMAIN == 'fatcat.wiki' %} - Author {% else %} - Author (prod)
Dummy
Realistic @@ -206,11 +205,10 @@

File Set
datasets, suplementary materials + Create {% if config.FATCAT_DOMAIN == 'fatcat.wiki' %} - Dataset {% else %} - Dataset (prod)
Dummy
Realistic @@ -218,12 +216,11 @@ Web Capture
HTML and interactive articles, blog posts + Create {% if config.FATCAT_DOMAIN == 'fatcat.wiki' %} - D-Lib
Blog Post {% else %} - D-Lib (prod)
Dummy
Realistic @@ -231,11 +228,10 @@ Work
for grouping Releases + Create {% if config.FATCAT_DOMAIN == 'fatcat.wiki' %} - Paper {% else %} - Paper (prod)
Dummy
Realistic diff --git a/python/fatcat_web/templates/release_create.html b/python/fatcat_web/templates/release_create.html index 5ec2efe5..4f5dabd7 100644 --- a/python/fatcat_web/templates/release_create.html +++ b/python/fatcat_web/templates/release_create.html @@ -5,13 +5,15 @@

Create New Release Entity

+

Experienced users can also use the TOML + creation form to access all metadata fields in a raw format. {% endblock %} {% block edit_form_suffix %}

- New entity will be part of the current editgroup, which needs to be + New release entity will be part of the current editgroup, which needs to be submited and approved before the entity will formally be included in the catalog. diff --git a/python/fatcat_web/templates/release_edit.html b/python/fatcat_web/templates/release_edit.html index a4a7e56f..21c8cf68 100644 --- a/python/fatcat_web/templates/release_edit.html +++ b/python/fatcat_web/templates/release_edit.html @@ -7,6 +7,11 @@

Edit Release Entity

+ +

Experienced users can also use the TOML editing form to access all metadata + fields in a raw format. {% endblock %}

See the catalog @@ -14,6 +19,7 @@ href="https://guide.fatcat.wiki/editing_quickstart.html">the editing tutorial if this is your first time making an edit. + {{ form.hidden_tag() }}

Editgroup Metadata

diff --git a/python/tests/web_editing.py b/python/tests/web_editing.py index 17f4f5ae..ea244388 100644 --- a/python/tests/web_editing.py +++ b/python/tests/web_editing.py @@ -2,7 +2,7 @@ from fixtures import * -def test_web_release_create_merge(app_admin, api): +def test_web_release_create_accept(app_admin, api): eg = quick_eg(api) @@ -129,18 +129,60 @@ def test_web_file_create(app_admin, api): follow_redirects=True) assert rv.status_code == 200 +DUMMY_DEMO_ENTITIES = { + 'container': 'aaaaaaaaaaaaaeiraaaaaaaaam', + 'creator': 'aaaaaaaaaaaaaircaaaaaaaaaq', + 'file': 'aaaaaaaaaaaaamztaaaaaaaaam', + 'fileset': 'aaaaaaaaaaaaaztgaaaaaaaaai', + 'webcapture': 'aaaaaaaaaaaaa53xaaaaaaaaai', + 'release': 'aaaaaaaaaaaaarceaaaaaaaaai', + 'work': 'aaaaaaaaaaaaavkvaaaaaaaaai', +} def test_web_edit_get(app_admin): # these are all existing entities - rv = app_admin.get('/release/aaaaaaaaaaaaarceaaaaaaaaai/edit') - assert rv.status_code == 200 - assert b'A bigger example' in rv.data - - rv = app_admin.get('/file/aaaaaaaaaaaaamztaaaaaaaaam/edit') - assert rv.status_code == 200 - assert b'ffc1005680cb620eec4c913437dfabbf311b535cfe16cbaeb2faec1f92afc362' in rv.data - - rv = app_admin.get('/container/aaaaaaaaaaaaaeiraaaaaaaaam/edit') - assert rv.status_code == 200 - assert b'1549-1277' in rv.data + for entity_type in ['release', 'file', 'container']: + rv = app_admin.get(f'/{entity_type}/{DUMMY_DEMO_ENTITIES[entity_type]}/edit') + assert rv.status_code == 200 + if entity_type == 'release': + assert b'A bigger example' in rv.data + elif entity_type == 'file': + assert b'ffc1005680cb620eec4c913437dfabbf311b535cfe16cbaeb2faec1f92afc362' in rv.data + elif entity_type == 'container': + assert b'1549-1277' in rv.data + + rv = app_admin.get(f'/{entity_type}/{DUMMY_DEMO_ENTITIES[entity_type]}/edit/toml') + assert rv.status_code == 200 + if entity_type == 'release': + assert b'A bigger example' in rv.data + elif entity_type == 'file': + assert b'ffc1005680cb620eec4c913437dfabbf311b535cfe16cbaeb2faec1f92afc362' in rv.data + elif entity_type == 'container': + assert b'1549-1277' in rv.data + + # TOML-only endpoints + for entity_type in ['creator', 'fileset', 'webcapture', 'work']: + rv = app_admin.get(f'/{entity_type}/{DUMMY_DEMO_ENTITIES[entity_type]}/edit') + assert rv.status_code == 302 + + rv = app_admin.get(f'/{entity_type}/{DUMMY_DEMO_ENTITIES[entity_type]}/edit/toml') + assert rv.status_code == 200 + + +def test_web_create_get(app_admin): + + for entity_type in ['release', 'file', 'container']: + rv = app_admin.get(f'/{entity_type}/create') + assert rv.status_code == 200 + + rv = app_admin.get(f'/{entity_type}/create/toml') + assert rv.status_code == 200 + + # these are TOML only + for entity_type in ['creator', 'fileset', 'webcapture', 'work']: + rv = app_admin.get(f'/{entity_type}/create') + assert rv.status_code == 302 + + rv = app_admin.get(f'/{entity_type}/create/toml') + assert rv.status_code == 200 diff --git a/python/tests/web_entity_views.py b/python/tests/web_entity_views.py index b01bd815..7b973ef2 100644 --- a/python/tests/web_entity_views.py +++ b/python/tests/web_entity_views.py @@ -210,9 +210,9 @@ def test_web_creator(app): rv = app.get('/creator/aaaaaaaaaaaaaircaaaaaaaaai') assert rv.status_code == 200 rv = app.get('/creator/aaaaaaaaaaaaaircaaaaaaaaai/edit') - assert rv.status_code == 404 + assert rv.status_code == 302 rv = app.get('/creator/create') - assert rv.status_code == 404 + assert rv.status_code == 302 def test_web_file(app): @@ -266,9 +266,9 @@ def test_web_fileset(app): rv = app.get('/fileset/aaaaaaaaaaaaaztgaaaaaaaaai') assert rv.status_code == 200 rv = app.get('/fileset/aaaaaaaaaaaaaztgaaaaaaaaai/edit') - assert rv.status_code == 404 + assert rv.status_code == 302 rv = app.get('/fileset/create') - assert rv.status_code == 404 + assert rv.status_code == 302 def test_web_webcatpure(app): @@ -277,9 +277,9 @@ def test_web_webcatpure(app): rv = app.get('/webcapture/aaaaaaaaaaaaa53xaaaaaaaaai') assert rv.status_code == 200 rv = app.get('/webcapture/aaaaaaaaaaaaa53xaaaaaaaaai/edit') - assert rv.status_code == 404 + assert rv.status_code == 302 rv = app.get('/webcapture/create') - assert rv.status_code == 404 + assert rv.status_code == 302 def test_web_release(app): @@ -376,6 +376,6 @@ def test_web_work(app): rv = app.get('/work/aaaaaaaaaaaaavkvaaaaaaaaai') assert rv.status_code == 200 rv = app.get('/work/aaaaaaaaaaaaavkvaaaaaaaaai/edit') - assert rv.status_code == 404 + assert rv.status_code == 302 rv = app.get('/work/create') - assert rv.status_code == 404 + assert rv.status_code == 302 -- cgit v1.2.3 From 29593e492b632c8884c31993230d258d646e3d8c Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Thu, 30 Jul 2020 20:31:56 -0700 Subject: routes: handle case of viewing deleted entity in editgroup context Eg, consider deleting an entity. When viewing the editgroup, want to be able to click the deleted entity and see the "deleted entity" page instead of a generic 404. --- python/fatcat_web/entity_helpers.py | 27 ++++++++++++++++++++++++- python/fatcat_web/routes.py | 2 +- python/fatcat_web/templates/deleted_entity.html | 2 +- python/fatcat_web/templates/entity_macros.html | 12 ++++++----- 4 files changed, 35 insertions(+), 8 deletions(-) (limited to 'python/fatcat_web/templates') diff --git a/python/fatcat_web/entity_helpers.py b/python/fatcat_web/entity_helpers.py index c9a57290..7156b9be 100644 --- a/python/fatcat_web/entity_helpers.py +++ b/python/fatcat_web/entity_helpers.py @@ -1,5 +1,6 @@ from flask import abort +from fatcat_openapi_client import * from fatcat_openapi_client.rest import ApiException, ApiValueError from fatcat_tools.transforms import * from fatcat_web import api @@ -148,6 +149,26 @@ def generic_get_entity_revision(entity_type, revision_id): except ApiValueError: abort(400) +def generic_deleted_entity(entity_type, ident): + if entity_type == 'container': + entity = ContainerEntity() + elif entity_type == 'creator': + entity = CreatorEntity() + elif entity_type == 'file': + entity = FileEntity() + elif entity_type == 'fileset': + entity = FilesetEntity() + elif entity_type == 'webcapture': + entity = WebcaptureEntity() + elif entity_type == 'release': + entity = ReleaseEntity() + elif entity_type == 'work': + entity = WorkEntity() + else: + raise NotImplementedError + entity.ident = ident + return entity + def generic_get_editgroup_entity(editgroup, entity_type, ident): if entity_type == 'container': edits = editgroup.edits.containers @@ -166,14 +187,18 @@ def generic_get_editgroup_entity(editgroup, entity_type, ident): else: raise NotImplementedError revision_id = None + edit = None for e in edits: if e.ident == ident: revision_id = e.revision edit = e break - if not revision_id: + if not edit: # couldn't find relevant edit in this editgroup abort(404) + if not revision_id: + # deletion, presumably + return generic_deleted_entity(entity_type, ident), edit try: entity = generic_get_entity_revision(entity_type, revision_id) diff --git a/python/fatcat_web/routes.py b/python/fatcat_web/routes.py index 74805c69..da2bb6cf 100644 --- a/python/fatcat_web/routes.py +++ b/python/fatcat_web/routes.py @@ -227,7 +227,7 @@ def generic_editgroup_entity_view(editgroup_id, entity_type, ident, view_templat entity, edit = generic_get_editgroup_entity(editgroup, entity_type, ident) - if entity.state == "deleted": + if entity.revision is None or entity.state == "deleted": return render_template('deleted_entity.html', entity=entity, entity_type=entity_type, editgroup=editgroup) diff --git a/python/fatcat_web/templates/deleted_entity.html b/python/fatcat_web/templates/deleted_entity.html index eefc87cf..4c6b14b6 100644 --- a/python/fatcat_web/templates/deleted_entity.html +++ b/python/fatcat_web/templates/deleted_entity.html @@ -30,7 +30,7 @@ Entity Type: {{ entity_type }}
-{{ entity_macros.fatcat_bits(entity, entity_type, "") }} +{{ entity_macros.fatcat_bits(entity, entity_type, "", editgroup=editgroup) }}
diff --git a/python/fatcat_web/templates/entity_macros.html b/python/fatcat_web/templates/entity_macros.html index 718c071c..0ce646bf 100644 --- a/python/fatcat_web/templates/entity_macros.html +++ b/python/fatcat_web/templates/entity_macros.html @@ -33,7 +33,7 @@ {% if entity.state %} State is "{{ entity.state }}". {% endif %} - {% if entity.state != "deleted" %} + {% if entity.revision %} Revision:
{{ entity.revision }} {% endif %} @@ -43,11 +43,13 @@ {%- else -%} https://api.{{ config.FATCAT_DOMAIN }} {%- endif -%} - /v0/{{ entity_type }} - {%- if entity.ident -%} - /{{ entity.ident }} + /v0 + {%- if editgroup and entity.ident -%} + /editgroup/{{ editgroup.editgroup_id }}{# /{{ entity_type }}/{{ entity.ident }} #} + {%- elif entity.ident -%} + /{{ entity_type }}/{{ entity.ident }} {%- elif entity.revision -%} - /rev/{{ entity.revision }} + /{{ entity_type }}/rev/{{ entity.revision }} {% endif %} {% if expand %}?expand={{ expand}}{% endif %}"> As JSON object via API -- cgit v1.2.3 From b7aed96075187af72b510c55e762d1e13c0a5306 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Thu, 30 Jul 2020 20:35:32 -0700 Subject: implement webface entity deletion --- python/fatcat_web/editing_routes.py | 229 ++++++++++++++++++++++--- python/fatcat_web/templates/entity_delete.html | 49 ++++++ python/tests/web_editing.py | 57 ++++++ 3 files changed, 308 insertions(+), 27 deletions(-) create mode 100644 python/fatcat_web/templates/entity_delete.html (limited to 'python/fatcat_web/templates') diff --git a/python/fatcat_web/editing_routes.py b/python/fatcat_web/editing_routes.py index e84b14f7..8e3b03b0 100644 --- a/python/fatcat_web/editing_routes.py +++ b/python/fatcat_web/editing_routes.py @@ -1,4 +1,6 @@ +from typing import Optional + from flask import render_template, abort, redirect, session, flash from flask_login import login_required @@ -63,6 +65,28 @@ def generic_entity_delete_edit(user_api, entity_type: str, editgroup_id: str, ed else: raise ae +def generic_entity_delete_entity(user_api, entity_type: str, editgroup_id: str, entity_ident: str) -> None: + try: + if entity_type == 'container': + edit = user_api.delete_container(editgroup_id, entity_ident) + elif entity_type == 'creator': + edit = user_api.delete_creator(editgroup_id, entity_ident) + elif entity_type == 'file': + edit = user_api.delete_file(editgroup_id, entity_ident) + elif entity_type == 'fileset': + edit = user_api.delete_fileset(editgroup_id, entity_ident) + elif entity_type == 'webcapture': + edit = user_api.delete_webcapture(editgroup_id, entity_ident) + elif entity_type == 'release': + edit = user_api.delete_release(editgroup_id, entity_ident) + elif entity_type == 'work': + edit = user_api.delete_work(editgroup_id, entity_ident) + else: + raise NotImplementedError + except ApiException as ae: + raise ae + return edit + def generic_entity_update_from_toml(user_api, entity_type: str, editgroup_id: str, existing_ident, toml_str: str) -> EntityEdit: if entity_type == 'container': entity = entity_from_toml(toml_str, ContainerEntity) @@ -359,6 +383,87 @@ def generic_entity_toml_edit(editgroup_id, entity_type, existing_ident, edit_tem existing_ident=existing_ident, editgroup=editgroup, potential_editgroups=potential_editgroups), status +def generic_entity_delete(editgroup_id: Optional[str], entity_type: str, existing_ident: str): + """ + Similar to generic_entity_edit(), but for deleting entities. This is a bit + simpler! + + Handles both creation and update/edit paths. + """ + + # fetch editgroup (if set) or 404 + editgroup = None + if editgroup_id: + try: + editgroup = api.get_editgroup(editgroup_id) + except ApiException as ae: + raise ae + + # check that editgroup is edit-able + if editgroup.changelog_index != None: + flash("Editgroup already merged") + abort(400) + + # fetch entity (if set) or 404 + existing = None + existing_edit = None + if editgroup and existing_ident: + existing, existing_edit = generic_get_editgroup_entity(editgroup, entity_type, existing_ident) + elif existing_ident: + existing = generic_get_entity(entity_type, existing_ident) + + # parse form (if submitted) + status = 200 + form = EntityEditForm() + + if form.is_submitted(): + if form.validate_on_submit(): + # API on behalf of user + user_api = auth_api(session['api_token']) + if not editgroup: + editgroup = form_editgroup_get_or_create(user_api, form) + + if editgroup: + # TODO: some danger of wiping database state here is + # "updated edit" causes, eg, a 4xx error. Better to allow + # this in the API itself. For now, form validation *should* + # catch most errors, and if not editor can hit back and try + # again. This means, need to allow failure of deletion. + if existing_edit: + # need to clear revision on object or this becomes just + # a "update pointer" edit + existing.revision = None + generic_entity_delete_edit(user_api, entity_type, editgroup.editgroup_id, existing_edit.edit_id) + try: + edit = generic_entity_delete_entity(user_api, entity_type, editgroup.editgroup_id, existing.ident) + except ApiException as ae: + app.log.warning(ae) + raise ae + if status == 200: + return redirect('/editgroup/{}/{}/{}'.format(editgroup.editgroup_id, entity_type, edit.ident)) + else: + status = 400 + elif form.errors: + status = 400 + app.log.info("form errors (did not validate): {}".format(form.errors)) + + else: # form is not submitted + if existing: + form = EntityTomlForm.from_entity(existing) + + editor_editgroups = api.get_editor_editgroups(session['editor']['editor_id'], limit=20) + potential_editgroups = [e for e in editor_editgroups if e.changelog_index == None and e.submitted == None] + + if not form.is_submitted(): + # default to most recent not submitted, fallback to "create new" + form.editgroup_id.data = "" + if potential_editgroups: + form.editgroup_id.data = potential_editgroups[0].editgroup_id + + return render_template("entity_delete.html", form=form, entity_type=entity_type, + existing_ident=existing_ident, editgroup=editgroup, + potential_editgroups=potential_editgroups), status + def generic_edit_delete(editgroup_id, entity_type, edit_id): # fetch editgroup (if set) or 404 editgroup = None @@ -390,19 +495,43 @@ def container_create_view(): @app.route('/container//edit', methods=['GET', 'POST']) @login_required -def container_edit(ident): +def container_edit_view(ident): return generic_entity_edit(None, 'container', ident, 'container_edit.html') +@app.route('/container//delete', methods=['GET', 'POST']) +@login_required +def container_delete_view(ident): + return generic_entity_delete(None, 'container', ident) + @app.route('/editgroup//container//edit', methods=['GET', 'POST']) @login_required -def container_editgroup_edit(editgroup_id, ident): +def container_editgroup_edit_view(editgroup_id, ident): return generic_entity_edit(editgroup_id, 'container', ident, 'container_edit.html') +@app.route('/editgroup//container//delete', methods=['GET', 'POST']) +@login_required +def container_editgroup_delete_view(editgroup_id, ident): + return generic_entity_delete(editgroup_id, 'container', ident) + @app.route('/editgroup//container/edit//delete', methods=['POST']) @login_required def container_edit_delete(editgroup_id, edit_id): return generic_edit_delete(editgroup_id, 'container', edit_id) +@app.route('/creator//delete', methods=['GET', 'POST']) +@login_required +def creator_delete_view(ident): + return generic_entity_delete(None, 'creator', ident) + +@app.route('/editgroup//creator/edit//delete', methods=['POST']) +def creator_edit_delete(editgroup_id, edit_id): + return generic_edit_delete(editgroup_id, 'creator', edit_id) + +@app.route('/editgroup//creator//delete', methods=['GET', 'POST']) +@login_required +def creator_editgroup_delete(editgroup_id, ident): + return generic_entity_delete(editgroup_id, 'creator', ident) + @app.route('/file/create', methods=['GET', 'POST']) @login_required def file_create_view(): @@ -410,19 +539,57 @@ def file_create_view(): @app.route('/file//edit', methods=['GET', 'POST']) @login_required -def file_edit(ident): +def file_edit_view(ident): return generic_entity_edit(None, 'file', ident, 'file_edit.html') +@app.route('/file//delete', methods=['GET', 'POST']) +@login_required +def file_delete_view(ident): + return generic_entity_delete(None, 'file', ident) + @app.route('/editgroup//file//edit', methods=['GET', 'POST']) @login_required -def file_editgroup_edit(editgroup_id, ident): +def file_editgroup_edit_view(editgroup_id, ident): return generic_entity_edit(editgroup_id, 'file', ident, 'file_edit.html') +@app.route('/editgroup//file//delete', methods=['GET', 'POST']) +@login_required +def file_editgroup_delete_view(editgroup_id, ident): + return generic_entity_delete(editgroup_id, 'file', ident) + @app.route('/editgroup//file/edit//delete', methods=['POST']) @login_required def file_edit_delete(editgroup_id, edit_id): return generic_edit_delete(editgroup_id, 'file', edit_id) +@app.route('/fileset//delete', methods=['GET', 'POST']) +@login_required +def fileset_delete_view(ident): + return generic_entity_delete(None, 'fileset', ident) + +@app.route('/editgroup//fileset/edit//delete', methods=['POST']) +def fileset_edit_delete(editgroup_id, edit_id): + return generic_edit_delete(editgroup_id, 'fileset', edit_id) + +@app.route('/editgroup//fileset//delete', methods=['GET', 'POST']) +@login_required +def fileset_editgroup_delete(editgroup_id, ident): + return generic_entity_delete(editgroup_id, 'fileset', ident) + +@app.route('/webcapture//delete', methods=['GET', 'POST']) +@login_required +def webcapture_delete_view(ident): + return generic_entity_delete(None, 'webcapture', ident) + +@app.route('/editgroup//webcapture/edit//delete', methods=['POST']) +def webcapture_edit_delete(editgroup_id, edit_id): + return generic_edit_delete(editgroup_id, 'webcapture', edit_id) + +@app.route('/editgroup//webcapture//delete', methods=['GET', 'POST']) +@login_required +def webcapture_editgroup_delete(editgroup_id, ident): + return generic_entity_delete(editgroup_id, 'webcapture', ident) + @app.route('/release/create', methods=['GET', 'POST']) @login_required def release_create_view(): @@ -430,35 +597,43 @@ def release_create_view(): @app.route('/release//edit', methods=['GET', 'POST']) @login_required -def release_edit(ident): +def release_edit_view(ident): return generic_entity_edit(None, 'release', ident, 'release_edit.html') +@app.route('/release//delete', methods=['GET', 'POST']) +@login_required +def release_delete_view(ident): + return generic_entity_delete(None, 'release', ident) + @app.route('/editgroup//release//edit', methods=['GET', 'POST']) @login_required def release_editgroup_edit(editgroup_id, ident): return generic_entity_edit(editgroup_id, 'release', ident, 'release_edit.html') +@app.route('/editgroup//release//delete', methods=['GET', 'POST']) +@login_required +def release_editgroup_delete(editgroup_id, ident): + return generic_entity_delete(editgroup_id, 'release', ident) + @app.route('/editgroup//release/edit//delete', methods=['POST']) @login_required def release_edit_delete(editgroup_id, edit_id): return generic_edit_delete(editgroup_id, 'release', edit_id) -@app.route('/editgroup//creator/edit//delete', methods=['POST']) -def creator_edit_delete(editgroup_id, edit_id): - return generic_edit_delete(editgroup_id, 'creator', edit_id) - -@app.route('/editgroup//fileset/edit//delete', methods=['POST']) -def fileset_edit_delete(editgroup_id, edit_id): - return generic_edit_delete(editgroup_id, 'fileset', edit_id) - -@app.route('/editgroup//webcapture/edit//delete', methods=['POST']) -def webcapture_edit_delete(editgroup_id, edit_id): - return generic_edit_delete(editgroup_id, 'webcapture', edit_id) +@app.route('/work//delete', methods=['GET', 'POST']) +@login_required +def work_delete_view(ident): + return generic_entity_delete(None, 'work', ident) @app.route('/editgroup//work/edit//delete', methods=['POST']) def work_edit_delete(editgroup_id, edit_id): return generic_edit_delete(editgroup_id, 'work', edit_id) +@app.route('/editgroup//work//delete', methods=['GET', 'POST']) +@login_required +def work_editgroup_delete(editgroup_id, ident): + return generic_entity_delete(editgroup_id, 'work', ident) + ### TOML Views ############################################################## @app.route('/container/create/toml', methods=['GET', 'POST']) @@ -468,7 +643,7 @@ def container_create_toml_view(): @app.route('/container//edit/toml', methods=['GET', 'POST']) @login_required -def container_edit_toml(ident): +def container_edit_toml_view(ident): return generic_entity_toml_edit(None, 'container', ident, 'entity_edit_toml.html') @app.route('/editgroup//container//edit/toml', methods=['GET', 'POST']) @@ -483,7 +658,7 @@ def creator_create_toml_view(): @app.route('/creator//edit/toml', methods=['GET', 'POST']) @login_required -def creator_edit_toml(ident): +def creator_edit_toml_view(ident): return generic_entity_toml_edit(None, 'creator', ident, 'entity_edit_toml.html') @app.route('/editgroup//creator//edit/toml', methods=['GET', 'POST']) @@ -498,7 +673,7 @@ def file_create_toml_view(): @app.route('/file//edit/toml', methods=['GET', 'POST']) @login_required -def file_edit_toml(ident): +def file_edit_toml_view(ident): return generic_entity_toml_edit(None, 'file', ident, 'entity_edit_toml.html') @app.route('/editgroup//file//edit/toml', methods=['GET', 'POST']) @@ -513,7 +688,7 @@ def fileset_create_toml_view(): @app.route('/fileset//edit/toml', methods=['GET', 'POST']) @login_required -def fileset_edit_toml(ident): +def fileset_edit_toml_view(ident): return generic_entity_toml_edit(None, 'fileset', ident, 'entity_edit_toml.html') @app.route('/editgroup//fileset//edit/toml', methods=['GET', 'POST']) @@ -528,7 +703,7 @@ def webcapture_create_toml_view(): @app.route('/webcapture//edit/toml', methods=['GET', 'POST']) @login_required -def webcapture_edit_toml(ident): +def webcapture_edit_toml_view(ident): return generic_entity_toml_edit(None, 'webcapture', ident, 'entity_edit_toml.html') @app.route('/editgroup//webcapture//edit/toml', methods=['GET', 'POST']) @@ -543,7 +718,7 @@ def release_create_toml_view(): @app.route('/release//edit/toml', methods=['GET', 'POST']) @login_required -def release_edit_toml(ident): +def release_edit_toml_view(ident): return generic_entity_toml_edit(None, 'release', ident, 'entity_edit_toml.html') @app.route('/editgroup//release//edit/toml', methods=['GET', 'POST']) @@ -558,7 +733,7 @@ def work_create_toml_view(): @app.route('/work//edit/toml', methods=['GET', 'POST']) @login_required -def work_edit_toml(ident): +def work_edit_toml_view(ident): return generic_entity_toml_edit(None, 'work', ident, 'entity_edit_toml.html') @app.route('/editgroup//work//edit/toml', methods=['GET', 'POST']) @@ -575,7 +750,7 @@ def creator_create_view(): @app.route('/creator//edit', methods=['GET']) @login_required -def creator_edit(ident): +def creator_edit_view(ident): return redirect(f'/creator/{ident}/edit/toml') @app.route('/editgroup//creator//edit', methods=['GET', 'POST']) @@ -590,7 +765,7 @@ def fileset_create_view(): @app.route('/fileset//edit', methods=['GET']) @login_required -def fileset_edit(ident): +def fileset_edit_view(ident): return redirect(f'/fileset/{ident}/edit/toml') @app.route('/editgroup//fileset//edit', methods=['GET', 'POST']) @@ -605,7 +780,7 @@ def webcapture_create_view(): @app.route('/webcapture//edit', methods=['GET']) @login_required -def webcapture_edit(ident): +def webcapture_edit_view(ident): return redirect(f'/webcapture/{ident}/edit/toml') @app.route('/editgroup//webcapture//edit', methods=['GET', 'POST']) @@ -620,7 +795,7 @@ def work_create_view(): @app.route('/work//edit', methods=['GET']) @login_required -def work_edit(ident): +def work_edit_view(ident): return redirect(f'/work/{ident}/edit/toml') @app.route('/editgroup//work//edit', methods=['GET', 'POST']) diff --git a/python/fatcat_web/templates/entity_delete.html b/python/fatcat_web/templates/entity_delete.html new file mode 100644 index 00000000..b2e13af4 --- /dev/null +++ b/python/fatcat_web/templates/entity_delete.html @@ -0,0 +1,49 @@ +{% import "edit_macros.html" as edit_macros %} +{% extends "base.html" %} + +{% block body %} +{% block edit_form_prefix %} +
+

Delete Entity

+ + +{% endblock %} + +

See the catalog + style guide for schema notes, and the editing + tutorial if this is your first time making an edit. + + {{ form.hidden_tag() }} + +

Editgroup Metadata

+ {{ edit_macros.editgroup_dropdown(form, editgroup, potential_editgroups) }} + +

Submit

+ {{ edit_macros.form_field_basic(form.edit_description) }} + This description will be attached to the individual edit, not to the + editgroup as a whole. + +{% block edit_form_suffix %} +

+ +

+ Deletion will be part of the current editgroup, which needs to be submited and + approved before the change is included in the catalog. + +

+{% endblock %} +{% endblock %} + +{% block postscript %} + + +{% endblock %} diff --git a/python/tests/web_editing.py b/python/tests/web_editing.py index ea244388..8386badb 100644 --- a/python/tests/web_editing.py +++ b/python/tests/web_editing.py @@ -129,6 +129,57 @@ def test_web_file_create(app_admin, api): follow_redirects=True) assert rv.status_code == 200 +def test_web_file_toml_create(app_admin, api): + + eg = quick_eg(api) + + # bogus/bad submit + rv = app_admin.post('/file/create/toml', + data={ + 'editgroup_id': eg.editgroup_id, + }, + follow_redirects=True) + assert rv.status_code == 400 + + # ok/valid submit + rv = app_admin.post('/file/create/toml', + data={ + 'editgroup_id': eg.editgroup_id, + 'toml': """ +size = 12345 +sha1 = "45be56a396c4d03faaa41e055170c23534dec736" + """, + }, + follow_redirects=True) + assert rv.status_code == 200 + + # upper-case SHA-1 + rv = app_admin.post('/file/create/toml', + data={ + 'editgroup_id': eg.editgroup_id, + 'toml': """ +size = 12345 +sha1 = "45BE56A396C4D03FAAA41E055170C23534DEC736" + """, + }, + follow_redirects=True) + assert rv.status_code == 400 + +def test_web_file_delete(app_admin, api): + + eg = quick_eg(api) + + rv = app_admin.get('/file/aaaaaaaaaaaaamztaaaaaaaaam/delete') + assert rv.status_code == 200 + + rv = app_admin.post('/file/aaaaaaaaaaaaamztaaaaaaaaam/delete', + data={ + 'editgroup_id': eg.editgroup_id, + }, + follow_redirects=True) + assert rv.status_code == 200 + # NOTE: did not *accept* the deletion edit + DUMMY_DEMO_ENTITIES = { 'container': 'aaaaaaaaaaaaaeiraaaaaaaaam', 'creator': 'aaaaaaaaaaaaaircaaaaaaaaaq', @@ -186,3 +237,9 @@ def test_web_create_get(app_admin): rv = app_admin.get(f'/{entity_type}/create/toml') assert rv.status_code == 200 + +def test_web_edit_delete(app_admin): + + for entity_type in DUMMY_DEMO_ENTITIES.keys(): + rv = app_admin.get(f'/{entity_type}/{DUMMY_DEMO_ENTITIES[entity_type]}/delete') + assert rv.status_code == 200 -- cgit v1.2.3 From 86e712c9cc1ffc2047df4e311de845462c223586 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 31 Jul 2020 12:12:38 -0700 Subject: editing: withdrawn_status, release_year --- python/fatcat_web/forms.py | 42 +++++++++++++++------------ python/fatcat_web/templates/release_edit.html | 26 +++++++++++++---- 2 files changed, 44 insertions(+), 24 deletions(-) (limited to 'python/fatcat_web/templates') diff --git a/python/fatcat_web/forms.py b/python/fatcat_web/forms.py index 2efc84a3..ff885fcb 100644 --- a/python/fatcat_web/forms.py +++ b/python/fatcat_web/forms.py @@ -4,6 +4,8 @@ Note: in thoery could use, eg, https://github.com/christabor/swagger_wtforms, but can't find one that is actually maintained. """ +import datetime + import toml from flask_wtf import FlaskForm from wtforms import SelectField, DateField, StringField, IntegerField, \ @@ -31,6 +33,16 @@ release_stage_options = [ ('published', 'Published'), ('updated', 'Updated'), ] +withdrawn_status_options = [ + ('', 'Not Withdrawn (blank)'), + ('retracted', 'Retracted'), + ('withdrawn', 'Withdrawn'), + ('concern', 'Concern Noted'), + ('spam', 'Spam'), + ('legal', 'Legal Taketown'), + ('safety', 'Public Safety'), + ('national-security', 'National Security'), +] role_type_options = [ ('author', 'Author'), ('editor', 'Editor'), @@ -70,25 +82,13 @@ RELEASE_SIMPLE_ATTRS = ['title', 'original_title', 'work_id', 'container_id', RELEASE_EXTID_ATTRS = ['doi', 'wikidata_qid', 'isbn13', 'pmid', 'pmcid'] -def valid_date(form, field): - try: - datetime.date.fromisoformat(field.data) - except ValueError as ve: - raise ValidationError( - f"Date must be valid ISO format (like '2017-04-20'): {field.data}") - def valid_year(form, field): - try: - year = int(field.data) - except ValueError as ve: - raise ValidationError( - f"Date must be valid ISO format (like '2017-04-20'): {field.data}") - if year > datetime.date.today().year + 5: + if field.data > datetime.date.today().year + 5: raise ValidationError( - f"Year is too far in the future: {year}") - if year < 10: + f"Year is too far in the future: {field.data}") + if field.data < 10: raise ValidationError( - f"Year is too far in the past: {year}") + f"Year is too far in the past: {field.data}") def valid_2char_ascii(form, field): if len(field.data) != 2 or len(field.data.encode('utf-8')) != 2 or not field.data.isalpha() or field.data != field.data.lower(): @@ -115,9 +115,13 @@ class ReleaseEntityForm(EntityEditForm): choices=release_type_options, default='') release_stage = SelectField(choices=release_stage_options) + withdrawn_status = SelectField("Withdrawn Status", + [validators.Optional(True)], + choices=withdrawn_status_options, + default='') release_date = DateField('Release Date', - [validators.Optional(True), valid_date]) - release_year = DateField('Release Year', + [validators.Optional(True)]) + release_year = IntegerField('Release Year', [validators.Optional(True), valid_year]) doi = StringField('DOI', [validators.Regexp(r'^10\..*\/.*', message="DOI must be valid"), @@ -185,6 +189,8 @@ class ReleaseEntityForm(EntityEditForm): if a == '': a = None setattr(re.ext_ids, extid_attr, a) + if self.release_date.data: + re.release_year = self.release_date.data.year # bunch of complexity here to preserve old contrib metadata (eg, # affiliation and extra) not included in current forms # TODO: this may be broken; either way needs tests diff --git a/python/fatcat_web/templates/release_edit.html b/python/fatcat_web/templates/release_edit.html index 21c8cf68..50f73b8c 100644 --- a/python/fatcat_web/templates/release_edit.html +++ b/python/fatcat_web/templates/release_edit.html @@ -27,21 +27,22 @@

The Basics

+ + {{ edit_macros.form_field_inline(form.release_type, "required") }} + {{ edit_macros.form_field_inline(form.title, "required") }} + {{ edit_macros.form_field_inline(form.original_title) }} +
- {{ edit_macros.form_field_basic(form.release_type, "required") }} - {{ edit_macros.form_field_basic(form.release_stage) }} + {{ edit_macros.form_field_basic(form.release_year) }} + {{ edit_macros.form_field_basic(form.release_date) }}
- {{ edit_macros.form_field_inline(form.title, "required") }} - {{ edit_macros.form_field_inline(form.original_title) }} - {{ edit_macros.form_field_inline(form.work_id) }} - {{ edit_macros.form_field_inline(form.release_date) }}
@@ -53,6 +54,19 @@
+
+
+
+
+ {{ edit_macros.form_field_basic(form.release_stage) }} + {{ edit_macros.form_field_basic(form.withdrawn_status) }} +
+
+
+
+ + {{ edit_macros.form_field_inline(form.work_id) }} +

Contributors

-- cgit v1.2.3 From 31f59b4b0ba7ff95b685c8826a7d019fb142f65c Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Fri, 31 Jul 2020 12:53:56 -0700 Subject: web: add links to deletion pages from edit pages --- python/fatcat_web/templates/container_edit.html | 3 +++ python/fatcat_web/templates/entity_edit_toml.html | 4 ++++ python/fatcat_web/templates/file_edit.html | 3 +++ python/fatcat_web/templates/release_edit.html | 3 +++ 4 files changed, 13 insertions(+) (limited to 'python/fatcat_web/templates') diff --git a/python/fatcat_web/templates/container_edit.html b/python/fatcat_web/templates/container_edit.html index fd07b3da..99f77d53 100644 --- a/python/fatcat_web/templates/container_edit.html +++ b/python/fatcat_web/templates/container_edit.html @@ -12,6 +12,9 @@ %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/container/{{ existing_ident }}/edit/toml">TOML editing form to access all metadata fields in a raw format. + {% if not editgroup %} + You can also delete this entity. + {% endif %} {% endblock %}

See the catalog diff --git a/python/fatcat_web/templates/entity_edit_toml.html b/python/fatcat_web/templates/entity_edit_toml.html index 807e4d2b..64768d6e 100644 --- a/python/fatcat_web/templates/entity_edit_toml.html +++ b/python/fatcat_web/templates/entity_edit_toml.html @@ -7,6 +7,10 @@

Edit Entity (TOML mode)

+ + {% if not editgroup %} +

You can also delete this entity. + {% endif %} {% endblock %}

See the catalog diff --git a/python/fatcat_web/templates/file_edit.html b/python/fatcat_web/templates/file_edit.html index b7876fc5..745b0c41 100644 --- a/python/fatcat_web/templates/file_edit.html +++ b/python/fatcat_web/templates/file_edit.html @@ -12,6 +12,9 @@ %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/file/{{ existing_ident }}/edit/toml">TOML editing form to access all metadata fields in a raw format. + {% if not editgroup %} + You can also delete this entity. + {% endif %} {% endblock %}

See the catalog diff --git a/python/fatcat_web/templates/release_edit.html b/python/fatcat_web/templates/release_edit.html index 50f73b8c..c26c9850 100644 --- a/python/fatcat_web/templates/release_edit.html +++ b/python/fatcat_web/templates/release_edit.html @@ -12,6 +12,9 @@ %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/release/{{ existing_ident }}/edit/toml">TOML editing form to access all metadata fields in a raw format. + {% if not editgroup %} + You can also delete this entity. + {% endif %} {% endblock %}

See the catalog -- cgit v1.2.3