From 20d47df8fbe49a011dbfdd4e9762903a48e26e9c Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Mon, 1 Apr 2019 20:48:13 -0700 Subject: basic working release creation --- python/fatcat_web/forms.py | 75 ++++++++++++++++++++-- python/fatcat_web/routes.py | 44 +++++++++---- python/fatcat_web/templates/release_create.html | 85 +++++++++++++++++++------ 3 files changed, 164 insertions(+), 40 deletions(-) (limited to 'python') diff --git a/python/fatcat_web/forms.py b/python/fatcat_web/forms.py index f961faa0..baff62d7 100644 --- a/python/fatcat_web/forms.py +++ b/python/fatcat_web/forms.py @@ -5,9 +5,14 @@ but can't find one that is actually maintained. """ from flask_wtf import FlaskForm -from wtforms import SelectField, DateField, StringField, FormField, FieldList, validators +from wtforms import SelectField, DateField, StringField, IntegerField, \ + FormField, FieldList, validators + +from fatcat_client import ContainerEntity, CreatorEntity, FileEntity, \ + ReleaseEntity, ReleaseContrib release_type_options = [ + ('', 'Unknown'), ('article-journal', 'Journal Article'), ('paper-conference', 'Conference Proceeding'), ('article', 'Article (non-journal)'), @@ -17,6 +22,7 @@ release_type_options = [ ('stub', 'Invalid/Stub'), ] release_status_options = [ + ('', 'Unknown'), ('draft', 'Draft'), ('submitted', 'Submitted'), ('accepted', 'Accepted'), @@ -31,19 +37,31 @@ role_type_options = [ class EntityEditForm(FlaskForm): editgroup_id = StringField('Editgroup ID', - [validators.DataRequired()]) + [validators.Optional(True)]) editgroup_description = StringField('Editgroup Description', [validators.Optional(True)]) edit_description = StringField('Description of Changes', [validators.Optional(True)]) class ReleaseContribForm(FlaskForm): + class Meta: + # this is a sub-form, so disable CSRF + csrf = False #surname #given_name #creator_id (?) #orcid (for match?) - raw_name = StringField('Display Name') - role = SelectField(choices=role_type_options) + raw_name = StringField('Display Name', + [validators.DataRequired()]) + role = SelectField( + [validators.DataRequired()], + choices=role_type_options, + default='author') + +RELEASE_SIMPLE_ATTRS = ['title', 'original_title', 'work_id', 'container_id', + 'release_type', 'release_status', 'release_date', 'doi', 'wikidata_qid', + 'isbn13', 'pmid', 'pmcid', 'volume', 'issue', 'pages', 'publisher', + 'language', 'license_slug'] class ReleaseEntityForm(EntityEditForm): """ @@ -51,17 +69,22 @@ class ReleaseEntityForm(EntityEditForm): - field types: fatcat id - date """ - title = StringField('Title', [validators.InputRequired()]) + title = StringField('Title', + [validators.InputRequired()]) original_title = StringField('Original Title') work_id = StringField('Work FCID') container_id = StringField('Container FCID') - release_type = SelectField(choices=release_type_options) + release_type = SelectField('Release Type', + [validators.DataRequired()], + choices=release_type_options, + default='') release_status = SelectField(choices=release_status_options) release_date = DateField('Release Date', [validators.Optional(True)]) #release_year doi = StringField('DOI', - [validators.Regexp('^10\..*\/.*', message="DOI must be valid")]) + [validators.Regexp('^10\..*\/.*', message="DOI must be valid"), + validators.Optional(True)]) wikidata_qid = StringField('Wikidata QID') isbn13 = StringField('ISBN-13') pmid = StringField('PubMed Id') @@ -79,3 +102,41 @@ class ReleaseEntityForm(EntityEditForm): #refs #abstracts + def from_entity(re): + """ + Initializes form with values from an existing release entity. + """ + ref = ReleaseEntityForm() + for simple_attr in RELEASE_SIMPLE_ATTRS: + setattr(ref, simple_attr, getattr(re, simple_attr)) + return ref + + def to_entity(self): + assert(self.title.data) + entity = ReleaseEntity(title=self.title.data) + self.update_entity(entity) + return entity + + def update_entity(self, re): + """ + Mutates a release entity in place, updating fields with values from + this form. + + Form must be validated *before* calling this function. + """ + for simple_attr in RELEASE_SIMPLE_ATTRS: + a = getattr(self, simple_attr).data + # special case blank strings + if a == '': + a = None + setattr(re, simple_attr, a) + # TODO: don't update authors unless necessary! + re.contribs = [] + for c in self.contribs: + re.contribs.append(ReleaseContrib( + role=c.role.data or None, + raw_name=c.raw_name.data or None, + )) + if self.edit_description.data: + re.edit_extra = dict(description=self.edit_description.data) + diff --git a/python/fatcat_web/routes.py b/python/fatcat_web/routes.py index 81c4c5c1..eb62d338 100644 --- a/python/fatcat_web/routes.py +++ b/python/fatcat_web/routes.py @@ -5,6 +5,7 @@ from flask import Flask, render_template, send_from_directory, request, \ url_for, abort, g, redirect, jsonify, session, flash, Response from flask_login import login_required +from fatcat_client import Editgroup from fatcat_client.rest import ApiException from fatcat_tools.transforms import * from fatcat_web import app, api, auth_api, priv_api @@ -312,23 +313,42 @@ def release_lookup(): abort(ae.status) return redirect('/release/{}'.format(resp.ident)) +# XXX: figure out CSRF stuff for local dev @app.route('/release/create', methods=['GET', 'POST']) @login_required +@app.csrf.exempt def release_create(): - form = ReleaseEntityForm() + form = ReleaseEntityForm(csrf_enabled=False) # XXX: if form.is_submitted(): - print("got form!") - print(form.errors) - if form.validate_on_submit(): - return redirect('/') - else: - print("didn't validate...") - if len(form.contribs) == 0: - form.contribs.append_entry() - form.contribs.append_entry() - form.contribs.append_entry() + if form.validate_on_submit(): + # API on behalf of user + user_api = auth_api(session['api_token']) + if form.editgroup_id.data: + # TODO: error handling + eg = api.get_editgroup(form.editgroup_id.data) + else: + # if no editgroup, create one from description + eg = user_api.create_editgroup( + Editgroup(description=form.editgroup_description.data or None)) + # set this session editgroup_id + session['active_editgroup_id'] = eg.editgroup_id + print(eg.editgroup_id) # XXX: debug + flash('Started new editgroup {}' \ + .format(eg.editgroup_id, eg.editgroup_id)) + # no merge or anything hard to do; just create the entity + entity = form.to_entity() + edit = user_api.create_release(entity, editgroup_id=eg.editgroup_id) + # redirect to new release + return redirect('/release/{}'.format(edit.ident)) + elif form.errors: + print("user form errors: {}".format(form.errors)) + print("didn't validate...") + elif len(form.contribs) == 0: form.contribs.append_entry() - return render_template('release_create.html', form=form) + # TODO: check for editgroup in session + editgroup_id = session.get('active_editgroup_id', None) + return render_template('release_create.html', + form=form, editgroup_id=editgroup_id) @app.route('/release//history', methods=['GET']) def release_history(ident): diff --git a/python/fatcat_web/templates/release_create.html b/python/fatcat_web/templates/release_create.html index e3a0c9ab..7ede5dfd 100644 --- a/python/fatcat_web/templates/release_create.html +++ b/python/fatcat_web/templates/release_create.html @@ -2,11 +2,11 @@ {% macro form_field_errors(field) -%} {% if field.errors %} - + {% endif %} {%- endmacro %} @@ -21,12 +21,12 @@ {% macro form_field_inline(field, div_classes="") -%}
-
+
{{ field.label }}
-
+
{{ field() }} {{ form_field_errors(field) }}
@@ -44,17 +44,32 @@
{{ form.hidden_tag() }} -

Edit Meta

- {{ form_field_inline(form.editgroup_id) }} - {{ form_field_inline(form.editgroup_description) }} - {{ form_field_inline(form.edit_description) }} +
+
+
+

Editgroup Meta

+
+
+ {% if editgroup_id %} +

You have an editgroup in progress, and this edit will be included by + default. You can override this below. + {% else %} +

No existing editgroup is in progress (or at least, not is selected). + An existing ID can be pasted in, or if you leave that blank but give a + description, a new editgroup will be created for this edit. + {% endif %} + {{ form_field_inline(form.editgroup_id) }} + {{ form_field_inline(form.editgroup_description) }} +

+
+

The Basics

- {{ form_field_basic(form.release_type) }} + {{ form_field_basic(form.release_type, "required") }} {{ form_field_basic(form.release_status) }}
@@ -76,6 +91,7 @@
+

Contributors

{% for cform in form.contribs %} @@ -84,10 +100,14 @@
- {{ cform.role }} +
+ {{ cform.role }} +
- {{ cform.raw_name}} +
+ {{ cform.raw_name}} +
@@ -101,27 +121,47 @@
+

Identifers


{{ form_field_inline(form.doi) }} {{ form_field_inline(form.wikidata_qid) }} {{ form_field_inline(form.isbn13) }} -
- {{ form_field_basic(form.pmid) }} - {{ form_field_basic(form.pmcid) }} +
+
+
+
+ {{ form_field_basic(form.pmid) }} + {{ form_field_basic(form.pmcid) }} +
+
+
+

Container


{{ form_field_inline(form.container_id) }} {{ form_field_inline(form.publisher) }}
-
- {{ form_field_basic(form.pages) }} - {{ form_field_basic(form.volume) }} - {{ form_field_basic(form.issue) }} +
+
+
+
+ {{ form_field_basic(form.pages) }} + {{ form_field_basic(form.volume) }} + {{ form_field_basic(form.issue) }} +
+
+
+

+

Submit

+ {{ form_field_basic(form.edit_description) }} + This description will be attached to this specific action, not to the + editgroup as a whole. +

@@ -135,8 +175,11 @@ $(document).ready(function() { - $('#release_type').dropdown(); - $('#release_status').dropdown(); + // these javascript dropdowns hide the original , which breaks browser + // form focusing (eg, for required fields) :( + //$('#release_type').dropdown(); + //$('#release_status').dropdown(); + $('.ui.accordion').accordion(); var fixup_contrib_numbering = function(group_item) { items = Array.from(group_item.querySelectorAll(".list-group-item")) -- cgit v1.2.3