From 07d72d799d4247f53ce17799b6af0cf75333d2f1 Mon Sep 17 00:00:00 2001 From: bkfox Date: Fri, 19 Apr 2024 15:06:23 +0200 Subject: [PATCH] rm file --- aircox/controllers/sound_monitor.py | 8 +- aircox/forms.py | 102 ------------------ aircox/forms/__init__.py | 23 ++++ aircox/forms/auth.py | 20 ++++ aircox/forms/episode.py | 34 ++++++ aircox/forms/page.py | 37 +++++++ aircox/forms/program.py | 11 ++ aircox/forms/sound.py | 26 +++++ aircox/forms/track.py | 23 ++++ aircox/migrations/0015_program_editors.py | 11 +- ...ion_legal_label_alter_schedule_timezone.py | 2 +- ...d_options_remove_sound_episode_and_more.py | 4 +- ...arent_remove_staticpage_parent_and_more.py | 5 - aircox/models/file.py | 7 +- aircox/models/program.py | 2 +- aircox/models/sound.py | 2 +- aircox/permissions.py | 14 +-- aircox/serializers/__init__.py | 2 + aircox/templates/aircox/base.html | 16 +-- aircox/templates/aircox/basepage_detail.html | 10 -- aircox/templates/aircox/basepage_list.html | 10 +- aircox/templates/aircox/dashboard/base.html | 7 +- .../dashboard/widgets/soundlist_editor.html | 4 +- aircox/templates/aircox/episode_form.html | 2 +- aircox/templates/aircox/errors/base.html | 2 +- aircox/templates/aircox/forms/form_field.html | 25 +++++ aircox/templates/aircox/forms/formset.html | 54 ++++++++++ .../templates/aircox/forms/v_form_field.html | 24 +++++ aircox/templates/aircox/home.html | 2 +- aircox/templates/aircox/page_detail.html | 2 +- aircox/templates/aircox/page_form.html | 7 +- aircox/templates/aircox/program_form.html | 39 ++----- aircox/templates/aircox/public.html | 17 +++ .../aircox/widgets/autocomplete.html | 4 + .../aircox/widgets/episode_item.html | 33 ++---- .../aircox/{dashboard => }/widgets/nav.html | 0 .../aircox/widgets/usergroup_formset.html | 10 ++ aircox/templatetags/aircox.py | 6 ++ aircox/urls.py | 1 + aircox/views/dashboard.py | 8 +- aircox/views/episode.py | 2 +- aircox/views/page.py | 4 - aircox/views/program.py | 36 ++++++- aircox/viewsets.py | 43 ++++++-- aircox_streamer/controllers/sources.py | 4 +- aircox_streamer/controllers/streamer.py | 2 + aircox_streamer/serializers.py | 6 +- .../aircox_streamer/scripts/station.liq | 2 +- .../templates/aircox_streamer/streamer.html | 15 ++- aircox_streamer/urls.py | 34 +++--- aircox_streamer/views.py | 6 +- aircox_streamer/viewsets.py | 4 +- assets/package.json | 3 +- assets/src/components/AAutocomplete.vue | 2 + docs/AIRCOX_Manuel d'utilisation.docx | Bin 724275 -> 1248658 bytes docs/AIRCOX_Manuel d'utilisation.pdf | Bin 1141931 -> 1328689 bytes instance/settings/base.py | 2 +- instance/urls.py | 18 ++-- radiocampus/templates/aircox/base.html | 2 +- requirements.txt | 8 +- 60 files changed, 503 insertions(+), 306 deletions(-) delete mode 100644 aircox/forms.py create mode 100644 aircox/forms/__init__.py create mode 100644 aircox/forms/auth.py create mode 100644 aircox/forms/episode.py create mode 100644 aircox/forms/page.py create mode 100644 aircox/forms/program.py create mode 100644 aircox/forms/sound.py create mode 100644 aircox/forms/track.py delete mode 100644 aircox/templates/aircox/basepage_detail.html create mode 100644 aircox/templates/aircox/forms/form_field.html create mode 100644 aircox/templates/aircox/forms/formset.html create mode 100644 aircox/templates/aircox/forms/v_form_field.html create mode 100644 aircox/templates/aircox/public.html create mode 100644 aircox/templates/aircox/widgets/autocomplete.html rename aircox/templates/aircox/{dashboard => }/widgets/nav.html (100%) create mode 100644 aircox/templates/aircox/widgets/usergroup_formset.html diff --git a/aircox/controllers/sound_monitor.py b/aircox/controllers/sound_monitor.py index 0822ed0..70a0880 100644 --- a/aircox/controllers/sound_monitor.py +++ b/aircox/controllers/sound_monitor.py @@ -233,12 +233,12 @@ class SoundMonitor: if not program.ensure_dir(subdir): return - subdir = os.path.join(program.abspath, subdir) + abs_subdir = os.path.join(program.abspath, subdir) sounds = [] # sounds in directory - for path in os.listdir(subdir): - path = os.path.join(subdir, path) + for path in os.listdir(abs_subdir): + path = os.path.join(abs_subdir, path) if not path.endswith(settings.SOUND_FILE_EXT): continue @@ -247,7 +247,7 @@ class SoundMonitor: sounds.append(sound_file.sound.pk) # sounds in db & unchecked - sounds = Sound.objects.filter(file__startswith=subdir).exclude(pk__in=sounds) + sounds = Sound.objects.filter(file__startswith=program.path).exclude(pk__in=sounds) self.check_sounds(sounds, program=program) def check_sounds(self, qs, **sync_kwargs): diff --git a/aircox/forms.py b/aircox/forms.py deleted file mode 100644 index ac6ae04..0000000 --- a/aircox/forms.py +++ /dev/null @@ -1,102 +0,0 @@ -from django import forms -from django.forms.models import modelformset_factory - - -from aircox import models - - -__all__ = ("CommentForm", "PageForm", "ProgramForm", "EpisodeForm", "SoundForm", "EpisodeSoundFormSet", "TrackFormSet") - - -class CommentForm(forms.ModelForm): - nickname = forms.CharField() - email = forms.EmailField(required=False) - content = forms.CharField(widget=forms.Textarea()) - - nickname.widget.attrs.update({"class": "input"}) - email.widget.attrs.update({"class": "input"}) - content.widget.attrs.update({"class": "textarea"}) - - class Meta: - model = models.Comment - fields = ["nickname", "email", "content"] - - -class ImageForm(forms.Form): - file = forms.ImageField() - - -class PageForm(forms.ModelForm): - class Meta: - fields = ("title", "category", "status", "cover", "content") - model = models.Page - - -class ChildPageForm(forms.ModelForm): - class Meta: - fields = ("title", "status", "cover", "content") - model = models.Page - - -class ProgramForm(PageForm): - class Meta: - fields = PageForm.Meta.fields - model = models.Program - - -class EpisodeForm(PageForm): - class Meta: - model = models.Episode - fields = ChildPageForm.Meta.fields - - -class SoundForm(forms.ModelForm): - """SoundForm used in EpisodeUpdateView.""" - - class Meta: - model = models.Sound - fields = ["name", "program", "file", "broadcast", "duration", "is_public", "is_downloadable"] - - -class SoundCreateForm(forms.ModelForm): - """SoundForm used in EpisodeUpdateView.""" - - class Meta: - model = models.Sound - fields = ["name", "program", "file", "broadcast", "is_public", "is_downloadable"] - widgets = {"program": forms.HiddenInput()} - - -TrackFormSet = modelformset_factory( - models.Track, - fields=[ - "position", - "episode", - "artist", - "title", - "tags", - ], - widgets={"episode": forms.HiddenInput(), "position": forms.HiddenInput()}, - can_delete=True, - extra=0, -) -"""Track formset used in EpisodeUpdateView.""" - - -EpisodeSoundFormSet = modelformset_factory( - models.EpisodeSound, - fields=( - "position", - "episode", - "sound", - "broadcast", - ), - widgets={ - "broadcast": forms.CheckboxInput(), - "episode": forms.HiddenInput(), - # "sound": forms.HiddenInput(), - "position": forms.HiddenInput(), - }, - can_delete=True, - extra=0, -) diff --git a/aircox/forms/__init__.py b/aircox/forms/__init__.py new file mode 100644 index 0000000..c15fee5 --- /dev/null +++ b/aircox/forms/__init__.py @@ -0,0 +1,23 @@ +from . import widgets + +from .auth import UserGroupFormSet +from .episode import EpisodeForm, EpisodeSoundFormSet +from .program import ProgramForm +from .page import CommentForm, ImageForm, PageForm, ChildPageForm +from .sound import SoundForm, SoundCreateForm + + +__all__ = ( + widgets, + # ---- forms + EpisodeForm, + EpisodeSoundFormSet, + ProgramForm, + CommentForm, + ImageForm, + PageForm, + ChildPageForm, + SoundForm, + SoundCreateForm, + UserGroupFormSet, +) diff --git a/aircox/forms/auth.py b/aircox/forms/auth.py new file mode 100644 index 0000000..1c8ba33 --- /dev/null +++ b/aircox/forms/auth.py @@ -0,0 +1,20 @@ +from django import forms +from django.forms.models import modelformset_factory +from django.contrib.auth.models import User + +from aircox.forms import widgets + + +__all__ = ("UserGroupFormSet",) + + +UserGroupFormSet = modelformset_factory( + User.groups.through, + fields=("group", "user"), + widgets={ + "group": forms.HiddenInput(), + "user": widgets.VueAutoComplete("api:usergroup-autocomplete", lookup="username"), + }, + extra=0, + can_delete=True, +) diff --git a/aircox/forms/episode.py b/aircox/forms/episode.py new file mode 100644 index 0000000..b288656 --- /dev/null +++ b/aircox/forms/episode.py @@ -0,0 +1,34 @@ +from django import forms +from django.forms.models import modelformset_factory + +from aircox import models +from .page import ChildPageForm + + +__all__ = ("EpisodeForm", "EpisodeSoundFormSet") + + +class EpisodeForm(ChildPageForm): + class Meta: + model = models.Episode + fields = ChildPageForm.Meta.fields + + +EpisodeSoundFormSet = modelformset_factory( + models.EpisodeSound, + fields=( + "position", + "episode", + "sound", + "broadcast", + ), + widgets={ + "broadcast": forms.CheckboxInput(), + "episode": forms.HiddenInput(), + # "sound": forms.HiddenInput(), + "position": forms.HiddenInput(), + }, + can_delete=True, + extra=0, +) +"""Formset used in EpisodeUpdateView.""" diff --git a/aircox/forms/page.py b/aircox/forms/page.py new file mode 100644 index 0000000..8af0aa9 --- /dev/null +++ b/aircox/forms/page.py @@ -0,0 +1,37 @@ +from django import forms + + +from aircox import models + + +__all__ = ("CommentForm", "ImageForm", "PageForm", "ChildPageForm") + + +class CommentForm(forms.ModelForm): + nickname = forms.CharField() + email = forms.EmailField(required=False) + content = forms.CharField(widget=forms.Textarea()) + + nickname.widget.attrs.update({"class": "input"}) + email.widget.attrs.update({"class": "input"}) + content.widget.attrs.update({"class": "textarea"}) + + class Meta: + model = models.Comment + fields = ["nickname", "email", "content"] + + +class ImageForm(forms.Form): + file = forms.ImageField() + + +class PageForm(forms.ModelForm): + class Meta: + fields = ("title", "category", "status", "cover", "content") + model = models.Page + + +class ChildPageForm(forms.ModelForm): + class Meta: + fields = ("title", "status", "cover", "content") + model = models.Page diff --git a/aircox/forms/program.py b/aircox/forms/program.py new file mode 100644 index 0000000..65d349b --- /dev/null +++ b/aircox/forms/program.py @@ -0,0 +1,11 @@ +from aircox import models +from .page import PageForm + + +__all__ = ("ProgramForm",) + + +class ProgramForm(PageForm): + class Meta: + fields = PageForm.Meta.fields + model = models.Program diff --git a/aircox/forms/sound.py b/aircox/forms/sound.py new file mode 100644 index 0000000..eb7388b --- /dev/null +++ b/aircox/forms/sound.py @@ -0,0 +1,26 @@ +from django import forms + +from aircox import models + + +__all__ = ( + "SoundForm", + "SoundCreateForm", +) + + +class SoundForm(forms.ModelForm): + """SoundForm used in EpisodeUpdateView.""" + + class Meta: + model = models.Sound + fields = ["name", "program", "file", "broadcast", "duration", "is_public", "is_downloadable"] + + +class SoundCreateForm(forms.ModelForm): + """SoundForm used in EpisodeUpdateView.""" + + class Meta: + model = models.Sound + fields = ["name", "program", "file", "broadcast", "is_public", "is_downloadable"] + widgets = {"program": forms.HiddenInput()} diff --git a/aircox/forms/track.py b/aircox/forms/track.py new file mode 100644 index 0000000..ecdf3ea --- /dev/null +++ b/aircox/forms/track.py @@ -0,0 +1,23 @@ +from django import forms +from django.forms.models import modelformset_factory + +from aircox import models + + +__all__ = ("TrackFormSet",) + + +TrackFormSet = modelformset_factory( + models.Track, + fields=[ + "position", + "episode", + "artist", + "title", + "tags", + ], + widgets={"episode": forms.HiddenInput(), "position": forms.HiddenInput()}, + can_delete=True, + extra=0, +) +"""Track formset used in EpisodeUpdateView.""" diff --git a/aircox/migrations/0015_program_editors.py b/aircox/migrations/0015_program_editors.py index 23737cc..1a18ab5 100644 --- a/aircox/migrations/0015_program_editors.py +++ b/aircox/migrations/0015_program_editors.py @@ -4,14 +4,6 @@ from django.db import migrations, models import django.db.models.deletion -def init_groups_and_permissions(app, schema_editor): - from aircox.permissions import program_permissions - - Program = app.get_model("aircox", "Program") - for program in Program.objects.all(): - program_permissions.init(program) - - class Migration(migrations.Migration): dependencies = [ ("auth", "0012_alter_user_first_name_max_length"), @@ -25,10 +17,9 @@ class Migration(migrations.Migration): field=models.ForeignKey( blank=True, null=True, - on_delete=django.db.models.deletion.CASCADE, + on_delete=django.db.models.deletion.SET_NULL, to="auth.group", verbose_name="editors", ), ), - migrations.RunPython(init_groups_and_permissions), ] diff --git a/aircox/migrations/0023_station_legal_label_alter_schedule_timezone.py b/aircox/migrations/0023_station_legal_label_alter_schedule_timezone.py index dba7c62..814fba5 100644 --- a/aircox/migrations/0023_station_legal_label_alter_schedule_timezone.py +++ b/aircox/migrations/0023_station_legal_label_alter_schedule_timezone.py @@ -7,7 +7,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("filer", "0017_image__transparent"), - ("aircox", "0021_alter_schedule_timezone"), + ("aircox", "0022_set_group_ownership"), ] operations = [ diff --git a/aircox/migrations/0026_alter_sound_options_remove_sound_episode_and_more.py b/aircox/migrations/0026_alter_sound_options_remove_sound_episode_and_more.py index ddefa11..83d1916 100644 --- a/aircox/migrations/0026_alter_sound_options_remove_sound_episode_and_more.py +++ b/aircox/migrations/0026_alter_sound_options_remove_sound_episode_and_more.py @@ -10,7 +10,7 @@ sounds_info = {} def get_sounds_info(apps, schema_editor): Sound = apps.get_model("aircox", "Sound") - objs = Sound.objects.filter(episode__isnull=False).values( + objs = Sound.objects.filter().values( "pk", "episode_id", "position", @@ -36,7 +36,7 @@ def restore_sounds_info(apps, schema_editor): sound.broadcast = info["type"] == TYPE_ARCHIVE sound.is_removed = info["type"] == TYPE_REMOVED sounds.append(sound) - if not sound.is_removed: + if not sound.is_removed and info["episode_id"]: obj = EpisodeSound( sound=sound, episode_id=info["episode_id"], diff --git a/aircox/migrations/0027_remove_page_parent_remove_staticpage_parent_and_more.py b/aircox/migrations/0027_remove_page_parent_remove_staticpage_parent_and_more.py index a927021..b9a397f 100644 --- a/aircox/migrations/0027_remove_page_parent_remove_staticpage_parent_and_more.py +++ b/aircox/migrations/0027_remove_page_parent_remove_staticpage_parent_and_more.py @@ -33,11 +33,6 @@ def _restore_for_objs(objs): return updated -def set_group_ownership(*args): - for program in Program.objects.all(): - program.set_group_ownership() - - class Migration(migrations.Migration): dependencies = [ ("aircox", "0026_alter_sound_options_remove_sound_episode_and_more"), diff --git a/aircox/models/file.py b/aircox/models/file.py index 24ff1c3..8d35d95 100644 --- a/aircox/models/file.py +++ b/aircox/models/file.py @@ -107,7 +107,10 @@ class File(models.Model): def file_updated(self): """Return True when file has been updated on filesystem.""" - return self.mtime != self.get_mtime() or self.is_removed != (not self.file_exists()) + exists = self.file_exists() + if self.is_removed != (not exists): + return True + return exists and self.mtime != self.get_mtime() def file_exists(self): """Return true if the file still exists.""" @@ -130,7 +133,7 @@ class File(models.Model): name = name.replace("_", " ").strip() is_removed = not self.file_exists() - mtime = self.get_mtime() + mtime = (not is_removed and self.get_mtime()) or None changed = is_removed != self.is_removed or mtime != self.mtime or name != self.name self.name, self.is_removed, self.mtime = name, is_removed, mtime diff --git a/aircox/models/program.py b/aircox/models/program.py index c204971..f0fedb0 100644 --- a/aircox/models/program.py +++ b/aircox/models/program.py @@ -63,7 +63,7 @@ class Program(Page): default=True, help_text=_("update later diffusions according to schedule changes"), ) - editors_group = models.ForeignKey(Group, models.CASCADE, blank=True, null=True, verbose_name=_("editors")) + editors_group = models.ForeignKey(Group, models.CASCADE, verbose_name=_("editors")) objects = ProgramQuerySet.as_manager() detail_url_name = "program-detail" diff --git a/aircox/models/sound.py b/aircox/models/sound.py index 736fb3b..afe4ee1 100644 --- a/aircox/models/sound.py +++ b/aircox/models/sound.py @@ -24,7 +24,7 @@ class SoundQuerySet(FileQuerySet): def broadcast(self): """Return sounds that are archives.""" - return self.filter(broadcast=True) + return self.filter(broadcast=True, is_removed=False) def playlist(self, order_by="file"): """Return files absolute paths as a flat list (exclude sound without diff --git a/aircox/permissions.py b/aircox/permissions.py index 40d6df1..22b3445 100644 --- a/aircox/permissions.py +++ b/aircox/permissions.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from .models import Program -__all__ = ("PagePermissions", "program_permissions") +__all__ = ("PagePermissions", "program") class PagePermissions: @@ -30,7 +30,6 @@ class PagePermissions: """Return True wether if user can edit Program or its children.""" from .models.page import ChildPage - breakpoint() if isinstance(obj, ChildPage): obj = obj.parent_subclass @@ -43,20 +42,23 @@ class PagePermissions: perm = self.perms_codename_format.format(self=self, perm=perm) return user.has_perm(perm) - # TODO: bulk init - def init(self, obj): + def init(self, obj, model=None): """Initialize permissions for the provided obj.""" + updated = False created_groups = [] # init groups for infos in self.groups: group = getattr(obj, infos["field"]) + if obj.pk == 12417: + breakpoint() if not group: group, created = self.init_group(obj, infos) setattr(obj, infos["field"], group.pk) + updated = True created and created_groups.append((group, infos)) - if created_groups: + if updated: obj.save() # init perms @@ -79,4 +81,4 @@ class PagePermissions: group.permissions.add(perm) -program_permissions = PagePermissions(Program) +program = PagePermissions(Program) diff --git a/aircox/serializers/__init__.py b/aircox/serializers/__init__.py index 1558f66..f450d35 100644 --- a/aircox/serializers/__init__.py +++ b/aircox/serializers/__init__.py @@ -1,3 +1,4 @@ +from . import auth from .admin import TrackSerializer, UserSettingsSerializer from .episode import EpisodeSoundSerializer, EpisodeSerializer from .log import LogInfo, LogInfoSerializer @@ -5,6 +6,7 @@ from .page import CommentSerializer from .sound import SoundSerializer __all__ = ( + "auth", "CommentSerializer", "LogInfo", "LogInfoSerializer", diff --git a/aircox/templates/aircox/base.html b/aircox/templates/aircox/base.html index f100b13..08e81e6 100644 --- a/aircox/templates/aircox/base.html +++ b/aircox/templates/aircox/base.html @@ -10,6 +10,7 @@ Usefull context: {% endcomment %} + {% block head %} @@ -27,17 +28,17 @@ Usefull context: {% endblock %} - {% block head_title %} - {% if page and page.title %}{{ page.title }} — {{ station.name }} - {% else %}{{ station.name }} + {% block head-title %} + {% if page and page.title %}{{ page.title }} — {% endif %} + {{ station.name }} {% endblock %} - {% block head_extra %}{% endblock %} + {% endblock %} - {% block body-head %}{% endblock %} + {% block body %}