snapshot: init models (#86634)
This commit is contained in:
parent
f6a0b58167
commit
3f8146c092
|
@ -0,0 +1,118 @@
|
|||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('snapshot', '0002_snapshot_models'),
|
||||
('agendas', '0170_alter_agenda_events_type'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='agenda',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='agenda',
|
||||
name='snapshot',
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='temporary_instance',
|
||||
to='snapshot.agendasnapshot',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='agenda',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='snapshot',
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='temporary_instance',
|
||||
to='snapshot.categorysnapshot',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='category',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='eventstype',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='eventstype',
|
||||
name='snapshot',
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='temporary_instance',
|
||||
to='snapshot.eventstypesnapshot',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='eventstype',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='resource',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='resource',
|
||||
name='snapshot',
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='temporary_instance',
|
||||
to='snapshot.resourcesnapshot',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='resource',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='unavailabilitycalendar',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='unavailabilitycalendar',
|
||||
name='snapshot',
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='temporary_instance',
|
||||
to='snapshot.unavailabilitycalendarsnapshot',
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='unavailabilitycalendar',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
]
|
|
@ -77,6 +77,15 @@ from django.utils.translation import gettext_lazy as _
|
|||
from django.utils.translation import ngettext, pgettext_lazy
|
||||
|
||||
from chrono.apps.export_import.models import WithApplicationMixin
|
||||
from chrono.apps.snapshot.models import (
|
||||
AgendaSnapshot,
|
||||
CategorySnapshot,
|
||||
EventsTypeSnapshot,
|
||||
ResourceSnapshot,
|
||||
UnavailabilityCalendarSnapshot,
|
||||
WithSnapshotManager,
|
||||
WithSnapshotMixin,
|
||||
)
|
||||
from chrono.utils.date import get_weekday_index
|
||||
from chrono.utils.db import ArraySubquery, SumCardinality
|
||||
from chrono.utils.interval import Interval, IntervalSet
|
||||
|
@ -173,7 +182,12 @@ TimeSlot = collections.namedtuple(
|
|||
)
|
||||
|
||||
|
||||
class Agenda(WithApplicationMixin, models.Model):
|
||||
class Agenda(WithSnapshotMixin, WithApplicationMixin, models.Model):
|
||||
# mark temporarily restored snapshots
|
||||
snapshot = models.ForeignKey(
|
||||
AgendaSnapshot, on_delete=models.CASCADE, null=True, related_name='temporary_instance'
|
||||
)
|
||||
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
kind = models.CharField(_('Kind'), max_length=20, choices=AGENDA_KINDS, default='events')
|
||||
|
@ -309,10 +323,16 @@ class Agenda(WithApplicationMixin, models.Model):
|
|||
validators=[MaxValueValidator(59)],
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
application_component_type = 'agendas'
|
||||
application_label_singular = _('Agenda')
|
||||
application_label_plural = _('Agendas')
|
||||
|
||||
objects = WithSnapshotManager()
|
||||
snapshots = WithSnapshotManager(snapshots=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['label']
|
||||
|
||||
|
@ -502,7 +522,7 @@ class Agenda(WithApplicationMixin, models.Model):
|
|||
return agenda
|
||||
|
||||
@classmethod
|
||||
def import_json(cls, data, overwrite=False):
|
||||
def import_json(cls, data, overwrite=False, snapshot=None):
|
||||
data = copy.deepcopy(data)
|
||||
permissions = data.pop('permissions') or {}
|
||||
reminder_settings = data.pop('reminder_settings', None)
|
||||
|
@ -536,7 +556,13 @@ class Agenda(WithApplicationMixin, models.Model):
|
|||
data['events_type'] = EventsType.objects.get(slug=data['events_type'])
|
||||
except EventsType.DoesNotExist:
|
||||
raise AgendaImportError(_('Missing "%s" events type') % data['events_type'])
|
||||
agenda, created = cls.objects.update_or_create(slug=data['slug'], defaults=data)
|
||||
slug = data.pop('slug')
|
||||
qs_kwargs = {}
|
||||
if snapshot:
|
||||
qs_kwargs = {'snapshot': snapshot}
|
||||
else:
|
||||
qs_kwargs = {'slug': slug}
|
||||
agenda, created = cls.objects.update_or_create(defaults=data, **qs_kwargs)
|
||||
if overwrite:
|
||||
AgendaReminderSettings.objects.filter(agenda=agenda).delete()
|
||||
if reminder_settings:
|
||||
|
@ -2817,15 +2843,26 @@ class Event(models.Model):
|
|||
return custom_fields
|
||||
|
||||
|
||||
class EventsType(WithApplicationMixin, models.Model):
|
||||
class EventsType(WithSnapshotMixin, WithApplicationMixin, models.Model):
|
||||
# mark temporarily restored snapshots
|
||||
snapshot = models.ForeignKey(
|
||||
EventsTypeSnapshot, on_delete=models.CASCADE, null=True, related_name='temporary_instance'
|
||||
)
|
||||
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
custom_fields = models.JSONField(blank=True, default=list)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
application_component_type = 'events_types'
|
||||
application_label_singular = _('Events type')
|
||||
application_label_plural = _('Events types')
|
||||
|
||||
objects = WithSnapshotManager()
|
||||
snapshots = WithSnapshotManager(snapshots=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
|
@ -2861,10 +2898,15 @@ class EventsType(WithApplicationMixin, models.Model):
|
|||
return []
|
||||
|
||||
@classmethod
|
||||
def import_json(cls, data, overwrite=False):
|
||||
def import_json(cls, data, overwrite=False, snapshot=None):
|
||||
data = clean_import_data(cls, data)
|
||||
slug = data.pop('slug')
|
||||
events_type, created = cls.objects.update_or_create(slug=slug, defaults=data)
|
||||
qs_kwargs = {}
|
||||
if snapshot:
|
||||
qs_kwargs = {'snapshot': snapshot}
|
||||
else:
|
||||
qs_kwargs = {'slug': slug}
|
||||
events_type, created = cls.objects.update_or_create(defaults=data, **qs_kwargs)
|
||||
return created, events_type
|
||||
|
||||
def export_json(self):
|
||||
|
@ -3432,15 +3474,26 @@ class Desk(models.Model):
|
|||
).delete() # source was not in settings anymore
|
||||
|
||||
|
||||
class Resource(WithApplicationMixin, models.Model):
|
||||
class Resource(WithSnapshotMixin, WithApplicationMixin, models.Model):
|
||||
# mark temporarily restored snapshots
|
||||
snapshot = models.ForeignKey(
|
||||
ResourceSnapshot, on_delete=models.CASCADE, null=True, related_name='temporary_instance'
|
||||
)
|
||||
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
description = models.TextField(_('Description'), blank=True, help_text=_('Optional description.'))
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
application_component_type = 'resources'
|
||||
application_label_singular = _('Resource')
|
||||
application_label_plural = _('Resources')
|
||||
|
||||
objects = WithSnapshotManager()
|
||||
snapshots = WithSnapshotManager(snapshots=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
|
@ -3466,10 +3519,15 @@ class Resource(WithApplicationMixin, models.Model):
|
|||
return []
|
||||
|
||||
@classmethod
|
||||
def import_json(cls, data, overwrite=False):
|
||||
def import_json(cls, data, overwrite=False, snapshot=None):
|
||||
data = clean_import_data(cls, data)
|
||||
slug = data.pop('slug')
|
||||
resource, created = cls.objects.update_or_create(slug=slug, defaults=data)
|
||||
qs_kwargs = {}
|
||||
if snapshot:
|
||||
qs_kwargs = {'snapshot': snapshot}
|
||||
else:
|
||||
qs_kwargs = {'slug': slug}
|
||||
resource, created = cls.objects.update_or_create(defaults=data, **qs_kwargs)
|
||||
return created, resource
|
||||
|
||||
def export_json(self):
|
||||
|
@ -3480,14 +3538,25 @@ class Resource(WithApplicationMixin, models.Model):
|
|||
}
|
||||
|
||||
|
||||
class Category(WithApplicationMixin, models.Model):
|
||||
class Category(WithSnapshotMixin, WithApplicationMixin, models.Model):
|
||||
# mark temporarily restored snapshots
|
||||
snapshot = models.ForeignKey(
|
||||
CategorySnapshot, on_delete=models.CASCADE, null=True, related_name='temporary_instance'
|
||||
)
|
||||
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
application_component_type = 'agendas_categories'
|
||||
application_label_singular = _('Category (agendas)')
|
||||
application_label_plural = _('Categories (agendas)')
|
||||
|
||||
objects = WithSnapshotManager()
|
||||
snapshots = WithSnapshotManager(snapshots=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.label
|
||||
|
||||
|
@ -3507,10 +3576,15 @@ class Category(WithApplicationMixin, models.Model):
|
|||
return []
|
||||
|
||||
@classmethod
|
||||
def import_json(cls, data, overwrite=False):
|
||||
def import_json(cls, data, overwrite=False, snapshot=None):
|
||||
data = clean_import_data(cls, data)
|
||||
slug = data.pop('slug')
|
||||
category, created = cls.objects.update_or_create(slug=slug, defaults=data)
|
||||
qs_kwargs = {}
|
||||
if snapshot:
|
||||
qs_kwargs = {'snapshot': snapshot}
|
||||
else:
|
||||
qs_kwargs = {'slug': slug}
|
||||
category, created = cls.objects.update_or_create(defaults=data, **qs_kwargs)
|
||||
return created, category
|
||||
|
||||
def export_json(self):
|
||||
|
@ -3796,7 +3870,12 @@ class TimePeriodExceptionSource(models.Model):
|
|||
}
|
||||
|
||||
|
||||
class UnavailabilityCalendar(WithApplicationMixin, models.Model):
|
||||
class UnavailabilityCalendar(WithSnapshotMixin, WithApplicationMixin, models.Model):
|
||||
# mark temporarily restored snapshots
|
||||
snapshot = models.ForeignKey(
|
||||
UnavailabilityCalendarSnapshot, on_delete=models.CASCADE, null=True, related_name='temporary_instance'
|
||||
)
|
||||
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
desks = models.ManyToManyField(Desk, related_name='unavailability_calendars')
|
||||
|
@ -3819,10 +3898,16 @@ class UnavailabilityCalendar(WithApplicationMixin, models.Model):
|
|||
on_delete=models.SET_NULL,
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
application_component_type = 'unavailability_calendars'
|
||||
application_label_singular = _('Unavailability calendar')
|
||||
application_label_plural = _('Unavailability calendars')
|
||||
|
||||
objects = WithSnapshotManager()
|
||||
snapshots = WithSnapshotManager(snapshots=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['label']
|
||||
|
||||
|
@ -3870,7 +3955,7 @@ class UnavailabilityCalendar(WithApplicationMixin, models.Model):
|
|||
return unavailability_calendar
|
||||
|
||||
@classmethod
|
||||
def import_json(cls, data, overwrite=False):
|
||||
def import_json(cls, data, overwrite=False, snapshot=None):
|
||||
data = data.copy()
|
||||
permissions = data.pop('permissions', {})
|
||||
exceptions = data.pop('exceptions', [])
|
||||
|
@ -3878,7 +3963,13 @@ class UnavailabilityCalendar(WithApplicationMixin, models.Model):
|
|||
if permissions.get(permission):
|
||||
data[permission + '_role'] = Group.objects.get(name=permissions[permission])
|
||||
data = clean_import_data(cls, data)
|
||||
unavailability_calendar, created = cls.objects.update_or_create(slug=data['slug'], defaults=data)
|
||||
slug = data.pop('slug')
|
||||
qs_kwargs = {}
|
||||
if snapshot:
|
||||
qs_kwargs = {'snapshot': snapshot}
|
||||
else:
|
||||
qs_kwargs = {'slug': slug}
|
||||
unavailability_calendar, created = cls.objects.update_or_create(defaults=data, **qs_kwargs)
|
||||
if overwrite:
|
||||
TimePeriodException.objects.filter(unavailability_calendar=unavailability_calendar).delete()
|
||||
for exception in exceptions:
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = []
|
||||
|
||||
operations = []
|
|
@ -0,0 +1,186 @@
|
|||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('agendas', '0170_alter_agenda_events_type'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('snapshot', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UnavailabilityCalendarSnapshot',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('comment', models.TextField(blank=True, null=True)),
|
||||
('serialization', models.JSONField(blank=True, default=dict)),
|
||||
('label', models.CharField(blank=True, max_length=150, verbose_name='Label')),
|
||||
('application_slug', models.CharField(max_length=100, null=True)),
|
||||
('application_version', models.CharField(max_length=100, null=True)),
|
||||
(
|
||||
'instance',
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to='agendas.unavailabilitycalendar',
|
||||
related_name='instance_snapshots',
|
||||
),
|
||||
),
|
||||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-timestamp',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ResourceSnapshot',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('comment', models.TextField(blank=True, null=True)),
|
||||
('serialization', models.JSONField(blank=True, default=dict)),
|
||||
('label', models.CharField(blank=True, max_length=150, verbose_name='Label')),
|
||||
('application_slug', models.CharField(max_length=100, null=True)),
|
||||
('application_version', models.CharField(max_length=100, null=True)),
|
||||
(
|
||||
'instance',
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to='agendas.resource',
|
||||
related_name='instance_snapshots',
|
||||
),
|
||||
),
|
||||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-timestamp',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EventsTypeSnapshot',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('comment', models.TextField(blank=True, null=True)),
|
||||
('serialization', models.JSONField(blank=True, default=dict)),
|
||||
('label', models.CharField(blank=True, max_length=150, verbose_name='Label')),
|
||||
('application_slug', models.CharField(max_length=100, null=True)),
|
||||
('application_version', models.CharField(max_length=100, null=True)),
|
||||
(
|
||||
'instance',
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to='agendas.eventstype',
|
||||
related_name='instance_snapshots',
|
||||
),
|
||||
),
|
||||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-timestamp',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CategorySnapshot',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('comment', models.TextField(blank=True, null=True)),
|
||||
('serialization', models.JSONField(blank=True, default=dict)),
|
||||
('label', models.CharField(blank=True, max_length=150, verbose_name='Label')),
|
||||
('application_slug', models.CharField(max_length=100, null=True)),
|
||||
('application_version', models.CharField(max_length=100, null=True)),
|
||||
(
|
||||
'instance',
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to='agendas.category',
|
||||
related_name='instance_snapshots',
|
||||
),
|
||||
),
|
||||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-timestamp',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AgendaSnapshot',
|
||||
fields=[
|
||||
(
|
||||
'id',
|
||||
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
|
||||
),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('comment', models.TextField(blank=True, null=True)),
|
||||
('serialization', models.JSONField(blank=True, default=dict)),
|
||||
('label', models.CharField(blank=True, max_length=150, verbose_name='Label')),
|
||||
('application_slug', models.CharField(max_length=100, null=True)),
|
||||
('application_version', models.CharField(max_length=100, null=True)),
|
||||
(
|
||||
'instance',
|
||||
models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to='agendas.agenda',
|
||||
related_name='instance_snapshots',
|
||||
),
|
||||
),
|
||||
(
|
||||
'user',
|
||||
models.ForeignKey(
|
||||
null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-timestamp',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,131 @@
|
|||
# chrono - agendas system
|
||||
# Copyright (C) 2016-2024 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class WithSnapshotManager(models.Manager):
|
||||
snapshots = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.snapshots = kwargs.pop('snapshots', False)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
if self.snapshots:
|
||||
return queryset.filter(snapshot__isnull=False)
|
||||
else:
|
||||
return queryset.filter(snapshot__isnull=True)
|
||||
|
||||
|
||||
class WithSnapshotMixin:
|
||||
@classmethod
|
||||
def get_snapshot_model(cls):
|
||||
return cls._meta.get_field('snapshot').related_model
|
||||
|
||||
def take_snapshot(self, *args, **kwargs):
|
||||
self.get_snapshot_model().take(self, *args, **kwargs)
|
||||
|
||||
|
||||
class AbstractSnapshot(models.Model):
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)
|
||||
comment = models.TextField(blank=True, null=True)
|
||||
serialization = models.JSONField(blank=True, default=dict)
|
||||
label = models.CharField(_('Label'), max_length=150, blank=True)
|
||||
application_slug = models.CharField(max_length=100, null=True)
|
||||
application_version = models.CharField(max_length=100, null=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
ordering = ('-timestamp',)
|
||||
|
||||
@classmethod
|
||||
def get_instance_model(cls):
|
||||
return cls._meta.get_field('instance').related_model
|
||||
|
||||
@classmethod
|
||||
def take(cls, instance, request=None, comment=None, deletion=False, label=None, application=None):
|
||||
snapshot = cls(instance=instance, comment=comment, label=label or '')
|
||||
if request and not request.user.is_anonymous:
|
||||
snapshot.user = request.user
|
||||
if not deletion:
|
||||
snapshot.serialization = instance.export_json()
|
||||
else:
|
||||
snapshot.serialization = {}
|
||||
snapshot.comment = comment or _('deletion')
|
||||
if application:
|
||||
snapshot.application_slug = application.slug
|
||||
snapshot.application_version = application.version_number
|
||||
snapshot.save()
|
||||
|
||||
def get_instance(self):
|
||||
try:
|
||||
# try reusing existing instance
|
||||
return self.get_instance_model().snapshots.get(snapshot=self)
|
||||
except self.get_instance_model().DoesNotExist:
|
||||
return self.load_instance(self.serialization, snapshot=self)
|
||||
|
||||
def load_instance(self, json_instance, snapshot=None):
|
||||
return self.get_instance_model().import_json(json_instance, snapshot=snapshot)[1]
|
||||
|
||||
|
||||
class AgendaSnapshot(AbstractSnapshot):
|
||||
instance = models.ForeignKey(
|
||||
'agendas.Agenda',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
related_name='instance_snapshots',
|
||||
)
|
||||
|
||||
|
||||
class CategorySnapshot(AbstractSnapshot):
|
||||
instance = models.ForeignKey(
|
||||
'agendas.Category',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
related_name='instance_snapshots',
|
||||
)
|
||||
|
||||
|
||||
class EventsTypeSnapshot(AbstractSnapshot):
|
||||
instance = models.ForeignKey(
|
||||
'agendas.EventsType',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
related_name='instance_snapshots',
|
||||
)
|
||||
|
||||
|
||||
class ResourceSnapshot(AbstractSnapshot):
|
||||
instance = models.ForeignKey(
|
||||
'agendas.Resource',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
related_name='instance_snapshots',
|
||||
)
|
||||
|
||||
|
||||
class UnavailabilityCalendarSnapshot(AbstractSnapshot):
|
||||
instance = models.ForeignKey(
|
||||
'agendas.UnavailabilityCalendar',
|
||||
on_delete=models.SET_NULL,
|
||||
null=True,
|
||||
related_name='instance_snapshots',
|
||||
)
|
|
@ -62,6 +62,7 @@ INSTALLED_APPS = (
|
|||
'chrono.manager',
|
||||
'chrono.apps.ants_hub',
|
||||
'chrono.apps.export_import',
|
||||
'chrono.apps.snapshot',
|
||||
)
|
||||
|
||||
MIDDLEWARE = (
|
||||
|
|
Loading…
Reference in New Issue