From a90bb1664afb747e041645c594aef18f6e392096 Mon Sep 17 00:00:00 2001 From: Roberta Takenaka Date: Mon, 3 Aug 2026 15:44:28 -0300 Subject: [PATCH 1/4] build: atualiza packtools para 4.16.11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Propósito Atualizar a dependência packtools de 4.16.8 para 4.16.11, disponibilizando recursos necessários para os ajustes subsequentes em Article, SPSPkg e Package (pkg_name_variations, deprecated_sps_pkg_name_list, get_complete_publication_date e path_in_zip em renditions). ## Solução técnica Bump de versão da referência git no requirements/base.txt (packtools.git@4.16.8 -> packtools.git@4.16.11). --- requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/base.txt b/requirements/base.txt index 8cf9c52e..f4975b9e 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -90,7 +90,7 @@ iso639-lang==2.6.3 # Mantendo versão maior # SciELO Specific Packages # ======================================== # Using specific versions for stability --e git+https://github.com/scieloorg/packtools.git@4.16.8#egg=packtools +-e git+https://github.com/scieloorg/packtools.git@4.16.11#egg=packtools -e git+https://github.com/scieloorg/scielo_scholarly_data#egg=scielo_scholarly_data -e git+https://github.com/scieloorg/opac_schema.git@v2.66#egg=opac_schema -e git+https://github.com/scieloorg/scielo_migration.git@1.10.8#egg=scielo_classic_website From 67b06d86bb60ca7aefea5c615c8801bc117da63c Mon Sep 17 00:00:00 2001 From: Roberta Takenaka Date: Mon, 3 Aug 2026 15:44:39 -0300 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20ignora=20itens=20sem=20conte=C3=BAdo?= =?UTF-8?q?=20ao=20enviar=20arquivos=20do=20SPSPkg=20para=20o=20storage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Propósito Evitar chamada de upload_to_the_cloud com content=None quando o item não possui conteúdo disponível (após KeyError tratado em item["content"]). ## Solução técnica Adicionado `if not content: continue` antes da chamada de upload_to_the_cloud, pulando itens sem conteúdo em vez de tentar enviá-los. --- package/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/models.py b/package/models.py index e100a6c0..a0885471 100644 --- a/package/models.py +++ b/package/models.py @@ -863,6 +863,8 @@ def upload_article_page_to_the_cloud(self, user, minio): content = item["content"].encode("utf-8") except KeyError: content = None + if not content: + continue response = self.upload_to_the_cloud( user, minio, From f24386e0393f4ef960dcf8b581e8c8b1bdd0b830 Mon Sep 17 00:00:00 2001 From: Roberta Takenaka Date: Mon, 3 Aug 2026 15:44:47 -0300 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20considera=20varia=C3=A7=C3=B5es=20de?= =?UTF-8?q?=20nome=20do=20pacote=20SPS=20ao=20localizar=20Article=20existe?= =?UTF-8?q?nte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Propósito Corrigir falha em localizar um Article já existente quando o sps_pkg_name do pacote mudou entre versões (nomes depreciados), o que causava criação de registros duplicados em vez de atualização do existente. ## Solução técnica - get_first passa a aceitar o parâmetro name_list e inclui Q(sps_pkg__sps_pkg_name__in=name_list) na busca. - create_or_update tenta obter xml_with_pre.pkg_name_variations; se o atributo não existir (AttributeError), monta a lista manualmente a partir de sps_pkg_name + deprecated_sps_pkg_name_list, e passa essa lista para get_first. --- article/models.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/article/models.py b/article/models.py index 602fcc77..a75a56e9 100644 --- a/article/models.py +++ b/article/models.py @@ -322,10 +322,12 @@ def get(cls, pid_v2=None, sps_pkg_name=None, pid_v3=None): return cls.objects.get(**params) @classmethod - def get_first(cls, pid_v2=None, sps_pkg_name=None, pid_v3=None, delete=False): + def get_first(cls, pid_v2=None, sps_pkg_name=None, pid_v3=None, delete=False, name_list=None): q = Q() if pid_v2: q |= Q(pid_v2=pid_v2) + if name_list: + q |= Q(sps_pkg__sps_pkg_name__in=name_list) if sps_pkg_name: q |= Q(sps_pkg__sps_pkg_name=sps_pkg_name) qs = cls.objects.filter(q).order_by("-updated") @@ -364,7 +366,12 @@ def create_or_update(cls, user, sps_pkg, issue=None, journal=None, position=None raise ValueError(f"SPSPkg {sps_pkg} xml_with_pre is missing pid_v2") try: - obj = cls.get_first(sps_pkg.sps_pkg_name, pid_v2, pid_v3, delete=True) + try: + name_list = self.xml_with_pre.pkg_name_variations + except AttributeError: + name_list = [self.xml_with_pre.sps_pkg_name] + name_list.extend(self.xml_with_pre.deprecated_sps_pkg_name_list) + obj = cls.get_first(sps_pkg.sps_pkg_name, pid_v2, pid_v3, delete=True, name_list=name_list) except cls.DoesNotExist: obj = cls() obj.creator = user From 527fe7bcc59ba505da3676d06a751441aa4ac2be Mon Sep 17 00:00:00 2001 From: Roberta Takenaka Date: Mon, 3 Aug 2026 15:44:59 -0300 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20ajusta=20publica=C3=A7=C3=A3o,=20lei?= =?UTF-8?q?tura=20de=20renditions=20e=20resolu=C3=A7=C3=A3o=20de=20PID=20v?= =?UTF-8?q?2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Propósito Corrigir múltiplos pontos relacionados à publicação de pacotes: leitura incorreta do conteúdo de renditions dentro do zip, ausência de fallback na extração da data de publicação, impossibilidade de forçar reprocessamento do SPSPkg já registrado no Core, e falha na localização de PidReservation/pid_v2 quando o nome do pacote mudou entre versões. Também remove logs de depuração (logging.info) que não agregam valor em produção e renomeia variável para refletir seu real significado. ## Solução técnica - upload_package_directory_path: renomeia variável sps_pkg_name para xml_name, já que representa o nome do arquivo e não necessariamente o nome do pacote SPS. - PackageZip.xmls: remove logging.info supérfluo. - Package.renditions: passa a ler o conteúdo via zf.read(rendition["path_in_zip"]) em vez de rendition["name"], já que o path dentro do zip pode diferir do nome exibido. - Package.set_pid_v2_and_publication_date: adiciona fallback para xml_with_pre.get_complete_publication_date() quando article_publication_date não está disponível/é inválida. - Package.prepare_sps_package / prepare_to_publish: adicionam o parâmetro force_update, permitindo forçar a atualização do SPSPkg mesmo quando já registrado e válido no Core; ajustado o filtro de pdf_langs em renditions removendo a checagem redundante contra xml_with_pre.filenames. - PidReservation.get: passa a aceitar name_list, permitindo buscar por múltiplas variações de pkg_name (pkg_name__in). - PidV2Generator.generate: usa xml_with_pre.pkg_name_variations (com fallback para sps_pkg_name + deprecated_sps_pkg_name_list) ao consultar PidReservation, evitando falha na resolução de pid_v2 quando o nome do pacote mudou; remove logs de depuração (logging.info) intermediários do fluxo. --- upload/models.py | 62 +++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/upload/models.py b/upload/models.py index 13542a37..2703ae23 100644 --- a/upload/models.py +++ b/upload/models.py @@ -86,14 +86,14 @@ def report_datetime(): def upload_package_directory_path(instance, filename): name, ext = os.path.splitext(filename) try: - sps_pkg_name = instance.name + xml_name = instance.name except AttributeError: - sps_pkg_name = instance.package.name + xml_name = instance.package.name - subdirs = (sps_pkg_name or name).split("-") - subdir_sps_pkg_name = "/".join(subdirs) + subdirs = (xml_name or name).split("-") + subdir_xml_name = "/".join(subdirs) - return f"upload/{subdir_sps_pkg_name}/{ext[1:]}/{filename}" + return f"upload/{subdir_xml_name}/{ext[1:]}/{filename}" class PackageZip(CommonControlField): @@ -148,7 +148,6 @@ def save_file(self, filename=None, content=None): @property def xmls(self): for item in XMLWithPre.create(path=self.file.path): - logging.info(item.filename) yield item.filename def split(self, user): @@ -440,20 +439,21 @@ def xml(self): @property def renditions(self): """ - Retorna um gerador de itens com este formato + Retorna um lista de itens com este formato { "name": name, "lang": item.language, "component_type": "rendition", "main": item.is_main_language, - "content": b'', + "sps_pkg_name": "dado ou construido ou ...", + "path_in_zip": "", } """ renditions = self.xml_with_pre.renditions with ZipFile(self.file.path) as zf: for rendition in renditions: - rendition["content"] = zf.read(rendition["name"]) + rendition["content"] = zf.read(rendition["path_in_zip"]) yield rendition def files_list(self): @@ -564,7 +564,8 @@ def process_system_decision(self, task_upload_workflow_publish_article): detail = {"is_ready_to_publish": is_ready_to_publish} response = self.prepare_to_publish( - user, qa=is_ready_to_preview, public=is_ready_to_publish + user, qa=is_ready_to_preview, public=is_ready_to_publish, + force_update=None ) detail.update(response or {}) @@ -853,7 +854,7 @@ def process_qa_decision( # é desejável que qa e public sejam publicados simultaneamente # exceto se há impedimento de em tornar público - response = self.prepare_to_publish(user, qa, public) + response = self.prepare_to_publish(user, qa, public, True) self.update_status_and_add_comments( user, response.get("result"), response.get("new_status") ) @@ -966,7 +967,11 @@ def xml_file_changed_pub_date(self, xml_with_pre): Atualiza data de publicação do artigo e/ou pid v2, se necessário """ try: - xml_pub_date = datetime.fromisoformat(xml_with_pre.article_publication_date) + xml_pub_date = xml_with_pre.article_publication_date + except Exception as e: + xml_pub_date = xml_with_pre.get_complete_publication_date() + try: + xml_pub_date = datetime.fromisoformat(xml_pub_date) except Exception as e: xml_pub_date = None @@ -1018,7 +1023,7 @@ def add_pid_v2(self, user, xml_with_pre): ) raise - def prepare_sps_package(self, user, xml_with_pre, xml_file_changed): + def prepare_sps_package(self, user, xml_with_pre, xml_file_changed, force_update=None): # Aplica-se também para um pacote de atualização de um conteúdo anteriormente migrado # TODO components, texts if xml_file_changed: @@ -1030,6 +1035,7 @@ def prepare_sps_package(self, user, xml_with_pre, xml_file_changed): or not self.sps_pkg or not self.sps_pkg.registered_in_core or not self.sps_pkg.valid_components + or force_update ): texts = { @@ -1037,7 +1043,6 @@ def prepare_sps_package(self, user, xml_with_pre, xml_file_changed): "pdf_langs": [ rendition["lang"] for rendition in xml_with_pre.renditions - if rendition["name"] in xml_with_pre.filenames ], } self.sps_pkg = SPSPkg.create_or_update( @@ -1212,7 +1217,7 @@ def register_qa_comment_as_error(self, user, data=None): report.creation = choices.REPORT_CREATION_DONE report.save() - def prepare_to_publish(self, user, qa=None, public=None): + def prepare_to_publish(self, user, qa=None, public=None, force_update=None): # verifica se há impedimentos de tornar o artigo público detail = {"qa": qa, "public": public} if not qa and not public: @@ -1225,7 +1230,7 @@ def prepare_to_publish(self, user, qa=None, public=None): xml_with_pre = self.xml_with_pre xml_changed = self.check_xml_changed(user, xml_with_pre, public) - self.prepare_sps_package(user, xml_with_pre, xml_changed) + self.prepare_sps_package(user, xml_with_pre, xml_changed, force_update) result = self.analyze_sps_package(qa, public, xml_changed) detail.update(result) @@ -2211,12 +2216,14 @@ class Meta: ] @classmethod - def get(cls, pid_v2=None, pkg_name=None): + def get(cls, pid_v2=None, pkg_name=None, name_list=None): params = {} if pid_v2: params["pid_v2"] = pid_v2 if pkg_name: params["pkg_name"] = pkg_name + if name_list: + params["pkg_name__in"] = name_list if not params: raise ValueError("PidReservation.is_reserved requires params") return cls.objects.get(**params) @@ -2248,30 +2255,30 @@ def __init__(self, xml_with_pre): def generate(self, user, journal, issue): self.log = [] - logging.info("PidV2Generator.generate PidProviderXML") registered = PidProviderXML.is_registered(self.xml_with_pre) if registered and registered.get("v2"): self.log.append(_("Setting package.pid_v2 from PidProviderXML")) return registered.get("v2") - logging.info("PidV2Generator.generate PidReservation") try: - return PidReservation.get(pkg_name=self.xml_with_pre.sps_pkg_name).pid_v2 + try: + name_list = self.xml_with_pre.pkg_name_variations + except AttributeError: + name_list = [self.xml_with_pre.sps_pkg_name] + name_list.extend(self.xml_with_pre.deprecated_sps_pkg_name_list) + return PidReservation.get(name_list=name_list).pid_v2 except PidReservation.DoesNotExist: pass - - logging.info("PidV2Generator.generate IssueProc") - + self.issue_pid = self.get_issue_pid(user, journal, issue) if not self.issue_pid: self.log.append( _( "Unable to set package.pid_v2 because issue ({}) is not registered" - ).format(self.issue) + ).format(issue) ) raise ValueError("Package.generate_pid_v2: Missing issue_pid") - logging.info("PidV2Generator.generate generate_pid_v2_from_metadata") pid_v2 = self.generate_pid_v2_from_metadata() # if not pid_v2: # logging.info("PidV2Generator.generate get_random_pid_v2") @@ -2282,10 +2289,8 @@ def generate(self, user, journal, issue): f"Unable to get pid v2 for {self.xml_with_pre.sps_pkg_name}" ) - logging.info("PidV2Generator.generate reserve_pid_v2") self.reserve_pid_v2(pid_v2) if registered: - logging.info("PidV2Generator.generate update_pid_provider_v2") self.update_pid_provider_v2(registered.get("v3"), pid_v2) return pid_v2 @@ -2347,9 +2352,6 @@ def generate_pid_v2_from_metadata(self): for name, source in sources: if not source: continue - logging.info( - f"PidV2Generator.generate generate_pid_v2_from_metadata {name}" - ) pid_v2 = self.generate_pid_v2_from_source(source) if pid_v2: self.log.append(_("Setting v2 ({}) from {}").format(pid_v2, name))