diff --git a/category/admin.py b/category/admin.py index 3d997b1..98ae1fe 100644 --- a/category/admin.py +++ b/category/admin.py @@ -8,6 +8,7 @@ from django.utils.translation import gettext_lazy as _ from category.models import Category +from tenants.models import Tenant @admin.register(Category) @@ -18,7 +19,7 @@ class CategoryAdmin(admin.ModelAdmin): # list_display_links = ["tenant_name"] actions = ["move_tenant"] - @admin.display(description=_("Tenant")) + @admin.display(description=Tenant._meta.verbose_name) def tenant_name(self, obj): """Shows Tenant name""" link = reverse("admin:tenants_tenant_change", args=[obj.tenant.id]) diff --git a/category/forms.py b/category/forms.py index 8c143d5..d757ee7 100644 --- a/category/forms.py +++ b/category/forms.py @@ -35,9 +35,3 @@ class NameForm(Form): pk = IntegerField(widget=HiddenInput()) name = CharField(disabled=True, required=False) - - -class ChooseTenantForm(Form): - """Form to choose Tenant""" - - tenant = ModelChoiceField(queryset=Tenant.objects.all(), label=_("Tenant")) diff --git a/category/locale/cs/LC_MESSAGES/django.po b/category/locale/cs/LC_MESSAGES/django.po index f70948f..004db5f 100644 --- a/category/locale/cs/LC_MESSAGES/django.po +++ b/category/locale/cs/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-22 22:15+0000\n" +"POT-Creation-Date: 2024-12-25 17:43+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -19,11 +19,7 @@ msgstr "" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " "<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" -#: category/admin.py:21 category/forms.py:43 -msgid "Tenant" -msgstr "Do Zpěvníku" - -#: category/admin.py:27 +#: category/admin.py:28 msgid "Move Category to a different Tenant" msgstr "Přesunout Kategorie do jiného Zpěvníku" @@ -48,28 +44,18 @@ msgstr "" msgid "Category" msgstr "Kategorie" -#: category/models.py:26 category/templates/admin/category/migrate.html:6 -#: category/templates/category/list.html:4 +#: category/models.py:26 category/templates/category/list.html:4 #: category/templates/category/list.html:5 msgid "Categories" msgstr "Kategorie" -#: category/templates/admin/category/migrate.html:4 -#: category/templates/admin/category/migrate.html:5 -msgid "Migrating categories to Tenant" -msgstr "Přesun kategorii do jiného Zpěvníku" - -#: category/templates/admin/category/migrate.html:7 -msgid "To Tenant" -msgstr "Do Zpěvníku" - #: category/templates/category/list.html:14 msgid "Language" msgstr "Jazyk" #: category/templates/category/list.html:15 msgid "Actions" -msgstr "" +msgstr "Akce" #: category/templates/category/list.html:23 msgid "Yes" @@ -85,7 +71,7 @@ msgstr "Upravit" #: category/templates/category/list.html:29 msgid "Toggle Dropdown" -msgstr "" +msgstr "Rozbalit" #: category/templates/category/list.html:34 msgid "Already in queue" diff --git a/category/templates/admin/category/migrate.html b/category/templates/admin/category/migrate.html deleted file mode 100644 index 7610759..0000000 --- a/category/templates/admin/category/migrate.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "admin/base/migrate.html" %} -{% load i18n %} - -{% block title %}{% trans "Migrating categories to Tenant" %}{% endblock %} -{% block content_title %}{% trans "Migrating categories to Tenant" %}{% endblock %} -{% block object_name %}{% trans "Categories" %}{% endblock %} -{% block target %}{% trans "To Tenant" %}{% endblock %} diff --git a/category/views.py b/category/views.py index ea2d4c3..c635bf4 100644 --- a/category/views.py +++ b/category/views.py @@ -4,24 +4,24 @@ from django.contrib import messages from django.core.cache import cache from django.db import transaction -from django.forms import formset_factory from django.http import Http404 from django.shortcuts import redirect from django.urls import reverse_lazy from django.utils.translation import gettext_lazy as _ from django.views import View -from django.views.generic import ListView, TemplateView +from django.views.generic import ListView from django.views.generic.detail import SingleObjectMixin from analytics.views import AnalyticsMixin from backend.generic import UniversalDeleteView, UniversalUpdateView, UniversalCreateView -from backend.mixins import RegenerateViewMixin, LocalAdminRequired, SuperAdminRequired +from backend.mixins import RegenerateViewMixin, LocalAdminRequired from backend.models import Song from backend.views import BaseSongListView -from category.forms import CategoryForm, NameForm, ChooseTenantForm +from category.forms import CategoryForm, NameForm from category.models import Category from pdf.models.request import PDFRequest, RequestType, Status from pdf.utils import request_pdf_regeneration +from tenants.views import AdminMoveView class CategorySongsListView(BaseSongListView, AnalyticsMixin): @@ -121,46 +121,22 @@ def post(self, request, *args, **kwargs): return response -class CategoryMoveView(SuperAdminRequired, TemplateView): +class CategoryMoveView(AdminMoveView): """Moves Categories to a different Tenant""" - template_name = "admin/category/migrate.html" - form_class = ChooseTenantForm - formset_class = formset_factory(NameForm, extra=0) - - def initial(self, pks): - """Initial Form population""" - query = Category.objects.filter(id__in=pks) - - form = self.form_class() - initial = [] - for category in query.values_list("id", "name"): - initial.append({"pk": category[0], "name": category[1]}) - - formset = self.formset_class(initial=initial) - return form, formset - - def get_context_data(self, **kwargs): - """Appends Form and Formset""" - context = super().get_context_data(**kwargs) - - pks = self.request.GET.getlist("pk") - form, formset = self.initial(pks) - - context["form"] = form - context["formset"] = formset - return context + formset_form = NameForm + model = Category - def action(self, tenant, ids): + def action(self, target, ids): """What should happen on POST with data from forms""" categories = Category.objects.filter(id__in=ids) songs = Song.objects.filter(categories__id__in=ids).distinct() requests = PDFRequest.objects.filter(category_id__in=ids).distinct() with transaction.atomic(): for category in categories: - category.tenant = tenant + category.tenant = target for request in requests: - request.tenant = tenant + request.tenant = target Category.objects.bulk_update(categories, ["tenant"]) PDFRequest.objects.bulk_update(requests, ["tenant"]) for song in songs: @@ -178,14 +154,3 @@ def action(self, tenant, ids): song.save() song.categories.set(to_remove) song.save() - - def post(self, request, *args, **kwargs): - """POST request""" - form = self.form_class(request.POST) - formset = self.formset_class(request.POST) - if all([form.is_valid(), formset.is_valid()]): - tenant = form.cleaned_data["tenant"] - ids = [inline_form.cleaned_data["pk"] for inline_form in formset] - self.action(tenant, ids) - return redirect("admin:index") - return self.render_to_response({"form": form, "formset": formset}) diff --git a/frontend/locale/cs/LC_MESSAGES/django.po b/frontend/locale/cs/LC_MESSAGES/django.po index d04c833..a119841 100644 --- a/frontend/locale/cs/LC_MESSAGES/django.po +++ b/frontend/locale/cs/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-22 21:56+0000\n" +"POT-Creation-Date: 2024-12-25 17:32+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -48,15 +48,14 @@ msgstr "Zpátky domů" msgid "URL %(request_path)s does not exist." msgstr "URL %(request_path)s neexistuje." -#: frontend/templates/admin/base/migrate.html:29 -#: frontend/templates/base/form.html:9 -msgid "Submit" -msgstr "Odeslat" - #: frontend/templates/base/error.html:8 msgid "Not found" msgstr "Nenalezeno" +#: frontend/templates/base/form.html:9 +msgid "Submit" +msgstr "Odeslat" + #: frontend/templates/base/index.html:38 msgid "Logo" msgstr "" diff --git a/frontend/templates/admin/base/migrate.html b/frontend/templates/admin/base/migrate.html deleted file mode 100644 index bd7c7c8..0000000 --- a/frontend/templates/admin/base/migrate.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends "admin/base.html" %} -{% load i18n %} -{% load django_bootstrap5 %} - -{% block title %}{% endblock %} -{% block content_title %}{% endblock %} -{% block content %} -
- {% csrf_token %} - {{ formset.management_form }} - {{ formset.non_form_errors.as_ul }} -

{% block object_name %}{% endblock %}

-
- {% for form in formset.forms %} -
- {% for hidden in form.hidden_fields %} - {{ hidden }} - {% endfor %} - {% for field in form.visible_fields %} - {% bootstrap_field field layout="horizontal" show_label=False size="sm" %} - {% endfor %} -
-
- {% endfor %} -
-
-

{% block target %}{% endblock %}

- {% bootstrap_form form %} - {% bootstrap_button _("Submit") button_type="submit" %} -
- {{ form.media }} -{% endblock %} \ No newline at end of file diff --git a/pdf/locale/cs/LC_MESSAGES/django.po b/pdf/locale/cs/LC_MESSAGES/django.po index 2e9ce49..4de07da 100644 --- a/pdf/locale/cs/LC_MESSAGES/django.po +++ b/pdf/locale/cs/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-20 12:45+0000\n" +"POT-Creation-Date: 2024-12-25 17:41+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -140,27 +140,18 @@ msgstr "Hotovo" msgid "Failed" msgstr "Selhalo" -#: pdf/models/request.py:76 pdf/templates/admin/pdf/migrate.html:6 +#: pdf/models/request.py:80 msgid "PDFRequest" msgstr "PDF požadavek" -#: pdf/models/request.py:77 +#: pdf/models/request.py:81 msgid "PDFRequests" msgstr "PDF požadavky" -#: pdf/models/request.py:93 +#: pdf/models/request.py:97 msgid "Song number" msgstr "Číslo písničky" -#: pdf/templates/admin/pdf/migrate.html:4 -#: pdf/templates/admin/pdf/migrate.html:5 -msgid "Migrating PDFRequests to Tenant" -msgstr "Přesouvání PDF požadavků do Tenant" - -#: pdf/templates/admin/pdf/migrate.html:7 -msgid "To Tenant" -msgstr "Do Tenanta" - #: pdf/templates/pdf/index.html:45 msgid "Table of Contents" msgstr "Obsah" @@ -303,41 +294,35 @@ msgstr "Postup" msgid "songbook" msgstr "zpevnik" -#: pdf/views.py:45 +#: pdf/views.py:47 #, python-format msgid "Request %(id)s is already in queue" msgstr "Požadavek %(id)s je již ve frontě" -#: pdf/views.py:51 +#: pdf/views.py:53 #, python-format msgid "Request %(id)s was scheduled for regeneration" msgstr "Požadavek %(id)s byl označen k přegenerování" -#: pdf/views.py:67 +#: pdf/views.py:69 #, python-format msgid "Unable to remove file from request %(id)s that doesn't have one" msgstr "Nepodařilo se odstranit soubor z požadavku %(id)s, protože žádný nemá" -#: pdf/views.py:74 +#: pdf/views.py:76 #, python-format msgid "File %(name)s was successfully deleted" msgstr "Soubor %(name)s byl úspěšně odstraněn" -#: pdf/views.py:111 +#: pdf/views.py:113 msgid "Required parameters not found" msgstr "Požadované parametry nenalezeny" -#: pdf/views.py:116 +#: pdf/views.py:118 msgid "You need to select at least one song" msgstr "Musíte vybrat alespoň jednu písničku" -#: pdf/views.py:141 +#: pdf/views.py:143 #, python-format msgid "PDF Request with id %(id)s was successfully created" msgstr "PDF požadavek se id %(id)s byl úspěšně vytvořen" - -#~ msgid "Search" -#~ msgstr "Hledat" - -#~ msgid "Insert query" -#~ msgstr "Vložte text" diff --git a/pdf/models/request.py b/pdf/models/request.py index c2ed929..ca9e8d2 100644 --- a/pdf/models/request.py +++ b/pdf/models/request.py @@ -1,5 +1,6 @@ """Models for PDF module""" +import os from datetime import datetime from typing import List @@ -68,6 +69,11 @@ class PDFRequest(PDFOptions): category = ForeignKey(Category, null=True, on_delete=SET_NULL) scheduled_at = DateTimeField(null=True) + @property + def name(self): + """Returns displayable name""" + return os.path.basename(self.file.name) + def get_songs(self) -> List[Song]: """Returns all songs for request""" return [transform_song(pdf_song) for pdf_song in PDFSong.objects.filter(request=self)] diff --git a/pdf/templates/admin/pdf/migrate.html b/pdf/templates/admin/pdf/migrate.html deleted file mode 100644 index ca732de..0000000 --- a/pdf/templates/admin/pdf/migrate.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "admin/base/migrate.html" %} -{% load i18n %} - -{% block title %}{% trans "Migrating PDFRequests to Tenant" %}{% endblock %} -{% block content_title %}{% trans "Migrating PDFRequests to Tenant" %}{% endblock %} -{% block object_name %}{% trans "PDFRequest" %}{% endblock %} -{% block target %}{% trans "To Tenant" %}{% endblock %} diff --git a/pdf/views.py b/pdf/views.py index dbbe03d..a53f85c 100644 --- a/pdf/views.py +++ b/pdf/views.py @@ -14,11 +14,12 @@ from backend.mixins import LocalAdminRequired from backend.models import Song +from category.forms import NameForm from category.models import Category -from category.views import CategoryMoveView -from pdf.forms import RequestForm, PDFSongForm, BasePDFSongFormset, FileForm +from pdf.forms import RequestForm, PDFSongForm, BasePDFSongFormset from pdf.generate import generate_pdf_job from pdf.models.request import PDFRequest, RequestType, Status +from tenants.views import AdminMoveView class RequestListView(LocalAdminRequired, ListView): @@ -175,26 +176,15 @@ def get(self, request, *args, **kwargs): ) -class RequestMoveView(CategoryMoveView): +class RequestMoveView(AdminMoveView): """Moves Requests to a different Tenant""" - template_name = "admin/pdf/migrate.html" - formset_class = formset_factory(FileForm, extra=0) - - def initial(self, pks): - query = PDFRequest.objects.filter(id__in=pks) - - form = self.form_class() - initial = [] - for category in query.values_list("id", "file", "title"): - initial.append({"pk": category[0], "file": category[1], "title": category[2]}) - - formset = self.formset_class(initial=initial) - return form, formset + formset_form = NameForm + model = PDFRequest - def action(self, tenant, ids): + def action(self, target, ids): requests = PDFRequest.objects.filter(id__in=ids).distinct() with transaction.atomic(): for request in requests: - request.tenant = tenant + request.tenant = target PDFRequest.objects.bulk_update(requests, ["tenant"]) diff --git a/tenants/forms.py b/tenants/forms.py index ff5a21b..0127efe 100644 --- a/tenants/forms.py +++ b/tenants/forms.py @@ -1,6 +1,6 @@ """Tenant forms""" -from django.forms import ModelForm, CharField +from django.forms import ModelForm, CharField, Form, ModelChoiceField from tenants.models import Tenant, Link @@ -31,3 +31,11 @@ class UserTenantForm(ModelForm): class Meta: model = Tenant fields = "__all__" + + +class ChooseTenantForm(Form): + """Form to choose Tenant""" + + tenant = ModelChoiceField( + queryset=Tenant.objects.all(), label=Tenant._meta.verbose_name, blank=False, empty_label=None + ) diff --git a/tenants/locale/cs/LC_MESSAGES/django.po b/tenants/locale/cs/LC_MESSAGES/django.po index 55453af..ae5223d 100644 --- a/tenants/locale/cs/LC_MESSAGES/django.po +++ b/tenants/locale/cs/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-12-22 21:56+0000\n" +"POT-Creation-Date: 2024-12-25 17:34+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -19,7 +19,7 @@ msgstr "" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " "<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" -#: tenants/admin.py:21 +#: tenants/admin.py:21 tenants/models.py:58 msgid "Tenant" msgstr "Zpěvník" @@ -99,10 +99,21 @@ msgstr "" "Logo zpěvníčku, nepovinné, mělo by mít rozměry kolem 50x50 a být v PNG " "formátu" -#: tenants/templates/tenant/add.html:4 tenants/templates/tenant/add.html:5 -msgid "Tenant Editor" -msgstr "Uprava Zpěvníku" +#: tenants/models.py:59 +msgid "Tenants" +msgstr "Zpěvníky" -#: tenants/views.py:20 -msgid "Tenant was successfully updated" -msgstr "Zpěvník byl úspěšně změněn" +#: tenants/templates/admin/base/migrate.html:4 +#: tenants/templates/admin/base/migrate.html:5 +#, python-format +msgid "Migrating %(source_verbose_name_plural)s to %(target_verbose_name)s" +msgstr "Přesunout %(source_verbose_name_plural)s do %(target_verbose_name)s" + +#: tenants/templates/admin/base/migrate.html:14 +#, python-format +msgid "To %(target_verbose_name)s" +msgstr "Do %(target_verbose_name)s" + +#: tenants/templates/admin/base/migrate.html:16 +msgid "Submit" +msgstr "Odeslat" diff --git a/tenants/models.py b/tenants/models.py index 151b916..796f58a 100644 --- a/tenants/models.py +++ b/tenants/models.py @@ -53,3 +53,7 @@ class Tenant(Model): def __str__(self): return f"{self.name} ({self.id})" + + class Meta: + verbose_name = _("Tenant") + verbose_name_plural = _("Tenants") diff --git a/tenants/templates/admin/base/migrate.html b/tenants/templates/admin/base/migrate.html new file mode 100644 index 0000000..cc0462c --- /dev/null +++ b/tenants/templates/admin/base/migrate.html @@ -0,0 +1,19 @@ +{% extends "admin/base.html" %} +{% load i18n %} + +{% block title %}{% blocktrans %}Migrating {{ source_verbose_name_plural }} to {{ target_verbose_name }}{% endblocktrans %}{% endblock %} +{% block content_title %}{% blocktrans %}Migrating {{ source_verbose_name_plural }} to {{ target_verbose_name }}{% endblocktrans %}{% endblock %} +{% block content %} +
+ {% csrf_token %} + {{ formset.management_form }} + {{ formset.non_form_errors.as_ul }} +

{{ source_verbose_name_plural }}

+ {{ formset.as_div }} +
+

{% blocktrans %}To {{ target_verbose_name }}{% endblocktrans %}

+ {{ form.as_div }} + +
+ {{ form.media }} +{% endblock %} diff --git a/tenants/templates/tenant/add.html b/tenants/templates/tenant/add.html deleted file mode 100644 index d078119..0000000 --- a/tenants/templates/tenant/add.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "base/form.html" %} -{% load i18n %} - -{% block title %} {% trans "Tenant Editor" %} {% endblock %} -{% block header %} {% trans "Tenant Editor" %} {% endblock %} diff --git a/tenants/views.py b/tenants/views.py index 8ac46ac..0c67dc8 100644 --- a/tenants/views.py +++ b/tenants/views.py @@ -1,23 +1,76 @@ """Tenant views""" -from django.contrib.messages.views import SuccessMessageMixin +import abc + +from django.forms import formset_factory +from django.shortcuts import redirect from django.urls import reverse_lazy -from django.views.generic import UpdateView -from django.utils.translation import gettext_lazy as _ +from django.views.generic import TemplateView -from backend.mixins import LocalAdminRequired -from tenants.forms import UserTenantForm +from backend.generic import UniversalUpdateView +from backend.mixins import LocalAdminRequired, SuperAdminRequired +from tenants.forms import UserTenantForm, ChooseTenantForm from tenants.models import Tenant -class TenantUpdateForm(LocalAdminRequired, UpdateView, SuccessMessageMixin): +class TenantUpdateForm(LocalAdminRequired, UniversalUpdateView): """Tenant update view""" form_class = UserTenantForm model = Tenant - template_name = "tenant/add.html" success_url = reverse_lazy("backend:index") - success_message = _("Tenant was successfully updated") def get_object(self, queryset=None): return self.request.tenant + + +class AdminMoveView(SuperAdminRequired, TemplateView): + """Moves Categories to a different Tenant""" + + template_name = "admin/base/migrate.html" + form_class = ChooseTenantForm + formset_form = None + model = None + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.formset_class = formset_factory(self.formset_form, extra=0) + + def initial(self, pks): + """Initial Form population""" + query = self.model.objects.filter(id__in=pks) + + initial = [] + for instance in query: + initial.append({"pk": instance.pk, "name": instance.name}) + + return initial + + def get_context_data(self, **kwargs): + """Appends Form and Formset""" + context = super().get_context_data(**kwargs) + + initial = {} + if "pk" in self.request.GET: + initial = self.initial(self.request.GET.getlist("pk")) + + context["form"] = self.form_class() + context["formset"] = self.formset_class(initial=initial) + context["source_verbose_name_plural"] = self.model._meta.verbose_name_plural + context["target_verbose_name"] = Tenant._meta.verbose_name + return context + + def post(self, request, *args, **kwargs): + """POST request""" + form = self.form_class(request.POST) + formset = self.formset_class(request.POST) + if form.is_valid() and formset.is_valid(): + tenant = form.cleaned_data["tenant"] + ids = [inline_form.cleaned_data["pk"] for inline_form in formset] + self.action(tenant, ids) + return redirect("admin:index") + return self.render_to_response(self.get_context_data(**kwargs)) + + @abc.abstractmethod + def action(self, target, ids): + """What should happen on the move"""