Added admin settings page. Improve dynamic_preferences integration.

This commit is contained in:
Tiberiu Chibici 2018-12-29 17:11:20 +02:00
parent 33ee71dcbb
commit 8eca3dc7a9
20 changed files with 233 additions and 82 deletions

View File

@ -5,7 +5,7 @@ import sys
from django.conf import settings as dj_settings
from .management.appconfig import global_prefs
from .management.appconfig import appconfig
from .management.jobs.synchronize import schedule_synchronize_global
from .scheduler import initialize_scheduler
from django.db.utils import OperationalError
@ -40,7 +40,7 @@ def main():
__initialize_logger()
try:
if global_prefs['hidden__initialized']:
if appconfig.initialized:
initialize_scheduler()
schedule_synchronize_global()
except OperationalError:

View File

@ -33,14 +33,6 @@ class YouTubeAPIKey(StringPreference):
required = True
@global_preferences_registry.register
class AllowCDN(BooleanPreference):
section = general
name = 'allow_cdn'
default = True
required = True
@global_preferences_registry.register
class AllowRegistrations(BooleanPreference):
section = general

View File

@ -1,3 +1,34 @@
from dynamic_preferences.registries import global_preferences_registry
from YtManagerApp.dynamic_preferences_registry import Initialized, YouTubeAPIKey, AllowRegistrations, SyncSchedule, SchedulerConcurrency
class AppConfig(object):
# Properties
props = {
'initialized': Initialized,
'youtube_api_key': YouTubeAPIKey,
'allow_registrations': AllowRegistrations,
'sync_schedule': SyncSchedule,
'concurrency': SchedulerConcurrency
}
# Init
def __init__(self, pref_manager):
self.__pref_manager = pref_manager
def __getattr__(self, item):
prop_class = AppConfig.props[item]
prop_full_name = prop_class.section.name + "__" + prop_class.name
return self.__pref_manager[prop_full_name]
def __setattr__(self, key, value):
if key in AppConfig.props:
prop_class = AppConfig.props[key]
prop_full_name = prop_class.section.name + "__" + prop_class.name
self.__pref_manager[prop_full_name] = value
else:
super().__setattr__(key, value)
global_prefs = global_preferences_registry.manager()
appconfig = AppConfig(global_prefs)

View File

@ -5,7 +5,7 @@ from threading import Lock
from apscheduler.triggers.cron import CronTrigger
from YtManagerApp import scheduler
from YtManagerApp.management.appconfig import global_prefs
from YtManagerApp.management.appconfig import appconfig
from YtManagerApp.management.downloader import fetch_thumbnail, downloader_process_all, downloader_process_subscription
from YtManagerApp.models import *
from YtManagerApp.utils import youtube
@ -149,7 +149,7 @@ def synchronize_subscription(subscription: Subscription):
def schedule_synchronize_global():
trigger = CronTrigger.from_crontab(global_prefs['scheduler__synchronization_schedule'])
trigger = CronTrigger.from_crontab(appconfig.sync_schedule)
job = scheduler.scheduler.add_job(synchronize, trigger, max_instances=1, coalesce=True)
log.info('Scheduled synchronize job job=%s', job.id)

View File

@ -1,11 +1,11 @@
import logging
from typing import Callable, Union, Any, Optional
import os
from typing import Callable, Union, Any, Optional
from django.contrib.auth.models import User
from django.contrib.auth.models import User
from django.db import models
from django.db.models.functions import Lower
from YtManagerApp.utils import youtube
# help_text = user shown text

View File

@ -2,7 +2,7 @@ import logging
from apscheduler.schedulers.background import BackgroundScheduler
from YtManagerApp.management.appconfig import global_prefs
from YtManagerApp.management.appconfig import appconfig
scheduler: BackgroundScheduler = None
@ -14,7 +14,7 @@ def initialize_scheduler():
executors = {
'default': {
'type': 'threadpool',
'max_workers': global_prefs['scheduler__concurrency']
'max_workers': appconfig.concurrency
}
}
job_defaults = {

View File

@ -9,7 +9,7 @@
bottom: 0;
height: 2rem;
line-height: 2rem;
padding: 0rem 1rem;
padding: 0 1rem;
display: flex;
align-content: center;
font-size: 10pt; }

View File

@ -1,6 +1,6 @@
{
"version": 3,
"mappings": "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",
"mappings": "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",
"sources": ["style.scss"],
"names": [],
"file": "style.css"

View File

@ -12,7 +12,7 @@ $accent-color: #007bff;
bottom: 0;
height: 2rem;
line-height: 2rem;
padding: 0rem 1rem;
padding: 0 1rem;
display: flex;
align-content: center;
font-size: 10pt;

View File

@ -46,6 +46,9 @@
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="userDropdown">
<a class="dropdown-item" href="{% url 'settings' %}">Settings</a>
{% if request.user.is_superuser %}
<a class="dropdown-item" href="{% url 'admin_settings' %}">Admin settings</a>
{% endif %}
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url 'logout' %}">Log out</a>
</div>
@ -54,10 +57,12 @@
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">Login</a>
</li>
{% if global_preferences.general__allow_registrations %}
<li class="nav-item">
<a class="nav-link" href="{% url 'register' %}">Register</a>
</li>
{% endif %}
{% endif %}
</ul>
</div>

View File

@ -0,0 +1,11 @@
{% extends "YtManagerApp/master_default.html" %}
{% load crispy_forms_tags %}
{% block body %}
<div class="container">
<h1>Admin settings</h1>
{% crispy form %}
</div>
{% endblock body %}

View File

@ -24,9 +24,17 @@
{% endif %}
{% endif %}
<h5>Register</h5>
{% if not global_preferences.general__allow_registrations %}
<div class="alert alert-danger" role="alert">
Registrations are disabled by the administrator!
</div>
{% else %}
<h5>Register</h5>
{% crispy form %}
{% endif %}
</div>
{% endblock %}

View File

@ -23,7 +23,7 @@ from .views.actions import SyncNowView, DeleteVideoFilesView, DownloadVideoFiles
from .views.auth import ExtendedLoginView, RegisterView, RegisterDoneView
from .views.index import index, ajax_get_tree, ajax_get_videos, CreateFolderModal, UpdateFolderModal, DeleteFolderModal, \
CreateSubscriptionModal, UpdateSubscriptionModal, DeleteSubscriptionModal, ImportSubscriptionsModal
from .views.settings import SettingsView
from .views.settings import SettingsView, AdminSettingsView
from .views import first_time
urlpatterns = [
@ -59,6 +59,7 @@ urlpatterns = [
# Pages
path('', index, name='home'),
path('settings/', SettingsView.as_view(), name='settings'),
path('admin_settings/', AdminSettingsView.as_view(), name='admin_settings'),
# First time setup
path('first_time/step0_welcome', first_time.Step0WelcomeView.as_view(), name='first_time_0'),

View File

@ -7,7 +7,8 @@ class YoutubeAPI(YouTube):
@staticmethod
def build_public() -> 'YoutubeAPI':
return YoutubeAPI(key=settings.YOUTUBE_API_KEY)
from YtManagerApp.management.appconfig import youtube_api_key
return YoutubeAPI(key=youtube_api_key)
# @staticmethod
# def build_oauth() -> 'YoutubeAPI':

View File

@ -2,10 +2,12 @@ from django.contrib.auth import login, authenticate
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.contrib.auth.views import LoginView
from django.http import HttpResponseForbidden
from django.urls import reverse_lazy
from django.views.generic import FormView, TemplateView
from .forms.auth import ExtendedAuthenticationForm, ExtendedUserCreationForm
from YtManagerApp.management.appconfig import appconfig
from YtManagerApp.views.forms.auth import ExtendedAuthenticationForm, ExtendedUserCreationForm
class ExtendedLoginView(LoginView):
@ -33,6 +35,12 @@ class RegisterView(FormView):
context['is_first_user'] = (User.objects.count() == 0)
return context
def post(self, request, *args, **kwargs):
if not appconfig.allow_registrations:
return HttpResponseForbidden("Registrations are disabled!")
return super().post(request, *args, **kwargs)
class RegisterDoneView(LoginRequiredMixin, TemplateView):
template_name = 'registration/register_done.html'

View File

@ -7,7 +7,7 @@ from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.views.generic import FormView
from YtManagerApp.management.appconfig import global_prefs
from YtManagerApp.management.appconfig import appconfig
from YtManagerApp.views.forms.auth import ExtendedAuthenticationForm
from YtManagerApp.views.forms.first_time import WelcomeForm, ApiKeyForm, PickAdminUserForm, ServerConfigForm, DoneForm, UserCreationForm
@ -18,7 +18,7 @@ class WizardStepMixin(object):
def get(self, request, *args, **kwargs):
# Prevent access if application is already initialized
if global_prefs['hidden__initialized']:
if appconfig.initialized:
logger.debug(f"Attempted to access {request.path}, but first time setup already run. Redirected to home "
f"page.")
return redirect('home')
@ -26,7 +26,7 @@ class WizardStepMixin(object):
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if global_prefs['hidden__initialized']:
if appconfig.initialized:
logger.debug(f"Attempted to post {request.path}, but first time setup already run.")
return HttpResponseForbidden()
return super().post(request, *args, **kwargs)
@ -51,14 +51,14 @@ class Step1ApiKeyView(WizardStepMixin, FormView):
def get_initial(self):
initial = super().get_initial()
initial['api_key'] = global_prefs['general__youtube_api_key']
initial['api_key'] = appconfig.youtube_api_key
return initial
def form_valid(self, form):
key = form.cleaned_data['api_key']
# TODO: validate key
if key is not None and len(key) > 0:
global_prefs['general__youtube_api_key'] = key
appconfig.youtube_api_key = key
#
@ -100,9 +100,6 @@ class Step2SetupAdminUserView(WizardStepMixin, FormView):
return super().get(request, *args, **kwargs)
def form_invalid(self, form):
print("FORM INVALID!")
def form_valid(self, form):
if isinstance(form, ExtendedAuthenticationForm):
login(self.request, form.get_user())
@ -139,8 +136,8 @@ class Step3ConfigureView(WizardStepMixin, FormView):
def get_initial(self):
initial = super().get_initial()
initial['allow_registrations'] = global_prefs['general__allow_registrations']
initial['sync_schedule'] = global_prefs['scheduler__synchronization_schedule']
initial['allow_registrations'] = appconfig.allow_registrations
initial['sync_schedule'] = appconfig.sync_schedule
initial['auto_download'] = self.request.user.preferences['downloader__auto_enabled']
initial['download_location'] = self.request.user.preferences['downloader__download_path']
return initial
@ -148,11 +145,11 @@ class Step3ConfigureView(WizardStepMixin, FormView):
def form_valid(self, form):
allow_registrations = form.cleaned_data['allow_registrations']
if allow_registrations is not None:
global_prefs['general__allow_registrations'] = allow_registrations
appconfig.allow_registrations = allow_registrations
sync_schedule = form.cleaned_data['sync_schedule']
if sync_schedule is not None and len(sync_schedule) > 0:
global_prefs['scheduler__synchronization_schedule'] = sync_schedule
appconfig.sync_schedule = sync_schedule
auto_download = form.cleaned_data['auto_download']
if auto_download is not None:
@ -163,7 +160,7 @@ class Step3ConfigureView(WizardStepMixin, FormView):
self.request.user.preferences['downloader__download_path'] = download_location
# Set initialized to true
global_prefs['hidden__initialized'] = True
appconfig.initialized = True
return super().form_valid(form)

View File

@ -3,11 +3,10 @@ import logging
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, HTML, Submit, Column
from django import forms
from django.contrib.auth import authenticate, login
from django.contrib.auth.models import User
from YtManagerApp.views.forms.auth import ExtendedUserCreationForm
from django.urls import reverse_lazy
from YtManagerApp.management.appconfig import global_prefs
from YtManagerApp.views.forms.auth import ExtendedUserCreationForm
logger = logging.getLogger("FirstTimeWizard")

View File

@ -0,0 +1,80 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, HTML, Submit
from django import forms
from YtManagerApp.models import UserSettings
class SettingsForm(forms.ModelForm):
class Meta:
model = UserSettings
exclude = ['user']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-3'
self.helper.field_class = 'col-lg-9'
self.helper.layout = Layout(
'mark_deleted_as_watched',
'delete_watched',
HTML('<h2>Download settings</h2>'),
'auto_download',
'download_path',
'download_file_pattern',
'download_format',
'download_order',
'download_global_limit',
'download_subscription_limit',
HTML('<h2>Subtitles download settings</h2>'),
'download_subtitles',
'download_subtitles_langs',
'download_subtitles_all',
'download_autogenerated_subtitles',
'download_subtitles_format',
Submit('submit', value='Save')
)
class AdminSettingsForm(forms.Form):
api_key = forms.CharField(label="YouTube API key")
allow_registrations = forms.BooleanField(
label="Allow user registrations",
help_text="Disabling this option will prevent anyone from registering to the site.",
initial=True,
required=False
)
sync_schedule = forms.CharField(
label="Synchronization schedule",
help_text="How often should the application look for new videos.",
initial="5 * * * *",
required=True
)
scheduler_concurrency = forms.IntegerField(
label="Synchronization concurrency",
help_text="How many jobs are executed executed in parallel. Since most jobs are I/O bound (mostly use the hard "
"drive and network), there is no significant advantage to increase it.",
initial=2,
required=True
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-3'
self.helper.field_class = 'col-lg-9'
self.helper.layout = Layout(
HTML('<h2>General settings</h2>'),
'api_key',
'allow_registrations',
HTML('<h2>Scheduler settings</h2>'),
'sync_schedule',
'scheduler_concurrency',
Submit('submit', value='Save')
)

View File

@ -10,7 +10,7 @@ from django.views.generic import CreateView, UpdateView, DeleteView, FormView
from django.views.generic.edit import FormMixin
from django.conf import settings
from YtManagerApp.management.videos import get_videos
from YtManagerApp.management.appconfig import global_prefs
from YtManagerApp.management.appconfig import appconfig
from YtManagerApp.models import Subscription, SubscriptionFolder, VIDEO_ORDER_CHOICES, VIDEO_ORDER_MAPPING
from YtManagerApp.utils import youtube, subscription_file_parser
from YtManagerApp.views.controls.modal import ModalMixin
@ -96,7 +96,7 @@ def __tree_sub_id(sub_id):
def index(request: HttpRequest):
if not global_prefs['hidden__initialized']:
if not appconfig.initialized:
return redirect('first_time_0')
context = {

View File

@ -1,43 +1,11 @@
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, HTML, Submit
from django import forms
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseForbidden
from django.urls import reverse_lazy
from django.views.generic import UpdateView
from django.views.generic import UpdateView, FormView
from YtManagerApp.management.appconfig import appconfig
from YtManagerApp.models import UserSettings
class SettingsForm(forms.ModelForm):
class Meta:
model = UserSettings
exclude = ['user']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-3'
self.helper.field_class = 'col-lg-9'
self.helper.layout = Layout(
'mark_deleted_as_watched',
'delete_watched',
HTML('<h2>Download settings</h2>'),
'auto_download',
'download_path',
'download_file_pattern',
'download_format',
'download_order',
'download_global_limit',
'download_subscription_limit',
HTML('<h2>Subtitles download settings</h2>'),
'download_subtitles',
'download_subtitles_langs',
'download_subtitles_all',
'download_autogenerated_subtitles',
'download_subtitles_format',
Submit('submit', value='Save')
)
from YtManagerApp.views.forms.settings import SettingsForm, AdminSettingsForm
class SettingsView(LoginRequiredMixin, UpdateView):
@ -49,3 +17,53 @@ class SettingsView(LoginRequiredMixin, UpdateView):
def get_object(self, queryset=None):
obj, _ = self.model.objects.get_or_create(user=self.request.user)
return obj
class AdminSettingsView(LoginRequiredMixin, FormView):
form_class = AdminSettingsForm
template_name = 'YtManagerApp/settings_admin.html'
success_url = reverse_lazy('home')
def get(self, request, *args, **kwargs):
if not request.user.is_authenticated or not request.user.is_superuser:
return HttpResponseForbidden()
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated or not request.user.is_superuser:
return HttpResponseForbidden()
return super().post(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# TODO: present stats
return context
def get_initial(self):
initial = super().get_initial()
initial['api_key'] = appconfig.youtube_api_key
initial['allow_registrations'] = appconfig.allow_registrations
initial['sync_schedule'] = appconfig.sync_schedule
initial['scheduler_concurrency'] = appconfig.concurrency
return initial
def form_valid(self, form):
api_key = form.cleaned_data['api_key']
if api_key is not None and len(api_key) > 0:
appconfig.youtube_api_key = api_key
allow_registrations = form.cleaned_data['allow_registrations']
if allow_registrations is not None:
appconfig.allow_registrations = allow_registrations
sync_schedule = form.cleaned_data['sync_schedule']
if sync_schedule is not None and len(sync_schedule) > 0:
appconfig.sync_schedule = sync_schedule
concurrency = form.cleaned_data['scheduler_concurrency']
if concurrency is not None:
appconfig.concurrency = concurrency
return super().form_valid(form)