From 35c0b1946fea23049590e59bfae3089d690cdccb Mon Sep 17 00:00:00 2001 From: George Lacey Date: Fri, 21 May 2021 20:19:46 +0100 Subject: [PATCH] Add monthly backup size graph --- borgweb/borg/models/repo.py | 24 +++++++-- borgweb/borg/static/borg/js/index.js | 3 ++ borgweb/borg/templates/borg/index.html | 3 ++ borgweb/borg/urls.py | 3 +- borgweb/borg/views/__init__.py | 2 +- borgweb/borg/views/json.py | 69 ++++++++++++++++++++++---- 6 files changed, 88 insertions(+), 16 deletions(-) diff --git a/borgweb/borg/models/repo.py b/borgweb/borg/models/repo.py index 90f1fcd..c91c749 100644 --- a/borgweb/borg/models/repo.py +++ b/borgweb/borg/models/repo.py @@ -96,11 +96,11 @@ class Repo(models.Model): def size_on_dates(self, units, dates: list): archives = self.archives_on_dates(dates) - return { - "id": self.id, - "label": self.label.label, - "size": list(self.series_csize(archives, units)) - } + return self.series_csize(archives, units) + + def size_on_months(self, units, months: int = 12): + archives = self.monthly_archives(months) + return self.series_csize(archives, units) @staticmethod def series_times(archives): @@ -114,6 +114,20 @@ class Repo(models.Model): def hourly_archive_string(self): return ''.join(['H' if archive is not None else '-' for archive in self.hourly_archives(8)]) + def monthly_archives(self, n_months: int = 12): + archives = [] + for month in range(n_months): + current_date = subtract_months(datetime.utcnow(), month) + archive_current_month = self.archive_set.all() \ + .filter(start__year=current_date.year, + start__month=current_date.month) \ + .order_by('-start') + if len(archive_current_month) > 0: + archives.append(archive_current_month[0]) + else: + archives.append(None) + return archives[::-1] + def archives_on_dates(self, dates: list): archives = [] archive_queryset = self.archive_set.all() diff --git a/borgweb/borg/static/borg/js/index.js b/borgweb/borg/static/borg/js/index.js index fb34a23..ebf6435 100644 --- a/borgweb/borg/static/borg/js/index.js +++ b/borgweb/borg/static/borg/js/index.js @@ -2,6 +2,9 @@ window.addEventListener("DOMContentLoaded", function () { $.getJSON( "repo_daily.json", function( json ) { draw_time_graph("daily_backup_size", json.repos, json.dates, json.units); }); + $.getJSON( "repo_monthly.json", function( json ) { + draw_time_graph("monthly_backup_size", json.repos, json.dates, json.units); + }); }, false); function draw_time_graph(chartID, repos, dateLabels, sizeUnits) { diff --git a/borgweb/borg/templates/borg/index.html b/borgweb/borg/templates/borg/index.html index d2150c2..f5412f8 100644 --- a/borgweb/borg/templates/borg/index.html +++ b/borgweb/borg/templates/borg/index.html @@ -57,6 +57,9 @@
+
+ +
{% else %}
diff --git a/borgweb/borg/urls.py b/borgweb/borg/urls.py index 485fafd..804092d 100644 --- a/borgweb/borg/urls.py +++ b/borgweb/borg/urls.py @@ -5,7 +5,8 @@ from . import views urlpatterns = [ path('', cache_page(60)(views.index), name='index'), - path('repo_daily.json', cache_page(3600)(views.repo_daily_json), name='repo json'), + path('repo_daily.json', cache_page(3600)(views.repo_daily_json), name='daily repo json'), + path('repo_monthly.json', cache_page(3600 * 12)(views.repo_monthly_json), name='monthly repo json'), path('repo/', views.repo, name='repo'), path('post/repo', views.post_repo, name='post repo'), path('post/archive', views.post_archive, name='post archive'), diff --git a/borgweb/borg/views/__init__.py b/borgweb/borg/views/__init__.py index 3bd35ca..944d908 100644 --- a/borgweb/borg/views/__init__.py +++ b/borgweb/borg/views/__init__.py @@ -1,3 +1,3 @@ from .views import index, repo, axes from .post import post_repo, post_archive, post_error, post_location -from .json import repo_daily_json +from .json import repo_daily_json, repo_monthly_json diff --git a/borgweb/borg/views/json.py b/borgweb/borg/views/json.py index 6944a08..b75153e 100644 --- a/borgweb/borg/views/json.py +++ b/borgweb/borg/views/json.py @@ -2,16 +2,65 @@ from datetime import datetime, timedelta from django.http import JsonResponse from ..models import Repo from ..utility import data +import calendar -def repo_daily_dict(repo_list, n_days=14): - dates = [(datetime.utcnow() - timedelta(days=day)) for day in range(n_days)][::-1] - date_labels = list([day.strftime("%d %b") for day in dates]) +def repo_monthly_json(request, months_ago: int = 12): + date_labels = monthly_date_labels(months_ago) - max_repo_size = max(repo.latest_archive().cache.unique_csize for repo in repo_list) - _, max_unit = data.convert_bytes(max_repo_size) + repo_list = Repo.objects.all() - repo_dicts = [repo.size_on_dates(max_unit, dates) for repo in repo_list] + max_unit = get_units(repo_list) + + repo_dicts = [{ + "id": repo.id, + "label": repo.label.label, + "size": repo.size_on_months(max_unit, months_ago) + } for repo in repo_list] + + response_dict = { + "dates": date_labels, + "repos": repo_dicts, + "units": max_unit + } + return JsonResponse(response_dict) + + +def monthly_date_labels(months_ago: int): + dates = [] + current_date = datetime.utcnow().date() + current_year = current_date.year + current_month = current_date.month + dates.append(current_date) + for month in range(months_ago - 1): + if current_month == 1: + current_year -= 1 + current_month = 12 + else: + current_month -= 1 + last_day = calendar.monthrange(current_year, current_month)[1] + current_date = current_date.replace(year=current_year, month=current_month, day=last_day) + dates.append(current_date) + + return [date.strftime("%b %Y") for date in dates][::-1] + + +def repo_daily_json(request, days_ago: int = 30): + repo_list = Repo.objects.all() + dates = [(datetime.utcnow() - timedelta(days=day)) for day in range(days_ago)][::-1] + return JsonResponse(repo_size_dict(repo_list, dates, "%d %b")) + + +def repo_size_dict(repo_list, dates: list, date_format: str): + date_labels = list([day.strftime(date_format) for day in dates]) + + max_unit = get_units(repo_list) + + repo_dicts = [{ + "id": repo.id, + "label": repo.label.label, + "size": repo.size_on_dates(max_unit, dates) + } for repo in repo_list] return { "dates": date_labels, @@ -20,6 +69,8 @@ def repo_daily_dict(repo_list, n_days=14): } -def repo_daily_json(request): - repo_list = Repo.objects.all() - return JsonResponse(repo_daily_dict(repo_list, 31)) +def get_units(repo_list): + max_repo_size = max(repo.latest_archive().cache.unique_csize for repo in repo_list) + _, max_unit = data.convert_bytes(max_repo_size) + return max_unit +