aboutsummaryrefslogtreecommitdiffstats
path: root/python/fatcat_web
diff options
context:
space:
mode:
authorBryan Newbold <bnewbold@robocracy.org>2019-04-01 20:48:13 -0700
committerBryan Newbold <bnewbold@robocracy.org>2019-04-01 20:48:13 -0700
commit20d47df8fbe49a011dbfdd4e9762903a48e26e9c (patch)
tree758495cdad6ecf59e820abbe924b5f226d94d51d /python/fatcat_web
parent5527ecc2b7bd81bab9bcb065adbbccd941b85be8 (diff)
downloadfatcat-20d47df8fbe49a011dbfdd4e9762903a48e26e9c.tar.gz
fatcat-20d47df8fbe49a011dbfdd4e9762903a48e26e9c.zip
basic working release creation
Diffstat (limited to 'python/fatcat_web')
-rw-r--r--python/fatcat_web/forms.py75
-rw-r--r--python/fatcat_web/routes.py44
-rw-r--r--python/fatcat_web/templates/release_create.html85
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"))