views.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. from collections import OrderedDict
  2. from django.contrib.contenttypes.models import ContentType
  3. from django.db.models import Count
  4. from django.http import Http404
  5. from rest_framework import status
  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 extras import filters
  11. from extras.models import (
  12. ConfigContext, CustomFieldChoice, ExportTemplate, Graph, ImageAttachment, ObjectChange, ReportResult, Tag,
  13. )
  14. from extras.reports import get_report, get_reports
  15. from extras.scripts import get_script, get_scripts, run_script
  16. from utilities.api import IsAuthenticatedOrLoginNotRequired, ModelViewSet
  17. from . import serializers
  18. #
  19. # Custom field choices
  20. #
  21. class CustomFieldChoicesViewSet(ViewSet):
  22. """
  23. """
  24. permission_classes = [IsAuthenticatedOrLoginNotRequired]
  25. def __init__(self, *args, **kwargs):
  26. super(CustomFieldChoicesViewSet, self).__init__(*args, **kwargs)
  27. self._fields = OrderedDict()
  28. for cfc in CustomFieldChoice.objects.all():
  29. self._fields.setdefault(cfc.field.name, {})
  30. self._fields[cfc.field.name][cfc.value] = cfc.pk
  31. def list(self, request):
  32. return Response(self._fields)
  33. def retrieve(self, request, pk):
  34. if pk not in self._fields:
  35. raise Http404
  36. return Response(self._fields[pk])
  37. def get_view_name(self):
  38. return "Custom Field choices"
  39. #
  40. # Custom fields
  41. #
  42. class CustomFieldModelViewSet(ModelViewSet):
  43. """
  44. Include the applicable set of CustomFields in the ModelViewSet context.
  45. """
  46. def get_serializer_context(self):
  47. # Gather all custom fields for the model
  48. content_type = ContentType.objects.get_for_model(self.queryset.model)
  49. custom_fields = content_type.custom_fields.prefetch_related('choices')
  50. # Cache all relevant CustomFieldChoices. This saves us from having to do a lookup per select field per object.
  51. custom_field_choices = {}
  52. for field in custom_fields:
  53. for cfc in field.choices.all():
  54. custom_field_choices[cfc.id] = cfc.value
  55. custom_field_choices = custom_field_choices
  56. context = super().get_serializer_context()
  57. context.update({
  58. 'custom_fields': custom_fields,
  59. 'custom_field_choices': custom_field_choices,
  60. })
  61. return context
  62. def get_queryset(self):
  63. # Prefetch custom field values
  64. return super().get_queryset().prefetch_related('custom_field_values__field')
  65. #
  66. # Graphs
  67. #
  68. class GraphViewSet(ModelViewSet):
  69. queryset = Graph.objects.all()
  70. serializer_class = serializers.GraphSerializer
  71. filterset_class = filters.GraphFilterSet
  72. #
  73. # Export templates
  74. #
  75. class ExportTemplateViewSet(ModelViewSet):
  76. queryset = ExportTemplate.objects.all()
  77. serializer_class = serializers.ExportTemplateSerializer
  78. filterset_class = filters.ExportTemplateFilterSet
  79. #
  80. # Tags
  81. #
  82. class TagViewSet(ModelViewSet):
  83. queryset = Tag.objects.annotate(
  84. tagged_items=Count('extras_taggeditem_items', distinct=True)
  85. )
  86. serializer_class = serializers.TagSerializer
  87. filterset_class = filters.TagFilterSet
  88. #
  89. # Image attachments
  90. #
  91. class ImageAttachmentViewSet(ModelViewSet):
  92. queryset = ImageAttachment.objects.all()
  93. serializer_class = serializers.ImageAttachmentSerializer
  94. #
  95. # Config contexts
  96. #
  97. class ConfigContextViewSet(ModelViewSet):
  98. queryset = ConfigContext.objects.prefetch_related(
  99. 'regions', 'sites', 'roles', 'platforms', 'tenant_groups', 'tenants',
  100. )
  101. serializer_class = serializers.ConfigContextSerializer
  102. filterset_class = filters.ConfigContextFilterSet
  103. #
  104. # Reports
  105. #
  106. class ReportViewSet(ViewSet):
  107. permission_classes = [IsAuthenticatedOrLoginNotRequired]
  108. _ignore_model_permissions = True
  109. exclude_from_schema = True
  110. lookup_value_regex = '[^/]+' # Allow dots
  111. def _retrieve_report(self, pk):
  112. # Read the PK as "<module>.<report>"
  113. if '.' not in pk:
  114. raise Http404
  115. module_name, report_name = pk.split('.', 1)
  116. # Raise a 404 on an invalid Report module/name
  117. report = get_report(module_name, report_name)
  118. if report is None:
  119. raise Http404
  120. return report
  121. def list(self, request):
  122. """
  123. Compile all reports and their related results (if any). Result data is deferred in the list view.
  124. """
  125. report_list = []
  126. # Iterate through all available Reports.
  127. for module_name, reports in get_reports():
  128. for report in reports:
  129. # Attach the relevant ReportResult (if any) to each Report.
  130. report.result = ReportResult.objects.filter(report=report.full_name).defer('data').first()
  131. report_list.append(report)
  132. serializer = serializers.ReportSerializer(report_list, many=True, context={
  133. 'request': request,
  134. })
  135. return Response(serializer.data)
  136. def retrieve(self, request, pk):
  137. """
  138. Retrieve a single Report identified as "<module>.<report>".
  139. """
  140. # Retrieve the Report and ReportResult, if any.
  141. report = self._retrieve_report(pk)
  142. report.result = ReportResult.objects.filter(report=report.full_name).first()
  143. serializer = serializers.ReportDetailSerializer(report)
  144. return Response(serializer.data)
  145. @action(detail=True, methods=['post'])
  146. def run(self, request, pk):
  147. """
  148. Run a Report and create a new ReportResult, overwriting any previous result for the Report.
  149. """
  150. # Check that the user has permission to run reports.
  151. if not request.user.has_perm('extras.add_reportresult'):
  152. raise PermissionDenied("This user does not have permission to run reports.")
  153. # Retrieve and run the Report. This will create a new ReportResult.
  154. report = self._retrieve_report(pk)
  155. report.run()
  156. serializer = serializers.ReportDetailSerializer(report)
  157. return Response(serializer.data)
  158. #
  159. # Scripts
  160. #
  161. class ScriptViewSet(ViewSet):
  162. permission_classes = [IsAuthenticatedOrLoginNotRequired]
  163. _ignore_model_permissions = True
  164. exclude_from_schema = True
  165. lookup_value_regex = '[^/]+' # Allow dots
  166. def _get_script(self, pk):
  167. module_name, script_name = pk.split('.')
  168. script = get_script(module_name, script_name)
  169. if script is None:
  170. raise Http404
  171. return script
  172. def list(self, request):
  173. flat_list = []
  174. for script_list in get_scripts().values():
  175. flat_list.extend(script_list.values())
  176. serializer = serializers.ScriptSerializer(flat_list, many=True, context={'request': request})
  177. return Response(serializer.data)
  178. def retrieve(self, request, pk):
  179. script = self._get_script(pk)
  180. serializer = serializers.ScriptSerializer(script, context={'request': request})
  181. return Response(serializer.data)
  182. def post(self, request, pk):
  183. """
  184. Run a Script identified as "<module>.<script>".
  185. """
  186. script = self._get_script(pk)()
  187. input_serializer = serializers.ScriptInputSerializer(data=request.data)
  188. if input_serializer.is_valid():
  189. data = input_serializer.data['data']
  190. commit = input_serializer.data['commit']
  191. script.output, execution_time = run_script(script, data, request, commit)
  192. output_serializer = serializers.ScriptOutputSerializer(script)
  193. return Response(output_serializer.data)
  194. return Response(input_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
  195. #
  196. # Change logging
  197. #
  198. class ObjectChangeViewSet(ReadOnlyModelViewSet):
  199. """
  200. Retrieve a list of recent changes.
  201. """
  202. queryset = ObjectChange.objects.prefetch_related('user')
  203. serializer_class = serializers.ObjectChangeSerializer
  204. filterset_class = filters.ObjectChangeFilterSet