views.py 11 KB

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