Skip to content

Commit

Permalink
Add import functionality from .qsp, Quelea archive
Browse files Browse the repository at this point in the history
  • Loading branch information
mdujava committed Dec 31, 2021
1 parent 83b6068 commit d5a8f8e
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 6 deletions.
7 changes: 6 additions & 1 deletion backend/forms.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Forms for backend app"""
from django.forms import ModelForm, ModelMultipleChoiceField, CheckboxSelectMultiple
from django.forms import ModelForm, ModelMultipleChoiceField, CheckboxSelectMultiple, Form, FileField

from backend.models import Song
from category.models import Category
Expand All @@ -13,3 +13,8 @@ class Meta:
model = Song
# pylint: disable=modelform-uses-exclude
exclude = ["prerendered_web", "prerendered_pdf"]

class UploadFileForm(Form):
"""File upload form with selection of category"""
categories = ModelMultipleChoiceField(widget=CheckboxSelectMultiple, queryset=Category.objects.all())
file = FileField(required=True, allow_empty_file=False)
2 changes: 2 additions & 0 deletions backend/menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
reverse("category:list")),
MenuItem(_("Add a song"),
reverse("backend:add")),
MenuItem(_("Import Quelue archive"),
reverse("backend:import")),
MenuItem(_("Add Songbook"),
reverse("category:add")),
MenuItem(_("Analytics"),
Expand Down
5 changes: 5 additions & 0 deletions backend/templates/songs/import.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends "base/form.html" %}
{% load i18n %}

{% block title %} {% trans "Song Editor" %} {% endblock %}
{% block header %} {% trans "Song Editor" %} {% endblock %}
3 changes: 2 additions & 1 deletion backend/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
from django.urls import path

from backend.views import SongCreateView, SongUpdateView, SongDeleteView, SongsDatatableView, \
IndexSongListView
IndexSongListView, UploadView

urlpatterns = [
path('', IndexSongListView.as_view(), name="index"),
path('add', SongCreateView.as_view(), name="add"),
path('edit/<int:pk>', SongUpdateView.as_view(), name="edit"),
path('delete/<int:pk>', SongDeleteView.as_view(), name="delete"),
path('api/songs', SongsDatatableView.as_view(), name="songs"),
path('import', UploadView.as_view(), name="import"),
]
63 changes: 61 additions & 2 deletions backend/views.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
"""Views for backend app"""
import json
from typing import Dict
import zipfile
import xml.etree.ElementTree as ET

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.messages.views import SuccessMessageMixin
from django.core.serializers.json import DjangoJSONEncoder
from django.forms import model_to_dict
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy, reverse
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _, gettext_noop
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
from django.views.generic import ListView, CreateView, UpdateView, DeleteView, FormView
from django_datatables_view.base_datatable_view import BaseDatatableView

from analytics.views import AnalyticsMixin
from backend.forms import SongForm
from backend.forms import SongForm, UploadFileForm
from backend.models import Song
from backend.utils import regenerate_pdf, regenerate_prerender
from category.models import Category
Expand Down Expand Up @@ -120,3 +123,59 @@ class SongsDatatableView(BaseDatatableView):
model = Song
max_display_length = 500
columns = ["name", "author", "capo"]


def quelea_song_import(cleaned_data):
"""Read data from file in cleaned_data, and import all songs in qsp archive"""

with zipfile.ZipFile(cleaned_data['file']) as file:
songs = {name: file.read(name) for name in file.namelist()}

lyrics = []

for name in songs.keys():
song = ET.fromstring(songs[name])
lyrics_lines = []
for child in song.findall('.//lyrics'):
if child.text is not None:
lyrics_lines.append(child.text)

lyrics.append((name, '\n\n'.join(lyrics_lines)))

num_songs = 0

for (name, text) in lyrics:
clean_name = name.rstrip('.xml')

song_obj = Song(name=clean_name, text=text)
song_obj.save()

list_ids = [f.pk for f in cleaned_data['categories']]

song_obj.categories.set(list_ids)
regenerate_pdf(song_obj)
regenerate_prerender(song_obj)
num_songs += 1

return num_songs


@method_decorator(login_required, name='dispatch')
class UploadView(FormView):
"""View to recieve qsp file and import all songs"""

form_class = UploadFileForm
template_name = 'songs/import.html'

success_url = reverse_lazy('backend:index')

# def get_success_url(self):
# regenerate_pdf(self.object)
# regenerate_prerender(self.object)
# return super().get_success_url()

def form_valid(self, form):
num = quelea_song_import(form.cleaned_data)
messages.success(self.request, f"{num} songs imported.")
return super().form_valid(form)

8 changes: 6 additions & 2 deletions frontend/templates/base/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
{% load bootstrap4 %}

{% block framed_body %}
<form method="post">
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post">
{% else %}
<form method="post">
{% endif %}
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">{% trans "Submit" %}</button>
{% endbuttons %}
</form>
{{ form.media }}
{% endblock %}
{% endblock %}

0 comments on commit d5a8f8e

Please sign in to comment.