diff --git a/editorialboard/migrations/0005_alter_editorialboardmember_unique_together_and_more.py b/editorialboard/migrations/0005_alter_editorialboardmember_unique_together_and_more.py new file mode 100644 index 000000000..42b9f03b3 --- /dev/null +++ b/editorialboard/migrations/0005_alter_editorialboardmember_unique_together_and_more.py @@ -0,0 +1,117 @@ +# Generated by Django 5.0.8 on 2025-03-07 05:01 + +import django.db.models.deletion +import modelcluster.fields +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("editorialboard", "0004_editorialboardmember_image"), + ("journal", "0029_alter_scielojournal_journal_acron"), + ("researcher", "0004_alter_institutionalauthor_unique_together"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name="editorialboardmember", + name="journal", + field=modelcluster.fields.ParentalKey( + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="editorial_board_member_journal", + to="journal.journal", + ), + ), + migrations.AlterUniqueTogether( + name="editorialboardmember", + unique_together={("journal", "researcher")}, + ), + migrations.CreateModel( + name="RoleEditorialBoard", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "sort_order", + models.IntegerField(blank=True, editable=False, null=True), + ), + ( + "created", + models.DateTimeField( + auto_now_add=True, verbose_name="Creation date" + ), + ), + ( + "updated", + models.DateTimeField( + auto_now=True, verbose_name="Last update date" + ), + ), + ("initial_year", models.CharField(blank=True, max_length=4, null=True)), + ("final_year", models.CharField(blank=True, max_length=4, null=True)), + ( + "creator", + models.ForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_creator", + to=settings.AUTH_USER_MODEL, + verbose_name="Creator", + ), + ), + ( + "editorial_board", + modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="role_editorial_board", + to="editorialboard.editorialboardmember", + ), + ), + ( + "role", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="editorialboard.rolemodel", + ), + ), + ( + "updated_by", + models.ForeignKey( + blank=True, + editable=False, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="%(class)s_last_mod_user", + to=settings.AUTH_USER_MODEL, + verbose_name="Updater", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.RemoveField( + model_name="editorialboardmember", + name="editorial_board", + ), + migrations.RemoveField( + model_name="editorialboardmember", + name="role", + ), + ] diff --git a/editorialboard/migrations/0006_alter_editorialboardmember_researcher_and_more.py b/editorialboard/migrations/0006_alter_editorialboardmember_researcher_and_more.py new file mode 100644 index 000000000..a3acda5f2 --- /dev/null +++ b/editorialboard/migrations/0006_alter_editorialboardmember_researcher_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0.8 on 2025-04-27 18:22 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("editorialboard", "0005_alter_editorialboardmember_unique_together_and_more"), + ("researcher", "0005_newresearcher_researcherids_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="editorialboardmember", + name="researcher", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="researcher.newresearcher", + ), + ), + migrations.DeleteModel( + name="EditorialBoard", + ), + ] diff --git a/editorialboard/migrations/0007_alter_roleeditorialboard_final_year_and_more.py b/editorialboard/migrations/0007_alter_roleeditorialboard_final_year_and_more.py new file mode 100644 index 000000000..1683a6298 --- /dev/null +++ b/editorialboard/migrations/0007_alter_roleeditorialboard_final_year_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.8 on 2025-04-27 18:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("editorialboard", "0006_alter_editorialboardmember_researcher_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="roleeditorialboard", + name="final_year", + field=models.DateField(blank=True, null=True), + ), + migrations.AlterField( + model_name="roleeditorialboard", + name="initial_year", + field=models.DateField(blank=True, null=True), + ), + ] diff --git a/editorialboard/models.py b/editorialboard/models.py index 06c0fe9be..fb9182bad 100644 --- a/editorialboard/models.py +++ b/editorialboard/models.py @@ -1,154 +1,30 @@ import logging import os -from itertools import cycle -from django.db import models, IntegrityError -from django.db.models import Q, Case, When, Value, IntegerField + +from django.db import IntegrityError, models +from django.db.models import Q from django.utils.translation import gettext_lazy as _ from modelcluster.fields import ParentalKey from modelcluster.models import ClusterableModel -from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel +from wagtail.admin.panels import FieldPanel, InlinePanel from wagtail.models import Orderable from wagtailautocomplete.edit_handlers import AutocompletePanel -from core.choices import MONTHS -from core.models import CommonControlField, Gender from core.forms import CoreAdminModelForm +from core.models import CommonControlField from core.utils.standardizer import remove_extra_spaces from journal.models import Journal -from location.models import Location, City, State, Country -from researcher.models import Researcher, Affiliation +from researcher.models import NewResearcher from . import choices -class EditorialBoard(CommonControlField, ClusterableModel): - journal = models.ForeignKey( - Journal, null=True, blank=True, related_name="+", on_delete=models.SET_NULL - ) - initial_year = models.CharField(max_length=4, blank=True, null=True) - final_year = models.CharField(max_length=4, blank=True, null=True) - - autocomplete_search_field = "journal__title" - - def autocomplete_label(self): - return str(self) - - class Meta: - unique_together = [("journal", "initial_year", "final_year")] - - indexes = [ - models.Index( - fields=[ - "initial_year", - ] - ), - models.Index( - fields=[ - "final_year", - ] - ), - ] - - panels = [ - AutocompletePanel("journal"), - FieldPanel("initial_year"), - FieldPanel("final_year"), - InlinePanel("editorial_board_member", label=_("Member")), - ] - base_form_class = CoreAdminModelForm - - def __str__(self): - return f"{self.journal} {self.initial_year}-{self.final_year}" - - - @property - def order_by_role(self): - role_order = [role[0] for role in choices.ROLE] - order = [When(role__std_role=role, then=Value(i)) for i, role in enumerate(role_order)] - - editorial_members = EditorialBoardMember.objects.filter(editorial_board=self) - - ordered_editorial_board = editorial_members.annotate( - editorial_order=Case(*order, default=Value(len(role_order)), output_field=IntegerField()) - ).order_by("editorial_order") - - return ordered_editorial_board - - - @classmethod - def create_or_update( - cls, - user, - journal, - initial_year, - final_year, - journal_title=None, - ): - if not journal and journal_title: - journal_title = journal_title.strip() - journal = Journal.objects.get( - Q(title__icontains=journal_title) - | Q(official__title__icontains=journal_title) - ) - logging.info(f"EditorialBoard {journal_title} OK") - try: - return cls.get(journal, initial_year, final_year) - except cls.DoesNotExist: - return cls.create(user, journal, initial_year, final_year) - - @classmethod - def get( - cls, - journal, - initial_year, - final_year, - ): - try: - return cls.objects.get( - journal=journal, - initial_year=initial_year, - final_year=final_year, - ) - except cls.MultipleObjectsReturned: - return cls.objects.filter( - journal=journal, - initial_year=initial_year, - final_year=final_year, - ).first() - - @classmethod - def create( - cls, - user, - journal, - initial_year, - final_year, - ): - try: - obj = cls() - obj.creator = user - obj.journal = journal - obj.initial_year = initial_year - obj.final_year = final_year - obj.save() - return obj - except IntegrityError: - return cls.objects.get( - journal=journal, - initial_year=initial_year, - final_year=final_year, - ) - - -class EditorialBoardMember(CommonControlField, Orderable): - editorial_board = ParentalKey( - EditorialBoard, related_name="editorial_board_member" +class EditorialBoardMember(CommonControlField, ClusterableModel, Orderable): + journal = ParentalKey( + Journal, related_name="editorial_board_member_journal", null=True ) researcher = models.ForeignKey( - Researcher, null=True, blank=True, related_name="+", on_delete=models.SET_NULL - ) - role = models.ForeignKey( - "RoleModel", null=True, blank=True, related_name="+", on_delete=models.SET_NULL + NewResearcher, null=True, blank=True, related_name="+", on_delete=models.SET_NULL ) image = models.ForeignKey( "wagtailimages.Image", @@ -159,14 +35,14 @@ class EditorialBoardMember(CommonControlField, Orderable): help_text=_("Upload a profile photo of the editorial board member."), ) area = models.TextField(null=True, blank=True) + class Meta: - unique_together = [("editorial_board", "researcher", "role")] + unique_together = [("journal", "researcher")] panels = [ AutocompletePanel("researcher"), - AutocompletePanel("role"), FieldPanel("image"), - # AutocompletePanel("editorial_board", read_only=True), + InlinePanel("role_editorial_board", label=_("Role")), ] base_form_class = CoreAdminModelForm @@ -187,58 +63,49 @@ def autocomplete_label(self): @classmethod def _get( cls, - editorial_board, + journal, researcher, - role, ): params = dict( - editorial_board=editorial_board, + journal=journal, researcher=researcher, - role=role, ) - logging.info(params) - if role and researcher: - try: + if researcher and journal: return cls.objects.get(**params) - except cls.MultipleObjectsReturned: - return cls.objects.filter(*params).first() - raise ValueError("EditorialBoardMember._get requires editorial_board and researcher and role") + raise ValueError("EditorialBoardMember._get requires journal and researcher and role") @classmethod def _create( cls, user, + journal, researcher, - editorial_board, - role, ): - if role and researcher and editorial_board: + if researcher and journal: try: obj = cls() obj.creator = user - obj.editorial_board = editorial_board + obj.journal = journal obj.researcher = researcher - obj.role = role obj.save() return obj except IntegrityError: - return cls._get(editorial_board, researcher, role) - raise ValueError("EditorialBoardMember._create requires editorial_board and researcher and role") + return cls._get(journal, researcher) + raise ValueError("EditorialBoardMember._create requires journal and researcher and role") @classmethod def _create_or_update( cls, user, - editorial_board, - researcher, - role, + journal, + researcher, ): - if role and researcher and editorial_board: + if researcher and journal: try: - return cls._get(editorial_board, researcher, role) + return cls._get(journal, researcher) except cls.DoesNotExist: - return cls._create(user, editorial_board, researcher, role) - raise ValueError("EditorialBoardMember._create requires editorial_board and researcher and journal") + return cls._create(user, journal, researcher) + raise ValueError("EditorialBoardMember._create requires journal and researcher and journal") @classmethod def create_or_update( @@ -246,190 +113,107 @@ def create_or_update( user, researcher=None, journal=None, - journal_title=None, - given_names=None, - last_name=None, - suffix=None, - declared_person_name=None, - lattes=None, - orcid=None, - email=None, - gender_code=None, - gender_identification_status=None, - institution_name=None, - institution_div1=None, - institution_div2=None, - institution_city_name=None, - institution_country_text=None, - institution_country_acronym=None, - institution_country_name=None, - institution_state_text=None, - institution_state_acronym=None, - institution_state_name=None, declared_role=None, std_role=None, - member_activity_initial_year=None, - member_activity_final_year=None, - member_activity_initial_month=None, - member_activity_final_month=None, editorial_board_initial_year=None, editorial_board_final_year=None, ): - if not researcher: - researcher = EditorialBoardMember._create_or_update_researcher( - user, - given_names, - last_name, - suffix, - declared_person_name, - lattes, - orcid, - email, - gender_code, - gender_identification_status, - institution_name, - institution_div1, - institution_div2, - institution_city_name, - institution_country_text, - institution_country_acronym, - institution_country_name, - institution_state_text, - institution_state_acronym, - institution_state_name, - ) - if not journal: - journal = EditorialBoardMember._create_or_update_journal(journal_title) - - editorial_board = EditorialBoard.create_or_update( - user, - journal, - editorial_board_initial_year, - editorial_board_final_year, - ) - + role = None if std_role or declared_role: role = RoleModel.create_or_update( user, std_role=std_role, declared_role=declared_role ) - - return cls._create_or_update(user, editorial_board, researcher, role) - - @staticmethod - def _create_or_update_researcher( - user, - given_names=None, - last_name=None, - suffix=None, - declared_person_name=None, - lattes=None, - orcid=None, - email=None, - gender_code=None, - gender_identification_status=None, - institution_name=None, - institution_div1=None, - institution_div2=None, - institution_city_name=None, - institution_country_text=None, - institution_country_acronym=None, - institution_country_name=None, - institution_state_text=None, - institution_state_acronym=None, - institution_state_name=None, - ): - location = EditorialBoardMember._create_or_update_location( - user, - institution_city_name, - institution_country_text, - institution_country_acronym, - institution_country_name, - institution_state_text, - institution_state_acronym, - institution_state_name, - ) - affiliation = Affiliation.create_or_update( - user, - name=institution_name, - acronym=None, - level_1=institution_div1, - level_2=institution_div2, - level_3=None, - location=location, - official=None, - is_official=None, - url=None, - institution_type=None, - ) - gender = Gender.create_or_update(user, code=gender_code, gender=gender_code) - return Researcher.create_or_update( + editorial_board_member = EditorialBoardMember._create_or_update( user, - given_names=given_names, - last_name=last_name, - suffix=suffix, - declared_name=declared_person_name, - affiliation=affiliation, - year=None, - orcid=orcid, - lattes=lattes, - other_ids=None, - email=email, - gender=gender, - gender_identification_status=gender_identification_status, + journal, + researcher, ) - @staticmethod - def _create_or_update_location( - user, - city_name=None, - country_text=None, - country_acronym=None, - country_name=None, - state_text=None, - state_acronym=None, - state_name=None, - ): - state = None - country = None - if state_text: - for item in State.standardize(state_text, user): - state = item.get("state") - if country_text: - for item in Country.standardize(country_text, user): - country = item.get("country") - - try: - return Location.create_or_update( - user, - country=country, - country_name=country_name, - country_acron3=None, - country_acronym=country_acronym, - state=state, - state_name=state_name, - state_acronym=state_acronym, - city=None, - city_name=city_name, - lang=None, - ) - except Exception as e: - logging.exception(e) - return + role_editorial_board = RoleEditorialBoard.create_or_update( + editorial_board_member, + role, + editorial_board_initial_year, + editorial_board_final_year, + ) + return editorial_board_member @staticmethod - def _create_or_update_journal(journal_title): + def _get_journal(journal_title): try: journal_title = journal_title and journal_title.strip() return Journal.objects.get( Q(title__icontains=journal_title) | Q(official__title__icontains=journal_title) ) - logging.info(f"EditorialBoard {journal_title} OK") except Journal.DoesNotExist as e: logging.info(f"EditorialBoard {journal_title} {e}") +class RoleEditorialBoard(CommonControlField, Orderable): + editorial_board = ParentalKey( + EditorialBoardMember, related_name="role_editorial_board" + ) + role = models.ForeignKey( + "RoleModel", null=True, blank=True, related_name="+", on_delete=models.SET_NULL + ) + initial_year = models.DateField(blank=True, null=True) + final_year = models.DateField(blank=True, null=True) + + panels = [ + AutocompletePanel("role"), + FieldPanel("initial_year"), + FieldPanel("final_year"), + ] + + @classmethod + def get( + cls, + editorial_board_member, + role, + initial_year, + final_year, + ): + params = dict( + editorial_board=editorial_board_member, + role=role, + initial_year=initial_year, + final_year=final_year, + ) + if editorial_board_member and role: + return cls.objects.get(**params) + raise ValueError("RoleEditorialBoard.get requires editorial_board_member and role") + + @classmethod + def create( + cls, + editorial_board_member, + role, + initial_year, + final_year, + ): + obj = cls() + obj.editorial_board = editorial_board_member + obj.role = role + obj.initial_year = initial_year + obj.final_year = final_year + obj.save() + return obj + + @classmethod + def create_or_update( + cls, + editorial_board_member, + role, + initial_year, + final_year, + ): + try: + return cls.get(editorial_board_member, role, initial_year, final_year) + except cls.DoesNotExist: + return cls.create(editorial_board_member, role, initial_year, final_year) + + class EditorialBoardMemberFile(models.Model): attachment = models.ForeignKey( "wagtaildocs.Document", diff --git a/editorialboard/tests.py b/editorialboard/tests.py index c82559ecd..ae2f94e3c 100644 --- a/editorialboard/tests.py +++ b/editorialboard/tests.py @@ -1,20 +1,24 @@ -import logging +from unittest.mock import patch -from django.test import TestCase +from django.test import TestCase, RequestFactory from django.contrib.auth import get_user_model +from django.contrib.messages.storage.fallback import FallbackStorage +from wagtail.documents.models import Document # Create your tests here. +from core.models import Gender +from location.models import Location from editorialboard.models import ( EditorialBoardMember, - EditorialBoardMemberActivity, - EditorialBoardRole, - EditorialBoard, + EditorialBoardMemberFile, ) -from researcher.models import ResearcherAKA +from editorialboard.views import import_file_ebm +from researcher.models import NewResearcher, ResearcherIds +from organization.models import Organization from journal.models import Journal - +from django.core.files.uploadedfile import SimpleUploadedFile User = get_user_model() @@ -22,143 +26,178 @@ class EditorialBoardMemberTest(TestCase): def setUp(self): self.user = User.objects.create(username="user") self.journal = Journal.objects.create(title="Revista XXXX") - logging.info(self.journal.title) - logging.info(self.user) - - def test__create_or_update_location(self): - - location = EditorialBoardMember._create_or_update_location( + self.gender = Gender.create_or_update(user=self.user, code="F", gender="F") + self.location = Location.create_or_update( self.user, - city_name="Campinas", + city_name="São Paulo", country_text="Brasil", - country_acronym=None, + country_acronym="BR", country_name=None, - state_text="SP", - state_acronym=None, - state_name=None, + state_name="São Paulo", + state_acronym="SP", ) - self.assertEqual("Brasil", location.country.name) - self.assertEqual("Campinas", location.city.name) - self.assertEqual("SP", location.state.acronym) - - def test_create_or_update_researcher(self): - - researcher = EditorialBoardMember._create_or_update_researcher( + self.organization = Organization.create_or_update( + user=self.user, + name="Name of institution", + acronym="Acronym of institution", + url="www.teste.com.br", + location=self.location, + institution_type_mec="outros", + is_official=True, + ) + self.researcher_identifier_orcid = ResearcherIds.get_or_create( + user=self.user, + identifier="0000-0002-9147-0547", + source_name="ORCID", + ) + self.researcher_identifier_lattes = ResearcherIds.get_or_create( + user=self.user, + identifier="qwertpoiuytkdiekd", + source_name="LATTES", + ) + self.researcher_identifier_email = ResearcherIds.get_or_create( + user=self.user, + identifier="user@dom.org", + source_name="EMAIL", + ) + self.researcher = NewResearcher.get_or_create( self.user, - given_names=None, - last_name=None, - suffix=None, - declared_person_name="Anna Taomeaome", - lattes="qwertpoiuytkdiekd", - orcid="1234-1234-0987-0987", - email="user@dom.org", - gender_code="F", + given_names="Anna", + last_name="Taomeaome", + suffix="Jr.", + researcher_identifier=self.researcher_identifier_orcid, + affiliation=self.organization, + gender=self.gender, gender_identification_status="DECLARED", - institution_name="Universidade Federal de São Carlos", - institution_div1=None, - institution_div2=None, - institution_city_name="São Carlos", - institution_country_text="Brasil", - institution_country_acronym=None, - institution_country_name=None, - institution_state_text="São Paulo", - institution_state_acronym=None, - institution_state_name=None, + ) + self.researcher = NewResearcher.get_or_create( + self.user, + given_names="Anna", + last_name="Taomeaome", + suffix="Jr.", + researcher_identifier=self.researcher_identifier_email, + affiliation=self.organization, + gender=self.gender, + gender_identification_status="DECLARED", + ) + self.researcher = NewResearcher.get_or_create( + self.user, + given_names="Anna", + last_name="Taomeaome", + suffix="Jr.", + researcher_identifier=self.researcher_identifier_lattes, + affiliation=self.organization, + gender=self.gender, + gender_identification_status="DECLARED", + ) + + def test_create_or_update_location(self): + self.assertEqual("Brasil", self.location.country.name) + self.assertEqual("São Paulo", self.location.city.name) + self.assertEqual("SP", self.location.state.acronym) + + def test_create_or_update_researcher(self): + editorial_board_member = EditorialBoardMember.create_or_update( + user=self.user, + researcher=self.researcher, + journal=self.journal, + declared_role="editor de seção", + std_role=None, + editorial_board_initial_year="2010-03-01", + editorial_board_final_year="2012-03-01", ) self.assertEqual( - "São Paulo", researcher.affiliation.institution.location.state.name + "São Paulo", editorial_board_member.researcher.affiliation.location.state.name ) self.assertEqual( - "São Carlos", researcher.affiliation.institution.location.city.name + "São Paulo", editorial_board_member.researcher.affiliation.location.city.name ) self.assertEqual( - "Brasil", researcher.affiliation.institution.location.country.name + "Brasil", editorial_board_member.researcher.affiliation.location.country.name ) self.assertEqual( - "Universidade Federal de São Carlos", - researcher.affiliation.institution.institution_identification.name, + "Name of institution", + editorial_board_member.researcher.affiliation.name, ) - self.assertEqual("F", researcher.person_name.gender.code) - self.assertEqual("F", researcher.person_name.gender.gender) + self.assertEqual("F", editorial_board_member.researcher.gender.code) + self.assertEqual("F", editorial_board_member.researcher.gender.gender) self.assertEqual( - "DECLARED", researcher.person_name.gender_identification_status + "DECLARED", editorial_board_member.researcher.gender_identification_status ) - self.assertEqual("Anna Taomeaome", researcher.person_name.declared_name) + self.assertEqual("Anna Taomeaome Jr.", editorial_board_member.researcher.fullname) + self.assertEqual( "qwertpoiuytkdiekd", - ResearcherAKA.objects.get( - researcher=researcher, researcher_identifier__source_name="LATTES" - ).researcher_identifier.identifier, + editorial_board_member.researcher.researcher_ids.filter(source_name="LATTES").first().identifier, ) self.assertEqual( - "1234-1234-0987-0987", - ResearcherAKA.objects.get( - researcher=researcher, researcher_identifier__source_name="ORCID" - ).researcher_identifier.identifier, + "0000-0002-9147-0547", + editorial_board_member.researcher.researcher_ids.filter(source_name="ORCID").first().identifier, ) self.assertEqual( "user@dom.org", - ResearcherAKA.objects.get( - researcher=researcher, researcher_identifier__source_name="EMAIL" - ).researcher_identifier.identifier, + editorial_board_member.researcher.researcher_ids.filter(source_name="EMAIL").first().identifier, ) - def test_create_or_update(self): - member = EditorialBoardMember.create_or_update( - self.user, - researcher=None, - journal=None, - journal_title="Revista XXXX", - given_names=None, - last_name=None, - suffix=None, - declared_person_name="Anna Taomeaome", - lattes="qwertpoiuytkdiekd", - orcid="1234-1234-0987-0987", - email="user@dom.org", - gender_code="F", - gender_identification_status="DECLARED", - institution_name="Universidade Federal de São Carlos", - institution_div1=None, - institution_div2=None, - institution_city_name="São Carlos", - institution_country_text="Brasil", - institution_country_acronym=None, - institution_country_name=None, - institution_state_text="São Paulo", - institution_state_acronym=None, - institution_state_name=None, + def test_editorial_board_member_create_or_update(self): + editorial_board = EditorialBoardMember.create_or_update( + user=self.user, + journal=self.journal, + researcher=self.researcher, declared_role="editor de seção", - std_role=None, - member_activity_initial_year="2000", - member_activity_final_year="2010", - member_activity_initial_month="01", - member_activity_final_month="12", - editorial_board_initial_year="2010", - editorial_board_final_year="2012", + editorial_board_initial_year="2010-01-01", + editorial_board_final_year="2012-01-01", ) + self.assertEqual(self.researcher, editorial_board.researcher) + self.assertEqual("Revista XXXX", editorial_board.journal.title) + self.assertEqual(1, EditorialBoardMember.objects.get(journal=self.journal).role_editorial_board.count()) + self.assertEqual("editor de seção", EditorialBoardMember.objects.get(journal=self.journal).role_editorial_board.first().role.declared_role) - self.assertEqual( - "2000", - EditorialBoardMemberActivity.objects.get( - member=member, - role__declared_role="editor de seção", - initial_year="2000", - ).initial_year, - ) - self.assertEqual( - "2010", EditorialBoard.objects.get(journal=self.journal).initial_year - ) - self.assertEqual( - "2012", EditorialBoard.objects.get(journal=self.journal).final_year + +class ImportFileEBMTest(TestCase): + def setUp(self): + self.user = User.objects.create(username="user") + self.journal = Journal.objects.create(title="Revista XXXX") + self.csv_content = """Nome do membro;Sobrenome;Periódico;Suffix;declared_person_name;CV Lattes;ORCID iD;Email;Gender;institution_city_name;institution_state_text;institution_state_acronym;institution_state_name;institution_country_text;institution_country_acronym;institution_country_name;institution_div1;institution_div2;Instituição;Cargo / instância do membro;Data +John;Doe;Revista XXXX;Jr;John Doe;lattes;0000-0000-0000-0000;john@doe.com;M;City;State;ST;State Name;Country;CN;Country Name;Div1;Div2;Institution;Editor;2020""" + self.factory = RequestFactory() + + def create_editorial_file(self, csv_content): + csv_file = SimpleUploadedFile( + name="test.csv", + content=csv_content.encode("utf-8"), + content_type="text/csv", ) + document = Document.objects.create(title="Test CSV", file=csv_file) + + return EditorialBoardMemberFile.objects.create(attachment=document) + + def add_messages_middleware(self, request): + """Add session and messages middleware to the request.""" + from django.contrib.sessions.middleware import SessionMiddleware + from django.contrib.messages.middleware import MessageMiddleware + + # Pass a dummy get_response function to the middleware + SessionMiddleware(lambda req: None).process_request(request) + MessageMiddleware(lambda req: None).process_request(request) + request.session.save() + request._messages = FallbackStorage(request) + return request - for item in EditorialBoardRole.objects.get( - editorial_board__journal=self.journal, - role__std_role="associate editor", - ).members.all(): - with self.subTest(item): - self.assertEqual( - "Anna Taomeaome", item.researcher.person_name.declared_name - ) + def test_import_file_edm(self): + editorial_file = self.create_editorial_file(self.csv_content) + request = self.factory.get(f"/import-ebm/?file_id={editorial_file.pk}") + request.user = self.user + request.META["HTTP_REFERER"] = "/some-valid-url/" + request = self.add_messages_middleware(request) + response = import_file_ebm(request) + self.assertEqual(response.status_code, 302) + self.assertEqual(Location.objects.all().count(), 1) + self.assertEqual(Researcher.objects.all().count(), 1) + self.assertEqual(EditorialBoardMember.objects.all().count(), 1) + self.assertEqual(EditorialBoardMember.objects.first().researcher.person_name.fullname, "John Doe Jr") + self.assertEqual(EditorialBoardMember.objects.first().journal.title, "Revista XXXX") + self.assertEqual(EditorialBoardMember.objects.first().role_editorial_board.first().role.declared_role, "Editor") + self.assertEqual(EditorialBoardMember.objects.first().role_editorial_board.first().initial_year, "2020") + self.assertEqual(EditorialBoardMember.objects.first().role_editorial_board.first().final_year, "2020") \ No newline at end of file diff --git a/editorialboard/views.py b/editorialboard/views.py index f2e543a16..6ce4d5cf3 100644 --- a/editorialboard/views.py +++ b/editorialboard/views.py @@ -1,9 +1,7 @@ import csv import os -from datetime import datetime +import sys -from django.contrib.auth import get_user_model -from django.http import Http404, HttpResponse from django.shortcuts import get_object_or_404, redirect from django.utils.translation import gettext as _ from wagtail.admin import messages @@ -11,6 +9,11 @@ from core.libs import chkcsv from .models import EditorialBoardMember, EditorialBoardMemberFile +from journal.models import Journal +from location.models import Location +from researcher.models import Researcher +from tracker.models import UnexpectedEvent +from core.models import Gender def validate_ebm(request): @@ -71,61 +74,60 @@ def import_file_ebm(request): with open(file_path, "r") as csvfile: data = csv.DictReader(csvfile, delimiter=";") for line, row in enumerate(data): - given_names = row["Nome do membro"] - last_name = row["Sobrenome"] - # ed = EditorialBoardMember() - # ed.get_or_create( - # row["Periódico"], - # row["Cargo / instância do membro"], - # row["Data"], - # row["Email"], - # row["Institution"], - # given_names, - # last_name, - # row["Suffix"], - # row["ORCID iD"], - # row["CV Lattes"], - # row["Gender"], - # row["Gender status"], - # request.user, - # ) - # ,User) - - EditorialBoardMember.create_or_update( - request.user, - researcher=None, - journal=None, - journal_title=row["Periódico"], + given_names = row.get("Nome do membro") + last_name = row.get("Sobrenome") + journal = Journal.objects.get(title__icontains=row.get("Periódico")) + gender = Gender.create_or_update(user=request.user, code=row.get("Gender"), gender="F") + location = Location.create_or_update( + user=request.user, + city_name=row.get("institution_city_name"), + state_text=row.get("institution_state_text"), + state_acronym=row.get("institution_state_acronym"), + state_name=row.get("institution_state_name"), + country_text=row.get("institution_country_text"), + country_acronym=row.get("institution_country_acronym"), + country_name=row.get("institution_country_name"), + ) + researcher = Researcher.create_or_update( + user=request.user, given_names=given_names, last_name=last_name, - suffix=row["Suffix"], - declared_person_name=row.get("declared_person_name"), - lattes=row["CV Lattes"], - orcid=row["ORCID iD"], - email=row["Email"], - gender_code=row["Gender"], - gender_identification_status=row["Gender status"], - institution_name=row["Institution"], - institution_div1=row.get("institution_div1"), - institution_div2=row.get("institution_div2"), - institution_city_name=row.get("institution_city_name"), - institution_country_text=row.get("institution_country_text"), - institution_country_acronym=row.get("institution_country_acronym"), - institution_country_name=row.get("institution_country_name"), - institution_state_text=row.get("institution_state_text"), - institution_state_acronym=row.get("institution_state_acronym"), - institution_state_name=row.get("institution_state_name"), + suffix=row.get("Suffix"), + declared_name=row.get("declared_person_name"), + lattes=row.get("CV Lattes"), + orcid=row.get("ORCID iD"), + email=row.get("Email"), + gender=gender, + location=location, + aff_div1=row.get("institution_div1"), + aff_div2=row.get("institution_div2"), + aff_name=row.get("Instituição"), + ) + EditorialBoardMember.create_or_update( + user=request.user, + researcher=researcher, + journal=journal, declared_role=row["Cargo / instância do membro"], std_role=None, - member_activity_initial_year=row["Data"], - member_activity_final_year=row["Data"], - member_activity_initial_month="01", - member_activity_final_month="12", editorial_board_initial_year=row["Data"], editorial_board_final_year=row["Data"], ) + except Exception as ex: - messages.error(request, _("Import error: %s, Line: %s") % (ex, str(line + 2))) + messages.error(request, _("Import error: %s, Line: %s. Exception: %s") % (ex, str(line + 2), ex)) + exc_type, exc_value, exc_traceback = sys.exc_info() + UnexpectedEvent.create( + item=str(file_upload), + action="editorialboard.views.import_file_ebm", + exception=ex, + exc_traceback=exc_traceback, + detail=dict( + line=line, + row=row, + file_path=file_path, + file_id=file_id, + ), + ) else: messages.success(request, _("File imported successfully!")) diff --git a/editorialboard/wagtail_hooks.py b/editorialboard/wagtail_hooks.py index b0b175659..9a864bf2a 100644 --- a/editorialboard/wagtail_hooks.py +++ b/editorialboard/wagtail_hooks.py @@ -12,7 +12,7 @@ from .button_helper import EditorialBoardMemberHelper from .models import ( - EditorialBoard, + # EditorialBoard, EditorialBoardMember, EditorialBoardMemberFile, RoleModel, @@ -46,11 +46,6 @@ class EditorialBoardMemberAdmin(ModelAdmin): "journal__title", "researcher__person_name__fullname", ) - custom_panels = [ - FieldPanel("researcher", read_only=True), - FieldPanel("role", read_only=True), - ] - edit_handler = ObjectList(custom_panels) class EditorialBoardMemberFileAdmin(ModelAdmin): @@ -91,27 +86,27 @@ def form_valid(self, form): return HttpResponseRedirect(self.get_success_url()) -class EditorialBoardAdmin(ModelAdmin): - model = EditorialBoard - create_view_class = EditorialBoardCreateView - menu_label = _("EditorialBoard") - menu_icon = "folder" - menu_order = 9 - add_to_settings_menu = False - exclude_from_explorer = False - list_display = ( - "journal", - "initial_year", - "final_year", - "created", - "updated", - ) - list_filter = ("initial_year", "final_year") - search_fields = ( - "journal__title", - "initial_year", - "final_year", - ) +# class EditorialBoardAdmin(ModelAdmin): +# model = EditorialBoard +# create_view_class = EditorialBoardCreateView +# menu_label = _("EditorialBoard") +# menu_icon = "folder" +# menu_order = 9 +# add_to_settings_menu = False +# exclude_from_explorer = False +# list_display = ( +# "journal", +# "initial_year", +# "final_year", +# "created", +# "updated", +# ) +# list_filter = ("initial_year", "final_year") +# search_fields = ( +# "journal__title", +# "initial_year", +# "final_year", +# ) class EditorialBoardAdminGroup(ModelAdminGroup): @@ -120,7 +115,7 @@ class EditorialBoardAdminGroup(ModelAdminGroup): menu_order = get_menu_order("editorialboard") # will put in 3rd place (000 being 1st, 100 2nd) items = ( RoleModelAdmin, - EditorialBoardAdmin, + # EditorialBoardAdmin, EditorialBoardMemberAdmin, EditorialBoardMemberFileAdmin, ) diff --git a/journal/models.py b/journal/models.py index ec2e32abd..02c4c7987 100755 --- a/journal/models.py +++ b/journal/models.py @@ -777,6 +777,10 @@ def autocomplete_label(self): AutocompletePanel("abstract_language"), ] + panels_editorial_board = [ + InlinePanel("editorial_board_member_journal", label=_("Editorial Board")), + ] + edit_handler = TabbedInterface( [ ObjectList(panels_titles, heading=_("Titles")), @@ -792,6 +796,7 @@ def autocomplete_label(self): ObjectList( panels_instructions_for_authors, heading=_("Instructions for Authors") ), + ObjectList(panels_editorial_board, heading=_("Editorial Board")), ] ) diff --git a/journal/wagtail_hooks.py b/journal/wagtail_hooks.py index 1d0bfa9a1..65ac3d05b 100755 --- a/journal/wagtail_hooks.py +++ b/journal/wagtail_hooks.py @@ -78,14 +78,15 @@ def get_edit_handler(self): """ edit_handler = super().get_edit_handler() user_permissions = self.request.user.get_all_permissions() - for object_list in edit_handler.children: - for field in object_list.children: - if isinstance(field, FieldPanel) and f"journal.can_edit_{field.field_name}" not in user_permissions: - field.__setattr__('read_only', True) - elif isinstance(field, InlinePanel) and f"journal.can_edit_{field.relation_name}" not in user_permissions: - field.classname = field.classname + ' read-only-inline-panel' - for inline_field in field.panel_definitions: - inline_field.__setattr__('read_only', True) + if not self.request.user.is_superuser: + for object_list in edit_handler.children: + for field in object_list.children: + if isinstance(field, FieldPanel) and f"journal.can_edit_{field.field_name}" not in user_permissions: + field.__setattr__('read_only', True) + elif isinstance(field, InlinePanel) and f"journal.can_edit_{field.relation_name}" not in user_permissions: + field.classname = field.classname + ' read-only-inline-panel' + for inline_field in field.panel_definitions: + inline_field.__setattr__('read_only', True) return edit_handler diff --git a/journalpage/utils/utils.py b/journalpage/utils/utils.py index ddc8509a5..ca400e006 100644 --- a/journalpage/utils/utils.py +++ b/journalpage/utils/utils.py @@ -1,5 +1,5 @@ from journal.models import Journal -from editorialboard.models import EditorialBoard +from editorialboard.models import EditorialBoardMember from editorialboard.choices import ROLE from django.shortcuts import render @@ -24,10 +24,10 @@ def find_most_recent_journal(journal): def get_editorial_board(journal): try: - editorial_board = EditorialBoard.objects.filter(journal=journal).latest( + editorial_board = EditorialBoardMember.objects.filter(journal=journal).latest( "initial_year" ) - except EditorialBoard.DoesNotExist: + except EditorialBoardMember.DoesNotExist: editorial_board = None return editorial_board diff --git a/researcher/models.py b/researcher/models.py index 545028c86..779800f54 100644 --- a/researcher/models.py +++ b/researcher/models.py @@ -747,9 +747,12 @@ def get(cls, suffix, given_names, last_name, researcher_identifier): "Researcher.get requires given_names, last_name parameters" ) fullname = cls.join_names(given_names, last_name, suffix) - if researcher_identifier: - qs = cls.get_by_orcid(researcher_identifier) - return qs.get(fullname__iexact=fullname) + if researcher_identifier and researcher_identifier.source_name == "ORCID": + try: + qs = cls.get_by_orcid(researcher_identifier) + return qs.get(fullname__iexact=fullname) + except cls.DoesNotExist: + pass return cls.objects.get(fullname__iexact=fullname) @classmethod @@ -775,9 +778,6 @@ def create( affiliation=affiliation, ) obj.save() - if researcher_identifier: - obj.researcher_ids.add(researcher_identifier) - obj.save() return obj except IntegrityError: return cls.get( @@ -800,14 +800,14 @@ def get_or_create( gender_identification_status=None, ): try: - return cls.get( + obj = cls.get( given_names=given_names, last_name=last_name, suffix=suffix, researcher_identifier=researcher_identifier, ) except cls.DoesNotExist: - return cls.create( + obj = cls.create( user=user, given_names=given_names, last_name=last_name, @@ -817,7 +817,6 @@ def get_or_create( gender=gender, gender_identification_status=gender_identification_status, ) - except (InvalidOrcidError, ValueError) as e: data = dict( given_names=given_names, @@ -835,7 +834,11 @@ def get_or_create( "data": data, }, ) - + finally: + if researcher_identifier: + obj.researcher_ids.add(researcher_identifier) + obj.save() + return obj class ResearcherIds(CommonControlField): """