Source code for inspirehep.modules.authors.forms

# -*- coding: utf-8 -*-
#
# This file is part of INSPIRE.
# Copyright (C) 2014-2018 CERN.
#
# INSPIRE is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# INSPIRE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with INSPIRE. If not, see <http://www.gnu.org/licenses/>.
#
# In applying this license, CERN does not waive the privileges and immunities
# granted to it by virtue of its status as an Intergovernmental Organization
# or submit itself to any jurisdiction.

from __future__ import absolute_import, division, print_function

from wtforms import validators
from wtforms.fields import Flags
from wtforms.widgets import (
    HTMLString,
    HiddenInput,
    Select,
    TextInput,
    html_params,
)

from inspire_schemas.api import load_schema
from inspirehep.modules.forms.field_widgets import (
    ColumnInput,
    DynamicItemWidget,
    DynamicListWidget,
    ExtendedListWidget,
    ItemWidget,
    WrappedInput,
)
from inspirehep.modules.forms.form import INSPIREForm
from inspirehep.modules.forms import fields
from inspirehep.modules.forms.filter_utils import clean_empty_list
from inspirehep.modules.forms.validators.simple_fields import (
    duplicated_orcid_validator,
)
from inspirehep.modules.forms.validators.dynamic_fields import LessThan
from inspirehep.modules.forms.validation_utils import (
    ORCIDValidator,
    RegexpStopValidator,
)


[docs]def currentCheckboxWidget(field, **kwargs): """Current institution checkbox widget.""" field_id = kwargs.pop('id', field.id) html = [u'<div class="col-md-10 col-margin-top pull-left">\ <input %s %s type="checkbox">\ <label for=%s>Current</label></div>' % (html_params(id=field_id, name=field_id), field.data and "checked" or "", field_id)] return HTMLString(u''.join(html))
[docs]class WrappedSelect(Select): """Widget to wrap select input in further markup.""" wrapper = '<div>%(field)s</div>' wrapped_widget = Select() def __init__(self, widget=None, wrapper=None, **kwargs): """Initialize wrapped input with widget and wrapper.""" self.wrapped_widget = widget or self.wrapped_widget self.wrapper_args = kwargs if wrapper is not None: self.wrapper = wrapper def __call__(self, field, **kwargs): """Render wrapped input.""" return HTMLString(self.wrapper % dict( field=self.wrapped_widget(field, **kwargs), **self.wrapper_args ))
[docs]class ColumnSelect(WrappedSelect): """Specialized column wrapped input.""" @property def wrapper(self): """Wrapper template with description support.""" if 'description' in self.wrapper_args: return ('<div class="%(class_)s">%(field)s' '<p class="text-muted field-desc">' '<small>%(description)s</small></p></div>') return '<div class="%(class_)s">%(field)s</div>'
[docs]class InstitutionInlineForm(INSPIREForm): """Institution inline form.""" rank_options = [ ("rank", "Rank"), ("SENIOR", "Senior (permanent)"), ("JUNIOR", "Junior (leads to Senior)"), ("STAFF", "Staff (non-research)"), ("VISITOR", "Visitor"), ("PD", "PostDoc"), ("PHD", "PhD"), ("MASTER", "Master"), ("UNDERGRADUATE", "Undergrad"), ("OTHER", "Other"), ] name = fields.StringField( widget_classes='form-control', widget=ColumnInput(class_="col-md-6"), autocomplete='affiliation', placeholder="Institution. Type for suggestions", ) rank = fields.SelectField( choices=rank_options, default="rank", widget=ColumnSelect(class_="col-md-6"), widget_classes='form-control', validators=[validators.DataRequired()], ) start_year = fields.StringField( placeholder='Start Year', description=u'Format: YYYY.', widget=WrappedInput( wrapped_widget=TextInput(), wrapper='<div class="col-md-6 col-margin-top">%(field)s</div>' ), validators=[ LessThan('end_year', message='Start year should be earlier than End year'), RegexpStopValidator( r'^(\d{4})?$', message='{} is not a valid year. Please use <i>yyyy</i> format.', ), ], widget_classes="form-control" ) end_year = fields.StringField( placeholder='End Year', description=u'Format: YYYY.', widget=WrappedInput( wrapped_widget=TextInput(), wrapper='<div class="col-md-6 col-margin-top">%(field)s</div>' ), validators=[RegexpStopValidator( "^(\d{4})?$", message="{} is not a valid year. Please use <i>yyyy</i> format." )], widget_classes="form-control" ) current = fields.BooleanField( widget=currentCheckboxWidget ) emails = fields.FieldList( fields.HiddenField(label=''), widget_classes='hidden-list' ) old_emails = fields.FieldList( fields.HiddenField(label=''), widget_classes='hidden-list' )
[docs]class EmailInlineForm(INSPIREForm): """Public emails inline form.""" email = fields.StringField( widget_classes="form-control", validators=[validators.Optional(), validators.Email()], ) original_email = fields.HiddenField()
[docs]class ExperimentsInlineForm(INSPIREForm): """Experiments inline form.""" name = fields.StringField( placeholder="Experiment. Type for suggestions", label='Experiment', widget=ColumnInput(class_="col-md-6"), widget_classes="form-control", autocomplete="experiment" ) start_year = fields.StringField( placeholder='Start Year', description=u'Format: YYYY.', widget=WrappedInput( wrapped_widget=TextInput(), wrapper='<div class="col-md-6">%(field)s</div>', ), validators=[ LessThan('end_year', message='Start year should be earlier than End year'), RegexpStopValidator( r'^(\d{4})?$', message='{} is not a valid year. Please use <i>yyyy</i> format.', ), ], widget_classes="form-control" ) end_year = fields.StringField( placeholder='End Year', description=u'Format: YYYY.', widget=WrappedInput( wrapped_widget=TextInput(), wrapper='<div class="col-md-6 col-margin-top">%(field)s</div>' ), validators=[ RegexpStopValidator( r'^(\d{4})?$', message='{} is not a valid year. Please use <i>yyyy</i> format.', ), ], widget_classes="form-control" ) current = fields.BooleanField( widget=currentCheckboxWidget )
[docs]class AdvisorsInlineForm(INSPIREForm): """Advisors inline form.""" name = fields.TextField( widget_classes='form-control', placeholder="Name. Type for suggestions", autocomplete='author', widget=ColumnInput( class_="col-xs-5", description=u"Family name, First name"), export_key='full_name', ) degree_types_schema = load_schema('elements/degree_type.json') degree_type_options = [ (val, val.capitalize()) for val in degree_types_schema['enum'] ] degree_type_options.sort(key=lambda x: x[1]) degree_type = fields.SelectField( choices=degree_type_options, label='Degree Type', widget_classes="form-control", default="phd", widget=ColumnSelect(class_="col-xs-5", description=u"Degree Type"), )
[docs]class WebpageInlineForm(INSPIREForm): """URL inline form.""" webpage = fields.StringField( label='Your Webpage', placeholder='http://www.example.com', widget=ColumnInput(class_="col-xs-10"), widget_classes="form-control", validators=[validators.URL(), validators.Optional()] )
[docs]class DynamicUnsortedItemWidget(DynamicItemWidget): def _sort_button(self): return ""
[docs]class DynamicUnsortedWidget(DynamicListWidget): def __init__(self, **kwargs): """Initialize dynamic list widget.""" self.item_widget = DynamicUnsortedItemWidget() super(DynamicUnsortedWidget, self).__init__(**kwargs)
[docs]class DynamicUnsortedNonRemoveItemWidget(DynamicItemWidget): def _sort_button(self): return "" def _remove_button(self): return ""
[docs]class DynamicUnsortedNonRemoveWidget(DynamicListWidget): def __init__(self, **kwargs): """Initialize dynamic list widget.""" self.item_widget = DynamicUnsortedNonRemoveItemWidget() super(DynamicUnsortedNonRemoveWidget, self).__init__(**kwargs)
[docs]class AuthorUpdateForm(INSPIREForm): """Author update form.""" bai = fields.StringField( label='Bai', description=u'e.g. M.Santos.1', widget=HiddenInput(), widget_classes="form-control", validators=[validators.Optional(), RegexpStopValidator( "(\\w+\\.)+\\d+", message="A valid Bai is in the form of 'M.Santos.1'.", )] ) inspireid = fields.StringField( label='Inspireid', description=u'e.g. INSPIRE-12345678', widget=HiddenInput(), widget_classes="form-control", validators=[validators.Optional(), RegexpStopValidator( r"^INSPIRE-\d{8}(?<!00000000)$", message="A valid Inspireid is in the form of 'INSPIRE-12345678'.", )] ) # Hidden field to hold record id information control_number = fields.IntegerField( widget=HiddenInput(), validators=[validators.Optional()], ) given_names = fields.StringField( label='Given Names', description=u'e.g. Diego', validators=[validators.DataRequired()], widget_classes="form-control" ) family_name = fields.StringField( label='Family Name', description=u'e.g. Martínez Santos', widget_classes="form-control" ) display_name = fields.StringField( label='Display Name', description=u'How should the author be addressed throughout the site? e.g. Diego Martínez', validators=[validators.DataRequired()], widget_classes="form-control" ) native_name = fields.StringField( label='Native Name', description=u'For non-Latin names e.g. 麦迪娜 or Эдгар Бугаев', widget_classes="form-control" ) public_emails = fields.DynamicFieldList( fields.FormField( EmailInlineForm, widget=ExtendedListWidget( item_widget=ItemWidget(), html_tag='div', ), widget_classes="col-xs-10" ), description=u"This emails will be displayed online in the INSPIRE Author Profile.", label='Public emails', add_label='Add another email', min_entries=1, widget=DynamicUnsortedNonRemoveWidget(), widget_classes="ui-disable-sort" ) orcid = fields.StringField( label='ORCID <img src="/oldui/images/orcid_icon_24.png" style="height:20px">', widget_classes="form-control", description=u"""ORCID provides a persistent digital identifier that distinguishes you from other researchers. Learn more at <a href="http://orcid.org" tabIndex="-1" target="_blank">orcid.org</a>""", widget=WrappedInput(wrapper=""" <div class="input-group"> <span class="input-group-addon" id="sizing-addon2">orcid.org/</span> %(field)s </div> """), placeholder="0000-0000-0000-0000", validators=[validators.Optional(), RegexpStopValidator( "\d{4}-\d{4}-\d{4}-\d{3}[\dX]", message="A valid ORCID iD consists of 16 digits separated by dashes.", ), ORCIDValidator, duplicated_orcid_validator] ) status_options = [("active", "Active"), ("retired", "Retired"), ("departed", "Departed"), ("deceased", "Deceased")] status = fields.SelectField( label='Status', choices=status_options, default="active", validators=[validators.DataRequired()], widget_classes='form-control', ) blog_url = fields.StringField( label='Blog', placeholder='http://www.example.com', icon="fa fa-wordpress", widget_classes="form-control", validators=[validators.URL(), validators.Optional()] ) twitter_url = fields.StringField( label='Twitter', placeholder='https://twitter.com/inspirehep', icon="fa fa-twitter", widget_classes="form-control", validators=[validators.URL(), validators.Optional()] ) linkedin_url = fields.StringField( label='Linkedin', placeholder='https://www.linkedin.com/pub/john-francis-lampen/16/750/778', icon="fa fa-linkedin-square", widget_classes="form-control", validators=[validators.URL(), validators.Optional()] ) websites = fields.DynamicFieldList( fields.FormField( WebpageInlineForm, widget=ExtendedListWidget( item_widget=ItemWidget(), html_tag='div', ), ), add_label='Add another website', min_entries=1, widget_classes='ui-disable-sort', icon="fa fa-globe", widget=DynamicUnsortedWidget() ) arxiv_categories_schema = load_schema('elements/arxiv_categories.json') research_field_options = [ (val, val) for val in arxiv_categories_schema['enum'] if '.' not in val or val in ('physics.ins-det', 'physics.acc-ph', 'physics.data-an') ] research_field = fields.SelectMultipleField( label='Field of Research', choices=research_field_options, widget_classes="form-control", filters=[clean_empty_list], validators=[validators.DataRequired()] ) institution_history = fields.DynamicFieldList( fields.FormField( InstitutionInlineForm, widget=ExtendedListWidget( item_widget=ItemWidget(), html_tag='div', ), widget_classes="col-xs-10" ), label='Institution History', add_label='Add another institution', min_entries=1, widget=DynamicUnsortedWidget(), widget_classes="ui-disable-sort" ) advisors = fields.DynamicFieldList( fields.FormField( AdvisorsInlineForm, widget=ExtendedListWidget( item_widget=ItemWidget(), html_tag='div', ), ), label='Advisors', add_label='Add another advisor', min_entries=1, widget=DynamicUnsortedWidget(), widget_classes="ui-disable-sort" ) experiments = fields.DynamicFieldList( fields.FormField( ExperimentsInlineForm, widget=ExtendedListWidget( item_widget=ItemWidget(), html_tag='div', ), widget_classes="col-xs-10" ), label='Experiment History', add_label='Add another experiment', min_entries=1, widget=DynamicUnsortedWidget(), widget_classes="ui-disable-sort" ) extra_comments = fields.TextAreaField( label='Comments', description=u'Send us any comments you might have. They will not be visible.', widget_classes="form-control" ) # # Form Configuration # _title = "Update author details" # Group fields in categories groups = [ ('Personal Information', ['given_names', 'family_name', 'display_name', 'native_name', 'email', 'public_emails', 'status', 'orcid', 'bai', 'inspireid'], {"icon": "fa fa-user"} ), ('Personal Websites', ['websites', 'linkedin_url', 'blog_url', 'twitter_url', "twitter_hidden"], {"icon": "fa fa-globe"} ), ('Career Information', ['research_field', 'institution_history', 'experiments', 'advisors'], {"icon": "fa fa-university"} ), ('Comments', ['extra_comments'], {"icon": "fa fa-comments"} ) ] def __init__(self, *args, **kwargs): """Constructor.""" super(AuthorUpdateForm, self).__init__(*args, **kwargs) is_review = kwargs.pop('is_review', False) is_update = kwargs.pop('is_update', False) if is_update: # remove validation for duplicate ORCIDs on update self.orcid.validators = self.orcid.validators[:-1] if is_review: self.bai.widget = TextInput() self.bai.flags = Flags() self.inspireid.widget = TextInput() self.inspireid.flags = Flags()