-
Notifications
You must be signed in to change notification settings - Fork 10
Adiciona trigger para atualizar os dados de coleção no opac_5 #1156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 17 commits
df2b135
02f061e
22f9159
cf7b1a1
1d730b2
a57d962
16d4349
03e82f7
45b4f04
247bfdd
4cf1d9b
9511cc5
823d7fe
36dd9fb
5bb31a7
4e5e7b4
d7bae8f
37c06aa
f79d60d
cdffec6
c76f5ed
cdee2ec
a9a4b3e
f5e1bf7
b234dfa
e6e6f50
36bdc83
3e8c2a9
24640cf
8857316
1e3e1e8
22344ae
cd1e4cd
b5091f4
98ff528
e530db2
627ab2c
d43f61d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| JWT_ISS="https://api.seu-django.com" | ||
| JWT_AUD="seu-flask-servico" | ||
| JWT_EXP_SECONDS=600 | ||
| JWT_ALG="RS256" | ||
| # JWT_PRIVATE_KEY JA CORRESPONDENTE AO jwt_public OPAC_5 PARA TESTES. | ||
| JWT_PRIVATE_KEY_PATH="/app/jwt_private_local.pem" |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -2,6 +2,8 @@ | |||||
| from collection import models | ||||||
| from core.api.v1.serializers import LanguageSerializer | ||||||
| from organization.api.v1.serializers import OrganizationSerializer | ||||||
| from wagtail.models.sites import Site | ||||||
|
|
||||||
|
|
||||||
| class CollectionNameSerializer(serializers.ModelSerializer): | ||||||
| """Serializer para nomes traduzidos da coleção""" | ||||||
|
|
@@ -14,6 +16,32 @@ class Meta: | |||||
| "language", | ||||||
| ] | ||||||
|
|
||||||
| class CollectionLogoListSerializer(serializers.ListSerializer): | ||||||
| """ | ||||||
| Agrupa os logos por 'purpose' e consolida por idioma, | ||||||
| eliminando duplicatas por (purpose, lang_code2). | ||||||
| """ | ||||||
| def to_representation(self, data): | ||||||
| items = super().to_representation(data) | ||||||
| grouped = {} | ||||||
| seen = set() | ||||||
|
|
||||||
| for item in items: | ||||||
| purpose = item.get("purpose") | ||||||
| url = item.get("logo_url") | ||||||
| lang = item.get("language", {}) | ||||||
| code2 = lang.get("code2") | ||||||
|
|
||||||
| if not purpose or not code2 or not url: | ||||||
| continue | ||||||
|
|
||||||
| key = (purpose, code2) | ||||||
| if key in seen: | ||||||
| continue | ||||||
| seen.add(key) | ||||||
|
|
||||||
| grouped.setdefault(purpose, {}).update({code2: url}) | ||||||
| return grouped | ||||||
|
|
||||||
| class CollectionLogoSerializer(serializers.ModelSerializer): | ||||||
| """Serializer para logos da coleção""" | ||||||
|
|
@@ -22,38 +50,21 @@ class CollectionLogoSerializer(serializers.ModelSerializer): | |||||
|
|
||||||
| class Meta: | ||||||
| model = models.CollectionLogo | ||||||
| list_serializer_class = CollectionLogoListSerializer | ||||||
| fields = [ | ||||||
| "logo", | ||||||
| "logo_url", | ||||||
| "size", | ||||||
| "purpose", | ||||||
| "language", | ||||||
| ] | ||||||
|
|
||||||
| def get_logo_url(self, obj): | ||||||
| """Retorna a URL do logo renderizado no tamanho apropriado""" | ||||||
| if obj.logo: | ||||||
| # Ajusta o rendition baseado no tamanho | ||||||
| rendition_specs = { | ||||||
| 'small': 'fill-100x100', | ||||||
| 'medium': 'fill-200x200', | ||||||
| 'large': 'fill-400x400', | ||||||
| 'banner': 'width-1200', | ||||||
| 'thumbnail': 'fill-150x150', | ||||||
| 'header': 'height-80', | ||||||
| 'footer': 'height-60', | ||||||
| } | ||||||
| spec = rendition_specs.get(obj.size, 'fill-200x200') | ||||||
| rendition = obj.logo.get_rendition(spec) | ||||||
|
|
||||||
| # Retorna URL completa se houver request no contexto | ||||||
| request = self.context.get('request') | ||||||
| if request: | ||||||
| return request.build_absolute_uri(rendition.url) | ||||||
| return rendition.url | ||||||
| domain = Site.objects.get(is_default_site=True).hostname | ||||||
| domain = f"http://{domain}" | ||||||
| return f"{domain}{obj.logo.file.url}" | ||||||
|
||||||
| return f"{domain}{obj.logo.file.url}" | |
| return f"{domain}{obj.logo.url}" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| # Generated by Django 5.2.3 on 2025-09-25 17:30 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ( | ||
| "collection", | ||
| "0006_alter_collection_options_alter_collection_acron2_and_more", | ||
| ), | ||
| ("core", "0007_alter_language_options_alter_license_options_and_more"), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterModelOptions( | ||
| name="collectionexecutingorganization", | ||
| options={ | ||
| "ordering": ["sort_order"], | ||
| "verbose_name": "Executing Organization", | ||
| "verbose_name_plural": "Executing Organizations", | ||
| }, | ||
| ), | ||
| migrations.AlterModelOptions( | ||
| name="collectionlogo", | ||
| options={ | ||
| "ordering": ["sort_order", "language"], | ||
| "verbose_name": "Collection Logo", | ||
| "verbose_name_plural": "Collection Logos", | ||
| }, | ||
| ), | ||
| migrations.AlterModelOptions( | ||
| name="collectionsupportingorganization", | ||
| options={ | ||
| "ordering": ["sort_order"], | ||
| "verbose_name": "Supporting Organization", | ||
| "verbose_name_plural": "Supporting Organizations", | ||
| }, | ||
| ), | ||
| migrations.AlterUniqueTogether( | ||
| name="collectionlogo", | ||
| unique_together={("collection", "language", "purpose")}, | ||
| ), | ||
| migrations.AddField( | ||
| model_name="collectionlogo", | ||
| name="purpose", | ||
| field=models.CharField( | ||
| blank=True, | ||
| choices=[ | ||
| ("homepage", "Homepage"), | ||
| ("header", "Header"), | ||
| ("logo_drop_menu", "Logo drop menu"), | ||
| ("footer", "Footer"), | ||
| ("menu", "Menu"), | ||
| ], | ||
| help_text="Select the purpose of this logo", | ||
| max_length=20, | ||
| ), | ||
| ), | ||
| migrations.RemoveField( | ||
| model_name="collectionlogo", | ||
| name="size", | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -278,6 +278,7 @@ def get_name_for_language(self, lang_code=None): | |||||
| return name_obj.text | ||||||
| return self.main_name or (self.collection_name.first().text if self.collection_name.exists() else "") | ||||||
|
|
||||||
|
|
||||||
| class CollectionSocialNetwork(Orderable, SocialNetwork): | ||||||
| page = ParentalKey( | ||||||
| Collection, | ||||||
|
|
@@ -309,6 +310,7 @@ class CollectionSupportingOrganization(Orderable, ClusterableModel, BaseHistory) | |||||
| class Meta: | ||||||
| verbose_name = _("Supporting Organization") | ||||||
| verbose_name_plural = _("Supporting Organizations") | ||||||
| ordering = ['sort_order'] # Ordena os icons no opac_5 | ||||||
|
|
||||||
| def __str__(self): | ||||||
| return str(self.organization) | ||||||
|
|
@@ -336,6 +338,7 @@ class CollectionExecutingOrganization(Orderable, ClusterableModel, BaseHistory): | |||||
| class Meta: | ||||||
| verbose_name = _("Executing Organization") | ||||||
| verbose_name_plural = _("Executing Organizations") | ||||||
| ordering = ['sort_order'] # Ordena os icons no opac_5 | ||||||
|
|
||||||
| def __str__(self): | ||||||
| return str(self.organization) | ||||||
|
|
@@ -352,14 +355,25 @@ class CollectionLogo(Orderable, BaseLogo): | |||||
| related_name='logos', | ||||||
| verbose_name=_("Collection") | ||||||
| ) | ||||||
|
|
||||||
| purpose = models.CharField( | ||||||
| max_length=20, | ||||||
| choices=choices.LOGO_PURPOSE, | ||||||
| blank=True, | ||||||
| help_text=_("Select the purpose of this logo"), | ||||||
| ) | ||||||
|
|
||||||
| panels = [ | ||||||
| FieldPanel("logo"), | ||||||
| AutocompletePanel("language"), | ||||||
| FieldPanel("purpose"), | ||||||
| ] | ||||||
| class Meta: | ||||||
| verbose_name = _("Collection Logo") | ||||||
| verbose_name_plural = _("Collection Logos") | ||||||
| ordering = ['sort_order', 'language', 'size'] | ||||||
| ordering = ['sort_order', 'language'] | ||||||
| unique_together = [ | ||||||
| ('collection', 'size', 'language', ), | ||||||
| ('collection', 'language', 'purpose'), | ||||||
|
||||||
| ('collection', 'language', 'purpose'), | |
| ("collection", "language", "purpose"), |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| from django.db import transaction | ||
| from django.db.models.signals import post_save | ||
| from django.dispatch import receiver | ||
| from django.conf import settings | ||
| from .models import Collection | ||
| from .tasks import build_collection_webhook | ||
|
|
||
|
|
||
| @receiver(post_save, sender=Collection, dispatch_uid="collection.signals.post_save") | ||
| def collection_post_save(sender, instance, created, **kwargs): | ||
| def _on_commit(): | ||
| if settings.ACTIVATE_UPDATE_COLLECTION_WEBHOOK: | ||
| event = "collection.created" if created else "collection.updated" | ||
| build_collection_webhook.apply_async( | ||
| kwargs=dict( | ||
| event=event, | ||
| collection_acron=instance.acron3, | ||
| # headers=headers, | ||
| ) | ||
| ) | ||
| transaction.on_commit(_on_commit) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,10 +1,19 @@ | ||||||||||||||||||||
| import logging | ||||||||||||||||||||
| import sys | ||||||||||||||||||||
|
|
||||||||||||||||||||
| import requests | ||||||||||||||||||||
| from django.conf import settings | ||||||||||||||||||||
| from django.contrib.auth import get_user_model | ||||||||||||||||||||
| from django.utils.translation import gettext_lazy as _ | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from collection.models import Collection | ||||||||||||||||||||
| from config import celery_app | ||||||||||||||||||||
| from core.utils.jwt import issue_jwt_for_flask | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from .api.v1.serializers import CollectionSerializer | ||||||||||||||||||||
|
|
||||||||||||||||||||
| User = get_user_model() | ||||||||||||||||||||
| from tracker.models import UnexpectedEvent | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| @celery_app.task(bind=True) | ||||||||||||||||||||
|
|
@@ -14,3 +23,84 @@ def task_load_collections(self, user_id=None, username=None): | |||||||||||||||||||
| if username: | ||||||||||||||||||||
| user = User.objects.get(username=username) | ||||||||||||||||||||
| Collection.load(user) | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
||||||||||||||||||||
| def fetch_with_schema_guess(host_or_url, timeout=10): | ||||||||||||||||||||
| """ | ||||||||||||||||||||
| Algumas coleções não possuem o schema no domínio, por isso é necessário | ||||||||||||||||||||
| tentar os schemas http e https para obter o resultado correto. | ||||||||||||||||||||
| """ | ||||||||||||||||||||
| if "://" in host_or_url: | ||||||||||||||||||||
| return host_or_url | ||||||||||||||||||||
|
|
||||||||||||||||||||
| for schema in ["http", "https"]: | ||||||||||||||||||||
| url = f"{schema}://{host_or_url}" | ||||||||||||||||||||
| try: | ||||||||||||||||||||
| resp = requests.post(url, timeout=timeout) | ||||||||||||||||||||
|
||||||||||||||||||||
| resp = requests.post(url, timeout=timeout) | |
| resp = requests.get(url, timeout=timeout) |
Copilot
AI
Oct 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error logging occurs before raise_for_status(), which means the function will continue and potentially raise an exception after logging. The logging should be inside an exception handler or the raise_for_status() should be moved before the logging.
| if resp.status_code != 200: | |
| logging.error(f"Erro ao enviar dados de coleção para {url}. Status: {resp.status_code}. Body: {resp.text}") | |
| resp.raise_for_status() | |
| return resp.json() | |
| except Exception as e: | |
| resp.raise_for_status() | |
| return resp.json() | |
| except Exception as e: | |
| logging.error(f"Erro ao enviar dados de coleção para {url}. Exception: {e}") |
Copilot
AI
Oct 7, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function returns None when both HTTP and HTTPS protocols fail, but the caller expects a URL string. This could cause issues in _send_payload when concatenating with settings.ENDPOINT_COLLECTION.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as in organization serializer - assumes there's always a default site. If no default site exists, this will raise a DoesNotExist exception.