diff --git a/src/agenda_culturel/import_tasks/extractor.py b/src/agenda_culturel/import_tasks/extractor.py index e8b8339..450a3cb 100644 --- a/src/agenda_culturel/import_tasks/extractor.py +++ b/src/agenda_culturel/import_tasks/extractor.py @@ -1,11 +1,17 @@ from abc import ABC, abstractmethod +from enum import IntEnum from datetime import datetime, time, date, timedelta import re import unicodedata from django.utils import timezone +import logging class Extractor(ABC): + class Warning(IntEnum): + NO_TITLE = 1 + NO_START_DATE = 2 + url_referer=None def __init__(self): @@ -177,16 +183,17 @@ class Extractor(ABC): image_alt=None, ): comments = '' + warnings = [] if title is None: print("WARNING: cannot publish an event without name") published = False title = _('Unknown title') + warnings.append(Extractor.Warning.NO_TITLE) if start_day is None: print("WARNING: cannot publish an event without start day") published = False start_day = datetime.now().date().strftime("%Y-%m-%d") - comments = 'Warning: the date has not been imported correctly.' - title += ' - Warning: the date has not been imported correctly.' + warnings.append(Extractor.Warning.NO_START_DATE) tags_default = self.default_value_if_exists(default_values, "tags") if not tags_default: @@ -206,6 +213,7 @@ class Extractor(ABC): "image_alt": image_alt, "email": self.default_value_if_exists(default_values, "email"), "comments": self.default_value_if_exists(default_values, "comments"), + "warnings": warnings, } if event["comments"] is None: event["comments"] = comments diff --git a/src/agenda_culturel/migrations/0138_alter_message_message_type.py b/src/agenda_culturel/migrations/0138_alter_message_message_type.py new file mode 100644 index 0000000..d93db3d --- /dev/null +++ b/src/agenda_culturel/migrations/0138_alter_message_message_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.9 on 2025-01-29 09:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0137_category_slug'), + ] + + operations = [ + migrations.AlterField( + model_name='message', + name='message_type', + field=models.CharField(choices=[('from_contributor', 'From contributor'), ('import_process', 'Import process'), ('update_process', 'Update process'), ('contact_form', 'Contact form'), ('event_report', 'Event report'), ('from_contrib_no_msg', 'From contributor (without message)')], default=None, max_length=20, null=True, verbose_name='Type'), + ), + ] diff --git a/src/agenda_culturel/migrations/0139_alter_message_message_type.py b/src/agenda_culturel/migrations/0139_alter_message_message_type.py new file mode 100644 index 0000000..e4c2a01 --- /dev/null +++ b/src/agenda_culturel/migrations/0139_alter_message_message_type.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.9 on 2025-01-31 23:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('agenda_culturel', '0138_alter_message_message_type'), + ] + + operations = [ + migrations.AlterField( + model_name='message', + name='message_type', + field=models.CharField(choices=[('from_contributor', 'From contributor'), ('import_process', 'Import process'), ('update_process', 'Update process'), ('contact_form', 'Contact form'), ('event_report', 'Event report'), ('from_contrib_no_msg', 'From contributor (without message)'), ('warning', 'Warning')], default=None, max_length=20, null=True, verbose_name='Type'), + ), + ] diff --git a/src/agenda_culturel/models.py b/src/agenda_culturel/models.py index e1f2505..cb37266 100644 --- a/src/agenda_culturel/models.py +++ b/src/agenda_culturel/models.py @@ -36,6 +36,7 @@ import copy import unicodedata from collections import defaultdict from .import_tasks.extractor_facebook import FacebookEventExtractor +from .import_tasks.extractor import Extractor from django.template.defaultfilters import date as _date from datetime import time, timedelta, date @@ -768,6 +769,7 @@ class Event(models.Model): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.processing_user = None + self._messages = [] def get_import_messages(self): return self.message_set.filter(message_type__in=[Message.TYPE.IMPORT_PROCESS, Message.TYPE.UPDATE_PROCESS]).order_by("date") @@ -798,14 +800,14 @@ class Event(models.Model): end_date = parse_date(end_date) return parse_date(self.start_day) + timedelta(days=min_days) < end_date - def set_message(self, msg): - self._message = msg + def add_message(self, msg): + self._messages.append(msg) - def get_message(self): - return self._message + def get_messages(self): + return self._messages def has_message(self): - return hasattr(self, '_message') + return len(self._messages) > 0 def contains_date(self, d, intuitive=True): return d >= self.start_day and d <= self.get_consolidated_end_day(intuitive) @@ -1055,6 +1057,18 @@ class Event(models.Model): def is_in_moderation_process(self): return hasattr(self, "in_moderation_process") + def set_invalid_start_date(self): + self.invalid_start_date = True + + def has_invalid_start_date(self): + return hasattr(self, "invalid_start_date") + + def set_invalid_title(self): + self.invalid_title = True + + def has_invalid_title(self): + return hasattr(self, "invalid_title") + def update_modification_dates(self): now = timezone.now() if not self.id: @@ -1283,9 +1297,9 @@ class Event(models.Model): # save message if required if self.has_message(): - msg = self.get_message() - msg.related_event = self - msg.save() + for msg in self.get_messages(): + msg.related_event = self + msg.save() # then if its a clone, update the representative if clone: @@ -1303,8 +1317,15 @@ class Event(models.Model): def from_structure(event_structure, import_source=None): # organisers is a manytomany relation thus cannot be initialised before creation of the event organisers = event_structure.pop('organisers', None) + # supplementary information email = event_structure.pop('email', None) comments = event_structure.pop('comments', None) + warnings = event_structure.pop('warnings', []) + + for w in warnings: + if w == Extractor.Warning.NO_START_DATE: + event_structure["title"] += " - " + _('Warning') + ": " + _('the date has not been imported correctly.') + if "category" in event_structure and event_structure["category"] is not None: try: @@ -1383,11 +1404,25 @@ class Event(models.Model): result.add_pending_organisers(organisers) if email or comments: has_comments = not comments in ["", None] - result.set_message(Message(subject=_('during import process'), + result.add_message(Message(subject=_('during import process'), email=email, message=comments, closed=False, message_type=Message.TYPE.FROM_CONTRIBUTOR if has_comments else Message.TYPE.FROM_CONTRIBUTOR_NO_MSG)) + for w in warnings: + if w == Extractor.Warning.NO_START_DATE: + result.set_invalid_start_date() + result.add_message(Message(subject=_('warning'), + closed=False, + message=_('the date has not been imported correctly.'), + message_type=Message.TYPE.WARNING)) + if w == Extractor.Warning.NO_TITLE: + result.set_invalid_title() + result.add_message(Message(subject=_('warning'), + closed=False, + message=_('the title has not been imported correctly.'), + message_type=Message.TYPE.WARNING)) + return result @@ -1522,6 +1557,10 @@ class Event(models.Model): res = Event.get_comparison([self, event], all) for r in res: if not r["similar"]: + if r["key"] == "title" and (self.has_invalid_title() or event.has_invalid_title()): + continue + if r["key"] == "start_date" and (self.has_invalid_start_date() or event.has_invalid_start_date()): + continue return False return True @@ -1606,8 +1645,13 @@ class Event(models.Model): if other.has_pending_organisers(): self.organisers.set(other.pending_organisers) + logger.warning("process update " + other.title + ' ' + str(other.has_invalid_start_date())) # set attributes for attr in Event.data_fields(all=all, no_m2m=True): + if attr == "title" and other.has_invalid_title(): + continue + if attr == "start_date" and other.has_invalid_start_date(): + continue setattr(self, attr, getattr(other, attr)) # adjust modified date if required @@ -1735,7 +1779,7 @@ class Event(models.Model): for e in to_import: if e.is_event_long_duration(): e.status = Event.STATUS.DRAFT - e.set_message( + e.add_message( Message(subject=_('Import'), name=_('import process'), message=_("The duration of the event is a little too long for direct publication. Moderators can choose to publish it or not."), @@ -1749,9 +1793,9 @@ class Event(models.Model): if ti.has_pending_organisers() and ti.pending_organisers is not None: i.organisers.set(ti.pending_organisers) if ti.has_message(): - msg = ti.get_message() - msg.related_event = i - msg.save() + for msg in ti.get_messages(): + msg.related_event = i + msg.save() nb_updated = Event.objects.bulk_update( to_update, @@ -1944,6 +1988,7 @@ class Message(models.Model): CONTACT_FORM = "contact_form", _("Contact form") EVENT_REPORT = "event_report", _("Event report") FROM_CONTRIBUTOR_NO_MSG = "from_contrib_no_msg", _("From contributor (without message)") + WARNING = "warning", _("Warning") class Meta: verbose_name = _("Message")