From b4a586b07a741c70cb928344476823395d7565b9 Mon Sep 17 00:00:00 2001 From: Jorran de Wit <jorrandewit@outlook.com> Date: Sun, 15 Apr 2018 10:20:29 +0200 Subject: [PATCH] Code improvement --- scipost/decorators.py | 13 +- scipost/forms.py | 29 ++- scipost/models.py | 30 ++- .../templates/scipost/contributor_info.html | 2 +- scipost/views.py | 182 +++++++----------- submissions/views.py | 2 - 6 files changed, 112 insertions(+), 146 deletions(-) diff --git a/scipost/decorators.py b/scipost/decorators.py index cebaccc02..fea4dc361 100644 --- a/scipost/decorators.py +++ b/scipost/decorators.py @@ -2,13 +2,24 @@ __copyright__ = "Copyright 2016-2018, Stichting SciPost (SciPost Foundation)" __license__ = "AGPL v3" +from django.contrib.auth.decorators import user_passes_test + from .models import Contributor def has_contributor(user): - """Requires user to be related to any Contributor.""" + """Require user to be related to any Contributor.""" try: user.contributor return True except Contributor.DoesNotExist: return False + + +def is_contributor_user(): + """Dceorator checking if user is related to any Contributor.""" + def test(u): + if u.is_authenticated(): + return has_contributor(u) + return False + return user_passes_test(test) diff --git a/scipost/forms.py b/scipost/forms.py index 3dcaaea21..7beb0185e 100644 --- a/scipost/forms.py +++ b/scipost/forms.py @@ -24,11 +24,11 @@ from ajax_select.fields import AutoCompleteSelectField from haystack.forms import ModelSearchForm as HayStackSearchForm from .behaviors import orcid_validator -from .constants import (SCIPOST_DISCIPLINES, TITLE_CHOICES, SCIPOST_FROM_ADDRESSES, - NO_SCIENTIST, DOUBLE_ACCOUNT, BARRED) +from .constants import ( + SCIPOST_DISCIPLINES, TITLE_CHOICES, SCIPOST_FROM_ADDRESSES, NO_SCIENTIST, DOUBLE_ACCOUNT, + BARRED) from .decorators import has_contributor -from .models import Contributor, DraftInvitation,\ - UnavailabilityPeriod, PrecookedEmail +from .models import Contributor, DraftInvitation, UnavailabilityPeriod, PrecookedEmail from affiliations.models import Affiliation, Institution from common.forms import MonthYearWidget @@ -81,10 +81,10 @@ class RegistrationForm(forms.Form): last_name = forms.CharField(label='* Last name', max_length=100) email = forms.EmailField(label='* Email address') invitation_key = forms.CharField(max_length=40, widget=forms.HiddenInput(), required=False) - orcid_id = forms.CharField(label="ORCID id", max_length=20, required=False, - validators=[orcid_validator], - widget=forms.TextInput( - {'placeholder': 'Recommended. Get one at orcid.org'})) + orcid_id = forms.CharField( + label="ORCID id", max_length=20, required=False, validators=[orcid_validator], + widget=forms.TextInput({ + 'placeholder': 'Recommended. Get one at orcid.org'})) discipline = forms.ChoiceField(choices=SCIPOST_DISCIPLINES, label='* Main discipline') country_of_employment = LazyTypedChoiceField( choices=countries, label='* Country of employment', initial='NL', @@ -94,12 +94,10 @@ class RegistrationForm(forms.Form): affiliation = forms.CharField(label='* Affiliation', max_length=300) address = forms.CharField( label='Address', max_length=1000, - widget=forms.TextInput({'placeholder': 'For postal correspondence'}), - required=False) + widget=forms.TextInput({'placeholder': 'For postal correspondence'}), required=False) personalwebpage = forms.URLField( - label='Personal web page', - widget=forms.TextInput({'placeholder': 'full URL, e.g. http://www.[yourpage].com'}), - required=False) + label='Personal web page', required=False, + widget=forms.TextInput({'placeholder': 'full URL, e.g. http://www.[yourpage].com'})) username = forms.CharField(label='* Username', max_length=100) password = forms.CharField(label='* Password', widget=forms.PasswordInput()) password_verif = forms.CharField(label='* Verify password', widget=forms.PasswordInput(), @@ -284,8 +282,9 @@ class AuthenticationForm(forms.Form): next = forms.CharField(widget=forms.HiddenInput(), required=False) def user_is_inactive(self): - """ - Check if the User is active but only if the password is valid, to prevent any + """Check if the User is active only if the password is valid. + + Only check to prevent any possible clue (?) of the password. """ username = self.cleaned_data['username'] diff --git a/scipost/models.py b/scipost/models.py index 83e5c3cb7..d5bc608c4 100644 --- a/scipost/models.py +++ b/scipost/models.py @@ -56,16 +56,12 @@ class Contributor(models.Model): blank=True, null=True) orcid_id = models.CharField(max_length=20, verbose_name="ORCID id", blank=True, validators=[orcid_validator]) - address = models.CharField(max_length=1000, verbose_name="address", - blank=True) - personalwebpage = models.URLField(verbose_name='personal web page', - blank=True) + address = models.CharField(max_length=1000, verbose_name="address", blank=True) + personalwebpage = models.URLField(verbose_name='personal web page', blank=True) vetted_by = models.ForeignKey('self', on_delete=models.SET(get_sentinel_user), - related_name="contrib_vetted_by", - blank=True, null=True) + related_name="contrib_vetted_by", blank=True, null=True) accepts_SciPost_emails = models.BooleanField( - default=True, - verbose_name="I accept to receive SciPost emails") + default=True, verbose_name="I accept to receive SciPost emails") objects = ContributorQuerySet.as_manager() @@ -73,48 +69,50 @@ class Contributor(models.Model): return '%s, %s' % (self.user.last_name, self.user.first_name) def save(self, *args, **kwargs): + """Generate new activitation key if not set.""" if not self.activation_key: self.generate_key() return super().save(*args, **kwargs) def get_absolute_url(self): + """Return public information page url.""" return reverse('scipost:contributor_info', args=(self.id,)) - @property - def get_formal_display(self): - return '%s %s %s' % (self.get_title_display(), self.user.first_name, self.user.last_name) - @property def is_currently_available(self): + """Check if Contributor is currently not marked as unavailable.""" return not self.unavailability_periods.today().exists() def is_EdCol_Admin(self): + """Check if Contributor is an Editorial Administrator.""" return (self.user.groups.filter(name='Editorial Administrators').exists() or self.user.is_superuser) def is_SP_Admin(self): + """Check if Contributor is a SciPost Administrator.""" return (self.user.groups.filter(name='SciPost Administrators').exists() or self.user.is_superuser) def is_MEC(self): + """Check if Contributor is a member of the Editorial College.""" return self.fellowships.active().exists() or self.user.is_superuser def is_VE(self): + """Check if Contributor is a Vetting Editor.""" return (self.user.groups.filter(name='Vetting Editors').exists() or self.user.is_superuser) def generate_key(self, feed=''): - """ - Generate and save a new activation_key for the contributor, given a certain feed. - """ + """Generate a new activation_key for the contributor, given a certain feed.""" for i in range(5): feed += random.choice(string.ascii_letters) feed = feed.encode('utf8') salt = self.user.username.encode('utf8') - self.activation_key = hashlib.sha1(salt+salt).hexdigest() + self.activation_key = hashlib.sha1(salt + salt).hexdigest() self.key_expires = datetime.datetime.now() + datetime.timedelta(days=2) def expertises_as_string(self): + """Return joined expertises.""" if self.expertises: return ', '.join([subject_areas_dict[exp].lower() for exp in self.expertises]) return '' diff --git a/scipost/templates/scipost/contributor_info.html b/scipost/templates/scipost/contributor_info.html index 26423a950..d65751ed7 100644 --- a/scipost/templates/scipost/contributor_info.html +++ b/scipost/templates/scipost/contributor_info.html @@ -4,7 +4,7 @@ {% block content %} -<h1 class="highlight mb-4">Contributor info: {{ contributor.get_formal_display }}</h1> +<h1 class="highlight mb-4">Contributor info: {{ contributor.get_title_display }} {{ contributor.user.first_name }} {{ contributor.user.last_name }}</h1> {% include "scipost/_public_info_as_table.html" with contributor=contributor %} diff --git a/scipost/views.py b/scipost/views.py index 2565a46f8..ae5c1d9e2 100644 --- a/scipost/views.py +++ b/scipost/views.py @@ -9,7 +9,7 @@ from django.shortcuts import get_object_or_404, render from django.conf import settings from django.contrib import messages from django.contrib.auth import login, logout, update_session_auth_hash -from django.contrib.auth.decorators import login_required, user_passes_test +from django.contrib.auth.decorators import login_required from django.contrib.auth.models import Group from django.contrib.auth.views import password_reset, password_reset_confirm from django.core import mail @@ -29,16 +29,16 @@ from django.views.static import serve from guardian.decorators import permission_required from haystack.generic_views import SearchView -from .constants import SCIPOST_SUBJECT_AREAS, subject_areas_raw_dict, SciPost_from_addresses_dict,\ - NORMAL_CONTRIBUTOR -from .decorators import has_contributor -from .models import Contributor, UnavailabilityPeriod,\ - AuthorshipClaim, EditorialCollege, EditorialCollegeFellowship -from .forms import AuthenticationForm, UnavailabilityPeriodForm,\ - RegistrationForm, AuthorshipClaimForm,\ - SearchForm, VetRegistrationForm, reg_ref_dict,\ - UpdatePersonalDataForm, UpdateUserDataForm, PasswordChangeForm,\ - EmailGroupMembersForm, EmailParticularForm, SendPrecookedEmailForm +from .constants import ( + SCIPOST_SUBJECT_AREAS, subject_areas_raw_dict, SciPost_from_addresses_dict, NORMAL_CONTRIBUTOR) +from .decorators import has_contributor, is_contributor_user +from .models import ( + Contributor, UnavailabilityPeriod, AuthorshipClaim, EditorialCollege, + EditorialCollegeFellowship) +from .forms import ( + AuthenticationForm, UnavailabilityPeriodForm, RegistrationForm, AuthorshipClaimForm, + SearchForm, VetRegistrationForm, reg_ref_dict, UpdatePersonalDataForm, UpdateUserDataForm, + PasswordChangeForm, EmailGroupMembersForm, EmailParticularForm, SendPrecookedEmailForm) from .utils import Utils, EMAIL_FOOTER, SCIPOST_SUMMARY_FOOTER, SCIPOST_SUMMARY_FOOTER_HTML from affiliations.forms import AffiliationsFormset @@ -49,8 +49,7 @@ from invitations.constants import STATUS_REGISTERED from invitations.models import RegistrationInvitation from journals.models import Publication, Journal, PublicationAuthorsTable from news.models import NewsItem -from submissions.models import Submission, RefereeInvitation,\ - Report, EICRecommendation +from submissions.models import Submission, RefereeInvitation, Report, EICRecommendation from partners.models import MembershipAgreement from theses.models import ThesisLink @@ -60,18 +59,18 @@ from theses.models import ThesisLink ############## def is_registered(user): - """ - This method checks if user is activated assuming an validated user - has at least one permission group (`Registered Contributor` or `Partner Accounts`). - """ + """Check if user is a validated user; has at least one permission group.""" return user.groups.exists() class SearchView(SearchView): + """Search CBV inherited from Haystack.""" + template_name = 'search/search.html' form_class = SearchForm def get_context_data(self, *args, **kwargs): + """Update context with some additional information.""" ctx = super().get_context_data(*args, **kwargs) ctx['search_query'] = self.request.GET.get('q') ctx['results_count'] = kwargs['object_list'].count() @@ -83,7 +82,7 @@ class SearchView(SearchView): ############# def index(request): - '''Main page.''' + """Homepage view of SciPost.""" context = { 'latest_newsitem': NewsItem.objects.filter(on_homepage=True).order_by('-date').first(), 'submissions': Submission.objects.public().order_by('-submission_date')[:3], @@ -96,7 +95,8 @@ def index(request): def protected_serve(request, path, show_indexes=False): - """ + """Serve media files from outside the public MEDIA_ROOT folder. + Serve files that are saved outside the default MEDIA_ROOT folder for superusers only! This will be usefull eg. in the admin pages. """ @@ -112,6 +112,7 @@ def protected_serve(request, path, show_indexes=False): ############### def feeds(request): + """Information page for RSS and Atom feeds.""" context = {'subject_areas_physics': SCIPOST_SUBJECT_AREAS[0][1]} return render(request, 'scipost/feeds.html', context) @@ -121,7 +122,8 @@ def feeds(request): ################ def register(request): - """ + """Contributor registration form page. + This public registration view shows and processes the form that will create new user account requests. After registration the Contributor will need to activate its account via the mail @@ -155,7 +157,8 @@ def register(request): def invitation(request, key): - """ + """Registration Invitation reception page. + If a scientist has recieved an invitation (RegistrationInvitation) he/she will finish it's invitation via still view which will prefill the default registration form. @@ -244,9 +247,8 @@ def unsubscribe(request, contributor_id, key): @permission_required('scipost.can_vet_registration_requests', return_403=True) def vet_registration_requests(request): - contributors_to_vet = (Contributor.objects - .awaiting_vetting() - .order_by('key_expires')) + """List of new Registration requests to vet.""" + contributors_to_vet = Contributor.objects.awaiting_vetting().order_by('key_expires') form = VetRegistrationForm() context = {'contributors_to_vet': contributors_to_vet, 'form': form} return render(request, 'scipost/vet_registration_requests.html', context) @@ -254,7 +256,7 @@ def vet_registration_requests(request): @permission_required('scipost.can_vet_registration_requests', return_403=True) def vet_registration_request_ack(request, contributor_id): - # process the form + """Form view to vet new Registration requests.""" form = VetRegistrationForm(request.POST or None) contributor = Contributor.objects.get(pk=contributor_id) if form.is_valid(): @@ -274,11 +276,10 @@ def vet_registration_request_ack(request, contributor_id): except RefereeInvitation.DoesNotExist: pending_ref_inv_exists = False - email_text = ('Dear ' + contributor.get_title_display() + ' ' - + contributor.user.last_name + - ', \n\nYour registration to the SciPost publication portal ' - 'has been accepted. ' - 'You can now login at https://scipost.org and contribute. \n\n') + email_text = ( + 'Dear ' + contributor.get_title_display() + ' ' + contributor.user.last_name + + ', \n\nYour registration to the SciPost publication portal has been accepted. ' + 'You can now login at https://scipost.org and contribute. \n\n') if pending_ref_inv_exists: email_text += ( 'Note that you have pending refereeing invitations; please navigate to ' @@ -293,17 +294,15 @@ def vet_registration_request_ack(request, contributor_id): emailmessage.send(fail_silently=False) else: ref_reason = form.cleaned_data['refusal_reason'] - email_text = ('Dear ' + contributor.get_title_display() + ' ' - + contributor.user.last_name + - ', \n\nYour registration to the SciPost publication portal ' - 'has been turned down, the reason being: ' - + reg_ref_dict[ref_reason] + '. You can however still view ' - 'all SciPost contents, just not submit papers, ' - 'comments or votes. We nonetheless thank you for your interest.' - '\n\nThe SciPost Team.') + email_text = ( + 'Dear ' + contributor.get_title_display() + ' ' + contributor.user.last_name + + ', \n\nYour registration to the SciPost publication portal has been turned down,' + ' the reason being: ' + reg_ref_dict[ref_reason] + '. You can however still view ' + 'all SciPost contents, just not submit papers, comments or votes. We nonetheless ' + 'thank you for your interest.\n\nThe SciPost Team.') if form.cleaned_data['email_response_field']: - email_text += ('\n\nFurther explanations: ' - + form.cleaned_data['email_response_field']) + email_text += ( + '\n\nFurther explanations: ' + form.cleaned_data['email_response_field']) emailmessage = EmailMessage('SciPost registration: unsuccessful', email_text, 'SciPost registration <registration@scipost.org>', @@ -337,9 +336,7 @@ def registration_requests(request): @require_POST @permission_required('scipost.can_resend_registration_requests', return_403=True) def registration_requests_reset(request, contributor_id): - ''' - Reset specific activation_key for Contributor and resend activation mail. - ''' + """Reset specific activation_key for Contributor and resend activation mail.""" contributor = get_object_or_404(Contributor.objects.awaiting_validation(), id=contributor_id) contributor.generate_key() contributor.save() @@ -351,15 +348,7 @@ def registration_requests_reset(request, contributor_id): def login_view(request): - """ - This view shows and processes a user's login session. - - The function based method login() is deprecated from - Django 1.11 and replaced by Class Based Views. - - See: - https://docs.djangoproject.com/en/1.11/releases/1.11/#django-contrib-auth - """ + """Login form page.""" form = AuthenticationForm(request.POST or None, initial=request.GET) if form.is_valid(): user = form.authenticate() @@ -381,13 +370,7 @@ def login_view(request): def logout_view(request): - """ - The function based method logout() is deprecated from - Django 1.11 and replaced by Class Based Views. - - See: - https://docs.djangoproject.com/en/1.11/releases/1.11/#django-contrib-auth - """ + """Logout form page.""" logout(request) messages.success(request, ('<h3>Keep contributing!</h3>' 'You are now logged out of SciPost.')) @@ -395,11 +378,9 @@ def logout_view(request): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def mark_unavailable_period(request): - ''' - Mark period unavailable for Contributor using this view. - ''' + """Form view to mark period unavailable for Contributor.""" unav_form = UnavailabilityPeriodForm(request.POST or None) if unav_form.is_valid(): unav = unav_form.save(commit=False) @@ -415,11 +396,9 @@ def mark_unavailable_period(request): @require_POST @login_required -@user_passes_test(has_contributor) +@is_contributor_user def delete_unavailable_period(request, period_id): - ''' - Delete period unavailable registered. - ''' + """Delete period unavailable registered.""" unav = get_object_or_404(UnavailabilityPeriod, contributor=request.user.contributor, id=int(period_id)) unav.delete() @@ -428,11 +407,9 @@ def delete_unavailable_period(request, period_id): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def _personal_page_editorial_account(request): - """ - The Personal Page tab: Account - """ + """Personal Page tab: Account.""" contributor = request.user.contributor context = { 'contributor': contributor, @@ -442,11 +419,9 @@ def _personal_page_editorial_account(request): return render(request, 'partials/scipost/personal_page/account.html', context) -@user_passes_test(has_contributor) +@is_contributor_user def _personal_page_editorial_actions(request): - """ - The Personal Page tab: Editorial Actions - """ + """Personal Page tab: Editorial Actions.""" permission = request.user.groups.filter(name__in=[ 'Ambassadors', 'Advisory Board', @@ -491,11 +466,9 @@ def _personal_page_editorial_actions(request): @permission_required('scipost.can_referee', return_403=True) -@user_passes_test(has_contributor) +@is_contributor_user def _personal_page_refereeing(request): - """ - The Personal Page tab: Refereeing - """ + """Personal Page tab: Refereeing.""" context = { 'contributor': request.user.contributor } @@ -503,11 +476,9 @@ def _personal_page_refereeing(request): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def _personal_page_publications(request): - """ - The Personal Page tab: Publications - """ + """Personal Page tab: Publications.""" contributor = request.user.contributor context = { 'contributor': contributor, @@ -522,11 +493,9 @@ def _personal_page_publications(request): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def _personal_page_submissions(request): - """ - The Personal Page tab: Submissions - """ + """Personal Page tab: Submissions.""" contributor = request.user.contributor context = {'contributor': contributor} @@ -541,11 +510,9 @@ def _personal_page_submissions(request): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def _personal_page_commentaries(request): - """ - The Personal Page tab: Commentaries - """ + """Personal Page tab: Commentaries.""" contributor = request.user.contributor context = {'contributor': contributor} @@ -559,11 +526,9 @@ def _personal_page_commentaries(request): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def _personal_page_theses(request): - """ - The Personal Page tab: Theses - """ + """Personal Page tab: Theses.""" contributor = request.user.contributor context = {'contributor': contributor} @@ -577,11 +542,9 @@ def _personal_page_theses(request): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def _personal_page_comments(request): - """ - The Personal Page tab: Comments - """ + """Personal Page tab: Comments.""" contributor = request.user.contributor context = { 'contributor': contributor, @@ -592,11 +555,9 @@ def _personal_page_comments(request): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def _personal_page_author_replies(request): - """ - The Personal Page tab: Author Replies - """ + """Personal Page tab: Author Replies.""" contributor = request.user.contributor context = { 'contributor': contributor, @@ -608,9 +569,7 @@ def _personal_page_author_replies(request): @login_required def personal_page(request, tab='account'): - """ - The Personal Page is the main view for accessing user functions. - """ + """Personal Page is the main view for accessing user functions.""" if request.is_ajax(): if tab == 'account': return _personal_page_editorial_account(request) @@ -659,6 +618,7 @@ def personal_page(request, tab='account'): @login_required def change_password(request): + """Change password form view.""" form = PasswordChangeForm(request.POST or None, current_user=request.user) if form.is_valid(): form.save_new_password() @@ -729,7 +689,7 @@ def update_personal_data(request): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def claim_authorships(request): """ The system auto-detects potential authorships (of submissions, @@ -778,7 +738,7 @@ def claim_authorships(request): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def claim_pub_authorship(request, publication_id, claim): if request.method == 'POST': contributor = Contributor.objects.get(user=request.user) @@ -794,7 +754,7 @@ def claim_pub_authorship(request, publication_id, claim): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def claim_sub_authorship(request, submission_id, claim): if request.method == 'POST': contributor = Contributor.objects.get(user=request.user) @@ -810,7 +770,7 @@ def claim_sub_authorship(request, submission_id, claim): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def claim_com_authorship(request, commentary_id, claim): if request.method == 'POST': contributor = Contributor.objects.get(user=request.user) @@ -826,7 +786,7 @@ def claim_com_authorship(request, commentary_id, claim): @login_required -@user_passes_test(has_contributor) +@is_contributor_user def claim_thesis_authorship(request, thesis_id, claim): if request.method == 'POST': contributor = Contributor.objects.get(user=request.user) diff --git a/submissions/views.py b/submissions/views.py index 124890e11..bac948e24 100644 --- a/submissions/views.py +++ b/submissions/views.py @@ -43,8 +43,6 @@ from .utils import SubmissionUtils from colleges.permissions import fellowship_required, fellowship_or_admin_required from comments.forms import CommentForm -from invitations.constants import INVITATION_REFEREEING -from invitations.models import RegistrationInvitation from journals.models import Journal from mails.views import MailEditingSubView from production.forms import ProofsDecisionForm -- GitLab