admin.py 9.3 KB

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