From 6fc17cadaa6536802b973ef360e0258e88eaa15b Mon Sep 17 00:00:00 2001 From: Mirio <1211529+Mirio@users.noreply.github.com> Date: Sat, 28 Oct 2023 23:14:38 +0200 Subject: [PATCH] Adding settings page + Bump deps --- config/settings/base.py | 1 + config/urls.py | 2 ++ core/migrations/0004_settings.py | 27 +++++++++++++++++++++ core/models.py | 8 +++++++ core/tasks.py | 25 ++++++++++++++++++++ core/templates/core/index.html | 6 ++--- core/templates/core/settings.html | 37 +++++++++++++++++++++++++++++ core/tests.py | 39 ++++++++++++++++++++++++++++++- core/views.py | 15 +++++++++++- fixtures/main.json | 2 +- requirements.txt | 10 ++++---- 11 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 core/migrations/0004_settings.py create mode 100644 core/tasks.py create mode 100644 core/templates/core/settings.html diff --git a/config/settings/base.py b/config/settings/base.py index b7086a6..e7e863c 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -149,6 +149,7 @@ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", + "allauth.account.middleware.AccountMiddleware", ] # STATIC diff --git a/config/urls.py b/config/urls.py index eab749f..2e78e65 100644 --- a/config/urls.py +++ b/config/urls.py @@ -14,6 +14,7 @@ Core_EpisodeView, Core_HomepageView, Core_PlayerView, + Core_Settings, ) from spreaker.views import Spreaker_AddPodcastView, Spreaker_DeletePodcastView from youtube.views import ( @@ -28,6 +29,7 @@ path("", cache_page(settings.CACHE_DEFAULT_TTL)(Core_HomepageView.as_view()), name="homepage"), path("player/", cache_page(settings.CACHE_DEFAULT_TTL)(Core_PlayerView.as_view()), name="player"), path("episode/", cache_page(settings.CACHE_SMART_TTL)(Core_EpisodeView.as_view()), name="episode"), + path("settings/", Core_Settings.as_view(), name="settings"), path( "add-datasource/", cache_page(settings.CACHE_DEFAULT_TTL)(Core_AddDataSourceView.as_view()), diff --git a/core/migrations/0004_settings.py b/core/migrations/0004_settings.py new file mode 100644 index 0000000..90f557e --- /dev/null +++ b/core/migrations/0004_settings.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.6 on 2023-10-23 21:02 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + dependencies = [ + ("core", "0003_provider_shortname"), + ] + + operations = [ + migrations.CreateModel( + name="Settings", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("created_at", models.DateTimeField(db_index=True, default=django.utils.timezone.now)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("enabled", models.BooleanField(default=True)), + ("name", models.CharField(help_text="Settings Name")), + ("value", models.CharField(help_text="Value of the setting")), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/core/models.py b/core/models.py index 14a8870..a70bda7 100644 --- a/core/models.py +++ b/core/models.py @@ -49,3 +49,11 @@ class Playlist(BaseModel): def __str__(self): return f"{self.episode.name}" + + +class Settings(BaseModel): + name = models.CharField(help_text="Settings Name") + value = models.CharField(help_text="Value of the setting") + + def __str__(self): + return f"{self.name}" diff --git a/core/tasks.py b/core/tasks.py new file mode 100644 index 0000000..eabb1e5 --- /dev/null +++ b/core/tasks.py @@ -0,0 +1,25 @@ +import os +from os.path import getsize, join + +from celery import Celery +from django.conf import settings + +from core.models import Settings +from core.shared import CommonResponse + +app = Celery("tasks") + + +@app.task +def calcolate_persistinfo() -> CommonResponse: + out = CommonResponse() + total_bytessize = 0 + total_counter = 0 + for root, _, files in os.walk(settings.PERSIST_AUDIO_ROOTDIR): + for fname in files: + total_bytessize += getsize(join(root, fname)) + total_counter += 1 + Settings.objects.get_or_create(name="persist_total_size", value=total_bytessize) + Settings.objects.get_or_create(name="persist_total_count", value=total_counter) + out.status = "success" + return out.__dict__ diff --git a/core/templates/core/index.html b/core/templates/core/index.html index fd937d2..6b6f320 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -8,7 +8,7 @@

Dashboard

-
Channels Followed
+
Datasource Followed

{{ stat.total_followed }} @@ -84,9 +84,9 @@

Latest Episode
Playlist
diff --git a/core/templates/core/settings.html b/core/templates/core/settings.html new file mode 100644 index 0000000..abc1a1f --- /dev/null +++ b/core/templates/core/settings.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} + +{% block title %} + VerbaCap - Settings +{% endblock title %} +{% block content %} +

Settings

+

The page contains all the settings for the tool

+
+
+
+
Persist Audio Counter
+
+
+

Current number of files: {{ total_count.value }}

+
+ Last Update: {{ total_count.updated_at | date:"SHORT_DATETIME_FORMAT" }} +
+
+
+
+
+
+
+
Persist Audio File Size
+
+
+

Total Size: {{ total_size.value | filesizeformat }}

+ +
+
+
+
+
+{% endblock content %} diff --git a/core/tests.py b/core/tests.py index a10f0a2..8dc8ee8 100644 --- a/core/tests.py +++ b/core/tests.py @@ -7,7 +7,8 @@ from django.utils import timezone from rest_framework.test import APIClient -from core.models import DataSource, Episode, Playlist, Provider +from core.models import DataSource, Episode, Playlist, Provider, Settings +from core.tasks import calcolate_persistinfo # Create your tests here. @@ -146,6 +147,34 @@ def test_player(self): client.logout() +class Tasks_TestCase(TestCase): + def setUp(self): + Provider.objects.create(name="Youtube", icon="aaaa", color="#fff") + provider = Provider.objects.get(name="Youtube") + DataSource.objects.create( + name="Youtube Official Channel", + provider=provider, + target="https://www.youtube.com/feeds/videos.xml?channel_id=UCBR8-60-B28hp2BmDPdntcQ", + ) + datasource = DataSource.objects.get(name="Youtube Official Channel") + Episode.objects.create( + name="Introducing the shorter side of YouTube", + datasource=datasource, + episode_date=timezone.now(), + target="https://www.youtube.com/watch?v=__NeP0RqACU", + ) + episode = Episode.objects.get(name="Introducing the shorter side of YouTube") + Playlist.objects.create(episode=episode, order_num=1) + + def test_calcolate_persistinfo(self): + persist_info = calcolate_persistinfo() + self.assertEqual(persist_info, {"status": "success", "message": None, "value": None}) + filesize_obj = Settings.objects.filter(name="persist_total_size").first() + self.assertTrue(isinstance(int(filesize_obj.value), int)) + filecount_obj = Settings.objects.filter(name="persist_total_count").first() + self.assertTrue(isinstance(int(filecount_obj.value), int)) + + class Views_TestCase(TestCase): def setUp(self): Provider.objects.create(name="Youtube", icon="aaaa", color="#fff") @@ -191,3 +220,11 @@ def test_episodeview(self): client.login(username="testuser", password="1234") response_logged = client.get("/episode/") self.assertEqual(response_logged.status_code, 200) + + def test_settingsview(self): + client = Client() + response = client.get("/settings/") + self.assertEqual(response.status_code, 302) + client.login(username="testuser", password="1234") + response_logged = client.get("/settings/") + self.assertEqual(response_logged.status_code, 200) diff --git a/core/views.py b/core/views.py index 07a0b00..6a406dd 100644 --- a/core/views.py +++ b/core/views.py @@ -2,7 +2,7 @@ from django.shortcuts import render from django.views import View -from core.models import DataSource, Episode, Playlist +from core.models import DataSource, Episode, Playlist, Settings # Create your views here. @@ -44,6 +44,19 @@ def get(self, request): return render(request, "core/delete_datasource.html") +class Core_Settings(LoginRequiredMixin, View): + def get(self, request): + try: + total_size = Settings.objects.get(name="persist_total_size") + except Settings.DoesNotExist: + total_size = 0 + try: + total_count = Settings.objects.get(name="persist_total_count") + except Settings.DoesNotExist: + total_count = 0 + return render(request, "core/settings.html", context={"total_size": total_size, "total_count": total_count}) + + class Core_EpisodeView(LoginRequiredMixin, View): def get(self, request): episode_extended = [] diff --git a/fixtures/main.json b/fixtures/main.json index f1a43a6..9762234 100644 --- a/fixtures/main.json +++ b/fixtures/main.json @@ -1 +1 @@ -[{"model":"core.provider","pk":1,"fields":{"created_at":"2023-08-08T20:45:50Z","updated_at":"2023-08-19T14:41:52.986Z","enabled":true,"name":"Youtube","icon":"fa-brands fa-youtube","color":"#FF0000","shortname":"yt"}},{"model":"core.provider","pk":2,"fields":{"created_at":"2023-08-08T20:45:50Z","updated_at":"2023-08-19T14:41:52.986Z","enabled":true,"name":"Youtube-Playlist","icon":"fa-brands fa-youtube","color":"#FF0000","shortname":"yt-playlist"}},{"model":"django_celery_beat.crontabschedule","pk":1,"fields":{"minute":"50","hour":"23","day_of_week":"*","day_of_month":"*","month_of_year":"*","timezone":"UTC"}},{"model":"django_celery_beat.periodictask","pk":2,"fields":{"name":"import_episodes_yt_channels","task":"youtube.tasks.import_episodes_yt_channels","interval":null,"crontab":1,"solar":null,"clocked":null,"args":"[]","kwargs":"{}","queue":null,"exchange":null,"routing_key":null,"headers":"{}","priority":null,"expires":null,"expire_seconds":null,"one_off":false,"start_time":null,"enabled":true,"last_run_at":"2023-09-05T20:45:00.042Z","total_run_count":1,"date_changed":"2023-09-05T20:48:00.995Z","description":""}},{"model":"django_celery_beat.periodictask","pk":3,"fields":{"name":"import_episodes_yt_playlist","task":"youtube.tasks.import_episodes_yt_playlist","interval":null,"crontab":1,"solar":null,"clocked":null,"args":"[]","kwargs":"{}","queue":null,"exchange":null,"routing_key":null,"headers":"{}","priority":null,"expires":null,"expire_seconds":null,"one_off":false,"start_time":null,"enabled":true,"last_run_at":"2023-09-05T20:45:00.042Z","total_run_count":1,"date_changed":"2023-09-05T20:48:00.995Z","description":""}},{"model":"django_celery_beat.periodictask","pk":4,"fields":{"name":"import_episodes_sk_playlist","task":"spreaker.tasks.import_episodes_sk","interval":null,"crontab":1,"solar":null,"clocked":null,"args":"[]","kwargs":"{}","queue":null,"exchange":null,"routing_key":null,"headers":"{}","priority":null,"expires":null,"expire_seconds":null,"one_off":false,"start_time":null,"enabled":true,"last_run_at":"2023-09-05T20:45:00.042Z","total_run_count":1,"date_changed":"2023-09-05T20:48:00.995Z","description":""}},{"model":"core.provider","pk":3,"fields":{"created_at":"2023-10-04T20:43:37Z","updated_at":"2023-10-04T20:45:12.736Z","enabled":true,"name":"Spreaker","icon":"fa-regular fa-star","color":"#ffc107","shortname":"sk"}}] +[{"model":"core.provider","pk":1,"fields":{"created_at":"2023-08-08T20:45:50Z","updated_at":"2023-08-19T14:41:52.986Z","enabled":true,"name":"Youtube","icon":"fa-brands fa-youtube","color":"#FF0000","shortname":"yt"}},{"model":"core.provider","pk":2,"fields":{"created_at":"2023-08-08T20:45:50Z","updated_at":"2023-08-19T14:41:52.986Z","enabled":true,"name":"Youtube-Playlist","icon":"fa-brands fa-youtube","color":"#FF0000","shortname":"yt-playlist"}},{"model":"django_celery_beat.crontabschedule","pk":1,"fields":{"minute":"50","hour":"23","day_of_week":"*","day_of_month":"*","month_of_year":"*","timezone":"UTC"}},{"model":"django_celery_beat.periodictask","pk":2,"fields":{"name":"import_episodes_yt_channels","task":"youtube.tasks.import_episodes_yt_channels","interval":null,"crontab":1,"solar":null,"clocked":null,"args":"[]","kwargs":"{}","queue":null,"exchange":null,"routing_key":null,"headers":"{}","priority":null,"expires":null,"expire_seconds":null,"one_off":false,"start_time":null,"enabled":true,"last_run_at":"2023-09-05T20:45:00.042Z","total_run_count":1,"date_changed":"2023-09-05T20:48:00.995Z","description":""}},{"model":"django_celery_beat.periodictask","pk":3,"fields":{"name":"import_episodes_yt_playlist","task":"youtube.tasks.import_episodes_yt_playlist","interval":null,"crontab":1,"solar":null,"clocked":null,"args":"[]","kwargs":"{}","queue":null,"exchange":null,"routing_key":null,"headers":"{}","priority":null,"expires":null,"expire_seconds":null,"one_off":false,"start_time":null,"enabled":true,"last_run_at":"2023-09-05T20:45:00.042Z","total_run_count":1,"date_changed":"2023-09-05T20:48:00.995Z","description":""}},{"model":"django_celery_beat.periodictask","pk":4,"fields":{"name":"import_episodes_sk_playlist","task":"spreaker.tasks.import_episodes_sk","interval":null,"crontab":1,"solar":null,"clocked":null,"args":"[]","kwargs":"{}","queue":null,"exchange":null,"routing_key":null,"headers":"{}","priority":null,"expires":null,"expire_seconds":null,"one_off":false,"start_time":null,"enabled":true,"last_run_at":"2023-09-05T20:45:00.042Z","total_run_count":1,"date_changed":"2023-09-05T20:48:00.995Z","description":""}},{"model":"django_celery_beat.periodictask","pk":5,"fields":{"name":"calcolate_persistsize","task":"core.tasks.calcolate_persistinfo","interval":null,"crontab":1,"solar":null,"clocked":null,"args":"[]","kwargs":"{}","queue":null,"exchange":null,"routing_key":null,"headers":"{}","priority":null,"expires":null,"expire_seconds":null,"one_off":false,"start_time":null,"enabled":true,"last_run_at":"2023-09-05T20:45:00.042Z","total_run_count":1,"date_changed":"2023-09-05T20:48:00.995Z","description":""}},{"model":"core.provider","pk":3,"fields":{"created_at":"2023-10-04T20:43:37Z","updated_at":"2023-10-04T20:45:12.736Z","enabled":true,"name":"Spreaker","icon":"fa-regular fa-star","color":"#ffc107","shortname":"sk"}}] diff --git a/requirements.txt b/requirements.txt index adb0ba9..b9122b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,7 +13,7 @@ django-celery-beat==2.5.0 # https://github.com/celery/django-celery-beat django==4.2.6 # pyup: < 5.0 # https://www.djangoproject.com/ django-environ==0.11.2 # https://github.com/joke2k/django-environ django-model-utils==4.3.1 # https://github.com/jazzband/django-model-utils -django-allauth==0.55.2 # https://github.com/pennersr/django-allauth +django-allauth==0.58.0 # https://github.com/pennersr/django-allauth django-crispy-forms==2.1 # https://github.com/django-crispy-forms/django-crispy-forms crispy-bootstrap5==0.7 # https://github.com/django-crispy-forms/crispy-bootstrap5 django-compressor==4.4 # https://github.com/django-compressor/django-compressor @@ -23,7 +23,7 @@ djangorestframework==3.14.0 # https://github.com/encode/django-rest-framework django-cors-headers==4.3.0 # https://github.com/adamchainz/django-cors-headers # DRF-spectacular for api documentation drf-spectacular==0.26.5 # https://github.com/tfranzel/drf-spectacular -Werkzeug[watchdog]==3.0.0 # https://github.com/pallets/werkzeug +Werkzeug[watchdog]==3.0.1 # https://github.com/pallets/werkzeug ipdb==0.13.13 # https://github.com/gotcha/ipdb psycopg[binary]==3.1.12 # https://github.com/psycopg/psycopg @@ -31,7 +31,7 @@ psycopg[binary]==3.1.12 # https://github.com/psycopg/psycopg # ------------------------------------------------------------------------------ mypy==1.4.1 # https://github.com/python/mypy django-stubs[compatible-mypy]==4.2.3 # https://github.com/typeddjango/django-stubs -pytest==7.4.2 # https://github.com/pytest-dev/pytest +pytest==7.4.3 # https://github.com/pytest-dev/pytest pytest-sugar==0.9.7 # https://github.com/Frozenball/pytest-sugar djangorestframework-stubs[compatible-mypy]==3.14.2 # https://github.com/typeddjango/djangorestframework-stubs @@ -40,9 +40,9 @@ djangorestframework-stubs[compatible-mypy]==3.14.2 # https://github.com/typeddj flake8==6.1.0 # https://github.com/PyCQA/flake8 flake8-isort==6.1.0 # https://github.com/gforcada/flake8-isort coverage==7.3.2 # https://github.com/nedbat/coveragepy -black==23.10.0 # https://github.com/psf/black +black==23.10.1 # https://github.com/psf/black djlint==1.34.0 # https://github.com/Riverside-Healthcare/djLint -pylint-django==2.5.3 # https://github.com/PyCQA/pylint-django +pylint-django==2.5.5 # https://github.com/PyCQA/pylint-django pre-commit==3.5.0 # https://github.com/pre-commit/pre-commit # Django