diff options
| author | Bryan Newbold <bnewbold@robocracy.org> | 2019-04-01 20:48:13 -0700 | 
|---|---|---|
| committer | Bryan Newbold <bnewbold@robocracy.org> | 2019-04-01 20:48:13 -0700 | 
| commit | 20d47df8fbe49a011dbfdd4e9762903a48e26e9c (patch) | |
| tree | 758495cdad6ecf59e820abbe924b5f226d94d51d /python | |
| parent | 5527ecc2b7bd81bab9bcb065adbbccd941b85be8 (diff) | |
| download | fatcat-20d47df8fbe49a011dbfdd4e9762903a48e26e9c.tar.gz fatcat-20d47df8fbe49a011dbfdd4e9762903a48e26e9c.zip | |
basic working release creation
Diffstat (limited to 'python')
| -rw-r--r-- | python/fatcat_web/forms.py | 75 | ||||
| -rw-r--r-- | python/fatcat_web/routes.py | 44 | ||||
| -rw-r--r-- | python/fatcat_web/templates/release_create.html | 85 | 
3 files changed, 164 insertions, 40 deletions
| 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 <a href="/editgroup/{}">{}</a>' \ +                    .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/<ident>/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 %} -    <ul class="errors"> +    <div class="ui pointing red label">      {% for err in field.errors %} -        <li>{{ err }} +        {{ err }}      {% endfor %} -    </ul> +    </div>    {% endif %}  {%- endmacro %} @@ -21,12 +21,12 @@  {% macro form_field_inline(field, div_classes="") -%}  <div class="ui grid">    <div class="three wide column middle aligned right aligned" {# style="padding-right: 0.5rem;" #}> -    <div class="field inline {% if field.errors %}error{% endif %}"> +    <div class="field inline {{ div_classes }} {% if field.errors %}error{% endif %}">        {{ field.label }}      </div>    </div>    <div class="twelve wide column" {# style="padding-left: 0.5rem;" #}> -    <div class="field {% if field.errors %}error{% endif %}"> +    <div class="field {{ div_classes }} {% if field.errors %}error{% endif %}">        {{ field() }}        {{ form_field_errors(field) }}      </div> @@ -44,17 +44,32 @@  <form class="ui form" id="add_work_form" method="POST" action="/release/create">    {{ form.hidden_tag() }} -  <h3 class="ui dividing header">Edit Meta</h3> -  {{ form_field_inline(form.editgroup_id) }} -  {{ form_field_inline(form.editgroup_description) }} -  {{ form_field_inline(form.edit_description) }} +  <br> +  <div class="ui accordion"> +    <div class="{% if not editgroup_id %}active{% endif %} title"> +      <h3><i class="dropdown icon"></i>Editgroup Meta</h3> +    </div> +    <div class="{% if not editgroup_id %}active{% endif %} content"> +      {% if editgroup_id %} +        <p>You have an editgroup in progress, and this edit will be included by +        default. You can override this below. +      {% else %} +        <p>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) }} +    </div> +  </div> +  <br>    <h3 class="ui dividing header">The Basics</h3>    <div class="ui grid">      <div class="three wide column" style="padding-bottom: 0px;"></div>      <div class="twelve wide column" style="padding-bottom: 0px;">        <div class="ui equal width fields"> -        {{ form_field_basic(form.release_type) }} +        {{ form_field_basic(form.release_type, "required") }}          {{ form_field_basic(form.release_status) }}        </div>      </div> @@ -76,6 +91,7 @@      <div class="one wide column" style="padding-bottom: 0px;"></div>    </div> +  <br>    <h3 class="ui dividing header">Contributors</h3>    <div class="list-group" id="contrib_list" name="contrib_list">    {% for cform in form.contribs %} @@ -84,10 +100,14 @@          <i class="arrows alternate vertical icon"></i>        </div>        <div class="three wide column" style="padding-bottom: 0px; padding-left: 0px;"> -        {{ cform.role }} +        <div class="field {% if cform.role.errors %}error{% endif %}"> +          {{ cform.role }} +        </div>        </div>        <div class="eleven wide column" style="padding-bottom: 0px;"> -        {{ cform.raw_name}} +        <div class="field {% if cform.raw_name.errors %}error{% endif %}"> +          {{ cform.raw_name}} +        </div>        </div>        <div class="one wide column right aligned" style="padding-bottom: 0px; padding-left: 0rem;">          <button type="button" class="ui icon red button delete-contrib-button"><i class="trash icon"></i></button> @@ -101,27 +121,47 @@    </button>    <br> +  <br>    <h3 class="ui dividing header">Identifers</h3>    <br>    {{ form_field_inline(form.doi) }}    {{ form_field_inline(form.wikidata_qid) }}    {{ form_field_inline(form.isbn13) }} -  <div class="ui equal width fields"> -    {{ form_field_basic(form.pmid) }} -    {{ form_field_basic(form.pmcid) }} +  <div class="ui grid"> +    <div class="three wide column" style="padding-bottom: 0px;"></div> +    <div class="twelve wide column" style="padding-bottom: 0px;"> +      <div class="ui equal width fields"> +        {{ form_field_basic(form.pmid) }} +        {{ form_field_basic(form.pmcid) }} +      </div> +    </div> +    <div class="one wide column" style="padding-bottom: 0px;"></div>    </div> +  <br>    <h3 class="ui dividing header">Container</h3>    <br>    {{ form_field_inline(form.container_id) }}    {{ form_field_inline(form.publisher) }}    <br> -  <div class="ui equal width fields"> -    {{ form_field_basic(form.pages) }} -    {{ form_field_basic(form.volume) }} -    {{ form_field_basic(form.issue) }} +  <div class="ui grid"> +    <div class="three wide column" style="padding-bottom: 0px;"></div> +    <div class="twelve wide column" style="padding-bottom: 0px;"> +      <div class="ui equal width fields"> +        {{ form_field_basic(form.pages) }} +        {{ form_field_basic(form.volume) }} +        {{ form_field_basic(form.issue) }} +      </div> +    </div> +    <div class="one wide column" style="padding-bottom: 0px;"></div>    </div> +  <br><br> +  <h3 class="ui dividing header">Submit</h3> +  {{ form_field_basic(form.edit_description) }} +  This description will be attached to this specific action, not to the +  editgroup as a whole. +  <br><br>    <input class="ui primary submit button" type="submit" value="Create Release!">  </form> @@ -135,8 +175,11 @@  <!-- Form code -->  $(document).ready(function() { -  $('#release_type').dropdown(); -  $('#release_status').dropdown(); +  // these javascript dropdowns hide the original <input>, 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")) | 
