views.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. from django import template
  2. from django.conf import settings
  3. from django.contrib import messages
  4. from django.contrib.auth.mixins import PermissionRequiredMixin
  5. from django.contrib.contenttypes.models import ContentType
  6. from django.db.models import Count, Q
  7. from django.http import Http404
  8. from django.shortcuts import get_object_or_404, redirect, render
  9. from django.utils.safestring import mark_safe
  10. from django.views.generic import View
  11. from django_tables2 import RequestConfig
  12. from taggit.models import Tag, TaggedItem
  13. from utilities.forms import ConfirmationForm
  14. from utilities.paginator import EnhancedPaginator
  15. from utilities.views import BulkDeleteView, BulkEditView, ObjectDeleteView, ObjectEditView, ObjectListView
  16. from . import filters
  17. from .forms import (
  18. ConfigContextForm, ConfigContextBulkEditForm, ConfigContextFilterForm, ImageAttachmentForm, ObjectChangeFilterForm,
  19. TagFilterForm, TagForm,
  20. )
  21. from .models import ConfigContext, ImageAttachment, ObjectChange, ReportResult
  22. from .reports import get_report, get_reports
  23. from .tables import ConfigContextTable, ObjectChangeTable, TagTable, TaggedItemTable
  24. #
  25. # Tags
  26. #
  27. class TagListView(ObjectListView):
  28. queryset = Tag.objects.annotate(
  29. items=Count('taggit_taggeditem_items')
  30. ).order_by(
  31. 'name'
  32. )
  33. filter = filters.TagFilter
  34. filter_form = TagFilterForm
  35. table = TagTable
  36. template_name = 'extras/tag_list.html'
  37. class TagView(View):
  38. def get(self, request, slug):
  39. tag = get_object_or_404(Tag, slug=slug)
  40. tagged_items = TaggedItem.objects.filter(
  41. tag=tag
  42. ).select_related(
  43. 'content_type'
  44. ).prefetch_related(
  45. 'content_object'
  46. )
  47. # Generate a table of all items tagged with this Tag
  48. items_table = TaggedItemTable(tagged_items)
  49. paginate = {
  50. 'paginator_class': EnhancedPaginator,
  51. 'per_page': request.GET.get('per_page', settings.PAGINATE_COUNT)
  52. }
  53. RequestConfig(request, paginate).configure(items_table)
  54. return render(request, 'extras/tag.html', {
  55. 'tag': tag,
  56. 'items_count': tagged_items.count(),
  57. 'items_table': items_table,
  58. })
  59. class TagEditView(PermissionRequiredMixin, ObjectEditView):
  60. permission_required = 'taggit.change_tag'
  61. model = Tag
  62. model_form = TagForm
  63. default_return_url = 'extras:tag_list'
  64. class TagDeleteView(PermissionRequiredMixin, ObjectDeleteView):
  65. permission_required = 'taggit.delete_tag'
  66. model = Tag
  67. default_return_url = 'extras:tag_list'
  68. class TagBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
  69. permission_required = 'circuits.delete_circuittype'
  70. queryset = Tag.objects.annotate(
  71. items=Count('taggit_taggeditem_items')
  72. ).order_by(
  73. 'name'
  74. )
  75. table = TagTable
  76. default_return_url = 'extras:tag_list'
  77. #
  78. # Config contexts
  79. #
  80. class ConfigContextListView(ObjectListView):
  81. queryset = ConfigContext.objects.all()
  82. filter = filters.ConfigContextFilter
  83. filter_form = ConfigContextFilterForm
  84. table = ConfigContextTable
  85. template_name = 'extras/configcontext_list.html'
  86. class ConfigContextView(View):
  87. def get(self, request, pk):
  88. configcontext = get_object_or_404(ConfigContext, pk=pk)
  89. return render(request, 'extras/configcontext.html', {
  90. 'configcontext': configcontext,
  91. })
  92. class ConfigContextCreateView(PermissionRequiredMixin, ObjectEditView):
  93. permission_required = 'extras.add_configcontext'
  94. model = ConfigContext
  95. model_form = ConfigContextForm
  96. default_return_url = 'extras:configcontext_list'
  97. template_name = 'extras/configcontext_edit.html'
  98. class ConfigContextEditView(ConfigContextCreateView):
  99. permission_required = 'extras.change_configcontext'
  100. class ConfigContextBulkEditView(PermissionRequiredMixin, BulkEditView):
  101. permission_required = 'extras.change_configcontext'
  102. queryset = ConfigContext.objects.all()
  103. filter = filters.ConfigContextFilter
  104. table = ConfigContextTable
  105. form = ConfigContextBulkEditForm
  106. default_return_url = 'extras:configcontext_list'
  107. class ConfigContextDeleteView(PermissionRequiredMixin, ObjectDeleteView):
  108. permission_required = 'extras.delete_configcontext'
  109. model = ConfigContext
  110. default_return_url = 'extras:configcontext_list'
  111. class ConfigContextBulkDeleteView(PermissionRequiredMixin, BulkDeleteView):
  112. permission_required = 'extras.delete_cconfigcontext'
  113. queryset = ConfigContext.objects.all()
  114. table = ConfigContextTable
  115. default_return_url = 'extras:configcontext_list'
  116. class ObjectConfigContextView(View):
  117. object_class = None
  118. base_template = None
  119. def get(self, request, pk):
  120. obj = get_object_or_404(self.object_class, pk=pk)
  121. source_contexts = ConfigContext.objects.get_for_object(obj)
  122. model_name = self.object_class._meta.model_name
  123. return render(request, 'extras/object_configcontext.html', {
  124. model_name: obj,
  125. 'obj': obj,
  126. 'rendered_context': obj.get_config_context(),
  127. 'source_contexts': source_contexts,
  128. 'base_template': self.base_template,
  129. 'active_tab': 'config-context',
  130. })
  131. #
  132. # Change logging
  133. #
  134. class ObjectChangeListView(ObjectListView):
  135. queryset = ObjectChange.objects.select_related('user', 'changed_object_type')
  136. filter = filters.ObjectChangeFilter
  137. filter_form = ObjectChangeFilterForm
  138. table = ObjectChangeTable
  139. template_name = 'extras/objectchange_list.html'
  140. class ObjectChangeView(View):
  141. def get(self, request, pk):
  142. objectchange = get_object_or_404(ObjectChange, pk=pk)
  143. related_changes = ObjectChange.objects.filter(request_id=objectchange.request_id).exclude(pk=objectchange.pk)
  144. related_changes_table = ObjectChangeTable(
  145. data=related_changes[:50],
  146. orderable=False
  147. )
  148. return render(request, 'extras/objectchange.html', {
  149. 'objectchange': objectchange,
  150. 'related_changes_table': related_changes_table,
  151. 'related_changes_count': related_changes.count()
  152. })
  153. class ObjectChangeLogView(View):
  154. """
  155. Present a history of changes made to a particular object.
  156. """
  157. def get(self, request, model, **kwargs):
  158. # Get object my model and kwargs (e.g. slug='foo')
  159. obj = get_object_or_404(model, **kwargs)
  160. # Gather all changes for this object (and its related objects)
  161. content_type = ContentType.objects.get_for_model(model)
  162. objectchanges = ObjectChange.objects.select_related(
  163. 'user', 'changed_object_type'
  164. ).filter(
  165. Q(changed_object_type=content_type, changed_object_id=obj.pk) |
  166. Q(related_object_type=content_type, related_object_id=obj.pk)
  167. )
  168. objectchanges_table = ObjectChangeTable(
  169. data=objectchanges,
  170. orderable=False
  171. )
  172. # Check whether a header template exists for this model
  173. base_template = '{}/{}.html'.format(model._meta.app_label, model._meta.model_name)
  174. try:
  175. template.loader.get_template(base_template)
  176. object_var = model._meta.model_name
  177. except template.TemplateDoesNotExist:
  178. base_template = '_base.html'
  179. object_var = 'obj'
  180. return render(request, 'extras/object_changelog.html', {
  181. object_var: obj,
  182. 'objectchanges_table': objectchanges_table,
  183. 'base_template': base_template,
  184. 'active_tab': 'changelog',
  185. })
  186. #
  187. # Image attachments
  188. #
  189. class ImageAttachmentEditView(PermissionRequiredMixin, ObjectEditView):
  190. permission_required = 'extras.change_imageattachment'
  191. model = ImageAttachment
  192. model_form = ImageAttachmentForm
  193. def alter_obj(self, imageattachment, request, args, kwargs):
  194. if not imageattachment.pk:
  195. # Assign the parent object based on URL kwargs
  196. model = kwargs.get('model')
  197. imageattachment.parent = get_object_or_404(model, pk=kwargs['object_id'])
  198. return imageattachment
  199. def get_return_url(self, request, imageattachment):
  200. return imageattachment.parent.get_absolute_url()
  201. class ImageAttachmentDeleteView(PermissionRequiredMixin, ObjectDeleteView):
  202. permission_required = 'extras.delete_imageattachment'
  203. model = ImageAttachment
  204. def get_return_url(self, request, imageattachment):
  205. return imageattachment.parent.get_absolute_url()
  206. #
  207. # Reports
  208. #
  209. class ReportListView(View):
  210. """
  211. Retrieve all of the available reports from disk and the recorded ReportResult (if any) for each.
  212. """
  213. def get(self, request):
  214. reports = get_reports()
  215. results = {r.report: r for r in ReportResult.objects.all()}
  216. ret = []
  217. for module, report_list in reports:
  218. module_reports = []
  219. for report in report_list:
  220. report.result = results.get(report.full_name, None)
  221. module_reports.append(report)
  222. ret.append((module, module_reports))
  223. return render(request, 'extras/report_list.html', {
  224. 'reports': ret,
  225. })
  226. class ReportView(View):
  227. """
  228. Display a single Report and its associated ReportResult (if any).
  229. """
  230. def get(self, request, name):
  231. # Retrieve the Report by "<module>.<report>"
  232. module_name, report_name = name.split('.')
  233. report = get_report(module_name, report_name)
  234. if report is None:
  235. raise Http404
  236. # Attach the ReportResult (if any)
  237. report.result = ReportResult.objects.filter(report=report.full_name).first()
  238. return render(request, 'extras/report.html', {
  239. 'report': report,
  240. 'run_form': ConfirmationForm(),
  241. })
  242. class ReportRunView(PermissionRequiredMixin, View):
  243. """
  244. Run a Report and record a new ReportResult.
  245. """
  246. permission_required = 'extras.add_reportresult'
  247. def post(self, request, name):
  248. # Retrieve the Report by "<module>.<report>"
  249. module_name, report_name = name.split('.')
  250. report = get_report(module_name, report_name)
  251. if report is None:
  252. raise Http404
  253. form = ConfirmationForm(request.POST)
  254. if form.is_valid():
  255. # Run the Report. A new ReportResult is created.
  256. report.run()
  257. result = 'failed' if report.failed else 'passed'
  258. msg = "Ran report {} ({})".format(report.full_name, result)
  259. messages.success(request, mark_safe(msg))
  260. return redirect('extras:report', name=report.full_name)