diff --git a/blechreiz/settings.py b/blechreiz/settings.py index e072f95..49c03d7 100644 --- a/blechreiz/settings.py +++ b/blechreiz/settings.py @@ -160,7 +160,7 @@ INSTALLED_APPS = ( 'eventplanner', # Event Management 'simpleforum', # Messages ( Forum ) 'location_field', # custom location field used in Event Management - + 'scoremanager', # manager of scores, repertoire etc. #'imagestore', #'sorl.thumbnail', #'tagging' diff --git a/blechreiz/urls.py b/blechreiz/urls.py index fcfb313..9640e25 100644 --- a/blechreiz/urls.py +++ b/blechreiz/urls.py @@ -8,7 +8,7 @@ import eventplanner.urls import musicians.urls #import imagestore.urls import website.urls - +import scoremanager.urls import settings from django.conf.urls.static import static @@ -19,6 +19,7 @@ urlpatterns = patterns('', url(r'^', include(website.urls) ), url(r'^events/', include( eventplanner.urls.urlpatterns) ), url(r'^musicians/', include( musicians.urls.urlpatterns) ), + url(r'^scores/', include( scoremanager.urls.urlpatterns) ), url(r'^messages/$', simpleforum.views.message_view ), url(r'^admin/', include(admin.site.urls) ), url(r'^location_field/', include('location_field.urls')), diff --git a/location_field/widgets.py b/location_field/widgets.py index c55c6f2..304a31f 100644 --- a/location_field/widgets.py +++ b/location_field/widgets.py @@ -48,7 +48,7 @@ class LocationWidget(widgets.TextInput): } # Use schemaless URL so it works with both, http and https websites js = ( - '//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js', # jquery + '//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js', # jquery '//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js', '//maps.google.com/maps/api/js?sensor=false&language=de', '/static/js/bindWithDelay.js', diff --git a/scoremanager/PdfImage.py b/scoremanager/PdfImage.py new file mode 100644 index 0000000..17978d2 --- /dev/null +++ b/scoremanager/PdfImage.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# encoding: utf-8 +"""matplotlib_example.py + An simple example of how to insert matplotlib generated figures + into a ReportLab platypus document. +""" + +import matplotlib +matplotlib.use('PDF') +import matplotlib.pyplot as plt +import cStringIO + +from pdfrw import PdfReader +from pdfrw.buildxobj import pagexobj +from pdfrw.toreportlab import makerl + +from reportlab.platypus import Flowable +from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT +from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer +from reportlab.lib.styles import getSampleStyleSheet +from reportlab.rl_config import defaultPageSize +from reportlab.lib.units import inch + +PAGE_HEIGHT=defaultPageSize[1]; PAGE_WIDTH=defaultPageSize[0] +styles = getSampleStyleSheet() + +class PdfImage(Flowable): + """PdfImage wraps the first page from a PDF file as a Flowable +which can be included into a ReportLab Platypus document. +Based on the vectorpdf extension in rst2pdf (http://code.google.com/p/rst2pdf/)""" + + def __init__(self, filename_or_object, width=None, height=None, kind='direct'): + from reportlab.lib.units import inch + # If using StringIO buffer, set pointer to begining + if hasattr(filename_or_object, 'read'): + filename_or_object.seek(0) + page = PdfReader(filename_or_object, decompress=False).pages[0] + self.xobj = pagexobj(page) + self.imageWidth = width + self.imageHeight = height + x1, y1, x2, y2 = self.xobj.BBox + + self._w, self._h = x2 - x1, y2 - y1 + if not self.imageWidth: + self.imageWidth = self._w + if not self.imageHeight: + self.imageHeight = self._h + self.__ratio = float(self.imageWidth)/self.imageHeight + if kind in ['direct','absolute'] or width==None or height==None: + self.drawWidth = width or self.imageWidth + self.drawHeight = height or self.imageHeight + elif kind in ['bound','proportional']: + factor = min(float(width)/self._w,float(height)/self._h) + self.drawWidth = self._w*factor + self.drawHeight = self._h*factor + + def wrap(self, aW, aH): + return self.drawWidth, self.drawHeight + + def drawOn(self, canv, x, y, _sW=0): + if _sW > 0 and hasattr(self, 'hAlign'): + a = self.hAlign + if a in ('CENTER', 'CENTRE', TA_CENTER): + x += 0.5*_sW + elif a in ('RIGHT', TA_RIGHT): + x += _sW + elif a not in ('LEFT', TA_LEFT): + raise ValueError("Bad hAlign value " + str(a)) + + xobj = self.xobj + xobj_name = makerl(canv._doc, xobj) + + xscale = self.drawWidth/self._w + yscale = self.drawHeight/self._h + + x -= xobj.BBox[0] * xscale + y -= xobj.BBox[1] * yscale + + canv.saveState() + canv.translate(x, y) + canv.scale(xscale, yscale) + canv.doForm(xobj_name) + canv.restoreState() + +Title = "Hello world" +pageinfo = "platypus example" +def myFirstPage(canvas, doc): + canvas.saveState() + canvas.setFont('Times-Bold',16) + canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title) + canvas.setFont('Times-Roman',9) + canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo) + canvas.restoreState() + + +def myLaterPages(canvas, doc): + canvas.saveState() + canvas.setFont('Times-Roman',9) + canvas.drawString(inch, 0.75 * inch, "Page %d %s" % (doc.page, pageinfo)) + canvas.restoreState() + +def go(): + fig = plt.figure(figsize=(4, 3)) + plt.plot([1,2,3,4]) + plt.ylabel('some numbers') + imgdata = cStringIO.StringIO() + fig.savefig(imgdata,format='PDF') + doc = SimpleDocTemplate("document.pdf") + Story = [Spacer(1,2*inch)] + style = styles["Normal"] + for i in range(5): + bogustext = ("This is Paragraph number %s. " % i) *20 + p = Paragraph(bogustext, style) + Story.append(p) + Story.append(Spacer(1,0.2*inch)) + pi = PdfImage(imgdata) + Story.append(pi) + Story.append(Spacer(1,0.2*inch)) + doc.build(Story, onFirstPage=myFirstPage, onLaterPages=myLaterPages) + + +if __name__ == '__main__': + go() \ No newline at end of file diff --git a/scoremanager/__init__.py b/scoremanager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/scoremanager/admin.py b/scoremanager/admin.py new file mode 100644 index 0000000..f9662f3 --- /dev/null +++ b/scoremanager/admin.py @@ -0,0 +1,22 @@ +from django.contrib import admin +from scoremanager.models import Piece, Score, Recording, BookLocation + + +class ScoreInline( admin.StackedInline ): + model = Score + extra = 1 + + +class RecordingInline( admin.StackedInline ): + model = Recording + extra = 1 + +class BookLocationInline( admin.StackedInline ): + model = BookLocation + extra = 1 + +class PieceAdmin (admin.ModelAdmin): + inlines = (BookLocationInline, ScoreInline, RecordingInline ) + + +admin.site.register( Piece, PieceAdmin ) \ No newline at end of file diff --git a/scoremanager/models.py b/scoremanager/models.py new file mode 100644 index 0000000..d1d920d --- /dev/null +++ b/scoremanager/models.py @@ -0,0 +1,183 @@ +import os +import re + +from django.db import models +from django.db.models.fields.related import ForeignKey +from django.utils.translation import ugettext as _ +from django.contrib.auth.models import User +from django.utils.safestring import mark_safe + + +############################# Helper Functions ####################################################### + + +def space_to_camelcase(value): + def camelcase(): + yield type(value).lower + while True: + yield type(value).capitalize + + c = camelcase() + return "".join(c.next()(x) if x else '_' for x in value.split() ) + + +def score_filename(score,original_name): + fileExtension = os.path.splitext(original_name)[1] + filename = "scores/" + filename += space_to_camelcase( score.piece.title ) + "/" + filename += space_to_camelcase( score.score_type ) + filename += fileExtension + return filename + + +def recordingFileName( recording, originalName ): + fileExtension = os.path.splitext(originalName)[1] + filename = "recordings/" + filename += space_to_camelcase( recording.piece.title ) + "/" + filename += space_to_camelcase( recording.artist ) + filename += fileExtension + return filename + +_ +####################################################################################################### + + +class Piece (models.Model): + title = models.CharField( max_length = 255, verbose_name = _("title"), unique=True ) + composer = models.CharField( max_length = 255, blank = True, verbose_name = _("composer") ) + repertoire_nr = models.IntegerField( null = True, blank=True, unique=True, default = None ) + + def __unicode__(self): + res = self.title + return res + + def isInRepertoire(self): + return self.repertoire_nr is not None + + def get_score_for_user(self, user): + try: + return ScoreUserMapping.objects.get( user=user, score__in = self.scores.all() ).score + except Piece.DoesNotExist: + if len( self.scores.all() ) > 0: + return self.scores.all()[0] + else: + return None + + + @staticmethod + def getRepertoire(): + return Piece.objects.filter( repertoire_nr__isnull = False ).order_by( 'repertoire_nr' ) + + class Meta: + permissions = ( + ("manage_scores", "Administrate and manage scores"), + ) + + + +class BookLocation ( models.Model ): + piece = models.ForeignKey ( 'Piece' ) + book = models.CharField( max_length = 100, blank = False, verbose_name = _("Buch") ) + page = models.IntegerField( verbose_name = _("page") ) + + def __unicode__(self): + return "%s, %d" % ( self.book, self.page ) + + + +class Score( models.Model ): + piece = ForeignKey('Piece', related_name="scores") + score_type = models.CharField(max_length=100, verbose_name="score type" ) # for example partitur, unterstimmen ... + file = models.FileField(upload_to=score_filename, verbose_name = _("file") ) + uploaded_by = ForeignKey( User, verbose_name=_("uploaded_by") ) + + @property + def image_file_name(self): + return os.path.splitext("image_cache/" + str(self.file))[0] + ".jpg" + + @staticmethod + def pdf2jpg(source_file, target_file, resolution = 100, crop = 15): + from wand.image import Image + ret = True + try: + with Image(filename=source_file, resolution=(resolution,resolution)) as img: + img.crop( crop,crop, width= img.width - 2*crop, height = int( 0.5 * img.height) - 2*crop ) + img.format = 'jpeg' + img.save(filename = target_file) + except Exception as e: + print(e) + ret = False + + return ret + + def get_image_file(self): + from django.conf import settings + + inputFile = settings.MEDIA_ROOT + "/" + str(self.file) + cacheFile = settings.MEDIA_ROOT + "/" + str(self.image_file_name) + # Create a jpg for this score, if it does not exist yet + if not os.path.exists( cacheFile ): + if not os.path.exists( os.path.dirname( cacheFile )): + os.makedirs( os.path.dirname(cacheFile) ) + Score.pdf2jpg ( inputFile, cacheFile ) + + return self.image_file_name + + def is_active_score(self,user): + return len( ScoreUserMapping.objects.filter( score= self, user=user ) ) > 0 + + class Meta: + unique_together = (("piece", "score_type"),) + ordering = ['score_type'] + + + + +class Recording( models.Model ): + piece = ForeignKey( 'Piece', related_name='recordings' ) + artist = models.CharField( max_length = 100, verbose_name = _("Artist") ) + file = models.FileField( upload_to = recordingFileName, verbose_name = _("file") ) + uploaded_by = ForeignKey( User, verbose_name=_("uploaded_by") ) + + class Meta: + unique_together = (("piece", "artist"),) + ordering = ['artist'] + + + +class YoutubeRecording( models.Model ): + piece = models.ForeignKey('Piece', related_name="youtubeLinks" ) + link = models.CharField( max_length = 300, blank=False) + uploaded_by = ForeignKey( User, verbose_name=_("uploaded_by") ) + + youtubeRegex = re.compile( u'(?:https://)?(?:http://)?www.youtube.(?:com|de)/watch\?v=(?P[-\w]*)' ) + + @property + def embed_html( self ): + replacement = """ +
+ """ + return mark_safe( YoutubeRecording.youtubeRegex.sub( replacement, self.link ) ) + + class Meta: + unique_together = (("link", "piece" )) + + + + +class ScoreUserMapping( models.Model): + score = ForeignKey( 'Score' ) + user = models.OneToOneField( User ) + piece = ForeignKey( 'Piece' ) + + @staticmethod + def add_user_score_mapping( score, user ): + piece = score.piece + ScoreUserMapping.objects.filter( user=user, piece=piece ).delete() + ScoreUserMapping.objects.create( score=score, user=user, piece= piece ) + + + class Meta: + unique_together = (("piece", "user" )) + + diff --git a/scoremanager/pdf_views.py b/scoremanager/pdf_views.py new file mode 100644 index 0000000..974fefc --- /dev/null +++ b/scoremanager/pdf_views.py @@ -0,0 +1,221 @@ +# ----------------------------- Pdf Views ------------------------------------------ + +from django.http import HttpResponse +from scoremanager.models import Piece + +from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate, Flowable, Table, PageBreak, NextPageTemplate +from reportlab.lib import pagesizes, units, utils +from reportlab.lib.colors import Color + +import os + +class RepertoireDocTemplate( BaseDocTemplate ): + def __init__(self, *args, **kwargs ): + BaseDocTemplate.__init__(self,*args, **kwargs) + + self.pagesize = kwargs['pagesize'] + + leftBorder = 1 * units.cm + rightBorder = 1 * units.cm + topBorder = 3 * units.cm + bottomBorder = 1 * units.cm + + frameWidth = (self.pagesize[0] - rightBorder - leftBorder ) /2 + frameHeight = (self.pagesize[1] - topBorder - bottomBorder ) + + leftColumn = Frame( leftBorder, bottomBorder, frameWidth, frameHeight, showBoundary=0 ) + rightColumn = Frame( leftBorder+frameWidth , bottomBorder, frameWidth, frameHeight, showBoundary=0 ) + + tocTemplate = PageTemplate( id='TOC', frames=[leftColumn,rightColumn], onPage=RepertoireDocTemplate._drawHeader ) + fullPageTemplate = PageTemplate( id='FullPage', frames = [ Frame(0,0, self.pagesize[0], self.pagesize[1] ) ] ) + + self.addPageTemplates( [ tocTemplate, fullPageTemplate ] ) + + @staticmethod + def _drawHeader(canvas,document): + + currentPath = os.path.dirname(os.path.realpath(__file__)) + logoImageFile = currentPath + "/static/pdfImg/logo.png" + bgImageFile = currentPath + "/static/pdfImg/body_bg.jpg" + + #Gradient + titleAreaHeight = 2 * units.cm + gradientColors = [ Color(0.1,0.1,0.1), Color(0.2,0.2,0.2)] + ps = document.pagesize + gradientStart = ( ps[0]/2, ps[1] ) + gradientEnd = ( ps[0]/2, ps[1] - titleAreaHeight) + canvas.linearGradient( gradientStart[0],gradientStart[1], gradientEnd[0], gradientEnd[1],gradientColors, extend=False ) + titleAreaMidY = 0.5 * ( gradientStart[1] + gradientEnd[1] ) + + # Draw Logo + logoImg = utils.ImageReader( logoImageFile ) + logoSize = logoImg.getSize() + logoFraction = 0.6 + logoSize = (logoFraction * logoSize[0], logoFraction*logoSize[1]) + logoPosition = ( ps[0] - 5*units.cm, titleAreaMidY - 0.5*logoSize[1] ) + canvas.drawImage( logoImg, logoPosition[0], logoPosition[1], width=logoSize[0], height=logoSize[1], mask=Color(0,0,0) ) + + # Draw Title + text = canvas.beginText() + text.setTextOrigin( 1 * units.cm, titleAreaMidY-25/2 ) + text.setFillColorRGB( 0.95,0.95,0.95 ) + text.setFont( 'Helvetica', 25 ) + text.textLine( "Inhaltsverzeichnis" ) + canvas.drawText( text ) + + # Draw Background + bgImage = utils.ImageReader( bgImageFile ) + bgImageSize = bgImage.getSize() + curPos = [0, gradientEnd[1]-bgImageSize[1] ] + while curPos[1] > -bgImageSize[1]: + curPos[0] = 0 + while curPos[0] < ps[0]: + canvas.drawImage( bgImage, curPos[0], curPos[1] ) + curPos[0] += bgImageSize[0] + + curPos[1] -= bgImageSize[1] + + +class PdfImage(Flowable): + """PdfImage wraps the first page from a PDF file as a Flowable + which can be included into a ReportLab Platypus document. + Based on the vectorpdf extension in rst2pdf (http://code.google.com/p/rst2pdf/)""" + + def __init__(self, filename_or_object, page=0, width=None, height=None, kind='direct'): + from pdfrw import PdfReader + from pdfrw.buildxobj import pagexobj + + # If using StringIO buffer, set pointer to beginning + if hasattr(filename_or_object, 'read'): + filename_or_object.seek(0) + page = PdfReader(filename_or_object, decompress=False).pages[page] + self.xobj = pagexobj(page) + self.imageWidth = width + self.imageHeight = height + x1, y1, x2, y2 = self.xobj.BBox + + self._w, self._h = x2 - x1, y2 - y1 + if not self.imageWidth: + self.imageWidth = self._w + if not self.imageHeight: + self.imageHeight = self._h + self.__ratio = float(self.imageWidth)/self.imageHeight + if kind in ['direct','absolute'] or width==None or height==None: + self.drawWidth = width or self.imageWidth + self.drawHeight = height or self.imageHeight + elif kind in ['bound','proportional']: + factor = min(float(width)/self._w,float(height)/self._h) + self.drawWidth = self._w*factor + self.drawHeight = self._h*factor + + def wrap(self, aW, aH): + return self.drawWidth, self.drawHeight + + def drawOn(self, canv, x, y, _sW=0): + from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT + from pdfrw.toreportlab import makerl + + if _sW > 0 and hasattr(self, 'hAlign'): + a = self.hAlign + if a in ('CENTER', 'CENTRE', TA_CENTER): + x += 0.5*_sW + elif a in ('RIGHT', TA_RIGHT): + x += _sW + elif a not in ('LEFT', TA_LEFT): + raise ValueError("Bad hAlign value " + str(a)) + + xobj = self.xobj + xobj_name = makerl(canv._doc, xobj) + + xscale = self.drawWidth/self._w + yscale = self.drawHeight/self._h + + x -= xobj.BBox[0] * xscale + y -= xobj.BBox[1] * yscale + + canv.saveState() + canv.translate(x, y) + canv.scale(xscale, yscale) + canv.doForm(xobj_name) + canv.restoreState() + + +class Bookmark(Flowable): + """ Utility class to display PDF bookmark. """ + def __init__(self, title, key): + self.title = title + self.key = key + Flowable.__init__(self) + + def wrap(self, availWidth, availHeight): + """ Doesn't take up any space. """ + return (0, 0) + + def draw(self): + # set the bookmark outline to show when the file's opened + self.canv.showOutline() + # step 1: put a bookmark on the + self.canv.bookmarkPage(self.key) + # step 2: put an entry in the bookmark outline + self.canv.addOutlineEntry(self.title, self.key, 0, 0) + + +def repertoire_pdf( request ): + response = HttpResponse(content_type='application/pdf') + response['Content-Disposition'] = 'attachment; filename="Repertoire.pdf"' + + # Create the PDF object, using the response object as its "file. + doc = RepertoireDocTemplate(response, pagesize=pagesizes.A4) + elements = [] + + elements.append( Bookmark("Inhaltsverzeichnis", "Contents") ) + #TOC + data = [] + for piece in Piece.getRepertoire(): + data.append( [ "%d %s" % ( piece.repertoire_nr, piece.title ) ] ) + table = Table(data) + table.hAlign = "LEFT" + elements.append(table) + + + elements.append( NextPageTemplate('FullPage') ) + elements.append(PageBreak()) + pagesize=pagesizes.A4 + for piece in Piece.getRepertoire(): + score = piece.get_score_for_user( request.user ) + filename = score.file + bookmarkTitle = "%02d %s - %s " % ( piece.repertoire_nr, piece.title, score.score_type ) + elements.append( Bookmark( bookmarkTitle, str(piece.id) ) ) + + #TODO support also multiple pages!! + image_flowable = PdfImage( filename, 0, width=pagesize[0]*0.98, height=pagesize[1] *0.98 ) + image_flowable.hAlign = "CENTER" + elements.append( image_flowable ) + + + doc.build(elements) + + return response + + + +def repertoire_toc( request ): + response = HttpResponse(content_type='application/pdf') + response['Content-Disposition'] = 'attachment; filename="Inhaltsverzeichnis.pdf"' + + # Create the PDF object, using the response object as its "file. + doc = RepertoireDocTemplate(response,pagesize=pagesizes.A4 ) + elements = [] + + data = [] + for piece in Piece.getRepertoire(): + data.append( [ "%d %s" % ( piece.repertoire_nr, piece.title ) ] ) + + table = Table(data) + table.hAlign = "LEFT" + elements.append(table) + + doc.build(elements) + + return response + diff --git a/scoremanager/static/img/score-icon.png b/scoremanager/static/img/score-icon.png new file mode 100644 index 0000000..72c63ef Binary files /dev/null and b/scoremanager/static/img/score-icon.png differ diff --git a/scoremanager/static/img/scoreSheet.jpg b/scoremanager/static/img/scoreSheet.jpg new file mode 100644 index 0000000..19cd0e1 Binary files /dev/null and b/scoremanager/static/img/scoreSheet.jpg differ diff --git a/scoremanager/templates/scoremanager/list_repertoire.html b/scoremanager/templates/scoremanager/list_repertoire.html new file mode 100644 index 0000000..b34f115 --- /dev/null +++ b/scoremanager/templates/scoremanager/list_repertoire.html @@ -0,0 +1,133 @@ + {% extends "website/base.html" %} {% load sekizai_tags staticfiles %} + + + {% block content %} + + {% addtoblock "css" %} + +{% endaddtoblock %} + + + +
+
+ +
+ +
+ +

Repertoire

+ + + + + + + + + + {% for piece in repertoire %} + + + + + + + + {% endfor %} +
# Stück
+ {{ piece.repertoire_nr }} + + + {{ piece.title }} + + + {{ piece.booklocation }} +
+
+ + +
+ +
+ +
+ + +{% endblock %} \ No newline at end of file diff --git a/scoremanager/templates/scoremanager/manage_repertoire.html b/scoremanager/templates/scoremanager/manage_repertoire.html new file mode 100644 index 0000000..57242fd --- /dev/null +++ b/scoremanager/templates/scoremanager/manage_repertoire.html @@ -0,0 +1,284 @@ + {% extends "website/base.html" %} {% load sekizai_tags staticfiles %} + + + + +{% block content %} + + +{% addtoblock "js" strip %} {% endaddtoblock %} +{% addtoblock "js" strip %} {% endaddtoblock %} +{% addtoblock "js" strip %} {% endaddtoblock %} + +{% addtoblock "css" strip %} {% endaddtoblock %} + + + +{% addtoblock "css" %} + +{% endaddtoblock %} + +{% addtoblock "js" %} + +{% endaddtoblock %} + +
+
+
+
+

Repertoire Manager

+
+
+ +
+ +
+

Alle Stücke

+ + + + +
    + {% for piece in allPieces %} +
  • +

    + + {{ piece.title }} +
    + {{ piece.composer}} + {% if piece.booklocation %} ( {{piece.booklocation}} ) {% endif %} + +

    +
  • + {% endfor %} +
+ + +
+ +
+ +

Repertoire

+ +
    + {% for piece in repertoire %} +
  1. +

    + + + {{ piece.title }} +
    + {{ piece.composer}} + {% if piece.booklocation %} ( {{piece.booklocation}} ) {% endif %} + +

    +
  2. + {% endfor %} +
+ + + + + +
+
+ +
+ +
+ + +{% endblock %} \ No newline at end of file diff --git a/scoremanager/templates/scoremanager/piece_view.html b/scoremanager/templates/scoremanager/piece_view.html new file mode 100644 index 0000000..552fc68 --- /dev/null +++ b/scoremanager/templates/scoremanager/piece_view.html @@ -0,0 +1,310 @@ +{% extends "website/base.html" %} + +{% load sekizai_tags staticfiles %} + +{% block content %} + +{% addtoblock "css" %} + +{% endaddtoblock %} + + +{% addtoblock "js" %} + +{% endaddtoblock %} + +
+
+
+ + +

{{ piece.title }}

+
+ {% if pictureScore %} + + + + {% endif %} + +

Info

+ + {% if piece.composer %} + + + + + {% endif %} + + + {% if piece.booklocation %} + + + + + {% endif %} +
Komponist:{{piece.composer}}
Buch: {{piece.booklocation }}
+ + + + {% if piece.scores.all|length > 0 %} +

Alle Noten

+ + {% for score in piece.scores.all %} + + + + + + {% endfor %} +
+ + {{ score.score_type }} + + + {% if score == activeScore %} + Meine Noten + {% else %} + Meine Noten + {% endif %} + + {% if score.uploaded_by == request.user or perms.scoremanager.manage_scores %} + Löschen + {% endif %} +
+ {% endif %} + + {% if piece.recordings.all|length > 0 %} +

Aufnahmen

+ + {% endif %} + + +
+ + + + +
+ + +
+
+ +
+
+
+
+ +

+ Youtube
+

+ +
+ +
+ + {% if piece.youtubeLinks.all|length > 0 %} +
+ {% for youtubeLink in piece.youtubeLinks.all %} +
+ {{ youtubeLink.embed_html }} + + {% if youtubeLink.uploaded_by == request.user or perms.scoremanager.manage_scores %} +
+ +
+ {% endif %} +
+ {% endfor %} +
+ {% endif %} +
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/scoremanager/urls.py b/scoremanager/urls.py new file mode 100644 index 0000000..1645cde --- /dev/null +++ b/scoremanager/urls.py @@ -0,0 +1,17 @@ +from django.conf.urls import patterns, url + +import scoremanager.views +import scoremanager.pdf_views + +urlpatterns = patterns('', + url(r'^repertoireManager$', scoremanager.views.manage_repertoire ), + url(r'^repertoireAjaxSave$$', scoremanager.views.manage_repertoire_ajax_save ), + url(r'^$', scoremanager.views.list_repertoire ), + url(r'^piece/(?P\d+)', scoremanager.views.piece_view ), + url(r'^score_usermapping_save', scoremanager.views.score_user_mapping_save ), + url(r'^score/(?P\d+)', scoremanager.views.score ), + url(r'^youtubeLink$', scoremanager.views.youtube_link ), + url(r'^inhaltsverzeichnis.pdf$',scoremanager.pdf_views.repertoire_toc), + url(r'^repertoire.pdf', scoremanager.pdf_views.repertoire_pdf ), +) + diff --git a/scoremanager/views.py b/scoremanager/views.py new file mode 100644 index 0000000..6fbff7c --- /dev/null +++ b/scoremanager/views.py @@ -0,0 +1,154 @@ + +from django.shortcuts import render +from django.http import HttpResponse +from django import forms +from django.conf import settings +from django.utils.translation import ugettext as _ +from django.core.exceptions import PermissionDenied + +from scoremanager.models import Piece, Score, Recording, ScoreUserMapping, YoutubeRecording +import json +import os + +def manage_repertoire( request ): + context = {} + context['repertoire'] = Piece.getRepertoire() + context['allPieces' ] = Piece.objects.all().order_by( 'title' ) + return render ( request, 'scoremanager/manage_repertoire.html', context ) + + +def manage_repertoire_ajax_save( request ): + if request.is_ajax(): + if request.method == 'POST': + result = json.loads( request.body ) + + parsedResult = { int(key): value for ( key,value ) in result.items() } + Piece.objects.all().update( repertoire_nr = None) + for piece in Piece.objects.all(): + if piece.pk in parsedResult: + piece.repertoire_nr = parsedResult[piece.pk] + piece.save() + + return HttpResponse("OK") + + + + +def list_repertoire( request ): + context = { 'repertoire': Piece.getRepertoire() } + return render( request, 'scoremanager/list_repertoire.html' , context ) + + + + + + + + +# ----------------------------- Piece View + Ajax views + Forms ------------------------------------------ + + + +def score_user_mapping_save( request ): + if request.is_ajax(): + if request.method == 'POST': + result = json.loads( request.body ) + print ( "Result " + str( result ) ) + print ( "Resulting score: " + str( result['myScore'] ) ) + result = int(result['myScore']) + ScoreUserMapping.add_user_score_mapping( user=request.user, score= Score.objects.get(pk=result) ) + + return HttpResponse("OK") + +def score( request, pk ): + requestedScore = Score.objects.get(pk=pk) + + if request.method == 'GET': + imageFile = requestedScore.get_image_file() + image_data = open( settings.MEDIA_ROOT + imageFile, "rb").read() + return HttpResponse(image_data, mimetype="image/jpeg") + if request.method == "DELETE": + if requestedScore.uploaded_by != request.user or request.user.has_perm('scoremanager.manage_scores'): + raise PermissionDenied + requestedScore.delete() + return HttpResponse("OK") + + +def youtube_link( request ): + result = json.loads( request.body ) + print("Youtube link: " + str(result) ) + + if request.method == 'DELETE': + linkId = int( result['linkid'] ) + youtubeRecording = YoutubeRecording.objects.get( pk = linkId ) + if youtubeRecording.uploaded_by == request.user or request.user.has_perm('scoremanager.manage_scores'): + youtubeRecording.delete() + else: + raise PermissionDenied + + return HttpResponse("OK") + + elif request.method == 'POST': + link = str( result['link'] ) + pieceId = int( result['pieceId'] ) + + newRecording = YoutubeRecording(piece=Piece.objects.get(pk=pieceId), link=link) + newRecording.uploaded_by = request.user + newRecording.save() + return HttpResponse("OK") + + + +class UploadFileForm(forms.Form): + file = forms.FileField( max_length="80") + + def clean_file(self): + f = self.cleaned_data['file'] + extension = os.path.splitext( f.name )[1] + if extension != ".pdf" and extension !=".mp3" and extension != ".zip": + raise forms.ValidationError( _("Unknown extension. Allowed extension are mp3, pdf and zip")) + + +def piece_view( request, pk ): + currentPiece = Piece.objects.get( pk=pk ) + context = {'piece': currentPiece } + + for score in currentPiece.scores.all(): + if score.is_active_score( request.user ): + context['activeScore'] = score + context['pictureScore'] = score + + + if not 'pictureScore' in context.keys() and len( currentPiece.scores.all()) > 0: + context['pictureScore'] = currentPiece.scores.all()[0] + + if request.method == 'POST': + form = UploadFileForm(request.POST, request.FILES) + + if form.is_valid(): + f = request.FILES['file'] + [basename,extension] = os.path.splitext( f.name ) + print ("extension " + extension + " basename " + basename) + if extension == ".mp3": + print("Uploaded Recording") + recording = Recording( piece=currentPiece, artist=basename, file=f ) + recording.uploaded_by=request.user + recording.save() + elif extension == ".pdf": + print("Uploaded Score") + score = Score( piece=currentPiece, score_type=basename, file=f ) + score.uploaded_by=request.user + score.save() + elif extension == ".zip": + #TODO + print ("uploaded zip - not yet supported") + + else: + form = UploadFileForm() + + context['form'] = form + + return render( request, 'scoremanager/piece_view.html', context ) + + + diff --git a/website/templates/website/base.html b/website/templates/website/base.html index 570d70b..5809ecb 100644 --- a/website/templates/website/base.html +++ b/website/templates/website/base.html @@ -37,6 +37,7 @@
  • Termine
  • Forum
  • Adressbuch
  • +
  • Noten
  • {% endblock %} {% if user.is_authenticated %} @@ -70,7 +71,7 @@