summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--python/fatcat_web/editing_routes.py106
-rw-r--r--python/fatcat_web/forms.py126
-rw-r--r--python/fatcat_web/templates/container_create.html167
-rw-r--r--python/fatcat_web/templates/container_edit.html65
4 files changed, 285 insertions, 179 deletions
diff --git a/python/fatcat_web/editing_routes.py b/python/fatcat_web/editing_routes.py
index dda0bc6f..4d06f6cd 100644
--- a/python/fatcat_web/editing_routes.py
+++ b/python/fatcat_web/editing_routes.py
@@ -17,33 +17,89 @@ from fatcat_web.forms import *
### Views ###################################################################
-@app.route('/container/<ident>/edit', methods=['GET'])
-def container_edit_view(ident):
+# XXX: figure out CSRF stuff for local dev
+@app.route('/container/create', methods=['GET', 'POST'])
+@login_required
+@app.csrf.exempt
+def container_create():
+ form = ContainerEntityForm(csrf_enabled=False) # XXX:
+ if form.is_submitted():
+ 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 = user_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_container(entity, editgroup_id=eg.editgroup_id)
+ # redirect to new entity
+ return redirect('/container/{}'.format(edit.ident))
+ elif form.errors:
+ print("user form errors: {}".format(form.errors))
+ print("didn't validate...")
+ if not form.is_submitted():
+ 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)
+
+# XXX: figure out CSRF stuff for local dev
+@login_required
+@app.csrf.exempt
+@app.route('/container/<ident>/edit', methods=['GET', 'POST'])
+def container_edit(ident):
+ # TODO: prev_rev interlock
+ # TODO: factor out editgroup active/creation stuff
try:
entity = api.get_container(ident)
except ApiException as ae:
abort(ae.status)
- return render_template('entity_edit.html')
-
-@app.route('/container/create', methods=['GET'])
-@login_required
-def container_create_view():
- return render_template('container_create.html')
-
-@app.route('/container/create', methods=['POST'])
-@login_required
-def container_create():
- raise NotImplementedError
- params = dict()
- for k in request.form:
- if k.startswith('container_'):
- params[k[10:]] = request.form[k]
- container = None
- #edit = api.create_container(container, params=params)
- #return redirect("/container/{}".format(edit.ident))
+ form = ContainerEntityForm(csrf_enabled=False) # XXX:
+ if form.is_submitted():
+ 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 = user_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))
+ # all the tricky logic is in the update method
+ form.update_entity(entity)
+ edit = user_api.update_container(entity.ident, entity,
+ editgroup_id=eg.editgroup_id)
+ # redirect to entity revision
+ # TODO: container_rev_view
+ return redirect('/container/{}'.format(edit.ident))
+ elif form.errors:
+ print("user form errors (didn't validate): {}".format(form.errors))
+ else:
+ form = ContainerEntityForm.from_entity(entity)
+ 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)
@app.route('/creator/<ident>/edit', methods=['GET'])
-def creator_edit_view(ident):
+def creator_edit(ident):
try:
entity = api.get_creator(ident)
except ApiException as ae:
@@ -51,7 +107,7 @@ def creator_edit_view(ident):
return render_template('entity_edit.html')
@app.route('/file/<ident>/edit', methods=['GET'])
-def file_edit_view(ident):
+def file_edit(ident):
try:
entity = api.get_file(ident)
except ApiException as ae:
@@ -59,7 +115,7 @@ def file_edit_view(ident):
return render_template('entity_edit.html')
@app.route('/fileset/<ident>/edit', methods=['GET'])
-def fileset_edit_view(ident):
+def fileset_edit(ident):
try:
entity = api.get_fileset(ident)
except ApiException as ae:
@@ -67,7 +123,7 @@ def fileset_edit_view(ident):
return render_template('entity_edit.html')
@app.route('/webcapture/<ident>/edit', methods=['GET'])
-def webcapture_edit_view(ident):
+def webcapture_edit(ident):
try:
entity = api.get_webcapture(ident)
except ApiException as ae:
@@ -144,7 +200,7 @@ def release_edit(ident):
form.update_entity(entity)
edit = user_api.update_release(entity.ident, entity,
editgroup_id=eg.editgroup_id)
- # redirect to release revision
+ # redirect to entity revision
# TODO: release_rev_view
return redirect('/release/{}'.format(edit.ident))
elif form.errors:
diff --git a/python/fatcat_web/forms.py b/python/fatcat_web/forms.py
index 632e36c6..ebe6fafe 100644
--- a/python/fatcat_web/forms.py
+++ b/python/fatcat_web/forms.py
@@ -161,3 +161,129 @@ class ReleaseEntityForm(EntityEditForm):
if self.edit_description.data:
re.edit_extra = dict(description=self.edit_description.data)
+container_type_options = (
+ ('journal', 'Journal'),
+ ('proceedings', 'Conference Proceedings'),
+ ('blog', 'Blog'),
+)
+
+CONTAINER_SIMPLE_ATTRS = ['name', 'container_type', 'publisher', 'issnl',
+ 'wikidata_qid']
+
+class ContainerEntityForm(EntityEditForm):
+ name = StringField('Name/Title',
+ [validators.InputRequired()])
+ container_type = SelectField('Container Type',
+ [validators.Optional(True)],
+ choices=container_type_options,
+ default='')
+ publisher = StringField("Publisher")
+ issnl = StringField("ISSN-L")
+ wikidata_qid = StringField('Wikidata QID')
+
+ def from_entity(re):
+ """
+ Initializes form with values from an existing container entity.
+ """
+ ref = ContainerEntityForm()
+ for simple_attr in CONTAINER_SIMPLE_ATTRS:
+ a = getattr(ref, simple_attr)
+ a.data = getattr(re, simple_attr)
+ return ref
+
+ def to_entity(self):
+ assert(self.name.data)
+ entity = ContainerEntity(name=self.name.data)
+ self.update_entity(entity)
+ return entity
+
+ def update_entity(self, ce):
+ """
+ Mutates a container entity in place, updating fields with values from
+ this form.
+
+ Form must be validated *before* calling this function.
+ """
+ for simple_attr in CONTAINER_SIMPLE_ATTRS:
+ a = getattr(self, simple_attr).data
+ # special case blank strings
+ if a == '':
+ a = None
+ setattr(ce, simple_attr, a)
+ if self.edit_description.data:
+ ce.edit_extra = dict(description=self.edit_description.data)
+
+url_rel_options = [
+ ('web', 'Public Web'),
+ ('webarchive', 'Web Archive'),
+ ('repository', 'Repository'),
+ ('social', 'Academic Social Network'),
+ ('publisher', 'Publisher'),
+ ('dweb', 'Decentralized Web'),
+]
+
+FILE_SIMPLE_ATTRS = ['size', 'md5', 'sha1', 'sha256', 'mimetype']
+
+class FileUrlForm(FlaskForm):
+ class Meta:
+ # this is a sub-form, so disable CSRF
+ csrf = False
+
+ url = StringField('Display Name',
+ [validators.DataRequired()])
+ rel = SelectField(
+ [validators.DataRequired()],
+ choices=url_rel_options,
+ default='web')
+
+class FileEntityForm(EntityEditForm):
+ size = IntegerField('Size (bytes)',
+ [validators.DataRequired()])
+ # TODO: positive definite
+ md5 = StringField("MD5")
+ sha1 = StringField("SHA-1")
+ sha256 = StringField("SHA-256")
+ urls = FieldList(FormField(FileUrlForm))
+ mimetype = StringField("Mimetype")
+ release_ids = FieldList(StringField("Release FCID"))
+
+ def from_entity(re):
+ """
+ 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)
+ return ref
+
+ def to_entity(self):
+ assert(self.name.data)
+ entity = FileEntity()
+ self.update_entity(entity)
+ return entity
+
+ def update_entity(self, fe):
+ """
+ Mutates in place, updating fields with values from this form.
+
+ Form must be validated *before* calling this function.
+ """
+ for simple_attr in FILE_SIMPLE_ATTRS:
+ a = getattr(self, simple_attr).data
+ # special case blank strings
+ if a == '':
+ a = None
+ setattr(fe, simple_attr, a)
+ re.urls = []
+ for u in self.urls:
+ re.contribs.append(FileUrl(
+ rel=u.role.data or None,
+ url=u.url.data or None,
+ ))
+ re.release_ids = []
+ for ri in self.release_ids:
+ re.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/templates/container_create.html b/python/fatcat_web/templates/container_create.html
index 15288142..5dde37bf 100644
--- a/python/fatcat_web/templates/container_create.html
+++ b/python/fatcat_web/templates/container_create.html
@@ -1,7 +1,8 @@
-{% extends "base.html" %}
-{% block body %}
+{% extends "container_edit.html" %}
+
+{% block edit_form_prefix %}
<div class="ui segment">
-<h1 class="ui header">Adding a New Container</h1>
+<h1 class="ui header">Create New Container Entity</h1>
<p>A "container" is a anything that groups publications together. For example,
a journal (eg, "New England Journal of Medicine"), conference proceedings, a
@@ -9,160 +10,18 @@ book series, or a blog.
<p>Not all publications are in a container.
-<form class="ui form" id="add_container_form" method="post" action="/container/create">
-
- <h3 class="ui dividing header">The Basics</h3>
-
- <div class="ui huge field required">
- <label>Name or Title</label>
- <input name="container_name" type="text" placeholder="Title of Container (in English)">
- </div>
-
- <div class="ui field required">
- <label>Type of Container</label>
- <select class="ui dropdown" id="container_type">
- <option value="">Primary Type</option>
- <option value="journal">Journal</option>
- <option value="book-series">Book Series</option>
- <option value="conference">Conference Proceedings</option>
- <option value="blog">Blog</option>
- <option value="other">Other</option>
- </select>
- </div>
-
- <!-- Publisher -->
- <div class="ui huge field required">
- <label>Name of Publisher</label>
- <input name="container_publisher" type="text" placeholder="Name of Publisher">
- </div>
-
- <!-- Identifier -->
- <div class="ui huge field required">
- <label>ISSN Number</label>
- <input name="container_issn" type="text" placeholder="eg, 1234-567X">
- </div>
-
- <!-- Primary/Original Language -->
- <div class="field">
- <label>Primary Language</label>
- <select class="ui search select dropdown" id="language-select">
- <option value="">Select if Appropriate</option>
- <option value="en">English</option>
- <option value="es">Spanish</option>
- <option value="">Russian</option>
- <option value="">Thai</option>
- <option value="">Indonesian</option>
- <option value="">Chinese</option>
- </select>
- </div>
-
- <!-- Subject / Categorization / Tags -->
- <div class="field">
- <label>Subject</label>
- <select multiple="" class="ui dropdown" id="subjects">
- <option value="">Select Subject/Tags</option>
- <option value="AF">Natural Sciences</option>
- <option value="AX">Humanities</option>
- <option value="AL">Arts</option>
- <option value="AL">Engineering</option>
- <option value="AL">Other</option>
- </select>
- </div>
-
- <!-- Date -->
- <!-- Container / Part-Of -->
- <!-- Region -->
- <!-- Anything Else? -->
- <h3 class="ui dividing header">Anything Else?</h3>
-
-<div class="ui submit button">Create container</div>
-
-<p><i>Entity will be created as part of the current edit group, which needs to be
-submited and approved before the entity will formally be included in the
-catalog.</i>
+<form class="ui form" id="create_container_form" method="POST" action="/container/create">
+{% endblock %}
+{% block edit_form_suffix %}
+ <br><br>
+ <input class="ui primary submit button" type="submit" value="Create Container!">
+ <p>
+ <i>New 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.</i>
</form>
-
</div>
{% endblock %}
-{% block postscript %}
-<script>
-<!-- Form validation code -->
-$(document).ready(function() {
-
- $('#add_container_form')
- .form({
- fields: {
- name: {
- identifier: 'name',
- rules: [
- {
- type : 'empty',
- prompt : 'Please enter your name'
- }
- ]
- },
- skills: {
- identifier: 'skills',
- rules: [
- {
- type : 'minCount[2]',
- prompt : 'Please select at least two skills'
- }
- ]
- },
- gender: {
- identifier: 'gender',
- rules: [
- {
- type : 'empty',
- prompt : 'Please select a gender'
- }
- ]
- },
- username: {
- identifier: 'username',
- rules: [
- {
- type : 'empty',
- prompt : 'Please enter a username'
- }
- ]
- },
- password: {
- identifier: 'password',
- rules: [
- {
- type : 'empty',
- prompt : 'Please enter a password'
- },
- {
- type : 'minLength[6]',
- prompt : 'Your password must be at least {ruleValue} characters'
- }
- ]
- },
- terms: {
- identifier: 'terms',
- rules: [
- {
- type : 'checked',
- prompt : 'You must agree to the terms and conditions'
- }
- ]
- }
- }
- })
- ;
-
- $('#container_type').dropdown();
- $('#subjects').dropdown();
- $('#language-select').dropdown();
-
- console.log("Page loaded");
-
-});
-</script>
-{% endblock %}
diff --git a/python/fatcat_web/templates/container_edit.html b/python/fatcat_web/templates/container_edit.html
new file mode 100644
index 00000000..4dbcfbd5
--- /dev/null
+++ b/python/fatcat_web/templates/container_edit.html
@@ -0,0 +1,65 @@
+{% import "edit_macros.html" as edit_macros %}
+{% extends "base.html" %}
+
+{% block body %}
+{% block edit_form_prefix %}
+<div class="ui segment">
+<h1 class="ui header">Edit Container Entity</h1>
+
+<form class="ui form" id="edit_container_form" method="POST" action="/container/{{ entity.ident }}/edit">
+{% endblock %}
+ {{ form.hidden_tag() }}
+
+ <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 %}
+ {{ edit_macros.form_field_inline(form.editgroup_id) }}
+ {{ edit_macros.form_field_inline(form.editgroup_description) }}
+ </div>
+ </div>
+
+ <h3 class="ui dividing header">The Basics</h3>
+ <br>
+ {{ edit_macros.form_field_inline(form.container_type, "required") }}
+ {{ edit_macros.form_field_inline(form.name, "required") }}
+ {{ edit_macros.form_field_inline(form.publisher) }}
+ {{ edit_macros.form_field_inline(form.issnl) }}
+ {{ edit_macros.form_field_inline(form.wikidata_qid) }}
+
+ <br>
+ <h3 class="ui dividing header">Submit</h3>
+ {{ 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 %}
+ <br><br>
+ <input class="ui primary submit button" type="submit" value="Update Container!">
+ <p>
+ <i>Edit will be part of the current editgroup, which needs to be submited and
+ approved before the change is included in the catalog.</i>
+</form>
+</div>
+{% endblock %}
+{% endblock %}
+
+{% block postscript %}
+<script>
+<!-- Form code -->
+$(document).ready(function() {
+
+ $('.ui.accordion').accordion();
+
+});
+</script>
+{% endblock %}
+