reset migrations, split user app

This commit is contained in:
Simon
2024-07-22 21:34:03 +02:00
parent f368322fea
commit 899bdc950b
28 changed files with 304 additions and 301 deletions

View File

@@ -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(),

View File

@@ -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

View File

@@ -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 = """

View File

@@ -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 = """

View File

@@ -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")):

View File

@@ -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),
]

View File

@@ -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,
]
)

View File

@@ -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()),
],
),
]

View File

@@ -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',),
),
]

View File

@@ -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",
),
]

View File

@@ -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"]

View File

@@ -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:

View File

@@ -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

View File

@@ -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"],

View File

@@ -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):

View File

@@ -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

View File

@@ -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),
]

View File

View File

@@ -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,
]
)

View File

@@ -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()),
],
),
]

View File

@@ -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"]

View File

@@ -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",
)

View File

View File

@@ -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"),
]

View File

@@ -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()],
}
)

View File

@@ -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