123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- from django.db import models
- from django.contrib.gis.db import models as geo_models
- from django.db.models import Q
- from django.core.validators import MaxValueValidator
- from django.utils import timezone
- from django.contrib.auth.models import Group
- from django.contrib.contenttypes.fields import GenericRelation
- from django.core.exceptions import ValidationError
- from django.urls import reverse
- from django.utils import timezone
- from django.core.exceptions import PermissionDenied
- from django.core.validators import RegexValidator
- from djadhere.utils import get_active_filter, is_overlapping
- from adhesions.models import Adhesion
- from banking.models import Payment
- class IPPrefix(models.Model):
- prefix = models.CharField(max_length=128)
- def __str__(self):
- return self.prefix
- class IPResourceManager(models.Manager):
- def get_queryset(self):
- qs = super().get_queryset()
- # On rajoute une super annotation « in_use » pour savoir si l’IP est dispo ou non :-)
- query = Q(resource=models.OuterRef('pk')) & get_active_filter()
- qs = qs.annotate(
- in_use_by_service=models.Exists(
- ServiceAllocation.objects.filter(query)
- ),
- in_use_by_antenna=models.Exists(
- AntennaAllocation.objects.filter(query)
- ),
- in_use=models.ExpressionWrapper(
- models.F('in_use_by_service') + models.F('in_use_by_antenna'),
- output_field=models.BooleanField()
- )
- )
- return qs
- class IPResource(models.Model):
- CATEGORIES = (
- (0, 'IP Public'),
- (1, 'IP Antenne'),
- )
- ip = models.GenericIPAddressField(verbose_name='IP', primary_key=True)
- prefixes = models.ManyToManyField(IPPrefix, verbose_name='préfixes')
- reserved = models.BooleanField(default=False, verbose_name='réservée')
- category = models.IntegerField(choices=CATEGORIES, verbose_name='catégorie')
- notes = models.TextField(blank=True, default='')
- objects = IPResourceManager()
- @property
- def allocations(self):
- if self.category == 0:
- return self.service_allocations
- if self.category == 1:
- return self.antenna_allocations
- class Meta:
- ordering = ['ip']
- verbose_name = 'IP'
- verbose_name_plural = 'IP'
- def __str__(self):
- return str(self.ip)
- class ServiceType(models.Model):
- name = models.CharField(max_length=64, verbose_name='Nom', unique=True)
- class Meta:
- ordering = ['name']
- verbose_name = 'type de service'
- verbose_name_plural = 'types de service'
- def __str__(self):
- return self.name
- class Service(models.Model):
- adhesion = models.ForeignKey(Adhesion, verbose_name='Adhérent', related_name='services')
- service_type = models.ForeignKey(ServiceType, related_name='services',
- verbose_name='Type de service')
- label = models.CharField(blank=True, default='', max_length=128)
- notes = models.TextField(blank=True, default='')
- active = models.BooleanField(default=True, verbose_name='actif')
- created = models.DateTimeField(auto_now_add=True)
- contributions = GenericRelation(Payment,
- content_type_field='reason_type',
- object_id_field='reason_id',
- related_query_name='service')
- @property
- def contribution(self):
- try:
- return self.contributions.exclude(period=0).get(get_active_filter())
- except Payment.DoesNotExist:
- return None
- # MultipleObjectsReturned non catché volontairement, cf remarque adhesions.Adhesion.contribution
- def clean(self):
- super().clean()
- # Vérification de l’unicité par type de service du label
- if self.label != '' and Service.objects.exclude(pk=self.pk).filter(service_type=self.service_type, label=self.label):
- raise ValidationError("Un service du même type existe déjà avec ce label.")
- def __str__(self):
- s = str(self.service_type)
- if self.label:
- s += ' ' + self.label
- return s
- class Antenna(models.Model):
- position = geo_models.PointField(null=True)
- mac = models.CharField(
- blank=True,
- default='',
- max_length=17,
- validators=[
- RegexValidator(r'^([0-9a-fA-F]{2}([:-]?|$)){6}$'),
- ])
- notes = models.TextField(blank=True)
- class Meta:
- verbose_name = 'antenne'
- def __str__(self):
- return 'Antenne %d' % self.pk
- class Route(models.Model):
- name = models.CharField(max_length=64)
- def __str__(self):
- return self.name
- class Allocation(models.Model):
- start = models.DateTimeField(verbose_name='Début de la période d’allocation', default=timezone.now)
- end = models.DateTimeField(null=True, blank=True, verbose_name='Fin de la période d’allocation')
- notes = models.TextField(blank=True, default='')
- @property
- def active(self):
- now = timezone.now()
- return self.start <= now and (self.end is None or self.end >= now)
- def clean(self):
- super().clean()
- # Vérification de la cohérence des champs start et end
- if self.end and self.start > self.end:
- raise ValidationError("La date de début de l’allocation doit être antérieur "
- "à la date de fin de l’allocation.")
- if self.resource_id:
- if self.resource.reserved:
- raise ValidationError("L’IP sélectionnée est réservée")
- # Vérification de l’abscence de chevauchement de la période d’allocation
- allocations = type(self).objects.filter(resource__pk=self.resource.pk)
- if is_overlapping(self, allocations):
- raise ValidationError("La période d’allocation de cette ressource chevauche "
- "avec une période d’allocation précédente.")
- class Meta:
- abstract = True
- ordering = ['-start']
- def __str__(self):
- return str(self.resource)
- class ServiceAllocation(Allocation):
- resource = models.ForeignKey(IPResource, verbose_name='Ressource', related_name='service_allocations',
- related_query_name='service_allocation', limit_choices_to={'category': 0})
- service = models.ForeignKey(Service, related_name='allocations', related_query_name='allocation')
- route = models.ForeignKey(Route, verbose_name='Route', related_name='services', related_query_name='allocation')
- class Meta:
- verbose_name = 'allocation'
- verbose_name_plural = 'allocations'
- class AntennaAllocation(Allocation):
- resource = models.ForeignKey(IPResource, verbose_name='Ressource', related_name='antenna_allocations',
- related_query_name='antenna_allocation', limit_choices_to={'category': 1})
- antenna = models.ForeignKey(Antenna, related_name='allocations', related_query_name='allocation')
- class Meta:
- verbose_name = 'allocation'
- verbose_name_plural = 'allocations'
|