From 35099131558739815a959b3f0bf123b41fa4803d Mon Sep 17 00:00:00 2001 From: Sajid Ali Date: Wed, 24 Jun 2026 14:57:43 -0400 Subject: [PATCH] check publication year modified: coldfront/core/publication/forms.py modified: coldfront/core/publication/templates/publication/publication_add_publication_search_result.html modified: coldfront/core/publication/views.py --- coldfront/core/publication/forms.py | 30 ++++- ...ication_add_publication_search_result.html | 125 ++++++++++-------- coldfront/core/publication/views.py | 122 +++++++---------- 3 files changed, 144 insertions(+), 133 deletions(-) diff --git a/coldfront/core/publication/forms.py b/coldfront/core/publication/forms.py index 74d6e54bbb..b231cba700 100644 --- a/coldfront/core/publication/forms.py +++ b/coldfront/core/publication/forms.py @@ -1,4 +1,8 @@ +import datetime as dt + from django import forms +from django.core.exceptions import ValidationError +from django.forms import BaseFormSet class PublicationAddForm(forms.Form): @@ -14,21 +18,39 @@ class PublicationSearchForm(forms.Form): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields[ - "search_id" - ].help_text = "
Enter ID such as DOI or Bibliographic Code to search." + self.fields["search_id"].help_text = "
Enter ID such as DOI or Bibliographic Code to search." class PublicationResultForm(forms.Form): title = forms.CharField(max_length=1024, disabled=True) author = forms.CharField(disabled=True) - year = forms.CharField(max_length=4, disabled=True) + year = forms.IntegerField(disabled=True) journal = forms.CharField(max_length=1024, disabled=True) unique_id = forms.CharField(max_length=255, disabled=True) source_pk = forms.IntegerField(widget=forms.HiddenInput(), disabled=True) selected = forms.BooleanField(initial=False, required=False) +class PublicationResultFormset(BaseFormSet): + def clean(self): + """Checks that no two articles have the same title.""" + if any(self.errors): + # Don't bother validating the formset unless each form is valid on its own + return + curr_year = dt.datetime.today().year + for form in self.forms: + year = form.cleaned_data.get("year") + if year < curr_year - 1: + raise ValidationError( + f"Publication year entered is: {year}. Please add recent publications (this year and previous year) only!" + ) + + if year > curr_year: + raise ValidationError( + f"Publication year entered is: {year} which is in the future. Please fix it.", + ) + + class PublicationDeleteForm(forms.Form): title = forms.CharField(max_length=255, disabled=True) year = forms.CharField(max_length=30, disabled=True) diff --git a/coldfront/core/publication/templates/publication/publication_add_publication_search_result.html b/coldfront/core/publication/templates/publication/publication_add_publication_search_result.html index 0337f4d5e9..f4191dba2f 100644 --- a/coldfront/core/publication/templates/publication/publication_add_publication_search_result.html +++ b/coldfront/core/publication/templates/publication/publication_add_publication_search_result.html @@ -1,65 +1,76 @@ {% load crispy_forms_tags %} +{% if formset.non_form_errors %} +
+ {{ formset.non_form_errors }} +
+{% else %} + {% if formset %} -
-
-
- {% csrf_token %} -
- - - - - - - - - - - {% for form in formset %} - - - - - - - {% endfor %} - -
- - #PublicationUnique ID
{{ form.selected }}{{ forloop.counter }} - Title: {{ form.title.value }}
- Author: {{ form.author.value }}
- Year: {{ form.year.value }}
- Journal: {{ form.journal.value }} -
{{ form.unique_id.value }}
-
- {{ formset.management_form }} -
- - Back to Project - - -
-
-
-
+
+
+
+ {% csrf_token %} +
+ + + + + + + + + + + {% for form in formset %} + + + + + + + {% endfor %} + +
+ + #PublicationUnique ID
{{ form.selected }}{{ forloop.counter }} + Title:{{ form.title.value }}
+ Author:{{ form.author.value }}
+ Year:{{ form.year.value }}
+ Journal:{{ form.journal.value }} +
{{ form.unique_id.value }}
+
+ {{ formset.management_form }} +
+ + + Back to Project + + +
+
+
+
{% else %} - Back to Project -
- No users to remove! -
+ +Back to Project +
+ No users to remove! +
{% endif %} - diff --git a/coldfront/core/publication/views.py b/coldfront/core/publication/views.py index 196ab9cebb..b14f50412f 100644 --- a/coldfront/core/publication/views.py +++ b/coldfront/core/publication/views.py @@ -1,30 +1,35 @@ import ast +import datetime as dt +import io +import logging import re import uuid + import requests -import io from bibtexparser.bibdatabase import as_text from bibtexparser.bparser import BibTexParser from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin from django.forms import formset_factory -from django.http import HttpResponseRedirect, HttpResponse +from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views.generic import TemplateView, View from django.views.generic.edit import FormView +from doi2bib import crossref from coldfront.core.project.models import Project from coldfront.core.publication.forms import ( PublicationAddForm, PublicationDeleteForm, + PublicationExportForm, PublicationResultForm, + PublicationResultFormset, PublicationSearchForm, - PublicationExportForm, ) from coldfront.core.publication.models import Publication, PublicationSource -from doi2bib import crossref +logger = logging.getLogger(__name__) MANUAL_SOURCE = "manual" @@ -53,12 +58,8 @@ def dispatch(self, request, *args, **kwargs): "Active", "New", ]: - messages.error( - request, "You cannot add publications to an archived project." - ) - return HttpResponseRedirect( - reverse("project-detail", kwargs={"pk": project_obj.pk}) - ) + messages.error(request, "You cannot add publications to an archived project.") + return HttpResponseRedirect(reverse("project-detail", kwargs={"pk": project_obj.pk})) else: return super().dispatch(request, *args, **kwargs) @@ -69,9 +70,7 @@ def get_context_data(self, *args, **kwargs): return context -class PublicationSearchResultView( - LoginRequiredMixin, UserPassesTestMixin, TemplateView -): +class PublicationSearchResultView(LoginRequiredMixin, UserPassesTestMixin, TemplateView): template_name = "publication/publication_add_publication_search_result.html" def test_func(self): @@ -95,12 +94,8 @@ def dispatch(self, request, *args, **kwargs): "Active", "New", ]: - messages.error( - request, "You cannot add publications to an archived project." - ) - return HttpResponseRedirect( - reverse("project-detail", kwargs={"project_pk": project_obj.pk}) - ) + messages.error(request, "You cannot add publications to an archived project.") + return HttpResponseRedirect(reverse("project-detail", kwargs={"project_pk": project_obj.pk})) else: return super().dispatch(request, *args, **kwargs) @@ -230,12 +225,8 @@ def dispatch(self, request, *args, **kwargs): "Active", "New", ]: - messages.error( - request, "You cannot add publications to an archived project." - ) - return HttpResponseRedirect( - reverse("project-detail", kwargs={"pk": project_obj.pk}) - ) + messages.error(request, "You cannot add publications to an archived project.") + return HttpResponseRedirect(reverse("project-detail", kwargs={"pk": project_obj.pk})) else: return super().dispatch(request, *args, **kwargs) @@ -244,22 +235,22 @@ def post(self, request, *args, **kwargs): project_pk = self.kwargs.get("project_pk") project_obj = get_object_or_404(Project, pk=project_pk) - formset = formset_factory(PublicationResultForm, max_num=len(pubs)) + formset = formset_factory(PublicationResultForm, formset=PublicationResultFormset, max_num=len(pubs)) formset = formset(request.POST, initial=pubs, prefix="pubform") publications_added = 0 publications_skipped = [] + if formset.is_valid(): for form in formset: form_data = form.cleaned_data if form_data["selected"]: - source_obj = PublicationSource.objects.get( - pk=form_data.get("source_pk") - ) + source_obj = PublicationSource.objects.get(pk=form_data.get("source_pk")) author = form_data.get("author") if len(author) > 1024: author = author[:1024] + publication_obj, created = Publication.objects.get_or_create( project=project_obj, unique_id=form_data.get("unique_id"), @@ -288,12 +279,10 @@ def post(self, request, *args, **kwargs): messages.success(request, msg) else: - for error in formset.errors: + for error in formset.non_form_errors(): messages.error(request, error) - return HttpResponseRedirect( - reverse("project-detail", kwargs={"pk": project_pk}) - ) + return HttpResponseRedirect(reverse("project-detail", kwargs={"pk": project_pk})) class PublicationAddManuallyView(LoginRequiredMixin, UserPassesTestMixin, FormView): @@ -326,12 +315,8 @@ def dispatch(self, request, *args, **kwargs): "Active", "New", ]: - messages.error( - request, "You cannot add publications to an archived project." - ) - return HttpResponseRedirect( - reverse("project-detail", kwargs={"pk": project_obj.pk}) - ) + messages.error(request, "You cannot add publications to an archived project.") + return HttpResponseRedirect(reverse("project-detail", kwargs={"pk": project_obj.pk})) else: return super().dispatch(request, *args, **kwargs) @@ -342,6 +327,21 @@ def get_initial(self): def form_valid(self, form): form_data = form.cleaned_data + curr_year = dt.datetime.today().year + if form_data.get("year") < curr_year - 1: + form.add_error( + None, + f"Publication year entered is: {form_data.get('year')}. Please add recent publications only!", + ) + return self.form_invalid(form) + + if form_data.get("year") > curr_year: + form.add_error( + None, + f"Publication year entered is: {form_data.get('year')} which is in the future. Please fix it.", + ) + return self.form_invalid(form) + project_obj = get_object_or_404(Project, pk=self.kwargs.get("project_pk")) pub_obj = Publication.objects.create( project=project_obj, @@ -365,9 +365,7 @@ def get_success_url(self): return reverse("project-detail", kwargs={"pk": self.kwargs.get("project_pk")}) -class PublicationDeletePublicationsView( - LoginRequiredMixin, UserPassesTestMixin, TemplateView -): +class PublicationDeletePublicationsView(LoginRequiredMixin, UserPassesTestMixin, TemplateView): template_name = "publication/publication_delete_publications.html" def test_func(self): @@ -405,9 +403,7 @@ def get(self, request, *args, **kwargs): context = {} if publications_do_delete: - formset = formset_factory( - PublicationDeleteForm, max_num=len(publications_do_delete) - ) + formset = formset_factory(PublicationDeleteForm, max_num=len(publications_do_delete)) formset = formset(initial=publications_do_delete, prefix="publicationform") context["formset"] = formset @@ -420,12 +416,8 @@ def post(self, request, *args, **kwargs): publications_do_delete = self.get_publications_to_delete(project_obj) context = {} - formset = formset_factory( - PublicationDeleteForm, max_num=len(publications_do_delete) - ) - formset = formset( - request.POST, initial=publications_do_delete, prefix="publicationform" - ) + formset = formset_factory(PublicationDeleteForm, max_num=len(publications_do_delete)) + formset = formset(request.POST, initial=publications_do_delete, prefix="publicationform") publications_deleted_count = 0 @@ -443,25 +435,19 @@ def post(self, request, *args, **kwargs): messages.success( request, - "Deleted {} publications from project.".format( - publications_deleted_count - ), + "Deleted {} publications from project.".format(publications_deleted_count), ) else: for error in formset.errors: messages.error(request, error) - return HttpResponseRedirect( - reverse("project-detail", kwargs={"pk": project_obj.pk}) - ) + return HttpResponseRedirect(reverse("project-detail", kwargs={"pk": project_obj.pk})) def get_success_url(self): return reverse("project-detail", kwargs={"pk": self.object.project.id}) -class PublicationExportPublicationsView( - LoginRequiredMixin, UserPassesTestMixin, TemplateView -): +class PublicationExportPublicationsView(LoginRequiredMixin, UserPassesTestMixin, TemplateView): template_name = "publication/publication_export_publications.html" def test_func(self): @@ -503,9 +489,7 @@ def get(self, request, *args, **kwargs): context = {} if publications_do_export: - formset = formset_factory( - PublicationExportForm, max_num=len(publications_do_export) - ) + formset = formset_factory(PublicationExportForm, max_num=len(publications_do_export)) formset = formset(initial=publications_do_export, prefix="publicationform") context["formset"] = formset @@ -518,12 +502,8 @@ def post(self, request, *args, **kwargs): publications_do_export = self.get_publications_to_export(project_obj) context = {} - formset = formset_factory( - PublicationExportForm, max_num=len(publications_do_export) - ) - formset = formset( - request.POST, initial=publications_do_export, prefix="publicationform" - ) + formset = formset_factory(PublicationExportForm, max_num=len(publications_do_export)) + formset = formset(request.POST, initial=publications_do_export, prefix="publicationform") publications_deleted_count = 0 bib_text = "" @@ -555,9 +535,7 @@ def post(self, request, *args, **kwargs): for error in formset.errors: messages.error(request, error) - return HttpResponseRedirect( - reverse("project-detail", kwargs={"pk": project_obj.pk}) - ) + return HttpResponseRedirect(reverse("project-detail", kwargs={"pk": project_obj.pk})) def get_success_url(self): return reverse("project-detail", kwargs={"pk": self.object.project.id})