From 3855180be1c65a63ae9d0411de57046b4fd368aa Mon Sep 17 00:00:00 2001 From: Rasul Kireev Date: Sun, 20 Oct 2024 09:46:23 +0300 Subject: [PATCH 1/3] updates --- core/views.py | 16 +- core/views_utils.py | 50 ++++-- frontend/templates/base.html | 5 +- .../templates/projects/project_status.html | 165 ++++++++++++++++++ 4 files changed, 219 insertions(+), 17 deletions(-) create mode 100644 frontend/templates/projects/project_status.html diff --git a/core/views.py b/core/views.py index 198b7b5..bfa03db 100644 --- a/core/views.py +++ b/core/views.py @@ -193,13 +193,27 @@ def form_valid(self, form): return response -class ProjectStatusPageView(DetailView): +class ProjectStatusPageView(StatusSummaryMixin, DetailView): model = Project template_name = 'projects/project_status.html' context_object_name = 'project' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) + services = self.object.services.all() + + ninety_days_ago = timezone.now() - timedelta(days=90) + + all_statuses = [] + for service in services: + service_statuses = service.statuses.filter(checked_at__gte=ninety_days_ago).order_by('checked_at') + service.status_summary = self.get_status_summary(service_statuses, timezone.now(), ninety_days_ago, 90) + service.current_status = service_statuses.last().status if service_statuses.exists() else 'unknown' + all_statuses.extend(service_statuses) + + context['project_overall_status'] = self.get_status_summary(all_statuses, timezone.now(), ninety_days_ago, 90) + context['services'] = services + return context diff --git a/core/views_utils.py b/core/views_utils.py index 72f966c..8930070 100644 --- a/core/views_utils.py +++ b/core/views_utils.py @@ -3,27 +3,51 @@ class StatusSummaryMixin: @staticmethod - def get_status_summary(statuses, now, start_time, number_of_sticks): + def get_status_summary(statuses, end_time, start_time, number_of_sticks): summary = [] + duration = end_time - start_time + interval = duration / number_of_sticks + for i in range(number_of_sticks): - minutes = 60*24 / number_of_sticks # Each stick represents X minutes - stick_time = start_time + timedelta(minutes=i*minutes) - status = statuses.filter(checked_at__lte=stick_time).last() - if status: - if status.is_up: - summary.append('up') - elif status.is_down: + stick_end = start_time + interval * (i + 1) + stick_start = start_time + interval * i + + stick_statuses = statuses.filter(checked_at__gte=stick_start, checked_at__lt=stick_end) + + if stick_statuses.exists(): + # Prioritize 'down' status, then 'degraded', then 'up' + if stick_statuses.filter(status='DOWN').exists(): summary.append('down') + elif stick_statuses.filter(status='DEGRADED').exists(): + summary.append('degraded') + elif stick_statuses.filter(status='UP').exists(): + summary.append('up') else: summary.append('unknown') else: summary.append('unknown') + return summary - def add_status_summary_to_services(self, services, number_of_sticks=85): - now = timezone.now() - twenty_four_hours_ago = now - timedelta(hours=24) + def add_status_summary_to_services(self, services, days=90, number_of_sticks=90): + end_time = timezone.now() + start_time = end_time - timedelta(days=days) for service in services: - statuses = service.statuses.filter(checked_at__gte=twenty_four_hours_ago).order_by('checked_at') - service.status_summary = self.get_status_summary(statuses, now, twenty_four_hours_ago, number_of_sticks) + statuses = service.statuses.filter(checked_at__gte=start_time).order_by('checked_at') + service.status_summary = self.get_status_summary(statuses, end_time, start_time, number_of_sticks) + + # Add current status + latest_status = statuses.last() + service.current_status = latest_status.status.lower() if latest_status else 'unknown' + + def get_overall_project_status(self, services, days=90, number_of_sticks=90): + end_time = timezone.now() + start_time = end_time - timedelta(days=days) + + all_statuses = [] + for service in services: + service_statuses = service.statuses.filter(checked_at__gte=start_time) + all_statuses.extend(service_statuses) + + return self.get_status_summary(all_statuses, end_time, start_time, number_of_sticks) diff --git a/frontend/templates/base.html b/frontend/templates/base.html index 0260ef6..371b184 100644 --- a/frontend/templates/base.html +++ b/frontend/templates/base.html @@ -44,7 +44,6 @@ - {% stylesheet_pack 'index' %} {% javascript_pack 'index' attrs='defer' %} @@ -87,8 +86,8 @@
- OSIG Logo - OSIG + StatusHen Logo + StatusHen Home diff --git a/frontend/templates/projects/project_status.html b/frontend/templates/projects/project_status.html new file mode 100644 index 0000000..4e569dc --- /dev/null +++ b/frontend/templates/projects/project_status.html @@ -0,0 +1,165 @@ +{% load webpack_loader static %} + + + + + + + + + + + + + + + StatusHen + + + + + + + + + + + + + + + + + + + + + + + + {% stylesheet_pack 'index' %} + {% javascript_pack 'index' attrs='defer' %} + + + + {% block default_messages %} + {% include 'components/messages.html' with messages=messages %} + {% endblock default_messages %} + +
+
+ +
+ +
+
+ +
+
+

{{ project.name }} - Overall Status

+ + Last 90 days + +
+
+ {% for status in project_overall_status %} +
+
+ {% endfor %} +
+
+ + +
+

System Status

+ {% for service in services %} +
+
+ + + {{ service.name }} +
+
+ {% for status in service.status_summary %} +
+
+ {% endfor %} +
+
+ {% endfor %} +
+
+
+ +
+
+
+

+ © 2024 LVTD, LLC. All rights reserved. +

+
+
+
+
+ + + From a534146621f906807164e277b896eb928777920e Mon Sep 17 00:00:00 2001 From: Rasul Kireev Date: Sun, 20 Oct 2024 09:47:39 +0300 Subject: [PATCH 2/3] updates --- core/views.py | 13 ++++--------- core/views_utils.py | 17 +++++++++-------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/core/views.py b/core/views.py index bfa03db..8130ce6 100644 --- a/core/views.py +++ b/core/views.py @@ -202,18 +202,13 @@ def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) services = self.object.services.all() - ninety_days_ago = timezone.now() - timedelta(days=90) + # Add status summary to services + self.add_status_summary_to_services(services) - all_statuses = [] - for service in services: - service_statuses = service.statuses.filter(checked_at__gte=ninety_days_ago).order_by('checked_at') - service.status_summary = self.get_status_summary(service_statuses, timezone.now(), ninety_days_ago, 90) - service.current_status = service_statuses.last().status if service_statuses.exists() else 'unknown' - all_statuses.extend(service_statuses) + # Get overall project status + context['project_overall_status'] = self.get_overall_project_status(services) - context['project_overall_status'] = self.get_status_summary(all_statuses, timezone.now(), ninety_days_ago, 90) context['services'] = services - return context diff --git a/core/views_utils.py b/core/views_utils.py index 8930070..f35e729 100644 --- a/core/views_utils.py +++ b/core/views_utils.py @@ -1,5 +1,6 @@ from django.utils import timezone from datetime import timedelta +from django.db.models import Q class StatusSummaryMixin: @staticmethod @@ -12,15 +13,15 @@ def get_status_summary(statuses, end_time, start_time, number_of_sticks): stick_end = start_time + interval * (i + 1) stick_start = start_time + interval * i - stick_statuses = statuses.filter(checked_at__gte=stick_start, checked_at__lt=stick_end) + stick_statuses = [s for s in statuses if stick_start <= s.checked_at < stick_end] - if stick_statuses.exists(): + if stick_statuses: # Prioritize 'down' status, then 'degraded', then 'up' - if stick_statuses.filter(status='DOWN').exists(): + if any(s.status == 'DOWN' for s in stick_statuses): summary.append('down') - elif stick_statuses.filter(status='DEGRADED').exists(): + elif any(s.status == 'DEGRADED' for s in stick_statuses): summary.append('degraded') - elif stick_statuses.filter(status='UP').exists(): + elif any(s.status == 'UP' for s in stick_statuses): summary.append('up') else: summary.append('unknown') @@ -34,11 +35,11 @@ def add_status_summary_to_services(self, services, days=90, number_of_sticks=90) start_time = end_time - timedelta(days=days) for service in services: - statuses = service.statuses.filter(checked_at__gte=start_time).order_by('checked_at') + statuses = list(service.statuses.filter(checked_at__gte=start_time).order_by('checked_at')) service.status_summary = self.get_status_summary(statuses, end_time, start_time, number_of_sticks) # Add current status - latest_status = statuses.last() + latest_status = statuses[-1] if statuses else None service.current_status = latest_status.status.lower() if latest_status else 'unknown' def get_overall_project_status(self, services, days=90, number_of_sticks=90): @@ -47,7 +48,7 @@ def get_overall_project_status(self, services, days=90, number_of_sticks=90): all_statuses = [] for service in services: - service_statuses = service.statuses.filter(checked_at__gte=start_time) + service_statuses = list(service.statuses.filter(checked_at__gte=start_time)) all_statuses.extend(service_statuses) return self.get_status_summary(all_statuses, end_time, start_time, number_of_sticks) From 7ab64e89cdc2e094350330627eae8d536b746992 Mon Sep 17 00:00:00 2001 From: Rasul Kireev Date: Sun, 20 Oct 2024 10:41:12 +0300 Subject: [PATCH 3/3] updates --- .pre-commit-config.yaml | 4 +- core/views.py | 54 ++++---- core/views_utils.py | 27 ++-- .../templates/projects/project_status.html | 125 ++++++++++++------ 4 files changed, 126 insertions(+), 84 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4fd1bf9..d9e0338 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,8 +24,8 @@ repos: - id: isort name: isort (python) -- repo: https://github.com/Riverside-Healthcare/djLint - rev: v1.19.16 +- repo: https://github.com/djlint/djLint + rev: v1.35.2 hooks: - id: djlint-django diff --git a/core/views.py b/core/views.py index 8130ce6..46dac7f 100644 --- a/core/views.py +++ b/core/views.py @@ -1,34 +1,29 @@ -from datetime import timedelta from urllib.parse import urlencode -from core.views_utils import StatusSummaryMixin import stripe - from allauth.account.models import EmailAddress from allauth.account.utils import send_email_confirmation +from django.conf import settings +from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.messages.views import SuccessMessageMixin -from django.shortcuts import redirect -from django.conf import settings -from django.contrib import messages +from django.shortcuts import get_object_or_404, redirect from django.urls import reverse, reverse_lazy -from django.utils import timezone -from django.views.generic import TemplateView, UpdateView, ListView, DetailView, CreateView, FormView -from django.shortcuts import get_object_or_404 - +from django.views.generic import CreateView, DetailView, FormView, ListView, TemplateView, UpdateView from djstripe import models as djstripe_models from core.forms import ProfileUpdateForm, ServiceForm -from core.models import Profile, BlogPost, Project +from core.models import BlogPost, Profile, Project from core.utils import check_if_profile_has_pro_subscription - +from core.views_utils import StatusSummaryMixin from statushen.utils import get_statushen_logger stripe.api_key = settings.STRIPE_SECRET_KEY logger = get_statushen_logger(__name__) + class HomeView(StatusSummaryMixin, TemplateView): template_name = "pages/home.html" @@ -38,7 +33,7 @@ def get_context_data(self, **kwargs): if user.is_authenticated: profile = self.request.user.profile - user_projects = Project.objects.filter(profile=profile).prefetch_related('services', 'services__statuses') + user_projects = Project.objects.filter(profile=profile).prefetch_related("services", "services__statuses") for project in user_projects: self.add_status_summary_to_services(project.services.all(), number_of_sticks=100) @@ -52,7 +47,6 @@ def get_context_data(self, **kwargs): elif payment_status == "failed": messages.error(self.request, "Something went wrong with the payment.") - return context @@ -77,7 +71,7 @@ def get_context_data(self, **kwargs): context["resend_confirmation_url"] = reverse("resend_confirmation") context["has_subscription"] = profile.subscription is not None - user_projects = profile.projects.all().prefetch_related('services', 'services__statuses') + user_projects = profile.projects.all().prefetch_related("services", "services__statuses") for project in user_projects: self.add_status_summary_to_services(project.services.all()) @@ -182,9 +176,9 @@ class BlogPostView(DetailView): class CreateProjectView(LoginRequiredMixin, CreateView): model = Project - template_name = 'projects/create_project.html' - fields = ['name', 'slug', 'icon', 'public'] - success_url = reverse_lazy('home') + template_name = "projects/create_project.html" + fields = ["name", "slug", "icon", "public"] + success_url = reverse_lazy("home") def form_valid(self, form): form.instance.profile = self.request.user.profile @@ -195,38 +189,38 @@ def form_valid(self, form): class ProjectStatusPageView(StatusSummaryMixin, DetailView): model = Project - template_name = 'projects/project_status.html' - context_object_name = 'project' + template_name = "projects/project_status.html" + context_object_name = "project" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) services = self.object.services.all() - # Add status summary to services - self.add_status_summary_to_services(services) + # Add status summary to services (24 hours, 24 sticks) + self.add_status_summary_to_services(services, days=1, number_of_sticks=45) - # Get overall project status - context['project_overall_status'] = self.get_overall_project_status(services) + # Get overall project status (90 days, 90 sticks) + context["project_overall_status"] = self.get_overall_project_status(services, days=90, number_of_sticks=90) - context['services'] = services + context["services"] = services return context class ProjectSettingsView(StatusSummaryMixin, LoginRequiredMixin, FormView): - template_name = 'projects/project_settings.html' + template_name = "projects/project_settings.html" form_class = ServiceForm def dispatch(self, request, *args, **kwargs): - self.project = get_object_or_404(Project, slug=self.kwargs['slug']) + self.project = get_object_or_404(Project, slug=self.kwargs["slug"]) return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['project'] = self.project + context["project"] = self.project services = self.project.services.all() self.add_status_summary_to_services(services) - context['services'] = services + context["services"] = services return context def form_valid(self, form): @@ -237,4 +231,4 @@ def form_valid(self, form): return super().form_valid(form) def get_success_url(self): - return reverse('project-settings', kwargs={'slug': self.project.slug}) + return reverse("project-settings", kwargs={"slug": self.project.slug}) diff --git a/core/views_utils.py b/core/views_utils.py index f35e729..63a2075 100644 --- a/core/views_utils.py +++ b/core/views_utils.py @@ -1,6 +1,7 @@ -from django.utils import timezone from datetime import timedelta -from django.db.models import Q + +from django.utils import timezone + class StatusSummaryMixin: @staticmethod @@ -17,30 +18,30 @@ def get_status_summary(statuses, end_time, start_time, number_of_sticks): if stick_statuses: # Prioritize 'down' status, then 'degraded', then 'up' - if any(s.status == 'DOWN' for s in stick_statuses): - summary.append('down') - elif any(s.status == 'DEGRADED' for s in stick_statuses): - summary.append('degraded') - elif any(s.status == 'UP' for s in stick_statuses): - summary.append('up') + if any(s.status == "DOWN" for s in stick_statuses): + summary.append("down") + elif any(s.status == "DEGRADED" for s in stick_statuses): + summary.append("degraded") + elif any(s.status == "UP" for s in stick_statuses): + summary.append("up") else: - summary.append('unknown') + summary.append("unknown") else: - summary.append('unknown') + summary.append("unknown") return summary - def add_status_summary_to_services(self, services, days=90, number_of_sticks=90): + def add_status_summary_to_services(self, services, days=1, number_of_sticks=24): end_time = timezone.now() start_time = end_time - timedelta(days=days) for service in services: - statuses = list(service.statuses.filter(checked_at__gte=start_time).order_by('checked_at')) + statuses = list(service.statuses.filter(checked_at__gte=start_time).order_by("checked_at")) service.status_summary = self.get_status_summary(statuses, end_time, start_time, number_of_sticks) # Add current status latest_status = statuses[-1] if statuses else None - service.current_status = latest_status.status.lower() if latest_status else 'unknown' + service.current_status = latest_status.status.lower() if latest_status else "unknown" def get_overall_project_status(self, services, days=90, number_of_sticks=90): end_time = timezone.now() diff --git a/frontend/templates/projects/project_status.html b/frontend/templates/projects/project_status.html index 4e569dc..4c18f1c 100644 --- a/frontend/templates/projects/project_status.html +++ b/frontend/templates/projects/project_status.html @@ -47,11 +47,11 @@ {% block default_messages %} - {% include 'components/messages.html' with messages=messages %} + {% include "components/messages.html" with messages=messages %} {% endblock default_messages %} -