From 899bdc950b8e273e9a41c911c853292c6185b459 Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 22 Jul 2024 21:34:03 +0200 Subject: [PATCH] reset migrations, split user app --- tubearchivist/api/urls.py | 6 -- tubearchivist/api/views.py | 67 ---------------- .../config/management/commands/ta_envcheck.py | 2 +- .../config/management/commands/ta_startup.py | 16 ++-- tubearchivist/config/settings.py | 3 +- tubearchivist/config/urls.py | 1 + tubearchivist/home/admin.py | 46 ----------- tubearchivist/home/migrations/0001_initial.py | 35 --------- .../migrations/0002_customperiodictask.py | 23 ------ .../0003_delete_customperiodictask.py | 17 ---- tubearchivist/home/models.py | 54 ------------- tubearchivist/home/src/index/generic.py | 2 +- tubearchivist/home/views.py | 2 +- tubearchivist/playlist/src/index.py | 14 ++-- tubearchivist/playlist/views.py | 2 +- tubearchivist/task/migrations/0001_initial.py | 2 +- .../task/migrations/0002_migrate_scheduler.py | 31 -------- tubearchivist/user/__init__.py | 0 tubearchivist/user/admin.py | 45 +++++++++++ tubearchivist/user/migrations/0001_initial.py | 75 ++++++++++++++++++ tubearchivist/user/migrations/__init__.py | 0 tubearchivist/user/models.py | 54 +++++++++++++ tubearchivist/user/serializers.py | 20 +++++ tubearchivist/user/src/__init__.py | 0 .../ta/users.py => user/src/user_config.py} | 0 tubearchivist/user/urls.py | 9 +++ tubearchivist/user/views.py | 77 +++++++++++++++++++ tubearchivist/video/src/index.py | 2 +- 28 files changed, 304 insertions(+), 301 deletions(-) delete mode 100644 tubearchivist/home/migrations/0001_initial.py delete mode 100644 tubearchivist/home/migrations/0002_customperiodictask.py delete mode 100644 tubearchivist/home/migrations/0003_delete_customperiodictask.py delete mode 100644 tubearchivist/task/migrations/0002_migrate_scheduler.py create mode 100644 tubearchivist/user/__init__.py create mode 100644 tubearchivist/user/admin.py create mode 100644 tubearchivist/user/migrations/0001_initial.py create mode 100644 tubearchivist/user/migrations/__init__.py create mode 100644 tubearchivist/user/models.py create mode 100644 tubearchivist/user/serializers.py create mode 100644 tubearchivist/user/src/__init__.py rename tubearchivist/{home/src/ta/users.py => user/src/user_config.py} (100%) create mode 100644 tubearchivist/user/urls.py create mode 100644 tubearchivist/user/views.py diff --git a/tubearchivist/api/urls.py b/tubearchivist/api/urls.py index 99be8e7c..daba4004 100644 --- a/tubearchivist/api/urls.py +++ b/tubearchivist/api/urls.py @@ -5,17 +5,11 @@ from django.urls import path urlpatterns = [ path("ping/", views.PingView.as_view(), name="ping"), - path("login/", views.LoginApiView.as_view(), name="api-login"), path( "refresh/", views.RefreshView.as_view(), name="api-refresh", ), - path( - "config/user/", - views.UserConfigView.as_view(), - name="api-config-user", - ), path( "watched/", views.WatchedView.as_view(), diff --git a/tubearchivist/api/views.py b/tubearchivist/api/views.py index 685b47c5..01fc0050 100644 --- a/tubearchivist/api/views.py +++ b/tubearchivist/api/views.py @@ -9,14 +9,11 @@ from home.src.index.generic import Pagination from home.src.ta.config import AppConfig, ReleaseVersion from home.src.ta.settings import EnvironmentSettings from home.src.ta.ta_redis import RedisArchivist -from home.src.ta.users import UserConfig from rest_framework import permissions from rest_framework.authentication import ( SessionAuthentication, TokenAuthentication, ) -from rest_framework.authtoken.models import Token -from rest_framework.authtoken.views import ObtainAuthToken from rest_framework.response import Response from rest_framework.views import APIView from task.tasks import check_reindex @@ -123,34 +120,6 @@ class PingView(ApiBaseView): return Response(data) -class LoginApiView(ObtainAuthToken): - """resolves to /api/login/ - POST: return token and username after successful login - """ - - def post(self, request, *args, **kwargs): - """post data""" - # pylint: disable=no-member - serializer = self.serializer_class( - data=request.data, context={"request": request} - ) - serializer.is_valid(raise_exception=True) - user = serializer.validated_data["user"] - token, _ = Token.objects.get_or_create(user=user) - - print(f"returning token for user with id {user.pk}") - - return Response( - { - "token": token.key, - "user_id": user.pk, - "is_superuser": user.is_superuser, - "is_staff": user.is_staff, - "user_groups": [group.name for group in user.groups.all()], - } - ) - - class RefreshView(ApiBaseView): """resolves to /api/refresh/ GET: get refresh progress @@ -185,42 +154,6 @@ class RefreshView(ApiBaseView): return Response(data) -class UserConfigView(ApiBaseView): - """resolves to /api/config/user/ - GET: return current user config - POST: update user config - """ - - def get(self, request): - """get config""" - user_id = request.user.id - response = UserConfig(user_id).get_config() - response.update({"user_id": user_id}) - - return Response(response) - - def post(self, request): - """update config""" - user_id = request.user.id - data = request.data - - user_conf = UserConfig(user_id) - for key, value in data.items(): - try: - user_conf.set_value(key, value) - except ValueError as err: - message = { - "status": "Bad Request", - "message": f"failed updating {key} to '{value}', {err}", - } - return Response(message, status=400) - - response = user_conf.get_config() - response.update({"user_id": user_id}) - - return Response(response) - - class WatchedView(ApiBaseView): """resolves to /api/watched/ POST: change watched state of video, channel or playlist diff --git a/tubearchivist/config/management/commands/ta_envcheck.py b/tubearchivist/config/management/commands/ta_envcheck.py index 76c5ba10..1fe56afa 100644 --- a/tubearchivist/config/management/commands/ta_envcheck.py +++ b/tubearchivist/config/management/commands/ta_envcheck.py @@ -10,8 +10,8 @@ import os import re from django.core.management.base import BaseCommand, CommandError -from home.models import Account from home.src.ta.settings import EnvironmentSettings +from user.models import Account LOGO = """ diff --git a/tubearchivist/config/management/commands/ta_startup.py b/tubearchivist/config/management/commands/ta_startup.py index ce74f547..41cf4f94 100644 --- a/tubearchivist/config/management/commands/ta_startup.py +++ b/tubearchivist/config/management/commands/ta_startup.py @@ -9,23 +9,23 @@ from datetime import datetime from random import randint from time import sleep +from appsettings.src.index_setup import ElasitIndexWrap +from appsettings.src.snapshot import ElasticSnapshot from django.conf import settings from django.core.management.base import BaseCommand, CommandError from django.utils import dateformat from django_celery_beat.models import CrontabSchedule, PeriodicTasks -from home.models import CustomPeriodicTask from home.src.es.connect import ElasticWrap -from home.src.es.index_setup import ElasitIndexWrap -from home.src.es.snapshot import ElasticSnapshot from home.src.ta.config import AppConfig, ReleaseVersion -from home.src.ta.config_schedule import ScheduleBuilder from home.src.ta.helper import clear_dl_cache -from home.src.ta.notify import Notifications from home.src.ta.settings import EnvironmentSettings from home.src.ta.ta_redis import RedisArchivist -from home.src.ta.task_config import TASK_CONFIG -from home.src.ta.task_manager import TaskManager -from home.tasks import version_check +from task.models import CustomPeriodicTask +from task.src.config_schedule import ScheduleBuilder +from task.src.notify import Notifications +from task.src.task_config import TASK_CONFIG +from task.src.task_manager import TaskManager +from task.tasks import version_check TOPIC = """ diff --git a/tubearchivist/config/settings.py b/tubearchivist/config/settings.py index 1b893029..a8605744 100644 --- a/tubearchivist/config/settings.py +++ b/tubearchivist/config/settings.py @@ -69,6 +69,7 @@ INSTALLED_APPS = [ "task", "appsettings", "stats", + "user", "config", ] @@ -226,7 +227,7 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] -AUTH_USER_MODEL = "home.Account" +AUTH_USER_MODEL = "user.Account" # Forward-auth authentication if bool(environ.get("TA_ENABLE_AUTH_PROXY")): diff --git a/tubearchivist/config/urls.py b/tubearchivist/config/urls.py index d9e71068..2aa26fc1 100644 --- a/tubearchivist/config/urls.py +++ b/tubearchivist/config/urls.py @@ -27,5 +27,6 @@ urlpatterns = [ path("api/task/", include("task.urls")), path("api/appsettings/", include("appsettings.urls")), path("api/stats/", include("stats.urls")), + path("api/user/", include("user.urls")), path("admin/", admin.site.urls), ] diff --git a/tubearchivist/home/admin.py b/tubearchivist/home/admin.py index 3c6e83c5..e69de29b 100644 --- a/tubearchivist/home/admin.py +++ b/tubearchivist/home/admin.py @@ -1,46 +0,0 @@ -"""custom admin classes""" - -from django.contrib import admin -from django.contrib.auth.admin import UserAdmin as BaseUserAdmin -from django_celery_beat import models as BeatModels - -from .models import Account - - -class HomeAdmin(BaseUserAdmin): - """register in admin page""" - - list_display = ("name", "is_staff", "is_superuser") - list_filter = ("is_superuser",) - - fieldsets = ( - (None, {"fields": ("is_staff", "is_superuser", "password")}), - ("Personal info", {"fields": ("name",)}), - ("Groups", {"fields": ("groups",)}), - ("Permissions", {"fields": ("user_permissions",)}), - ) - add_fieldsets = ( - ( - None, - {"fields": ("is_staff", "is_superuser", "password1", "password2")}, - ), - ("Personal info", {"fields": ("name",)}), - ("Groups", {"fields": ("groups",)}), - ("Permissions", {"fields": ("user_permissions",)}), - ) - - search_fields = ("name",) - ordering = ("name",) - filter_horizontal = () - - -admin.site.register(Account, HomeAdmin) -admin.site.unregister( - [ - BeatModels.ClockedSchedule, - BeatModels.CrontabSchedule, - BeatModels.IntervalSchedule, - BeatModels.PeriodicTask, - BeatModels.SolarSchedule, - ] -) diff --git a/tubearchivist/home/migrations/0001_initial.py b/tubearchivist/home/migrations/0001_initial.py deleted file mode 100644 index fb51fff9..00000000 --- a/tubearchivist/home/migrations/0001_initial.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 4.1.5 on 2023-02-02 06:49 - -from django.db import migrations, models -import home.models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), - ] - - operations = [ - migrations.CreateModel( - name='Account', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('name', models.CharField(max_length=150, unique=True)), - ('is_staff', models.BooleanField(default=False)), - ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), - ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), - ], - options={ - 'abstract': False, - }, - managers=[ - ('objects', home.models.AccountManager()), - ], - ), - ] diff --git a/tubearchivist/home/migrations/0002_customperiodictask.py b/tubearchivist/home/migrations/0002_customperiodictask.py deleted file mode 100644 index cc584980..00000000 --- a/tubearchivist/home/migrations/0002_customperiodictask.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.2.7 on 2023-12-05 13:47 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('django_celery_beat', '0018_improve_crontab_helptext'), - ('home', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='CustomPeriodicTask', - fields=[ - ('periodictask_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='django_celery_beat.periodictask')), - ('task_config', models.JSONField(default=dict)), - ], - bases=('django_celery_beat.periodictask',), - ), - ] diff --git a/tubearchivist/home/migrations/0003_delete_customperiodictask.py b/tubearchivist/home/migrations/0003_delete_customperiodictask.py deleted file mode 100644 index dacc0e3e..00000000 --- a/tubearchivist/home/migrations/0003_delete_customperiodictask.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.0.7 on 2024-07-20 10:09 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("home", "0002_customperiodictask"), - ("task", "0002_migrate_scheduler"), - ] - - operations = [ - migrations.DeleteModel( - name="CustomPeriodicTask", - ), - ] diff --git a/tubearchivist/home/models.py b/tubearchivist/home/models.py index 2dacd0c7..e69de29b 100644 --- a/tubearchivist/home/models.py +++ b/tubearchivist/home/models.py @@ -1,54 +0,0 @@ -"""custom models""" - -from django.contrib.auth.models import ( - AbstractBaseUser, - BaseUserManager, - PermissionsMixin, -) -from django.db import models - - -class AccountManager(BaseUserManager): - """manage user creation methods""" - - use_in_migrations = True - - def _create_user(self, name, password, **extra_fields): - """create regular user private""" - values = [name, password] - field_value_map = dict(zip(self.model.REQUIRED_FIELDS, values)) - for field_name, value in field_value_map.items(): - if not value: - raise ValueError(f"The {field_name} value must be set") - - user = self.model(name=name, **extra_fields) - user.set_password(password) - user.save(using=self._db) - return user - - def create_user(self, name, password): - """create regular user public""" - return self._create_user(name, password) - - def create_superuser(self, name, password, **extra_fields): - """create super user""" - extra_fields.setdefault("is_staff", True) - extra_fields.setdefault("is_superuser", True) - - if extra_fields.get("is_staff") is not True: - raise ValueError("Superuser must have is_staff=True.") - if extra_fields.get("is_superuser") is not True: - raise ValueError("Superuser must have is_superuser=True.") - - return self._create_user(name, password, **extra_fields) - - -class Account(AbstractBaseUser, PermissionsMixin): - """handle account creation""" - - name = models.CharField(max_length=150, unique=True) - is_staff = models.BooleanField(default=False) - objects = AccountManager() - - USERNAME_FIELD = "name" - REQUIRED_FIELDS = ["password"] diff --git a/tubearchivist/home/src/index/generic.py b/tubearchivist/home/src/index/generic.py index 4211de36..0febec5e 100644 --- a/tubearchivist/home/src/index/generic.py +++ b/tubearchivist/home/src/index/generic.py @@ -8,7 +8,7 @@ import math from download.src.yt_dlp_base import YtWrap from home.src.es.connect import ElasticWrap from home.src.ta.config import AppConfig -from home.src.ta.users import UserConfig +from user.src.user_config import UserConfig class YouTubeItem: diff --git a/tubearchivist/home/views.py b/tubearchivist/home/views.py index 8ac1d9e9..a3509324 100644 --- a/tubearchivist/home/views.py +++ b/tubearchivist/home/views.py @@ -46,13 +46,13 @@ from home.src.ta.config import AppConfig, ReleaseVersion from home.src.ta.helper import check_stylesheet, time_parser from home.src.ta.settings import EnvironmentSettings from home.src.ta.ta_redis import RedisArchivist -from home.src.ta.users import UserConfig from playlist.src.index import YoutubePlaylist from rest_framework.authtoken.models import Token from task.models import CustomPeriodicTask from task.src.config_schedule import ScheduleBuilder from task.src.notify import Notifications, get_all_notifications from task.tasks import index_channel_playlists, subscribe_to +from user.src.user_config import UserConfig from video.src.constants import VideoTypeEnum diff --git a/tubearchivist/playlist/src/index.py b/tubearchivist/playlist/src/index.py index ffd316a6..d7c6cce2 100644 --- a/tubearchivist/playlist/src/index.py +++ b/tubearchivist/playlist/src/index.py @@ -11,7 +11,7 @@ from channel.src import index as channel from download.src.thumbnails import ThumbManager from home.src.es.connect import ElasticWrap, IndexPaginate from home.src.index.generic import YouTubeItem -from video.src.index import YoutubeVideo +from video.src import index as ta_video class YoutubePlaylist(YouTubeItem): @@ -255,7 +255,7 @@ class YoutubePlaylist(YouTubeItem): i = 0 while i < len(playlist): video_id = playlist[i]["youtube_id"] - video = YoutubeVideo(video_id) + video = ta_video.YoutubeVideo(video_id) video.get_from_es() if ( channel_id is None @@ -278,7 +278,7 @@ class YoutubePlaylist(YouTubeItem): if i["downloaded"] ] for youtube_id in all_youtube_id: - YoutubeVideo(youtube_id).delete_media_file() + ta_video.YoutubeVideo(youtube_id).delete_media_file() self.delete_metadata() @@ -312,7 +312,7 @@ class YoutubePlaylist(YouTubeItem): ) self.set_playlist_thumbnail() self.upload_to_es() - video = YoutubeVideo(video_id) + video = ta_video.YoutubeVideo(video_id) video.get_from_es() if "playlist" not in video.json_data: video.json_data["playlist"] = [] @@ -321,7 +321,7 @@ class YoutubePlaylist(YouTubeItem): return True def remove_playlist_from_video(self, video_id): - video = YoutubeVideo(video_id) + video = ta_video.YoutubeVideo(video_id) video.get_from_es() if video.json_data is not None and "playlist" in video.json_data: video.json_data["playlist"].remove(self.youtube_id) @@ -410,7 +410,7 @@ class YoutubePlaylist(YouTubeItem): ) def get_video_is_watched(self, video_id): - video = YoutubeVideo(video_id) + video = ta_video.YoutubeVideo(video_id) video.get_from_es() return video.json_data["player"]["watched"] @@ -426,7 +426,7 @@ class YoutubePlaylist(YouTubeItem): self.get_playlist_art() def get_video_metadata(self, video_id): - video = YoutubeVideo(video_id) + video = ta_video.YoutubeVideo(video_id) video.get_from_es() video_json_data = { "youtube_id": video.json_data["youtube_id"], diff --git a/tubearchivist/playlist/views.py b/tubearchivist/playlist/views.py index 2286588e..a676f7d2 100644 --- a/tubearchivist/playlist/views.py +++ b/tubearchivist/playlist/views.py @@ -2,11 +2,11 @@ from api.views import AdminWriteOnly, ApiBaseView from download.src.subscriptions import PlaylistSubscription -from home.src.ta.users import UserConfig from playlist.src.index import YoutubePlaylist from rest_framework import status from rest_framework.response import Response from task.tasks import subscribe_to +from user.src.user_config import UserConfig class PlaylistApiListView(ApiBaseView): diff --git a/tubearchivist/task/migrations/0001_initial.py b/tubearchivist/task/migrations/0001_initial.py index 66447422..38b9dac8 100644 --- a/tubearchivist/task/migrations/0001_initial.py +++ b/tubearchivist/task/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.0.7 on 2024-07-20 09:54 +# Generated by Django 5.0.7 on 2024-07-22 18:39 import django.db.models.deletion from django.db import migrations, models diff --git a/tubearchivist/task/migrations/0002_migrate_scheduler.py b/tubearchivist/task/migrations/0002_migrate_scheduler.py deleted file mode 100644 index 2b8b24d9..00000000 --- a/tubearchivist/task/migrations/0002_migrate_scheduler.py +++ /dev/null @@ -1,31 +0,0 @@ -"""custom migration to copy CustomPeriodicTask to new task app""" - -from django.db import migrations - -def copy_data(apps, schema_editor): - """copy between apps""" - - OldCustomPeriodicTask = apps.get_model("home", "CustomPeriodicTask") - NewCustomPeriodicTask = apps.get_model("task", "CustomPeriodicTask") - - for old_instance in OldCustomPeriodicTask.objects.all(): - field_data = { - field.name: getattr(old_instance, field.name) - for field in OldCustomPeriodicTask._meta.fields - } - field_data.pop("id", None) - new_instance = NewCustomPeriodicTask(**field_data) - new_instance.save() - - -class Migration(migrations.Migration): - """migration""" - - dependencies = [ - ("task", "0001_initial"), - ("home", "0002_customperiodictask"), - ] - - operations = [ - migrations.RunPython(copy_data), - ] diff --git a/tubearchivist/user/__init__.py b/tubearchivist/user/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tubearchivist/user/admin.py b/tubearchivist/user/admin.py new file mode 100644 index 00000000..662a4626 --- /dev/null +++ b/tubearchivist/user/admin.py @@ -0,0 +1,45 @@ +"""custom admin classes""" + +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin as BaseUserAdmin +from django_celery_beat import models as BeatModels +from user.models import Account + + +class HomeAdmin(BaseUserAdmin): + """register in admin page""" + + list_display = ("name", "is_staff", "is_superuser") + list_filter = ("is_superuser",) + + fieldsets = ( + (None, {"fields": ("is_staff", "is_superuser", "password")}), + ("Personal info", {"fields": ("name",)}), + ("Groups", {"fields": ("groups",)}), + ("Permissions", {"fields": ("user_permissions",)}), + ) + add_fieldsets = ( + ( + None, + {"fields": ("is_staff", "is_superuser", "password1", "password2")}, + ), + ("Personal info", {"fields": ("name",)}), + ("Groups", {"fields": ("groups",)}), + ("Permissions", {"fields": ("user_permissions",)}), + ) + + search_fields = ("name",) + ordering = ("name",) + filter_horizontal = () + + +admin.site.register(Account, HomeAdmin) +admin.site.unregister( + [ + BeatModels.ClockedSchedule, + BeatModels.CrontabSchedule, + BeatModels.IntervalSchedule, + BeatModels.PeriodicTask, + BeatModels.SolarSchedule, + ] +) diff --git a/tubearchivist/user/migrations/0001_initial.py b/tubearchivist/user/migrations/0001_initial.py new file mode 100644 index 00000000..2b00fb27 --- /dev/null +++ b/tubearchivist/user/migrations/0001_initial.py @@ -0,0 +1,75 @@ +# Generated by Django 5.0.7 on 2024-07-22 19:26 + +import user.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("auth", "0012_alter_user_first_name_max_length"), + ] + + operations = [ + migrations.CreateModel( + name="Account", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("password", models.CharField(max_length=128, verbose_name="password")), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "is_superuser", + models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + ("name", models.CharField(max_length=150, unique=True)), + ("is_staff", models.BooleanField(default=False)), + ( + "groups", + models.ManyToManyField( + blank=True, + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.group", + verbose_name="groups", + ), + ), + ( + "user_permissions", + models.ManyToManyField( + blank=True, + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.permission", + verbose_name="user permissions", + ), + ), + ], + options={ + "abstract": False, + }, + managers=[ + ("objects", user.models.AccountManager()), + ], + ), + ] diff --git a/tubearchivist/user/migrations/__init__.py b/tubearchivist/user/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tubearchivist/user/models.py b/tubearchivist/user/models.py new file mode 100644 index 00000000..2dacd0c7 --- /dev/null +++ b/tubearchivist/user/models.py @@ -0,0 +1,54 @@ +"""custom models""" + +from django.contrib.auth.models import ( + AbstractBaseUser, + BaseUserManager, + PermissionsMixin, +) +from django.db import models + + +class AccountManager(BaseUserManager): + """manage user creation methods""" + + use_in_migrations = True + + def _create_user(self, name, password, **extra_fields): + """create regular user private""" + values = [name, password] + field_value_map = dict(zip(self.model.REQUIRED_FIELDS, values)) + for field_name, value in field_value_map.items(): + if not value: + raise ValueError(f"The {field_name} value must be set") + + user = self.model(name=name, **extra_fields) + user.set_password(password) + user.save(using=self._db) + return user + + def create_user(self, name, password): + """create regular user public""" + return self._create_user(name, password) + + def create_superuser(self, name, password, **extra_fields): + """create super user""" + extra_fields.setdefault("is_staff", True) + extra_fields.setdefault("is_superuser", True) + + if extra_fields.get("is_staff") is not True: + raise ValueError("Superuser must have is_staff=True.") + if extra_fields.get("is_superuser") is not True: + raise ValueError("Superuser must have is_superuser=True.") + + return self._create_user(name, password, **extra_fields) + + +class Account(AbstractBaseUser, PermissionsMixin): + """handle account creation""" + + name = models.CharField(max_length=150, unique=True) + is_staff = models.BooleanField(default=False) + objects = AccountManager() + + USERNAME_FIELD = "name" + REQUIRED_FIELDS = ["password"] diff --git a/tubearchivist/user/serializers.py b/tubearchivist/user/serializers.py new file mode 100644 index 00000000..e87a10c0 --- /dev/null +++ b/tubearchivist/user/serializers.py @@ -0,0 +1,20 @@ +"""serializer for account model""" + +from rest_framework import serializers +from user.models import Account + + +class AccountSerializer(serializers.ModelSerializer): + """serialize account""" + + class Meta: + model = Account + fields = ( + "id", + "name", + "is_superuser", + "is_staff", + "groups", + "user_permissions", + "last_login", + ) diff --git a/tubearchivist/user/src/__init__.py b/tubearchivist/user/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tubearchivist/home/src/ta/users.py b/tubearchivist/user/src/user_config.py similarity index 100% rename from tubearchivist/home/src/ta/users.py rename to tubearchivist/user/src/user_config.py diff --git a/tubearchivist/user/urls.py b/tubearchivist/user/urls.py new file mode 100644 index 00000000..03c8ed2d --- /dev/null +++ b/tubearchivist/user/urls.py @@ -0,0 +1,9 @@ +"""all user API urls""" + +from django.urls import path +from user import views + +urlpatterns = [ + path("login/", views.LoginApiView.as_view(), name="api-user-login"), + path("me/", views.UserConfigView.as_view(), name="api-user-me"), +] diff --git a/tubearchivist/user/views.py b/tubearchivist/user/views.py new file mode 100644 index 00000000..8fe58e81 --- /dev/null +++ b/tubearchivist/user/views.py @@ -0,0 +1,77 @@ +"""all user api views""" + +from api.views import ApiBaseView +from rest_framework.authtoken.models import Token +from rest_framework.authtoken.views import ObtainAuthToken +from rest_framework.response import Response +from user.models import Account +from user.serializers import AccountSerializer +from user.src.user_config import UserConfig + + +class UserConfigView(ApiBaseView): + """resolves to /api/config/user/ + GET: return current user config + POST: update user config + """ + + def get(self, request): + """get config""" + user_id = request.user.id + account = Account.objects.get(id=user_id) + serializer = AccountSerializer(account) + response = serializer.data.copy() + + config = UserConfig(user_id).get_config() + response.update({"config": config}) + + return Response(response) + + def post(self, request): + """update config""" + user_id = request.user.id + data = request.data + + user_conf = UserConfig(user_id) + for key, value in data.items(): + try: + user_conf.set_value(key, value) + except ValueError as err: + message = { + "status": "Bad Request", + "message": f"failed updating {key} to '{value}', {err}", + } + return Response(message, status=400) + + response = user_conf.get_config() + response.update({"user_id": user_id}) + + return Response(response) + + +class LoginApiView(ObtainAuthToken): + """resolves to /api/login/ + POST: return token and username after successful login + """ + + def post(self, request, *args, **kwargs): + """post data""" + # pylint: disable=no-member + serializer = self.serializer_class( + data=request.data, context={"request": request} + ) + serializer.is_valid(raise_exception=True) + user = serializer.validated_data["user"] + token, _ = Token.objects.get_or_create(user=user) + + print(f"returning token for user with id {user.pk}") + + return Response( + { + "token": token.key, + "user_id": user.pk, + "is_superuser": user.is_superuser, + "is_staff": user.is_staff, + "user_groups": [group.name for group in user.groups.all()], + } + ) diff --git a/tubearchivist/video/src/index.py b/tubearchivist/video/src/index.py index 408fae32..cea69cce 100644 --- a/tubearchivist/video/src/index.py +++ b/tubearchivist/video/src/index.py @@ -14,9 +14,9 @@ from home.src.es.connect import ElasticWrap from home.src.index.generic import YouTubeItem from home.src.ta.helper import get_duration_sec, get_duration_str, randomizor from home.src.ta.settings import EnvironmentSettings -from home.src.ta.users import UserConfig from playlist.src import index as ta_playlist from ryd_client import ryd_client +from user.src.user_config import UserConfig from video.src.comments import Comments from video.src.constants import VideoTypeEnum from video.src.media_streams import MediaStreamExtractor