diff options
| author | Bryan Newbold <bnewbold@robocracy.org> | 2019-04-02 16:04:37 -0700 | 
|---|---|---|
| committer | Bryan Newbold <bnewbold@robocracy.org> | 2019-04-02 16:04:37 -0700 | 
| commit | 4987611cf602751a8248662511438acb2d1c45b7 (patch) | |
| tree | a32fc295e2a8fbb1c19cd95f0401b13d39bef23d /python/fatcat_web | |
| parent | ab3a8a8a020921d8d0821917ced0cf6128812667 (diff) | |
| download | fatcat-4987611cf602751a8248662511438acb2d1c45b7.tar.gz fatcat-4987611cf602751a8248662511438acb2d1c45b7.zip | |
basic container create/edit forms
Diffstat (limited to 'python/fatcat_web')
| -rw-r--r-- | python/fatcat_web/editing_routes.py | 106 | ||||
| -rw-r--r-- | python/fatcat_web/forms.py | 126 | ||||
| -rw-r--r-- | python/fatcat_web/templates/container_create.html | 167 | ||||
| -rw-r--r-- | python/fatcat_web/templates/container_edit.html | 65 | 
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 %} + | 
