Skip to content

Commit 02bb83c

Browse files
Jonny LangefeldJonny Langefeld
Jonny Langefeld
authored and
Jonny Langefeld
committed
initial commit
0 parents  commit 02bb83c

20 files changed

+865
-0
lines changed

.DS_Store

6 KB
Binary file not shown.

Akkorde.csv

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
C;C#;Cb;D;D#;Db;E;E#;Eb;F;F#;Fb;G;G#;Gb;A;A#;Ab;B;B#;Bb;StufenC;C#;Cb;D;D#;Db;E;E#;Eb;F;F#;Fb;G;G#;Gb;A;A#;Ab;B;B#;Bb;1Dm;D#m;Dbm;Em;E#m;Ebm;F#m;F##m;Fm;Gm;G#m;Gbm;Am;A#m;Abm;Bm;B#m;Bbm;C#m;C##m;Cm;2mEm;E#m;Ebm;F#m;F##m;Fm;G#m;G##m;Gm;Am;A#m;Abm;Bm;B#m;Bbm;C#m;C##m;Cm;D#m;D##m;Dm;3mF;F#;Fb;G;G#;Gb;A;A#;Ab;Bb;B;Bbb;C;C#;Cb;D;D#;Db;E;E#;Eb;4G;G#;Gb;A;A#;Ab;B;B#;Bb;C;C#;Cb;D;D#;Db;E;E#;Eb;F#;F##;F;5Am;A#m;Abm;Bm;B#m;Bbm;C#m;C##m;Cm;Dm;D#m;Dbm;Em;E#m;Ebm;F#m;F##m;Fm;G#m;G##m;Gm;6mD;D#;Db;E;E#;Eb;F#;F##;F;G;G#;Gb;A;A#;Ab;B;B#;Bb;C#;C##;C;2E;E#;Eb;F#;F##;F;G#;G##;G;A;A#;Ab;B;B#;Bb;C#;C##;C;D#;D##;D;3G/B;G#/B#;Gb/Bb;A/C#;A#/C##;Ab/C;B/D#;B#/D##;Bb/D;C/E;C#/E#;Cb/Eb;D/F#;D#/F##;Db/F;E/G#;E#/G##;Eb/G;F#/A#;F##/A##;F/A;5/7C/E;C#/E#;Cb/Eb;D/F#;D#/F##;Db/F;E/G#;E#/G##;Eb/G;F/A;F#/A#;Fb/Ab;G/B;G#/B#;Gb/Bb;A/C#;A#/C##;Ab/C;B/D#;B#/D##;Bb/D;1/3F/A;F#/A#;Fb/Ab;G/B;G#/B#;Gb/Bb;A/C#;A#/C##;Ab/C;Bb/D;B/D#;Bbb/Db;C/E;C#/E#;Cb/Eb;D/F#;D#/F##;Db/F;E/G#;E#/G##;Eb/G;4/6F/G;F#/G#;Fb/Gb;G/A;G#/A#;Gb/Ab;A/B;A#/B#;Ab/Bb;Bb/C;B/C#;Bbb/Cb;C/D;C#/D#;Cb/Db;D/E;D#/E#;Db/Eb;E/F#;E#/F##;Eb/F;4/5G/C;G#/C#;Gb/Cb;A/D;A#/D#;Ab/Db;B/E;B#/E#;Bb/Eb;C/F;C#/F#;Cb/Fb;D/G;D#/G#;Db/Gb;E/A;E#/A#;Eb/Ab;F#/B;F##/B#;F/Bb;5/1

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
## Nov 16, 2015
2+
- Added changelog

ChordProToPDF.py

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
from flask import Flask
5+
from flask import Flask, flash, redirect, render_template, request, session, abort, redirect, send_file
6+
import os
7+
from sqlalchemy.orm import sessionmaker
8+
from tabledef import *
9+
from create_song import *
10+
from passlib.hash import sha256_crypt
11+
from pandas import read_csv, Index
12+
import shutil
13+
from datetime import datetime
14+
from PyPDF2 import PdfFileMerger
15+
import StringIO
16+
engine = create_engine('sqlite:///users.db', echo=True)
17+
18+
app = Flask(__name__)
19+
20+
port = int(os.getenv('PORT', 8080))
21+
22+
f = open('Akkorde.csv', 'rU')
23+
fline = f.readline().replace('\n','')
24+
25+
@app.route('/',methods = ['GET', 'POST'])
26+
def home():
27+
if not session.get('logged_in'):
28+
return render_template('login.html')
29+
else:
30+
optionKeys = fline.split(';')
31+
options = ''
32+
for o in optionKeys:
33+
options += '<label ><input type="checkbox" name="keys" value="'+o+'" style="display:none">'+o+'</label>\n'
34+
if request.method == 'POST':
35+
fileList = request.files.getlist('files[]')
36+
keyOptions = request.form.getlist('keys')
37+
result_type = request.form['result_type']
38+
folder = 'ChordPro_download_'+datetime.now().strftime('%Y%m%d_%H%M')+'_'+str(len(fileList)*len(keyOptions))+'_'
39+
path = ('user'+os.sep+session['user']+os.sep+'download'+os.sep+folder+os.sep).encode('ascii','ignore')
40+
41+
if os.path.exists('user'+os.sep+session['user']+os.sep):
42+
shutil.rmtree('user'+os.sep+session['user']+os.sep)
43+
if not os.path.exists(path):
44+
os.makedirs(path)
45+
46+
for f in fileList:
47+
if f.content_type == "text/rtf":
48+
for keyOption in keyOptions:
49+
print f.filename, keyOption
50+
keyOption = keyOption.encode('ascii','ignore')
51+
if keyOption == 'original':
52+
keyOption = None
53+
try:
54+
convert(path,f,keyOption)
55+
except KeyError as e:
56+
print str(e) + ' not found in chords for key of the file. Are you sure your file has the correct key?'
57+
flash('hi')
58+
except Exception as e:
59+
print 'error:', e
60+
print result_type
61+
if result_type == 'PDF':
62+
songs = os.listdir(path)
63+
print songs
64+
merger = PdfFileMerger()
65+
result = StringIO.StringIO()
66+
for song in songs:
67+
merger.append(open(path+song, 'rb'))
68+
merger.write(result)
69+
result.seek(0)
70+
result_name = folder+'.pdf'
71+
#return send_file(result,attachment_filename=folder+'.pdf', as_attachment=True)
72+
if result_type == 'ZIP':
73+
shutil.make_archive('user'+os.sep+session['user']+os.sep+folder, 'zip', 'user'+os.sep+session['user']+os.sep+'download'+os.sep)
74+
result = 'user'+os.sep+session['user']+os.sep+folder+'.zip'
75+
result_name = folder+'.zip'
76+
#return send_file('user'+os.sep+session['user']+os.sep+folder+'.zip',folder+'.zip', as_attachment=True)
77+
else:
78+
return render_template('uploader.html', error = "Please only upload *.rtf files", options = options)
79+
return send_file(result,attachment_filename=result_name, as_attachment=True)
80+
else:
81+
return render_template('uploader.html', options = options)
82+
83+
@app.route('/login', methods=['POST'])
84+
def do_admin_login():
85+
POST_USERNAME = str(request.form['username']).lower()
86+
POST_PASSWORD = str(request.form['password'])
87+
Session = sessionmaker(bind=engine)
88+
s = Session()
89+
query = s.query(User).filter(User.username.in_([POST_USERNAME]))
90+
result = query.first()
91+
if result:
92+
if sha256_crypt.verify( POST_PASSWORD, result.password ):
93+
session['user'] = result.username
94+
session['logged_in'] = True
95+
else:
96+
return render_template('login.html', error = "Wrong password")
97+
else:
98+
return render_template('login.html', error = "User does not exist")
99+
return redirect('/')#home()
100+
101+
@app.route('/akkorde')
102+
def akkorde():
103+
if session.get('user') == 'sven' and session.get('logged_in'):
104+
return render_template('akkorde_uploader.html')
105+
else:
106+
return "Hey du bist nicht Sven und hast hier nix zu suchen!"
107+
108+
@app.route('/akkorde_upload', methods = ['GET', 'POST'])
109+
def akkorde_upload():
110+
if request.method == 'POST':
111+
f = request.files['file']
112+
f.save('Akkorde.csv')
113+
return "Neues 'Akkorde.csv' erfolgreich hochgeladen."
114+
115+
@app.route('/download_akkorde')
116+
def download_akkorde():
117+
if session.get('user') == 'sven' and session.get('logged_in'):
118+
return send_file('Akkorde.csv',attachment_filename='Akkorde.csv', as_attachment=True)
119+
else:
120+
return "Hey du bist nicht Sven und hast hier nix zu suchen!"
121+
122+
@app.route("/logout")
123+
def logout():
124+
session['logged_in'] = False
125+
return home()
126+
127+
if __name__ == "__main__":
128+
app.secret_key = os.urandom(12)
129+
app.run(host ="0.0.0.0", port = port, debug=True) #host ="0.0.0.0",

Procfile

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: python ChordProToPDF.py

README.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Python Starter Overview
2+
3+
The Python Starter demonstrates a simple, reusable Python web application.
4+
5+
## Run the app locally
6+
7+
1. [Install Python][]
8+
2. Download and extract the starter code from the Bluemix UI
9+
3. cd into the app directory
10+
4. Run `python server.py`
11+
5. Access the running app in a browser at http://localhost:8000
12+
13+
[Install Python]: https://www.python.org/downloads/# ChordPro

create_song.py

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#!/usr/bin/python
2+
# -*- coding: utf-8 -*-
3+
4+
def convert(path, f, target=None):
5+
import re #für Regular Expressions (google es ;))
6+
from collections import OrderedDict #andernfalls würde sich Vers1, Chorus usw immer alphabetisch sortieren
7+
from pandas import read_csv, Index
8+
#alle Imports für die PDF Generierung (unbedingt für nächster Zelle mit pyth ausführen)
9+
from reportlab.pdfgen import canvas
10+
from reportlab.lib.pagesizes import A4
11+
from reportlab.lib.units import inch
12+
from reportlab.pdfbase.pdfmetrics import stringWidth
13+
from reportlab.lib import styles, colors
14+
from reportlab.platypus import Paragraph
15+
#RTF einlese Package
16+
from pyth.plugins.rtf15.reader import Rtf15Reader
17+
from pyth.plugins.plaintext.writer import PlaintextWriter
18+
#RTF einlesen
19+
doc = Rtf15Reader.read(f)
20+
raw = PlaintextWriter.write(doc).getvalue()
21+
pattern="(^[\xc3\x9f\xc3\x84\xc3\x96\xc3\x9c\xc3\xa4\xc3\xbc\xc3\xb6\w\s]+\n+)(key:[\w#]+\n+)?(bpm:[\d]+\n+)?(.+)(CCLI Song # (\d+)\\n+(.+)\\n+\\xc2\\xa9 (.+))"
22+
match = re.search(pattern,raw,re.DOTALL)
23+
info_dict = {}
24+
info_dict['title'] = match.group(1).replace('\n','')
25+
if match.group(2):
26+
info_dict['key'] = match.group(2).replace('\n','').replace('key:','')
27+
else:
28+
print "No key found"
29+
if match.group(3):
30+
info_dict['bpm'] = match.group(3).replace('\n','').replace('bpm:','')
31+
else:
32+
print "No bpm found"
33+
info_dict['song'] = match.group(4)
34+
info_dict['ccli_nr'] = match.group(6)
35+
info_dict['composer'] = match.group(7).replace('\n','')
36+
info_dict['copyright'] = match.group(8)
37+
akkorde = read_csv("Akkorde.csv",sep=";")
38+
def getTransformedKey (source, target, chord):
39+
return(akkorde[target][Index(akkorde[source]).get_loc(chord)])
40+
def replChords (matchObj):
41+
return ('['+getTransformedKey(source = info_dict['key'], target = target, chord = matchObj.group(1))+']')
42+
def transform():
43+
info_dict['song'] = re.sub('\[([\w\d#/]+)\]', replChords, info_dict['song'])
44+
info_dict['key'] = target
45+
#target = request.form['trans_key']
46+
if (target and target != info_dict['key']):
47+
transform()
48+
#Einzelne Zeilen aus dem RTF in Liste laden
49+
line_list = info_dict.get('song').split('\n\n')
50+
line_list
51+
pattern = '^(Verse\s?\d*|Chorus\s?\d*|Instrumental|Bridge|Pre-Chorus|Intro)$' #Dieses Pattern funktioniert auf alles VersX und Chorus in eckiger Klammer (porbier regexr.com)
52+
song_dict = OrderedDict() #Das oben erwähnte Ordered Dict
53+
in_element = False #mit diesem Flag könnte man sich später noch title: composer: key: usw holen (so weit bin ich noch nicht)
54+
element = None #hier wird gleich drin gespeichert, in welcher Untergruppe wir jeweils sind
55+
for i in range(len(line_list)):
56+
if in_element: #wenn wir in einem Element sind, werden alle folgenden Zeilen zu diesem Eintrag hinzugefügt
57+
if not re.search(pattern,line_list[i]):
58+
song_dict[element].extend([line_list[i]])
59+
match = re.search(pattern,line_list[i]) #Bis wir den ersten Match haben (zB VersX oder Chorus), gibt es auch kein Element
60+
if match: #Wenn wir jedoch ein Match haben, sind wir in einem Element. Dieses wird neu im Dictonary angelegt.
61+
in_element = True
62+
element = match.group(1)
63+
song_dict[element] = [] #Wir geben an, dass hinter diesem Dictonary Eintrag eine neue Liste steht.
64+
65+
66+
def createPDF(fontSize = 13):
67+
width, height = A4 #keep for later
68+
font = 'Helvetica'
69+
lineHeight = fontSize + .75 * fontSize
70+
wordSpace = 3
71+
boarder = inch
72+
topBoarder = .75*boarder
73+
instrSpaces = 5
74+
chordstyle = styles.ParagraphStyle('chord')
75+
chordstyle.fontSize = fontSize
76+
hstyle = styles.ParagraphStyle('heading')
77+
hstyle.fontSize = fontSize +1
78+
tstyle = styles.ParagraphStyle('title')
79+
tstyle.fontSize = fontSize +5
80+
copyrightstyle = styles.ParagraphStyle('copyright')
81+
copyrightstyle.fontSize = 8
82+
83+
84+
pattern = '\[([\w\d#/]+)\]'
85+
y = height - topBoarder - fontSize
86+
x = boarder
87+
realWidth = width - 2*boarder
88+
c = canvas.Canvas(path+info_dict['title']+'-'+info_dict['key']+'.pdf', pagesize=A4)
89+
c.setFont(font, fontSize-1)
90+
91+
P1 = Paragraph("<u><b>"+info_dict['title']+"</b></u>",tstyle)
92+
P1.wrap(realWidth, height)
93+
P1.drawOn(c,x,y)
94+
95+
if info_dict.has_key('key'):
96+
P1 = Paragraph("<b>"+info_dict['key']+"</b>",chordstyle)
97+
P1.wrap(realWidth, height)
98+
P1.drawOn(c,width-boarder-stringWidth(info_dict['key'], font, chordstyle.fontSize),y)
99+
if info_dict.has_key('bpm'):
100+
c.drawRightString(width-boarder,y-lineHeight,'%s'%info_dict['bpm'])
101+
P1 = Paragraph(info_dict['composer'],copyrightstyle)
102+
P1.wrap(realWidth, height)
103+
P1.drawOn(c,x,y-lineHeight)
104+
105+
c.setFont(font, fontSize)
106+
y -= hstyle.fontSize + 2*lineHeight
107+
108+
for key in song_dict:
109+
P1 = Paragraph("<b><i>"+key+"</i></b>",hstyle)
110+
P1.wrap(realWidth, height)
111+
P1.drawOn(c,x,y)
112+
xOfLast = boarder
113+
lineCount = 0
114+
if re.search(pattern, song_dict.get(key)[0]):
115+
y -= 1.8 * (lineHeight) #Abstand von Überschrift zu erster Zeile wenn Akkorde
116+
else:
117+
y -= 1.2 * (lineHeight) #Abstand von Überschrift zu erster Zeile wenn keine Akkorde
118+
if (key in ["Instrumental", "Intro"]):
119+
for line in song_dict.get(key):
120+
line = line.replace('[','').replace(']','').replace(' ','&nbsp;'*(instrSpaces))
121+
P1 = Paragraph("<b>"+line+"</b>",chordstyle)
122+
P1.wrap(realWidth, height)
123+
P1.drawOn(c,x,y)
124+
y -= 1.5 * lineHeight #Abstand nach jedem Abschnitt
125+
else:
126+
for line in song_dict.get(key):
127+
if ((xOfLast + stringWidth(line, font, fontSize)) < (width - boarder)) and (lineCount < 2):
128+
x = xOfLast
129+
lineCount += 1
130+
elif not re.search(pattern, line):
131+
y -= 1 * lineHeight
132+
else:
133+
y -= 1.5 * lineHeight
134+
lineCount = 1
135+
line = line.decode('utf-8')
136+
last_was_chord = False
137+
x_min = 0
138+
cursor = 0
139+
while cursor < len(line):
140+
l = line[cursor]
141+
if l == ' ':
142+
if last_was_chord:
143+
x += last_cord_length
144+
last_was_chord = False
145+
else:
146+
x += wordSpace
147+
elif l == '[':
148+
end = line.find(']', cursor)
149+
chord = line[cursor+1:end]
150+
P1 = Paragraph("<b>"+chord+"</b>",chordstyle)
151+
P1.wrap(realWidth, height)
152+
if x < x_min:
153+
x = x_min
154+
P1.drawOn(c,x,y+fontSize+0.01*fontSize**2)
155+
cursor = end
156+
last_was_chord = True
157+
last_cord_length = stringWidth(chord, font, fontSize)
158+
x_min = x + last_cord_length + wordSpace*7
159+
else:
160+
last_was_chord = False
161+
c.drawString(x,y,l)
162+
x += stringWidth(l, font, fontSize)
163+
cursor += 1
164+
xOfLast = x + wordSpace
165+
x = boarder
166+
y -= 1.5 * lineHeight #Abstand nach jedem Abschnitt
167+
168+
169+
P1 = Paragraph(u'\u00a9 '+
170+
info_dict['copyright']+
171+
'<br/>Gebrauch nur zur Nutzung im Rahmen von Veranstaltungen der City Chapel Stuttgart',copyrightstyle)
172+
P1.wrap(realWidth, height)
173+
P1.drawOn(c,x,boarder-P1.height)# + lineHeight)
174+
175+
c.showPage()
176+
c.save()
177+
return(y < boarder)
178+
179+
nochmal = True
180+
fontSize = 13
181+
while (nochmal):
182+
nochmal = createPDF(fontSize)
183+
fontSize -= .5

create_song.pyc

6.24 KB
Binary file not shown.

manifest.yml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
applications:
2+
- path: .
3+
memory: 128M
4+
instances: 1
5+
domain: mybluemix.net
6+
name: chordpro
7+
host: chord-pro
8+
disk_quota: 1024M

requirements.txt

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Flask==0.11.1
2+
cf-deployment-tracker==1.0.2
3+
reportlab
4+
pyth
5+
pandas
6+
Flask-SqlAlchemy
7+
passlib
8+
PyPDF2

static/img/logo.png

38.4 KB
Loading

static/jquery-3.1.1.min.js

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)