123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- from django.contrib import admin
- from django.contrib.gis import admin as geo_admin
- from django.db import models
- from django.db.models import Q
- from django.forms import ModelForm, BaseInlineFormSet
- from django.utils import timezone
- from django.core.urlresolvers import reverse
- from django.utils.html import format_html
- from django.core.mail import mail_managers
- from django.conf.urls import url
- from django.template.response import TemplateResponse
- from django.core.serializers import serialize
- from django.http import HttpResponse
- from adhesions.models import Adhesion
- from .models import Service, ServiceType, IPPrefix, IPResource, Route, Tunnel, ServiceAllocation, Antenna, AntennaAllocation, Allocation
- from .utils import notify_allocation
- ### Filters
- class ResourceInUseFilter(admin.SimpleListFilter):
- title = 'disponibilité'
- parameter_name = 'available'
- def lookups(self, request, model_admin):
- return (
- (1, 'Disponible'),
- (0, 'Non disponible'),
- )
- def queryset(self, request, queryset):
- available_filter = Q(reserved=False, in_use=False)
- if self.value() == '0': # non disponible
- return queryset.exclude(available_filter)
- if self.value() == '1': # disponible
- return queryset.filter(available_filter)
- class RouteFilter(admin.SimpleListFilter):
- title = 'route'
- parameter_name = 'route'
- def lookups(self, request, model_admin):
- return ServiceAllocation.objects.filter(active=True).values_list('route__pk', 'route__name').distinct()
- def queryset(self, request, queryset):
- try:
- route = int(self.value())
- except (TypeError, ValueError):
- pass
- else:
- allocations = ServiceAllocation.objects.filter(active=True, route__pk=route).values_list('pk', flat=True)
- queryset = queryset.filter(service_allocation__in=allocations)
- return queryset
- class AntennaPrefixFilter(admin.SimpleListFilter):
- title = 'préfix'
- parameter_name = 'prefix'
- def lookups(self, request, model_admin):
- resources = AntennaAllocation.objects.filter(active=True).values_list('resource__pk', flat=True)
- prefixes = IPPrefix.objects.filter(ipresource__in=resources).values_list('pk', 'prefix').distinct()
- return prefixes
- def queryset(self, request, queryset):
- try:
- prefix = int(self.value())
- except (TypeError, ValueError):
- pass
- else:
- allocations = AntennaAllocation.objects.filter(active=True, resource__prefixes__pk=prefix).values_list('pk', flat=True)
- queryset = queryset.filter(allocation__in=allocations)
- return queryset
- class ActiveTunnelFilter(admin.SimpleListFilter):
- title = 'status'
- parameter_name = 'active'
- def lookups(self, request, model_admin):
- return (
- ('1', 'Actif'),
- ('0', 'Désactivé'),
- )
- def queryset(self, request, queryset):
- query = Q(ended__isnull=True)
- if self.value() == '0':
- return queryset.exclude(query)
- if self.value() == '1':
- return queryset.filter(query)
- return queryset
- ### Inlines
- class AllocationInlineFormSet(BaseInlineFormSet):
- def save_new(self, form, commit=True):
- obj = super().save_new(form, commit)
- if type(obj) == ServiceAllocation:
- notify_allocation(self.request, obj)
- return obj
- def save_existing(self, form, instance, commit=True):
- old = type(instance).objects.get(pk=instance.pk)
- if type(instance) == ServiceAllocation:
- notify_allocation(self.request, instance, old)
- return super().save_existing(form, instance, commit)
- class AllocationInline(admin.TabularInline):
- formset = AllocationInlineFormSet
- extra = 0
- verbose_name_plural = 'Allocations'
- show_change_link = True
- def get_formset(self, request, obj=None, **kwargs):
- formset = super().get_formset(request, obj, **kwargs)
- formset.request = request
- return formset
- def get_max_num(self, request, obj=None, **kwargs):
- existing = obj.allocations.count() if obj else 0
- # pour simplifier la validation, on ajoute qu’une allocation à la fois
- # il faudrait surcharger la méthode clean du formset pour supprimer cette limite
- return existing + 1
- def has_delete_permission(self, request, obj=None):
- return False
- class ServiceAllocationInline(AllocationInline):
- model = ServiceAllocation
- fields = ('id', 'service', 'resource', 'route', 'start', 'end')
- raw_id_fields = ('service', 'resource',)
- class AntennaAllocationInline(AllocationInline):
- model = AntennaAllocation
- fields = ('id', 'antenna', 'resource', 'start', 'end')
- raw_id_fields = ('antenna', 'resource',)
- ### Actions
- def ends_resource(resource, request, queryset):
- now = timezone.now()
- queryset.exclude(start__lte=now, end__isnull=False).update(end=now)
- # TODO: send mail
- ends_resource.short_description = 'Terminer les allocations sélectionnées'
- ### ModelAdmin
- class ServiceAdmin(admin.ModelAdmin):
- list_display = ('id', 'get_adhesion_link', 'get_adherent_link', 'service_type', 'label', 'active')
- list_select_related = ('adhesion', 'adhesion__user', 'adhesion__user__profile', 'adhesion__corporation', 'service_type')
- list_filter = (
- 'active',
- ('service_type', admin.RelatedOnlyFieldListFilter),
- )
- inlines = (ServiceAllocationInline,)
- search_fields = ('=id', 'service_type__name', 'label', 'notes',)
- raw_id_fields = ('adhesion',)
- get_adhesion_link = lambda self, service: service.adhesion.get_adhesion_link()
- get_adhesion_link.short_description = Adhesion.get_adhesion_link.short_description
- get_adherent_link = lambda self, service: service.adhesion.get_adherent_link()
- get_adherent_link.short_description = Adhesion.get_adherent_link.short_description
- def get_actions(self, request):
- actions = super().get_actions(request)
- if 'delete_selected' in actions:
- del actions['delete_selected']
- return actions
- def has_delete_permission(self, request, obj=None):
- return False
- class IPPrefixAdmin(admin.ModelAdmin):
- readonly_fields = ('prefix',)
- def has_delete_permission(self, request, obj=None):
- # Interdiction de supprimer le préfix s’il est assigné à une route
- if obj and obj.tunnel_set.exists():
- return False
- else:
- return True
- # pour embêcher de by-passer le check has_delete_permission, on désactive l’action delete
- def get_actions(self, request):
- actions = super().get_actions(request)
- if 'delete_selected' in actions:
- del actions['delete_selected']
- return actions
- class IPResourceAdmin(admin.ModelAdmin):
- list_display = ('__str__', 'available_display', 'last_use',)
- list_filter = (
- 'category',
- ResourceInUseFilter,
- ('prefixes', admin.RelatedOnlyFieldListFilter),
- RouteFilter,
- )
- fields = ('ip', 'reserved', 'notes')
- readonly_fields = ('ip', 'reserved',)
- search_fields = ('=ip',)
- def get_inline_instances(self, request, obj=None):
- if obj:
- if obj.category == 0:
- inlines = (ServiceAllocationInline,)
- elif obj.category == 1:
- inlines = (AntennaAllocationInline,)
- else:
- inlines = ()
- return [inline(self.model, self.admin_site) for inline in inlines]
- def get_queryset(self, request):
- qs = super().get_queryset(request)
- qs = qs.annotate(last_use=models.Case(
- models.When(category=0, then=models.Max('service_allocation__end')),
- models.When(category=1, then=models.Max('antenna_allocation__end')),
- ))
- return qs
- def available_display(self, obj):
- return not obj.reserved and not obj.in_use
- available_display.short_description = 'Disponible'
- available_display.boolean = True
- def last_use(self, obj):
- if obj.allocations.exists():
- return obj.allocations.last().end
- else:
- return None
- last_use.short_description = 'Dernière utilisation'
- last_use.admin_order_field = 'last_use'
- def get_actions(self, request):
- actions = super().get_actions(request)
- if 'delete_selected' in actions:
- del actions['delete_selected']
- return actions
- def has_add_permission(self, request, obj=None):
- return False
- def has_delete_permission(self, request, obj=None):
- return False
- class RouteAdmin(admin.ModelAdmin):
- list_display = ('name',)
- def get_readonly_fields(self, request, obj=None):
- if obj:
- return ('name',)
- else:
- return ()
- def get_actions(self, request):
- actions = super().get_actions(request)
- if 'delete_selected' in actions:
- del actions['delete_selected']
- return actions
- def has_delete_permission(self, request, obj=None):
- return False
- class TunnelAdmin(RouteAdmin):
- list_display = ('name', 'description', 'created', 'active')
- list_filter = (
- ActiveTunnelFilter,
- )
- def active(self, obj):
- return not obj.ended
- active.short_description = 'Actif'
- active.boolean = True
- class ServiceTypeAdmin(admin.ModelAdmin):
- fields = ('name',)
- readonly_fields = ('name',)
- def get_actions(self, request):
- actions = super().get_actions(request)
- if 'delete_selected' in actions:
- del actions['delete_selected']
- return actions
- def has_add_permission(self, request, obj=None):
- return False
- def has_delete_permission(self, request, obj=None):
- return False
- class AntennaAdmin(geo_admin.OSMGeoAdmin):
- list_display = ('id', 'label', 'ip_display')
- inlines = (AntennaAllocationInline,)
- list_filter = (
- AntennaPrefixFilter,
- )
- def ip_display(self, obj):
- allocations = obj.allocations.filter(active=True)
- return ', '.join(allocations.values_list('resource__ip', flat=True)) or '-'
- ip_display.short_description = 'IP'
- def get_actions(self, request):
- actions = super().get_actions(request)
- if 'delete_selected' in actions:
- del actions['delete_selected']
- return actions
- def has_delete_permission(self, request, obj=None):
- return False
- def get_urls(self):
- urls = super().get_urls()
- from djgeojson.views import GeoJSONLayerView
- my_urls = [
- url(r'^map/$', self.admin_site.admin_view(self.map_view, cacheable=True), name='antenna-map'),
- url(r'^map/data.json$', self.admin_site.admin_view(GeoJSONLayerView.as_view(model=Antenna, geometry_field='position')), name='antenna-map-data'),
- ]
- return my_urls + urls
- def map_view(self, request):
- return TemplateResponse(request, 'services/antenna_map.html', {
- 'json_url': reverse('admin:antenna-map-data'),
- })
- def map_data_view(self, request):
- geojson = serialize('geojson', Antenna.objects.all(), geometry_field='point', fields=('position',))
- return HttpResponse(geojson, content_type='application/json')
- admin.site.register(ServiceType, ServiceTypeAdmin)
- admin.site.register(Service, ServiceAdmin)
- admin.site.register(IPPrefix, IPPrefixAdmin)
- admin.site.register(IPResource, IPResourceAdmin)
- admin.site.register(Route, RouteAdmin)
- admin.site.register(Tunnel, TunnelAdmin)
- geo_admin.site.register(Antenna, AntennaAdmin)
|