Skip to content

Section admin

section.admin#

PlaylistAdmin #

Bases: InlineActionsModelAdminMixin, ModelAdmin

Source code in section/admin.py
class PlaylistAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin):
    form = PlaylistAdminForm
    change_form_template = "change_form.html"
    list_display = ("name", "_section_count", "_block_count")
    search_fields = ["name", "section__song__artist", "section__song__name"]
    inline_actions = ["add_sections", "edit_sections", "export_csv"]

    def redirect_to_overview(self):
        return redirect(reverse("admin:section_playlist_changelist"))

    def save_model(self, request, obj, form, change):
        # store proces value
        process_csv = obj.process_csv

        # save playlist (so it sure has an id)
        super().save_model(request, obj, form, change)

        # process csv
        if process_csv:
            csv_result = obj._update_sections()

            # create message based on csv_result (CSV_ERROR or CSV_OK)
            if csv_result["status"] == Playlist.CSV_ERROR:
                for message in csv_result["messages"]:
                    messages.add_message(request, messages.ERROR, message)

            elif csv_result["status"] == Playlist.CSV_OK:
                messages.add_message(request, messages.INFO, csv_result["message"])

    def add_sections(self, request, obj, parent_obj=None):
        """Add multiple sections"""
        sections = Section.objects.filter(playlist=obj)
        form = AddSections()
        # Get the info for new sections
        if "_add" in request.POST:
            this_artist = request.POST.get("artist")
            this_name = request.POST.get("name")
            this_tag = request.POST.get("tag")
            this_group = request.POST.get("group")
            new_sections = request.FILES.getlist("files")
            # Create section object for each file
            for section in new_sections:
                new_section = Section.objects.create(
                    playlist=obj, tag=this_tag, group=this_group, filename=section
                )
                try:
                    new_section.clean_fields()
                except ValidationError as e:
                    new_section.delete()
                    file_errors = form.errors.get("file", [])
                    file_errors.append(
                        _("Cannot upload {}: {}").format(str(section), e.messages[0])
                    )
                    form.errors["file"] = file_errors
                    continue

                # Retrieve or create Song object
                song = None
                if this_artist or this_name:
                    song = get_or_create_song(this_artist, this_name)
                new_section.song = song

                file_path = join(settings.MEDIA_ROOT, str(new_section.filename))
                with audioread.audio_open(file_path) as f:
                    new_section.duration = f.duration
                new_section.save()

            obj.save()
            if not form.errors:
                return self.redirect_to_overview()
        # Go back to admin playlist overview
        if "_back" in request.POST:
            return self.redirect_to_overview()
        return render(
            request,
            "add-sections.html",
            context={"playlist": obj, "sections": sections, "form": form},
        )

    def edit_sections(self, request, obj, parent_obj=None):
        """Edit multiple section in a playlist"""
        sections = Section.objects.filter(playlist=obj)
        # Get form data for each section in the playlist
        if "_update" in request.POST:
            for section in sections:
                # Create pre fix to get the right section fields
                pre_fix = str(section.id)
                # Get data and update section
                this_artist = request.POST.get(pre_fix + "_artist")
                this_name = request.POST.get(pre_fix + "_name")

                # Retrieve or create Song object
                song = None
                if this_artist or this_name:
                    song = get_or_create_song(this_artist, this_name)
                section.song = song

                section.start_time = request.POST.get(pre_fix + "_start_time")

                new_duration = float(request.POST.get(pre_fix + "_duration"))
                # while running tests this would throw an error
                if "test" not in sys.argv:
                    # Check if the duration in the csv exceeds the actual duration of the audio file
                    file_path = join(settings.MEDIA_ROOT, str(section.filename))

                    # while running tests this would throw an error
                    with audioread.audio_open(file_path) as f:
                        actual_duration = f.duration
                    if new_duration > actual_duration:
                        # Add or edit this row, but show an error message containing the actual saved duration
                        section.duration = actual_duration

                        messages.error(request, f"Error: The duration of {section.filename} exceeds the actual duration of the audio file and has been set to {actual_duration} seconds.")
                    else:
                        section.duration = new_duration
                else:
                    section.duration = new_duration

                section.tag = request.POST.get(pre_fix + "_tag")
                section.group = request.POST.get(pre_fix + "_group")
                section.save()
            obj.process_csv = False
            obj.save()
            return self.redirect_to_overview()
        if "_back" in request.POST:
            return self.redirect_to_overview()
        return render(
            request,
            "edit-sections.html",
            context={"playlist": obj, "sections": sections},
        )

    def export_csv(self, request, obj, parent_obj=None):
        """Export playlist sections to csv, force download"""

        response = HttpResponse(content_type="text/csv")

        writer = csv.writer(response)
        for section in obj.section_set.all():
            writer.writerow(section._export_admin_csv())

        # force download attachment
        response["Content-Disposition"] = (
            'attachment; filename="playlist_' + str(obj.id) + '.csv"'
        )
        return response

    export_csv.short_description = "Export Sections CSV"

    def export_csv_view(self, request, pk):
        obj = self.get_object(request, pk)
        return self.export_csv(request, obj)

    def get_urls(self):
        urls = super().get_urls()
        custom_urls = [
            path(
                "<int:pk>/export_csv/",
                self.export_csv_view,
                name="section_playlist_export_csv",
            ),
        ]
        return custom_urls + urls

add_sections(request, obj, parent_obj=None) #

Add multiple sections

Source code in section/admin.py
def add_sections(self, request, obj, parent_obj=None):
    """Add multiple sections"""
    sections = Section.objects.filter(playlist=obj)
    form = AddSections()
    # Get the info for new sections
    if "_add" in request.POST:
        this_artist = request.POST.get("artist")
        this_name = request.POST.get("name")
        this_tag = request.POST.get("tag")
        this_group = request.POST.get("group")
        new_sections = request.FILES.getlist("files")
        # Create section object for each file
        for section in new_sections:
            new_section = Section.objects.create(
                playlist=obj, tag=this_tag, group=this_group, filename=section
            )
            try:
                new_section.clean_fields()
            except ValidationError as e:
                new_section.delete()
                file_errors = form.errors.get("file", [])
                file_errors.append(
                    _("Cannot upload {}: {}").format(str(section), e.messages[0])
                )
                form.errors["file"] = file_errors
                continue

            # Retrieve or create Song object
            song = None
            if this_artist or this_name:
                song = get_or_create_song(this_artist, this_name)
            new_section.song = song

            file_path = join(settings.MEDIA_ROOT, str(new_section.filename))
            with audioread.audio_open(file_path) as f:
                new_section.duration = f.duration
            new_section.save()

        obj.save()
        if not form.errors:
            return self.redirect_to_overview()
    # Go back to admin playlist overview
    if "_back" in request.POST:
        return self.redirect_to_overview()
    return render(
        request,
        "add-sections.html",
        context={"playlist": obj, "sections": sections, "form": form},
    )

edit_sections(request, obj, parent_obj=None) #

Edit multiple section in a playlist

Source code in section/admin.py
def edit_sections(self, request, obj, parent_obj=None):
    """Edit multiple section in a playlist"""
    sections = Section.objects.filter(playlist=obj)
    # Get form data for each section in the playlist
    if "_update" in request.POST:
        for section in sections:
            # Create pre fix to get the right section fields
            pre_fix = str(section.id)
            # Get data and update section
            this_artist = request.POST.get(pre_fix + "_artist")
            this_name = request.POST.get(pre_fix + "_name")

            # Retrieve or create Song object
            song = None
            if this_artist or this_name:
                song = get_or_create_song(this_artist, this_name)
            section.song = song

            section.start_time = request.POST.get(pre_fix + "_start_time")

            new_duration = float(request.POST.get(pre_fix + "_duration"))
            # while running tests this would throw an error
            if "test" not in sys.argv:
                # Check if the duration in the csv exceeds the actual duration of the audio file
                file_path = join(settings.MEDIA_ROOT, str(section.filename))

                # while running tests this would throw an error
                with audioread.audio_open(file_path) as f:
                    actual_duration = f.duration
                if new_duration > actual_duration:
                    # Add or edit this row, but show an error message containing the actual saved duration
                    section.duration = actual_duration

                    messages.error(request, f"Error: The duration of {section.filename} exceeds the actual duration of the audio file and has been set to {actual_duration} seconds.")
                else:
                    section.duration = new_duration
            else:
                section.duration = new_duration

            section.tag = request.POST.get(pre_fix + "_tag")
            section.group = request.POST.get(pre_fix + "_group")
            section.save()
        obj.process_csv = False
        obj.save()
        return self.redirect_to_overview()
    if "_back" in request.POST:
        return self.redirect_to_overview()
    return render(
        request,
        "edit-sections.html",
        context={"playlist": obj, "sections": sections},
    )

export_csv(request, obj, parent_obj=None) #

Export playlist sections to csv, force download

Source code in section/admin.py
def export_csv(self, request, obj, parent_obj=None):
    """Export playlist sections to csv, force download"""

    response = HttpResponse(content_type="text/csv")

    writer = csv.writer(response)
    for section in obj.section_set.all():
        writer.writerow(section._export_admin_csv())

    # force download attachment
    response["Content-Disposition"] = (
        'attachment; filename="playlist_' + str(obj.id) + '.csv"'
    )
    return response