You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
7.0 KiB

10 years ago
5 years ago
10 years ago
10 years ago
5 years ago
6 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
5 years ago
9 years ago
5 years ago
5 years ago
6 years ago
5 years ago
6 years ago
9 years ago
6 years ago
9 years ago
9 years ago
9 years ago
5 years ago
  1. from django.contrib import admin
  2. from django.core.paginator import Paginator
  3. from django.db import connection
  4. from django.db.models import Count, F
  5. from django.urls import reverse
  6. from django.utils.html import escape
  7. from django.utils.safestring import mark_safe
  8. from hc.api.models import Channel, Check, Flip, Notification, Ping
  9. from hc.lib.date import format_duration
  10. @admin.register(Check)
  11. class ChecksAdmin(admin.ModelAdmin):
  12. class Media:
  13. css = {"all": ("css/admin/checks.css",)}
  14. search_fields = ["name", "code", "project__owner__email"]
  15. raw_id_fields = ("project",)
  16. list_display = (
  17. "id",
  18. "name_tags",
  19. "project_",
  20. "created",
  21. "n_pings",
  22. "timeout_schedule",
  23. "status",
  24. "last_start",
  25. "last_ping",
  26. )
  27. list_filter = ("status", "kind", "last_ping", "last_start")
  28. actions = ["send_alert"]
  29. def get_queryset(self, request):
  30. qs = super().get_queryset(request)
  31. qs = qs.annotate(email=F("project__owner__email"))
  32. qs = qs.annotate(project_name=F("project__name"))
  33. return qs
  34. @mark_safe
  35. def project_(self, obj):
  36. url = reverse("hc-checks", args=[obj.project.code])
  37. name = escape(obj.project_name or "Default")
  38. email = escape(obj.email)
  39. return f'{email} &rsaquo; <a href="{url}"">{name}</a>'
  40. @mark_safe
  41. def name_tags(self, obj):
  42. url = reverse("hc-details", args=[obj.code])
  43. name = escape(obj.name or "unnamed")
  44. s = f'<a href="{url}"">{name}</a>'
  45. for tag in obj.tags_list():
  46. s += " <span>%s</span>" % escape(tag)
  47. return s
  48. def timeout_schedule(self, obj):
  49. if obj.kind == "simple":
  50. return format_duration(obj.timeout)
  51. elif obj.kind == "cron":
  52. return obj.schedule
  53. else:
  54. return "Unknown"
  55. timeout_schedule.short_description = "Schedule"
  56. def send_alert(self, request, qs):
  57. for check in qs:
  58. for channel in check.channel_set.all():
  59. channel.notify(check)
  60. self.message_user(request, "%d alert(s) sent" % qs.count())
  61. send_alert.short_description = "Send Alert"
  62. class SchemeListFilter(admin.SimpleListFilter):
  63. title = "Scheme"
  64. parameter_name = "scheme"
  65. def lookups(self, request, model_admin):
  66. return (("http", "HTTP"), ("https", "HTTPS"), ("email", "Email"))
  67. def queryset(self, request, queryset):
  68. if self.value():
  69. queryset = queryset.filter(scheme=self.value())
  70. return queryset
  71. class MethodListFilter(admin.SimpleListFilter):
  72. title = "Method"
  73. parameter_name = "method"
  74. methods = ["HEAD", "GET", "POST", "PUT", "DELETE"]
  75. def lookups(self, request, model_admin):
  76. return zip(self.methods, self.methods)
  77. def queryset(self, request, queryset):
  78. if self.value():
  79. queryset = queryset.filter(method=self.value())
  80. return queryset
  81. class KindListFilter(admin.SimpleListFilter):
  82. title = "Kind"
  83. parameter_name = "kind"
  84. kinds = ["start", "fail"]
  85. def lookups(self, request, model_admin):
  86. return zip(self.kinds, self.kinds)
  87. def queryset(self, request, queryset):
  88. if self.value():
  89. queryset = queryset.filter(kind=self.value())
  90. return queryset
  91. # Adapted from: https://djangosnippets.org/snippets/2593/
  92. class LargeTablePaginator(Paginator):
  93. """ Overrides the count method to get an estimate instead of actual count
  94. when not filtered
  95. """
  96. def _get_estimate(self):
  97. try:
  98. cursor = connection.cursor()
  99. cursor.execute(
  100. "SELECT reltuples FROM pg_class WHERE relname = %s",
  101. [self.object_list.query.model._meta.db_table],
  102. )
  103. return int(cursor.fetchone()[0])
  104. except:
  105. return 0
  106. def _get_count(self):
  107. """
  108. Changed to use an estimate if the estimate is greater than 10,000
  109. Returns the total number of objects, across all pages.
  110. """
  111. if not hasattr(self, "_count") or self._count is None:
  112. try:
  113. estimate = 0
  114. if not self.object_list.query.where:
  115. estimate = self._get_estimate()
  116. if estimate < 10000:
  117. self._count = self.object_list.count()
  118. else:
  119. self._count = estimate
  120. except (AttributeError, TypeError):
  121. # AttributeError if object_list has no count() method.
  122. # TypeError if object_list.count() requires arguments
  123. # (i.e. is of type list).
  124. self._count = len(self.object_list)
  125. return self._count
  126. count = property(_get_count)
  127. @admin.register(Ping)
  128. class PingsAdmin(admin.ModelAdmin):
  129. search_fields = ("owner__name", "owner__code")
  130. readonly_fields = ("owner",)
  131. list_select_related = ("owner",)
  132. list_display = ("id", "created", "owner", "scheme", "method", "ua")
  133. list_filter = ("created", SchemeListFilter, MethodListFilter, KindListFilter)
  134. paginator = LargeTablePaginator
  135. show_full_result_count = False
  136. @admin.register(Channel)
  137. class ChannelsAdmin(admin.ModelAdmin):
  138. class Media:
  139. css = {"all": ("css/admin/channels.css",)}
  140. search_fields = ["value", "project__owner__email"]
  141. list_display = (
  142. "id",
  143. "kind_",
  144. "name",
  145. "project_",
  146. "value",
  147. "num_notifications",
  148. )
  149. list_filter = ("kind",)
  150. raw_id_fields = ("project", "checks")
  151. @mark_safe
  152. def project_(self, obj):
  153. url = reverse("hc-checks", args=[obj.project.code])
  154. name = escape(obj.project_name or "Default")
  155. email = escape(obj.email)
  156. return f"{email} &rsaquo; <a href='{url}'>{name}</a>"
  157. def get_queryset(self, request):
  158. qs = super().get_queryset(request)
  159. qs = qs.annotate(Count("notification", distinct=True))
  160. qs = qs.annotate(project_name=F("project__name"))
  161. qs = qs.annotate(email=F("project__owner__email"))
  162. return qs
  163. @mark_safe
  164. def kind_(self, obj):
  165. return f'<span class="icon-{ obj.kind }"></span> &nbsp; {obj.kind}'
  166. def num_notifications(self, obj):
  167. return obj.notification__count
  168. num_notifications.short_description = "# Notifications"
  169. @admin.register(Notification)
  170. class NotificationsAdmin(admin.ModelAdmin):
  171. search_fields = ["owner__name", "owner__code", "channel__value"]
  172. readonly_fields = ("owner",)
  173. list_select_related = ("owner", "channel")
  174. list_display = (
  175. "id",
  176. "created",
  177. "check_status",
  178. "owner",
  179. "channel_kind",
  180. "channel_value",
  181. "error",
  182. )
  183. list_filter = ("created", "check_status", "channel__kind")
  184. raw_id_fields = ("channel",)
  185. def channel_kind(self, obj):
  186. return obj.channel.kind
  187. def channel_value(self, obj):
  188. return obj.channel.value
  189. @admin.register(Flip)
  190. class FlipsAdmin(admin.ModelAdmin):
  191. list_display = ("id", "created", "processed", "owner", "old_status", "new_status")
  192. raw_id_fields = ("owner",)