views.py 6.9 KB

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