diff --git a/article/controller.py b/article/controller.py index dce6e3859..48ede7f3e 100644 --- a/article/controller.py +++ b/article/controller.py @@ -345,7 +345,7 @@ def bulk_export_articles_to_articlemeta( ) return False - for article in queryset.iterator(): + for article in queryset.select_related("journal", "journal__official", "pp_xml").iterator(): try: if force_update: article.check_availability(user) diff --git a/bigbang/tasks.py b/bigbang/tasks.py index 410596a4a..4ade2cb43 100644 --- a/bigbang/tasks.py +++ b/bigbang/tasks.py @@ -8,6 +8,7 @@ from collection.models import Collection from config import celery_app from core.models import Gender, Language, License +from core.utils.utils import _get_user from editorialboard.models import RoleModel from institution.models import Institution, InstitutionType from journal.models import ( @@ -26,13 +27,6 @@ User = get_user_model() -def _get_user(user_id, username): - if user_id: - return User.objects.get(pk=user_id) - if username: - return User.objects.get(username=username) - - @celery_app.task(bind=True) def task_start( self, @@ -40,7 +34,7 @@ def task_start( username=None, ): try: - user = _get_user(user_id, username) + user = _get_user(request=None, user_id=user_id, username=username) Language.load(user) Collection.load(user) Vocabulary.load(user) @@ -73,7 +67,7 @@ def task_start( @celery_app.task(bind=True) def task_create_tasks(self, user_id=None, username=None, tasks_data=None): if not tasks_data: - user = _get_user(user_id, username) + user = _get_user(request=None, user_id=user_id, username=username) return schedule_tasks(user.username) for task_data in tasks_data: # { diff --git a/collection/tasks.py b/collection/tasks.py index 264e94943..e5244043e 100644 --- a/collection/tasks.py +++ b/collection/tasks.py @@ -11,6 +11,8 @@ def task_load_collections(self, user_id=None, username=None): if user_id: user = User.objects.get(pk=user_id) - if username: + elif username: user = User.objects.get(username=username) + else: + raise ValueError("user_id or username is required") Collection.load(user) diff --git a/config/settings/base.py b/config/settings/base.py index 9140a2bb7..78c59ea37 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -374,11 +374,21 @@ # http://docs.celeryproject.org/en/latest/userguide/configuration.html#std:setting-result_serializer CELERY_RESULT_SERIALIZER = "json" # http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-time-limit -# TODO: set to whatever value is adequate in your circumstances -CELERY_TASK_TIME_LIMIT = 5 * 60 +# Hard time limit: o worker é terminado (SIGKILL) após este tempo. +# Deve ser MAIOR que o soft time limit. +CELERY_TASK_TIME_LIMIT = env.int('CELERY_TASK_TIME_LIMIT', default=36000) # http://docs.celeryproject.org/en/latest/userguide/configuration.html#task-soft-time-limit -# TODO: set to whatever value is adequate in your circumstances -CELERY_TASK_SOFT_TIME_LIMIT = 36000 +# Soft time limit: levanta SoftTimeLimitExceeded, permitindo cleanup. +# Deve ser MENOR que o hard time limit. +CELERY_TASK_SOFT_TIME_LIMIT = env.int('CELERY_TASK_SOFT_TIME_LIMIT', default=3600) + +# Recicla worker após N tarefas para liberar memória e conexões de banco de dados. +# Cada worker filho é substituído após processar este número de tarefas. +CELERY_WORKER_MAX_TASKS_PER_CHILD = env.int('CELERY_WORKER_MAX_TASKS_PER_CHILD', default=100) + +# Limita a concorrência do Celery worker. +# Controla quantas tarefas cada worker processa simultaneamente. +CELERY_WORKER_CONCURRENCY = env.int('CELERY_WORKER_CONCURRENCY', default=4) # http://docs.celeryproject.org/en/latest/userguide/configuration.html#beat-scheduler CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler" # http://docs.celeryproject.org/en/latest/userguide/configuration.html @@ -404,8 +414,11 @@ RUN_ASYNC = env.bool('RUN_ASYNC', default=0) # Celery Results # ------------------------------------------------------------------------------ -# https: // django-celery-results.readthedocs.io/en/latest/getting_started.html -CELERY_RESULT_BACKEND = "django-db" +# https://django-celery-results.readthedocs.io/en/latest/getting_started.html +# NOTA: Não usar "django-db" como result backend em produção. +# O result backend já está configurado como Redis (CELERY_BROKER_URL) acima. +# Manter "django-db" aqui causaria escritas extras no PostgreSQL a cada tarefa concluída. +# CELERY_RESULT_BACKEND = "django-db" # REMOVIDO: já usa Redis acima CELERY_CACHE_BACKEND = "django-cache" CELERY_RESULT_EXTENDED = True diff --git a/config/settings/production.py b/config/settings/production.py index 75fe9dc13..ff7569d10 100755 --- a/config/settings/production.py +++ b/config/settings/production.py @@ -14,19 +14,13 @@ # ------------------------------------------------------------------------------ DATABASES["default"] = env.db("DATABASE_URL") # noqa F405 DATABASES["default"]["ATOMIC_REQUESTS"] = True # noqa F405 -DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=0) or env.int("DJANGO_CONN_MAX_AGE", default=60) # noqa F405 +# Reutilizar conexões por 60s por padrão para reduzir overhead de reconexão. +# Em produção com gunicorn+gevent ou Celery, isto evita abrir/fechar conexão a cada request. +DATABASES["default"]["CONN_MAX_AGE"] = env.int("CONN_MAX_AGE", default=60) # noqa F405 DATABASES["default"]["CONN_HEALTH_CHECKS"] = env.bool('DJANGO_CONN_HEALTH_CHECKS', True) DATABASES["default"]["ENGINE"] = 'django_prometheus.db.backends.postgresql' -# Melhoria: Usando variáveis de ambiente para OPTIONS e POOL_OPTIONS com defaults DATABASES["default"]["OPTIONS"] = { "connect_timeout": env.int("DB_CONNECT_TIMEOUT", default=10), - # Adicione outras opções de conexão aqui se necessário -} -DATABASES["default"]["POOL_OPTIONS"] = { - 'POOL_SIZE': env.int("DB_POOL_SIZE", default=10), - 'MAX_OVERFLOW': env.int("DB_MAX_OVERFLOW", default=20), - 'RECYCLE': env.int("DB_RECYCLE", default=300), - # Adicione outras opções do pool aqui se necessário } # CACHES # ------------------------------------------------------------------------------ @@ -82,6 +76,11 @@ # Ex: export DJANGO_SESSION_SAVE_EVERY_REQUEST=False SESSION_SAVE_EVERY_REQUEST = env.bool('DJANGO_SESSION_SAVE_EVERY_REQUEST', True) +# Sessões armazenadas no Redis em vez do PostgreSQL. +# Reduz escritas no banco de dados a cada request quando SESSION_SAVE_EVERY_REQUEST=True. +SESSION_ENGINE = "django.contrib.sessions.backends.cache" +SESSION_CACHE_ALIAS = "default" + # STATIC # ------------------------ STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" diff --git a/core/middleware.py b/core/middleware.py index 5ca37f062..e24f0cb3a 100644 --- a/core/middleware.py +++ b/core/middleware.py @@ -15,9 +15,10 @@ def __call__(self, request): if request.user.is_authenticated: set_current_user(request.user) - set_current_collections(request.user.collection.all()) + collections = request.user.collection.all() + set_current_collections(collections) - request.user_collection = request.user.collection.all() + request.user_collection = collections else: set_current_user(None) set_current_collections(None) diff --git a/core/utils/utils.py b/core/utils/utils.py index 71e754f85..d57cd63be 100644 --- a/core/utils/utils.py +++ b/core/utils/utils.py @@ -91,12 +91,14 @@ def fetch_data(url, headers=None, json=False, timeout=FETCH_DATA_TIMEOUT, verify def _get_user(request, username=None, user_id=None): try: - return User.objects.get(pk=request.user_id) + if request.user.is_authenticated: + return request.user except AttributeError: - if user_id: - return User.objects.get(pk=user_id) - if username: - return User.objects.get(username=username) + pass + if user_id: + return User.objects.get(pk=user_id) + if username: + return User.objects.get(username=username) def formated_date_api_params(query_params): diff --git a/core/wagtail_hooks.py b/core/wagtail_hooks.py index 4842c5d75..0f253c4cc 100755 --- a/core/wagtail_hooks.py +++ b/core/wagtail_hooks.py @@ -175,3 +175,6 @@ class LicenseViewSet(SnippetViewSet): search_fields = ("license_type", "version") list_export = ("license_type", "version") inspect_view_enabled = True + + def get_queryset(self, request): + return super().get_queryset(request).select_related("creator") diff --git a/editorialboard/views.py b/editorialboard/views.py index 6e6d5ba4c..d56d2427c 100644 --- a/editorialboard/views.py +++ b/editorialboard/views.py @@ -71,15 +71,16 @@ def import_file_ebm(request): file_path = file_upload.attachment.file.path try: + user = request.user with open(file_path, "r") as csvfile: data = csv.DictReader(csvfile, delimiter=";") for line, row in enumerate(data): 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") + gender = Gender.create_or_update(user=user, code=row.get("Gender"), gender="F") location = Location.create_or_update( - user=request.user, + user=user, city_name=row.get("institution_city_name"), state_text=row.get("institution_state_text"), state_acronym=row.get("institution_state_acronym"), @@ -89,7 +90,7 @@ def import_file_ebm(request): country_name=row.get("institution_country_name"), ) researcher = Researcher.create_or_update( - user=request.user, + user=user, given_names=given_names, last_name=last_name, suffix=row.get("Suffix"), @@ -104,7 +105,7 @@ def import_file_ebm(request): aff_name=row.get("Instituição"), ) EditorialBoardMember.create_or_update( - user=request.user, + user=user, researcher=researcher, journal=journal, declared_role=row["Cargo / instância do membro"], diff --git a/institution/models.py b/institution/models.py index d70774da5..c017f6ad4 100755 --- a/institution/models.py +++ b/institution/models.py @@ -201,10 +201,16 @@ def create_or_update( level_3=level_3, location=location, ) - institution.updated_by = user - institution.institution_type = institution_type or institution.institution_type - institution.url = url or institution.url - institution.save() + changed = False + if institution_type and institution_type != institution.institution_type: + institution.institution_type = institution_type + changed = True + if url and url != institution.url: + institution.url = url + changed = True + if changed: + institution.updated_by = user + institution.save() return institution except cls.DoesNotExist: return cls._create( @@ -968,10 +974,16 @@ def create_or_update( try: obj = cls._get(name=name, acronym=acronym) - obj.updated_by = user - obj.is_official = is_official or obj.is_official - obj.official = official or obj.official - obj.save() + changed = False + if is_official is not None and is_official != obj.is_official: + obj.is_official = is_official + changed = True + if official and official != obj.official: + obj.official = official + changed = True + if changed: + obj.updated_by = user + obj.save() return obj except cls.DoesNotExist: return cls._create( diff --git a/issue/wagtail_hooks.py b/issue/wagtail_hooks.py index 34f17c832..a1b2de5b6 100644 --- a/issue/wagtail_hooks.py +++ b/issue/wagtail_hooks.py @@ -89,6 +89,8 @@ def get_queryset(self, request): # Base queryset com otimizações qs = Issue.objects.select_related( "journal", + "creator", + "updated_by", ) user = request.user diff --git a/journal/models.py b/journal/models.py index 93ccfa952..b5666d10a 100755 --- a/journal/models.py +++ b/journal/models.py @@ -287,26 +287,20 @@ def create_or_update( def add_old_title(self, user, title): if not title: return - old_title = None - for item in OfficialJournal.objects.filter(title=title).iterator(): - old_title = item - break + old_title = OfficialJournal.objects.filter(title=title).first() if not old_title: old_title = OfficialJournal.objects.create(title=title, creator=user) self.old_title.add(old_title) - self.save() + # M2M .add() grava diretamente na tabela intermediária; não precisa de self.save() def add_new_title(self, user, title): if not title: return - new_title = None - for item in OfficialJournal.objects.filter(title=title).iterator(): - new_title = item - break + new_title = OfficialJournal.objects.filter(title=title).first() if not new_title: new_title = OfficialJournal.objects.create(title=title, creator=user) self.new_title = new_title - self.save() + # Não faz save() aqui; chamador é responsável por salvar @property def parallel_titles(self): @@ -1026,10 +1020,10 @@ def get_issn_list(cls, collection_acron_list=None, journal_acron_list=None): @property def collection_acrons(self): - acrons = [] - for item in self.scielojournal_set.all(): - acrons.append(item.collection.acron3) - return "|".join(acrons) + return "|".join( + self.scielojournal_set.select_related("collection") + .values_list("collection__acron3", flat=True) + ) @classmethod def get_ids(cls, collection_acron_list=None, journal_acron_list=None): @@ -1042,7 +1036,7 @@ def select_collections(self, collection_acron_list=None, is_active=None): params["collection__is_active"] = is_active if collection_acron_list: params["collection__acron3__in"] = collection_acron_list - for item in self.scielojournal_set.filter(**params): + for item in self.scielojournal_set.select_related("collection").filter(**params): yield item.collection def get_legacy_keys(self, collection_acron_list=None, is_active=None): @@ -1052,7 +1046,7 @@ def get_legacy_keys(self, collection_acron_list=None, is_active=None): if is_active: params["collection__is_active"] = bool(is_active) data = {} - for item in self.scielojournal_set.filter(**params): + for item in self.scielojournal_set.select_related("collection").filter(**params): data[item.collection.acron3] = item.legacy_keys if not data: UnexpectedEvent.create( @@ -2930,39 +2924,25 @@ def am_to_core( "suspended-by-committee": "by-committee", "suspended-by-editor": "by-editor", } - try: - obj = cls.objects.get( - scielo_journal=scielo_journal, - year=initial_year, - month=initial_month, - day=initial_day, - ) - except cls.DoesNotExist: - obj = cls() - obj.scielo_journal = scielo_journal - obj.year = initial_year - obj.month = initial_month - obj.day = initial_day - obj.event_type = "ADMITTED" - obj.save() + obj, _ = cls.objects.update_or_create( + scielo_journal=scielo_journal, + year=initial_year, + month=initial_month, + day=initial_day, + defaults={"event_type": "ADMITTED"}, + ) if final_year and event_type: - try: - obj = cls.objects.get( - scielo_journal=scielo_journal, - year=final_year, - month=final_month, - day=final_day, - ) - except cls.DoesNotExist: - obj = cls() - obj.scielo_journal = scielo_journal - obj.year = final_year - obj.month = final_month - obj.day = final_day - obj.event_type = "INTERRUPTED" - obj.interruption_reason = reasons.get(interruption_reason) - obj.save() + obj, _ = cls.objects.update_or_create( + scielo_journal=scielo_journal, + year=final_year, + month=final_month, + day=final_day, + defaults={ + "event_type": "INTERRUPTED", + "interruption_reason": reasons.get(interruption_reason), + }, + ) class TitleInDatabase(Orderable, CommonControlField): diff --git a/location/models.py b/location/models.py index 77cf7cd8c..e96b18283 100755 --- a/location/models.py +++ b/location/models.py @@ -202,10 +202,16 @@ def create_or_update(cls, user, name=None, acronym=None): acronym = remove_extra_spaces(acronym) try: obj = cls.get(name=name, acronym=acronym) - obj.updated_by = user - obj.name = name or obj.name - obj.acronym = acronym or obj.acronym - obj.save() + changed = False + if name and name != obj.name: + obj.name = name + changed = True + if acronym and acronym != obj.acronym: + obj.acronym = acronym + changed = True + if changed: + obj.updated_by = user + obj.save() except cls.DoesNotExist: obj = cls.create(user, name, acronym) return obj @@ -292,11 +298,19 @@ def create_or_update(cls, user, country, language, text): text = remove_extra_spaces(text) try: obj = cls.get(country, language) - obj.updated_by = user - obj.country = country or obj.country - obj.language = language or obj.language - obj.text = text or obj.text - obj.save() + changed = False + if country and country != obj.country: + obj.country = country + changed = True + if language and language != obj.language: + obj.language = language + changed = True + if text and text != obj.text: + obj.text = text + changed = True + if changed: + obj.updated_by = user + obj.save() return obj except cls.DoesNotExist: return cls.create(user, country, language, text) @@ -319,9 +333,11 @@ def create(cls, user, country, language, text): def get_country(cls, name): name = remove_extra_spaces(name) if name: - for item in CountryName.objects.filter(text=name).iterator(): - if item.country: - return item.country + item = CountryName.objects.filter( + text=name, country__isnull=False + ).select_related("country").first() + if item: + return item.country raise cls.DoesNotExist(f"CountryName {name} does not exist") @@ -441,15 +457,26 @@ def create_or_update( try: obj = cls.get(name, acronym, acron3) - obj.updated_by = user + changed = False + if name and name != obj.name: + obj.name = name + changed = True + if acronym and acronym != obj.acronym: + obj.acronym = acronym + changed = True + if acron3 and acron3 != obj.acron3: + obj.acron3 = acron3 + changed = True + if changed: + obj.updated_by = user + obj.save() except cls.DoesNotExist: obj = cls() obj.creator = user - - obj.name = name or obj.name - obj.acronym = acronym or obj.acronym - obj.acron3 = acron3 or obj.acron3 - obj.save() + obj.name = name + obj.acronym = acronym + obj.acron3 = acron3 + obj.save() country_names = country_names or {} diff --git a/location/scripts/bulk_cities.py b/location/scripts/bulk_cities.py index f9fbb9065..1273f8455 100755 --- a/location/scripts/bulk_cities.py +++ b/location/scripts/bulk_cities.py @@ -17,16 +17,14 @@ def run(*args): # Delete all cities models.City.objects.all().delete() + # User - busca uma única vez fora do loop + if args: + user_id = args[0] + creator = User.objects.get(id=user_id) + with open( os.path.dirname(os.path.realpath(__file__)) + "/../fixtures/cities.csv", "r" ) as fp: for line in fp.readlines(): name = line.strip() - - # User - if args: - user_id = args[0] - - creator = User.objects.get(id=user_id) - models.City.get_or_create(name=name, user=creator) diff --git a/location/wagtail_hooks.py b/location/wagtail_hooks.py index d27870131..af6e2976e 100755 --- a/location/wagtail_hooks.py +++ b/location/wagtail_hooks.py @@ -44,6 +44,11 @@ class LocationAdmin(SnippetViewSet): ) export_filename = "locations" + def get_queryset(self, request): + return super().get_queryset(request).select_related( + "country", "state", "city", "creator", + ) + class CityAdmin(SnippetViewSet): model = City diff --git a/organization/models.py b/organization/models.py index 416a8e43f..35aaa2069 100644 --- a/organization/models.py +++ b/organization/models.py @@ -107,24 +107,22 @@ def create( return cls.get(name=name, acronym=acronym, location=location) def update_logo(obj, user, logo=None): - update = False + """Atualiza logo no objeto sem salvar. Retorna True se houve mudança.""" if logo is not None and obj.logo != logo: obj.logo = logo - update = True - if update: if hasattr(obj, "updated_by") and user: obj.updated_by = user - obj.save() + return True + return False def update_url(obj, user, url=None): - update = False + """Atualiza url no objeto sem salvar. Retorna True se houve mudança.""" if url is not None and obj.url != url: obj.url = url - update = True - if update: if hasattr(obj, "updated_by") and user: obj.updated_by = user - obj.save() + return True + return False @classmethod def create_or_update( @@ -146,8 +144,10 @@ def create_or_update( try: obj = cls.get(name=name, acronym=acronym, location=location) - obj.update_logo(user, logo) - obj.update_url(user, url) + changed_logo = obj.update_logo(user, logo) + changed_url = obj.update_url(user, url) + if changed_logo or changed_url: + obj.save() return obj except cls.DoesNotExist: @@ -238,7 +238,7 @@ def update_institutions( if updated: self.updated_by = user - self.save() + # Não faz save() aqui; chamador é responsável por salvar if institution_type_scielo is not None: if isinstance(institution_type_scielo, list): @@ -246,6 +246,8 @@ def update_institutions( else: self.institution_type_scielo.add(institution_type_scielo) + return updated + @classmethod def create_or_update( cls, @@ -269,9 +271,11 @@ def create_or_update( url=url, logo=logo, ) - obj.update_institutions( + inst_changed = obj.update_institutions( user, institution_type_mec, institution_type_scielo, is_official ) + if inst_changed: + obj.save() return obj diff --git a/organization/tasks.py b/organization/tasks.py index 31798b36f..02bdb9198 100644 --- a/organization/tasks.py +++ b/organization/tasks.py @@ -22,6 +22,7 @@ def task_migrate_date_institution_to_organization_publisher( objects = model_cls.objects.select_related( "institution__institution__location", "institution__institution__institution_identification", + "institution__institution__institution_identification__official", ).filter( institution__institution__institution_identification__is_official=True, institution__institution__location__city__isnull=False, diff --git a/pid_provider/models.py b/pid_provider/models.py index 28384fcc5..20d5dc932 100644 --- a/pid_provider/models.py +++ b/pid_provider/models.py @@ -136,10 +136,14 @@ def create( obj.pid_provider_xml = pid_provider_xml obj.finger_print = xml_with_pre.finger_print obj.creator = user + # Salvar primeiro sem arquivo para obter o PK obj.save() + # save_file já faz self.file.save() que persiste o campo file, + # mas precisamos persistir o registro completo com o path do arquivo obj.save_file( f"{pid_provider_xml.v3}.xml", xml_with_pre.tostring(pretty_print=True) ) + # Único save final após salvar o arquivo obj.save() return obj except IntegrityError: @@ -569,7 +573,7 @@ def get_queryset( @profile_classmethod def public_items(cls, from_date): now = datetime.utcnow().isoformat()[:10] - return cls.objects.filter( + return cls.objects.select_related("current_version").filter( (Q(available_since__isnull=True) | Q(available_since__lte=now)) & (Q(created__gte=from_date) | Q(updated__gte=from_date)), current_version__pid_provider_xml__v3__isnull=False, @@ -848,11 +852,16 @@ def _save( registered._add_journal(xml_adapter) registered._add_issue(xml_adapter) - registered.save() + # Primeiro save: necessário para obter PK (se novo) antes de criar XMLVersion / OtherPid + if registered.pk is None: + registered.save() if registered_changed: registered._add_other_pid(registered_changed, user) registered._add_current_version(xml_adapter.xml_with_pre, user) + + # Save final consolidado: persiste current_version e other_pid_count + registered.save() q = Q() if COLLECTION_PREFIX == "scielojournal": if xml_adapter.journal_issn_print: @@ -1023,7 +1032,7 @@ def title_similarity(self, xml_adapter): def best_matches(cls, results, xml_adapter): data = [] matched = [] - for item in results.iterator(): + for item in results.select_related("current_version").iterator(): response = item.match(xml_adapter) score = response["score"] @@ -1115,7 +1124,7 @@ def _add_current_version(self, xml_with_pre, user, delete=False): pass self.current_version = XMLVersion.get_or_create(user, self, xml_with_pre) - self.save() + # Não faz save() aqui; chamador é responsável por consolidar o save @profile_method def check_registered_pids_changed(self, xml_with_pre): @@ -1164,7 +1173,7 @@ def _add_other_pid(self, registered_changed, user): OtherPid.get_or_create(**change_args) self.other_pid_count = self.other_pid.count() - self.save() + # Não é necessário save() aqui; será consolidado no _save() pai @classmethod @profile_classmethod diff --git a/pid_provider/tasks.py b/pid_provider/tasks.py index 3cf3ef74d..0977eee10 100644 --- a/pid_provider/tasks.py +++ b/pid_provider/tasks.py @@ -2,12 +2,11 @@ import sys import logging -from django.contrib.auth import get_user_model - from config import celery_app from core.utils.profiling_tools import ( profile_function, ) # ajuste o import conforme sua estrutura +from core.utils.utils import _get_user from pid_provider.provider import PidProvider from pid_provider.models import PidProviderXML from journal.models import Journal, SciELOJournal @@ -16,19 +15,6 @@ # from django.utils.translation import gettext_lazy as _ -User = get_user_model() - - -def _get_user(request, username=None, user_id=None): - try: - return User.objects.get(pk=request.user.id) - except AttributeError: - if user_id: - return User.objects.get(pk=user_id) - if username: - return User.objects.get(username=username) - - @celery_app.task(bind=True) def task_provide_pid_for_xml_zip( self, diff --git a/researcher/models.py b/researcher/models.py index 7ee33fd17..7fc622384 100644 --- a/researcher/models.py +++ b/researcher/models.py @@ -299,22 +299,28 @@ def get_full_name(self): @property def orcid(self): try: - for item in ResearcherAKA.objects.filter( - researcher=self, - researcher_identifier__source_name__iexact="ORCID", - ): - return item.researcher_identifier.identifier + return ( + ResearcherAKA.objects.filter( + researcher=self, + researcher_identifier__source_name__iexact="ORCID", + ) + .values_list("researcher_identifier__identifier", flat=True) + .first() + ) except Exception as e: return None @property def lattes(self): try: - for item in ResearcherAKA.objects.filter( - researcher=self, - researcher_identifier__source_name__iexact="LATTES", - ): - return item.researcher_identifier.identifier + return ( + ResearcherAKA.objects.filter( + researcher=self, + researcher_identifier__source_name__iexact="LATTES", + ) + .values_list("researcher_identifier__identifier", flat=True) + .first() + ) except Exception as e: return None diff --git a/researcher/tasks.py b/researcher/tasks.py index af6785601..253c14516 100644 --- a/researcher/tasks.py +++ b/researcher/tasks.py @@ -23,6 +23,9 @@ def migrate_old_researcher_to_new_researcher(username=None, user_id=None): ).select_related( "person_name", "affiliation__institution__institution_identification", + "affiliation__institution__location", + ).prefetch_related( + "researcheraka_set__researcher_identifier", ) for old_researcher in old_researchers: orcid = ( diff --git a/thematic_areas/wagtail_hooks.py b/thematic_areas/wagtail_hooks.py index 019612588..16179db5a 100644 --- a/thematic_areas/wagtail_hooks.py +++ b/thematic_areas/wagtail_hooks.py @@ -66,6 +66,9 @@ class GenericThematicAreaAdmin(SnippetViewSet): ) export_filename = "generic_thematic_areas" + def get_queryset(self, request): + return super().get_queryset(request).select_related("creator") + class GenericThematicAreaFileAdmin(SnippetViewSet): model = GenericThematicAreaFile @@ -87,6 +90,9 @@ class GenericThematicAreaFileAdmin(SnippetViewSet): list_filter = ("is_valid",) search_fields = ("attachment__title",) + def get_queryset(self, request): + return super().get_queryset(request).select_related("creator") + class GenericThematicAreaAdminGroup(SnippetViewSetGroup): menu_label = _("Generic Thematic Areas") @@ -159,6 +165,9 @@ class ThematicAreaAdmin(SnippetViewSet): ) export_filename = "thematic_areas" + def get_queryset(self, request): + return super().get_queryset(request).select_related("creator") + class ThematicAreaFileAdmin(SnippetViewSet): model = ThematicAreaFile @@ -180,6 +189,9 @@ class ThematicAreaFileAdmin(SnippetViewSet): list_filter = ("is_valid",) search_fields = ("attachment__title",) + def get_queryset(self, request): + return super().get_queryset(request).select_related("creator") + class ThematicAreaAdminGroup(SnippetViewSetGroup): menu_label = _("Thematic Areas") diff --git a/tracker/tasks.py b/tracker/tasks.py index a6b078e1e..add9e7269 100644 --- a/tracker/tasks.py +++ b/tracker/tasks.py @@ -3,27 +3,12 @@ import sys from datetime import datetime -from django.contrib.auth import get_user_model - from config import celery_app from .models import UnexpectedEvent, Hello -User = get_user_model() - - -def _get_user(request, username=None, user_id=None): - try: - return User.objects.get(pk=request.user.id) - except AttributeError: - if user_id: - return User.objects.get(pk=user_id) - if username: - return User.objects.get(username=username) - - @celery_app.task(bind=True, name="cleanup_unexpected_events") -def delete_unexpected_events(self, exception_type, start_date=None, end_date=None, user_id=None, username=None): +def delete_unexpected_events(self, exception_type, start_date=None, end_date=None): """ Delete UnexpectedEvent records based on exception type and optional date range. """