admin.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. from django.contrib import admin
  2. from django.contrib.gis import admin as geo_admin
  3. from django.db import models
  4. from django.db.models import Q
  5. from django.forms import ModelForm, BaseInlineFormSet
  6. from django.utils import timezone
  7. from django.core.urlresolvers import reverse
  8. from django.utils.html import format_html
  9. from django.core.mail import mail_managers
  10. from adhesions.models import Adhesion
  11. from .models import Service, ServiceType, IPPrefix, IPResource, Route, ServiceAllocation, Antenna, AntennaAllocation, Allocation
  12. from .utils import notify_allocation
  13. ### Filters
  14. class ResourceInUseFilter(admin.SimpleListFilter):
  15. title = 'disponibilité'
  16. parameter_name = 'available'
  17. def lookups(self, request, model_admin):
  18. return (
  19. (1, 'Disponible'),
  20. (0, 'Non disponible'),
  21. )
  22. def queryset(self, request, queryset):
  23. available_filter = Q(reserved=False, in_use=False)
  24. if self.value() == '0': # non disponible
  25. return queryset.exclude(available_filter)
  26. if self.value() == '1': # disponible
  27. return queryset.filter(available_filter)
  28. class AntennaPrefixFilter(admin.SimpleListFilter):
  29. title = 'préfix'
  30. parameter_name = 'prefix'
  31. def lookups(self, request, model_admin):
  32. resources = AntennaAllocation.actives.filter(active=True).values_list('resource__pk', flat=True)
  33. prefixes = IPPrefix.objects.filter(ipresource__in=resources).values_list('pk', 'prefix').distinct()
  34. return prefixes
  35. def queryset(self, request, queryset):
  36. try:
  37. prefix = int(self.value())
  38. except TypeError:
  39. pass
  40. else:
  41. allocations = AntennaAllocation.actives.filter(active=True, resource__prefixes__pk=prefix).values_list('pk', flat=True)
  42. queryset = queryset.filter(allocation__in=allocations)
  43. return queryset
  44. ### Inlines
  45. class AllocationInlineFormSet(BaseInlineFormSet):
  46. def save_new(self, form, commit=True):
  47. obj = super().save_new(form, commit)
  48. if type(obj) == ServiceAllocation:
  49. notify_allocation(self.request, obj)
  50. return obj
  51. def save_existing(self, form, instance, commit=True):
  52. old = type(instance).objects.get(pk=instance.pk)
  53. if type(instance) == ServiceAllocation:
  54. notify_allocation(self.request, instance, old)
  55. return super().save_existing(form, instance, commit)
  56. class AllocationInline(admin.TabularInline):
  57. formset = AllocationInlineFormSet
  58. extra = 0
  59. verbose_name_plural = 'Allocations'
  60. show_change_link = True
  61. def get_formset(self, request, obj=None, **kwargs):
  62. formset = super().get_formset(request, obj, **kwargs)
  63. formset.request = request
  64. return formset
  65. def get_max_num(self, request, obj=None, **kwargs):
  66. existing = obj.allocations.count() if obj else 0
  67. # pour simplifier la validation, on ajoute qu’une allocation à la fois
  68. # il faudrait surcharger la méthode clean du formset pour supprimer cette limite
  69. return existing + 1
  70. def has_delete_permission(self, request, obj=None):
  71. return False
  72. class ServiceAllocationInline(AllocationInline):
  73. model = ServiceAllocation
  74. fields = ('id', 'service', 'resource', 'route', 'start', 'end')
  75. raw_id_fields = ('service', 'resource',)
  76. class AntennaAllocationInline(AllocationInline):
  77. model = AntennaAllocation
  78. fields = ('id', 'antenna', 'resource', 'start', 'end')
  79. raw_id_fields = ('antenna', 'resource',)
  80. ### Actions
  81. def ends_resource(resource, request, queryset):
  82. now = timezone.now()
  83. queryset.exclude(start__lte=now, end__isnull=False).update(end=now)
  84. # TODO: send mail
  85. ends_resource.short_description = 'Terminer les allocations sélectionnées'
  86. ### ModelAdmin
  87. class ServiceAdmin(admin.ModelAdmin):
  88. list_display = ('id', 'get_adhesion_link', 'get_adherent_link', 'service_type', 'label', 'active')
  89. list_select_related = ('adhesion',) # to reduce database requests
  90. list_filter = (
  91. 'active',
  92. ('service_type', admin.RelatedOnlyFieldListFilter),
  93. )
  94. inlines = (ServiceAllocationInline,)
  95. search_fields = ('=id', 'service_type__name', 'label', 'notes',)
  96. raw_id_fields = ('adhesion',)
  97. get_adhesion_link = lambda self, service: service.adhesion.get_adhesion_link()
  98. get_adhesion_link.short_description = Adhesion.get_adhesion_link.short_description
  99. get_adherent_link = lambda self, service: service.adhesion.get_adherent_link()
  100. get_adherent_link.short_description = Adhesion.get_adherent_link.short_description
  101. def get_actions(self, request):
  102. actions = super().get_actions(request)
  103. if 'delete_selected' in actions:
  104. del actions['delete_selected']
  105. return actions
  106. def has_delete_permission(self, request, obj=None):
  107. return False
  108. class IPResourceAdmin(admin.ModelAdmin):
  109. list_display = ('__str__', 'available_display', 'last_use',)
  110. list_filter = (
  111. 'category',
  112. ResourceInUseFilter,
  113. ('prefixes', admin.RelatedOnlyFieldListFilter),
  114. )
  115. fields = ('ip', 'reserved', 'notes')
  116. readonly_fields = ('ip', 'reserved',)
  117. search_fields = ('=ip',)
  118. def get_inline_instances(self, request, obj=None):
  119. if obj:
  120. if obj.category == 0:
  121. inlines = (ServiceAllocationInline,)
  122. elif obj.category == 1:
  123. inlines = (AntennaAllocationInline,)
  124. else:
  125. inlines = ()
  126. return [inline(self.model, self.admin_site) for inline in inlines]
  127. def get_queryset(self, request):
  128. qs = super().get_queryset(request)
  129. qs = qs.annotate(last_use=models.Case(
  130. models.When(category=0, then=models.Max('service_allocation__end')),
  131. models.When(category=1, then=models.Max('antenna_allocation__end')),
  132. ))
  133. return qs
  134. def available_display(self, obj):
  135. return not obj.reserved and not obj.in_use
  136. available_display.short_description = 'Disponible'
  137. available_display.boolean = True
  138. def last_use(self, obj):
  139. if obj.allocations.exists():
  140. return obj.allocations.last().end
  141. else:
  142. return None
  143. last_use.short_description = 'Dernière utilisation'
  144. last_use.admin_order_field = 'last_use'
  145. def get_actions(self, request):
  146. actions = super().get_actions(request)
  147. if 'delete_selected' in actions:
  148. del actions['delete_selected']
  149. return actions
  150. def has_add_permission(self, request, obj=None):
  151. return False
  152. def has_delete_permission(self, request, obj=None):
  153. return False
  154. class RouteAdmin(admin.ModelAdmin):
  155. fields = ('name',)
  156. readonly_fields = ('name',)
  157. def has_add_permission(self, request, obj=None):
  158. return False
  159. def get_actions(self, request):
  160. actions = super().get_actions(request)
  161. if 'delete_selected' in actions:
  162. del actions['delete_selected']
  163. return actions
  164. def has_delete_permission(self, request, obj=None):
  165. return False
  166. class ServiceTypeAdmin(admin.ModelAdmin):
  167. fields = ('name',)
  168. readonly_fields = ('name',)
  169. def get_actions(self, request):
  170. actions = super().get_actions(request)
  171. if 'delete_selected' in actions:
  172. del actions['delete_selected']
  173. return actions
  174. def has_add_permission(self, request, obj=None):
  175. return False
  176. def has_delete_permission(self, request, obj=None):
  177. return False
  178. class AntennaAdmin(geo_admin.OSMGeoAdmin):
  179. inlines = (AntennaAllocationInline,)
  180. list_filter = (
  181. AntennaPrefixFilter,
  182. )
  183. admin.site.register(ServiceType, ServiceTypeAdmin)
  184. admin.site.register(Service, ServiceAdmin)
  185. admin.site.register(IPResource, IPResourceAdmin)
  186. admin.site.register(Route, RouteAdmin)
  187. geo_admin.site.register(Antenna, AntennaAdmin)