views.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. from __future__ import unicode_literals
  2. from rest_framework.decorators import detail_route
  3. from rest_framework.exceptions import PermissionDenied
  4. from rest_framework.response import Response
  5. from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet
  6. from django.contrib.contenttypes.models import ContentType
  7. from django.http import Http404, HttpResponse
  8. from django.shortcuts import get_object_or_404
  9. from extras import filters
  10. from extras.models import CustomField, ExportTemplate, Graph, ImageAttachment, ReportResult, TopologyMap, UserAction
  11. from extras.reports import get_report, get_reports
  12. from utilities.api import FieldChoicesViewSet, IsAuthenticatedOrLoginNotRequired, WritableSerializerMixin
  13. from . import serializers
  14. #
  15. # Field choices
  16. #
  17. class ExtrasFieldChoicesViewSet(FieldChoicesViewSet):
  18. fields = (
  19. (CustomField, ['type']),
  20. (Graph, ['type']),
  21. )
  22. #
  23. # Custom fields
  24. #
  25. class CustomFieldModelViewSet(ModelViewSet):
  26. """
  27. Include the applicable set of CustomFields in the ModelViewSet context.
  28. """
  29. def get_serializer_context(self):
  30. # Gather all custom fields for the model
  31. content_type = ContentType.objects.get_for_model(self.queryset.model)
  32. custom_fields = content_type.custom_fields.prefetch_related('choices')
  33. # Cache all relevant CustomFieldChoices. This saves us from having to do a lookup per select field per object.
  34. custom_field_choices = {}
  35. for field in custom_fields:
  36. for cfc in field.choices.all():
  37. custom_field_choices[cfc.id] = cfc.value
  38. custom_field_choices = custom_field_choices
  39. context = super(CustomFieldModelViewSet, self).get_serializer_context()
  40. context.update({
  41. 'custom_fields': custom_fields,
  42. 'custom_field_choices': custom_field_choices,
  43. })
  44. return context
  45. def get_queryset(self):
  46. # Prefetch custom field values
  47. return super(CustomFieldModelViewSet, self).get_queryset().prefetch_related('custom_field_values__field')
  48. #
  49. # Graphs
  50. #
  51. class GraphViewSet(WritableSerializerMixin, ModelViewSet):
  52. queryset = Graph.objects.all()
  53. serializer_class = serializers.GraphSerializer
  54. write_serializer_class = serializers.WritableGraphSerializer
  55. filter_class = filters.GraphFilter
  56. #
  57. # Export templates
  58. #
  59. class ExportTemplateViewSet(WritableSerializerMixin, ModelViewSet):
  60. queryset = ExportTemplate.objects.all()
  61. serializer_class = serializers.ExportTemplateSerializer
  62. filter_class = filters.ExportTemplateFilter
  63. #
  64. # Topology maps
  65. #
  66. class TopologyMapViewSet(WritableSerializerMixin, ModelViewSet):
  67. queryset = TopologyMap.objects.select_related('site')
  68. serializer_class = serializers.TopologyMapSerializer
  69. write_serializer_class = serializers.WritableTopologyMapSerializer
  70. filter_class = filters.TopologyMapFilter
  71. @detail_route()
  72. def render(self, request, pk):
  73. tmap = get_object_or_404(TopologyMap, pk=pk)
  74. img_format = 'png'
  75. try:
  76. data = tmap.render(img_format=img_format)
  77. except:
  78. return HttpResponse(
  79. "There was an error generating the requested graph. Ensure that the GraphViz executables have been "
  80. "installed correctly."
  81. )
  82. response = HttpResponse(data, content_type='image/{}'.format(img_format))
  83. response['Content-Disposition'] = 'inline; filename="{}.{}"'.format(tmap.slug, img_format)
  84. return response
  85. #
  86. # Image attachments
  87. #
  88. class ImageAttachmentViewSet(WritableSerializerMixin, ModelViewSet):
  89. queryset = ImageAttachment.objects.all()
  90. serializer_class = serializers.ImageAttachmentSerializer
  91. write_serializer_class = serializers.WritableImageAttachmentSerializer
  92. #
  93. # Reports
  94. #
  95. class ReportViewSet(ViewSet):
  96. permission_classes = [IsAuthenticatedOrLoginNotRequired]
  97. _ignore_model_permissions = True
  98. exclude_from_schema = True
  99. lookup_value_regex = '[^/]+' # Allow dots
  100. def _retrieve_report(self, pk):
  101. # Read the PK as "<module>.<report>"
  102. if '.' not in pk:
  103. raise Http404
  104. module_name, report_name = pk.split('.', 1)
  105. # Raise a 404 on an invalid Report module/name
  106. report = get_report(module_name, report_name)
  107. if report is None:
  108. raise Http404
  109. return report
  110. def list(self, request):
  111. """
  112. Compile all reports and their related results (if any). Result data is deferred in the list view.
  113. """
  114. report_list = []
  115. # Iterate through all available Reports.
  116. for module_name, reports in get_reports():
  117. for report in reports:
  118. # Attach the relevant ReportResult (if any) to each Report.
  119. report.result = ReportResult.objects.filter(report=report.full_name).defer('data').first()
  120. report_list.append(report)
  121. serializer = serializers.ReportSerializer(report_list, many=True, context={
  122. 'request': request,
  123. })
  124. return Response(serializer.data)
  125. def retrieve(self, request, pk):
  126. """
  127. Retrieve a single Report identified as "<module>.<report>".
  128. """
  129. # Retrieve the Report and ReportResult, if any.
  130. report = self._retrieve_report(pk)
  131. report.result = ReportResult.objects.filter(report=report.full_name).first()
  132. serializer = serializers.ReportDetailSerializer(report)
  133. return Response(serializer.data)
  134. @detail_route(methods=['post'])
  135. def run(self, request, pk):
  136. """
  137. Run a Report and create a new ReportResult, overwriting any previous result for the Report.
  138. """
  139. # Check that the user has permission to run reports.
  140. if not request.user.has_perm('extras.add_reportresult'):
  141. raise PermissionDenied("This user does not have permission to run reports.")
  142. # Retrieve and run the Report. This will create a new ReportResult.
  143. report = self._retrieve_report(pk)
  144. report.run()
  145. serializer = serializers.ReportDetailSerializer(report)
  146. return Response(serializer.data)
  147. #
  148. # User activity
  149. #
  150. class RecentActivityViewSet(ReadOnlyModelViewSet):
  151. """
  152. List all UserActions to provide a log of recent activity.
  153. """
  154. queryset = UserAction.objects.all()
  155. serializer_class = serializers.UserActionSerializer
  156. filter_class = filters.UserActionFilter