Dernière étape manquante de l'import: suppression d'un événement s'il n'est pas présent dans le json d'import.
Fix #46
This commit is contained in:
parent
5a63073cf7
commit
25f9b8c8e6
@ -151,6 +151,7 @@ class ICALExtractor(Extractor):
|
|||||||
print("Extracting ical events from {}".format(url))
|
print("Extracting ical events from {}".format(url))
|
||||||
self.set_header(url)
|
self.set_header(url)
|
||||||
self.clear_events()
|
self.clear_events()
|
||||||
|
self.uuids = {}
|
||||||
|
|
||||||
calendar = icalendar.Calendar.from_ical(content)
|
calendar = icalendar.Calendar.from_ical(content)
|
||||||
|
|
||||||
@ -179,6 +180,11 @@ class ICALExtractor(Extractor):
|
|||||||
uuid = self.get_item_from_vevent(event, "UID")
|
uuid = self.get_item_from_vevent(event, "UID")
|
||||||
|
|
||||||
if uuid is not None:
|
if uuid is not None:
|
||||||
|
if uuid in self.uuids:
|
||||||
|
self.uuids[uuid] += 1
|
||||||
|
uuid += ":{:04}".format(self.uuids[uuid] - 1)
|
||||||
|
else:
|
||||||
|
self.uuids[uuid] = 1
|
||||||
event_url = url + "#" + uuid
|
event_url = url + "#" + uuid
|
||||||
|
|
||||||
tags = self.default_value_if_exists(default_values, "tags")
|
tags = self.default_value_if_exists(default_values, "tags")
|
||||||
|
@ -20,6 +20,7 @@ class EventForm(ModelForm):
|
|||||||
'end_day': TextInput(attrs={'type': 'date'}),
|
'end_day': TextInput(attrs={'type': 'date'}),
|
||||||
'end_time': TextInput(attrs={'type': 'time'}),
|
'end_time': TextInput(attrs={'type': 'time'}),
|
||||||
'uuids': MultipleHiddenInput(),
|
'uuids': MultipleHiddenInput(),
|
||||||
|
'import_sources': MultipleHiddenInput(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ class EventsImporter:
|
|||||||
return event["end_day"] >= self.today
|
return event["end_day"] >= self.today
|
||||||
|
|
||||||
def save_imported(self):
|
def save_imported(self):
|
||||||
self.db_event_objects, self.nb_updated, self.nb_removed = Event.import_events(self.event_objects, remove_missing=True)
|
self.db_event_objects, self.nb_updated, self.nb_removed = Event.import_events(self.event_objects, remove_missing_from_source=self.url)
|
||||||
|
|
||||||
|
|
||||||
def is_valid_event_structure(self, event):
|
def is_valid_event_structure(self, event):
|
||||||
@ -97,7 +97,7 @@ class EventsImporter:
|
|||||||
|
|
||||||
def load_event(self, event):
|
def load_event(self, event):
|
||||||
if self.is_valid_event_structure(event):
|
if self.is_valid_event_structure(event):
|
||||||
event_obj = Event.from_structure(event)
|
event_obj = Event.from_structure(event, self.url)
|
||||||
self.event_objects.append(event_obj)
|
self.event_objects.append(event_obj)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
|
19
src/agenda_culturel/migrations/0022_event_import_sources.py
Normal file
19
src/agenda_culturel/migrations/0022_event_import_sources.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.2.7 on 2023-12-31 20:06
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django_better_admin_arrayfield.models.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('agenda_culturel', '0021_alter_event_possibly_duplicated'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='event',
|
||||||
|
name='import_sources',
|
||||||
|
field=django_better_admin_arrayfield.models.fields.ArrayField(base_field=models.CharField(max_length=512), blank=True, help_text='Importation source used to detect removed entries.', null=True, size=None, verbose_name='Importation source'),
|
||||||
|
),
|
||||||
|
]
|
@ -15,7 +15,7 @@ from django.db.models import Q
|
|||||||
|
|
||||||
|
|
||||||
from django.template.defaultfilters import date as _date
|
from django.template.defaultfilters import date as _date
|
||||||
from datetime import time, timedelta
|
from datetime import time, timedelta, date
|
||||||
from django.utils.timezone import datetime
|
from django.utils.timezone import datetime
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
@ -160,6 +160,7 @@ class Event(models.Model):
|
|||||||
image = models.URLField(verbose_name=_('Illustration'), help_text=_("URL of the illustration image"), max_length=1024, blank=True, null=True)
|
image = models.URLField(verbose_name=_('Illustration'), help_text=_("URL of the illustration image"), max_length=1024, blank=True, null=True)
|
||||||
image_alt = models.CharField(verbose_name=_('Illustration description'), help_text=_('Alternative text used by screen readers for the image'), blank=True, null=True, max_length=1024)
|
image_alt = models.CharField(verbose_name=_('Illustration description'), help_text=_('Alternative text used by screen readers for the image'), blank=True, null=True, max_length=1024)
|
||||||
|
|
||||||
|
import_sources = ArrayField(models.CharField(max_length=512), verbose_name=_('Importation source'), help_text=_("Importation source used to detect removed entries."), blank=True, null=True)
|
||||||
uuids = ArrayField(models.CharField(max_length=512), verbose_name=_('UUIDs'), help_text=_("UUIDs from import to detect duplicated entries."), blank=True, null=True)
|
uuids = ArrayField(models.CharField(max_length=512), verbose_name=_('UUIDs'), help_text=_("UUIDs from import to detect duplicated entries."), blank=True, null=True)
|
||||||
reference_urls = ArrayField(models.URLField(max_length=512), verbose_name=_('URLs'), help_text=_("List of all the urls where this event can be found."), blank=True, null=True)
|
reference_urls = ArrayField(models.URLField(max_length=512), verbose_name=_('URLs'), help_text=_("List of all the urls where this event can be found."), blank=True, null=True)
|
||||||
|
|
||||||
@ -295,7 +296,7 @@ class Event(models.Model):
|
|||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def from_structure(event_structure):
|
def from_structure(event_structure, import_source = None):
|
||||||
if "category" in event_structure and event_structure["category"] is not None:
|
if "category" in event_structure and event_structure["category"] is not None:
|
||||||
event_structure["category"] = Category.objects.get(name=event_structure["category"])
|
event_structure["category"] = Category.objects.get(name=event_structure["category"])
|
||||||
|
|
||||||
@ -331,6 +332,9 @@ class Event(models.Model):
|
|||||||
if "description" in event_structure and event_structure["description"] is None:
|
if "description" in event_structure and event_structure["description"] is None:
|
||||||
event_structure["description"] = ""
|
event_structure["description"] = ""
|
||||||
|
|
||||||
|
if import_source is not None:
|
||||||
|
event_structure["import_sources"] = [import_source]
|
||||||
|
|
||||||
return Event(**event_structure)
|
return Event(**event_structure)
|
||||||
|
|
||||||
|
|
||||||
@ -369,11 +373,11 @@ class Event(models.Model):
|
|||||||
# do we have to create a new group?
|
# do we have to create a new group?
|
||||||
if len(groups) == 0:
|
if len(groups) == 0:
|
||||||
group = DuplicatedEvents.objects.create()
|
group = DuplicatedEvents.objects.create()
|
||||||
logger.warning("set possibily duplicated 0 {}".format(group))
|
logger.warning("set possibly duplicated 0 {}".format(group))
|
||||||
else:
|
else:
|
||||||
# otherwise merge existing groups
|
# otherwise merge existing groups
|
||||||
group = DuplicatedEvents.merge_groups(groups)
|
group = DuplicatedEvents.merge_groups(groups)
|
||||||
logger.warning("set possibily duplicated not 0 {}".format(group))
|
logger.warning("set possibly duplicated not 0 {}".format(group))
|
||||||
group.save()
|
group.save()
|
||||||
|
|
||||||
# set the possibly duplicated group for the current object
|
# set the possibly duplicated group for the current object
|
||||||
@ -392,7 +396,6 @@ class Event(models.Model):
|
|||||||
def same_event_by_data(self, other):
|
def same_event_by_data(self, other):
|
||||||
for attr in Event.data_fields():
|
for attr in Event.data_fields():
|
||||||
if str(getattr(self, attr)) != str(getattr(other, attr)):
|
if str(getattr(self, attr)) != str(getattr(other, attr)):
|
||||||
logger.warning("on trouve une différence dans {}: {} vs {}".format(attr, getattr(self, attr), getattr(other, attr)))
|
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -414,6 +417,11 @@ class Event(models.Model):
|
|||||||
# set attributes
|
# set attributes
|
||||||
for attr in Event.data_fields():
|
for attr in Event.data_fields():
|
||||||
setattr(self, attr, getattr(other, attr))
|
setattr(self, attr, getattr(other, attr))
|
||||||
|
|
||||||
|
# set status according to the input status
|
||||||
|
if other.status is not None:
|
||||||
|
self.status = other.status
|
||||||
|
|
||||||
# add a possible missing uuid
|
# add a possible missing uuid
|
||||||
if self.uuids is None:
|
if self.uuids is None:
|
||||||
self.uuids = []
|
self.uuids = []
|
||||||
@ -423,12 +431,27 @@ class Event(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
# Limitation: the given events should not be considered similar one to another...
|
# Limitation: the given events should not be considered similar one to another...
|
||||||
def import_events(events, remove_missing=False):
|
def import_events(events, remove_missing_from_source=None):
|
||||||
to_import = []
|
to_import = []
|
||||||
to_update = []
|
to_update = []
|
||||||
|
|
||||||
|
min_date = timezone.now().date()
|
||||||
|
max_date = None
|
||||||
|
uuids = set()
|
||||||
|
|
||||||
# for each event, check if it's a new one, or a one to be updated
|
# for each event, check if it's a new one, or a one to be updated
|
||||||
for event in events:
|
for event in events:
|
||||||
|
sdate = date.fromisoformat(event.start_day)
|
||||||
|
edate = date.fromisoformat(event.end_day)
|
||||||
|
if min_date is None or min_date > sdate:
|
||||||
|
min_date = sdate
|
||||||
|
if max_date is None or max_date < sdate:
|
||||||
|
max_date = sdate
|
||||||
|
if max_date is None or (event.end_day is not None and max_date < edate):
|
||||||
|
max_date = edate
|
||||||
|
if len(event.uuids) > 0:
|
||||||
|
uuids.add(event.uuids[0])
|
||||||
|
|
||||||
# imported events should be updated
|
# imported events should be updated
|
||||||
event.set_in_importation_process()
|
event.set_in_importation_process()
|
||||||
event.prepare_save()
|
event.prepare_save()
|
||||||
@ -442,8 +465,7 @@ class Event(models.Model):
|
|||||||
|
|
||||||
if same_imported:
|
if same_imported:
|
||||||
# if this event exists, it will be updated with new data only if the data is fresher
|
# if this event exists, it will be updated with new data only if the data is fresher
|
||||||
logger.warning("{} vs {}".format(same_imported.modified_date, event.modified_date))
|
if same_imported.modified_date < event.modified_date or event.status != same_imported.status:
|
||||||
if same_imported.modified_date < event.modified_date:
|
|
||||||
same_imported.update(event)
|
same_imported.update(event)
|
||||||
same_imported.set_in_importation_process()
|
same_imported.set_in_importation_process()
|
||||||
same_imported.prepare_save()
|
same_imported.prepare_save()
|
||||||
@ -478,17 +500,26 @@ class Event(models.Model):
|
|||||||
|
|
||||||
# then import all the new events
|
# then import all the new events
|
||||||
imported = Event.objects.bulk_create(to_import)
|
imported = Event.objects.bulk_create(to_import)
|
||||||
nb_updated = Event.objects.bulk_update(to_update, fields = Event.data_fields() + ["imported_date", "modified_date", "uuids"])
|
nb_updated = Event.objects.bulk_update(to_update, fields = Event.data_fields() + ["imported_date", "modified_date", "uuids", "status"])
|
||||||
|
|
||||||
nb_removed = 0
|
nb_draft = 0
|
||||||
if remove_missing:
|
if remove_missing_from_source is not None:
|
||||||
# events that are missing from the import but in database are turned into drafts
|
# events that are missing from the import but in database are turned into drafts
|
||||||
# only if they are in the future
|
# only if they are in the future
|
||||||
# TODO
|
|
||||||
# TODO: ajouter self.source, ou faire référence à l'objet BatchImportation
|
|
||||||
pass
|
|
||||||
|
|
||||||
return imported, nb_updated, nb_removed
|
in_interval = Event.objects.filter(((Q(end_day__isnull=True) & Q(start_day__gte=min_date) & Q(start_day__lte=max_date)) |
|
||||||
|
(Q(end_day__isnull=False) & ~(Q(start_day__gt=max_date) | Q(end_day__lt=min_date)))) & Q(import_sources__contains=[remove_missing_from_source]) & Q(status=Event.STATUS.PUBLISHED) & Q(uuids__len__gt=0))
|
||||||
|
|
||||||
|
to_draft = []
|
||||||
|
for e in in_interval:
|
||||||
|
if len(uuids.intersection(e.uuids)) == 0:
|
||||||
|
e.status = Event.STATUS.TRASH
|
||||||
|
e.prepare_save()
|
||||||
|
to_draft.append(e)
|
||||||
|
|
||||||
|
nb_draft = Event.objects.bulk_update(to_draft, fields = ["status"])
|
||||||
|
|
||||||
|
return imported, nb_updated, nb_draft
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
<th>initial</th>
|
<th>initial</th>
|
||||||
<th>importés</th>
|
<th>importés</th>
|
||||||
<th>mis à jour</th>
|
<th>mis à jour</th>
|
||||||
<th>supprimés</th>
|
<th>dépubliés</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
Loading…
Reference in New Issue
Block a user