summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThorsten Behrens <tbehrens@suse.com>2013-04-10 03:13:12 +0200
committerThorsten Behrens <tbehrens@suse.com>2013-04-10 03:14:57 +0200
commit6c8f3dd807790500d2552161df5bb5c77061119e (patch)
treebdd0594963ac5986e80c32505ab620fedc057ea8
parent70852f4d85a014f829afbbaf5fd30cc92caae001 (diff)
Make upload really work, have templates for user-facing pages.
split up stuff into /api/* and /*/ user-facing pages.
-rwxr-xr-xconvert_deck.sh19
-rwxr-xr-xslideapi.py250
-rw-r--r--views/decks.tpl9
-rw-r--r--views/revs.tpl9
-rw-r--r--views/slides.tpl17
-rw-r--r--views/tags.tpl11
-rw-r--r--views/users.tpl13
7 files changed, 260 insertions, 68 deletions
diff --git a/convert_deck.sh b/convert_deck.sh
new file mode 100755
index 0000000..e410b94
--- /dev/null
+++ b/convert_deck.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+soffice --headless --convert-to pdf --outdir $1 $1/deck.odp
+convert -resize $2 $1/deck.pdf $1/deck.png
+rm $1/deck.pdf
+for i in $1/deck-*.png; do
+ rev1=${i/*deck-/}
+ rev2=${rev1/.png/}
+ mkdir -p $1/$rev2
+ mv $i $1/$rev2/$rev2.png
+done
+
diff --git a/slideapi.py b/slideapi.py
index bc26c83..b212fdd 100755
--- a/slideapi.py
+++ b/slideapi.py
@@ -7,47 +7,20 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
-import os, json
-from bottle import get, post, auth_basic, run, static_file, request, debug
+import os, json, time
+from bottle import get, post, auth_basic, run, static_file, request, debug, view, HTTPError
from subprocess import call
-
-# -----
-
-@get('/api/users/')
-def users():
- return "<a href='test/'>User Test</a>"
-
-@get('/api/topics/')
-def topics():
- return "<a href='test/'>Topic Test</a>"
-
-@get('/api/users/test/')
-def test():
- return "<a href='deck1/'>deck1</a><br/><a href='deck2/'>deck2</a>"
-
-@get('/api/users/test/deck1/')
-def deck1():
- return "<a href='1/'>revision 1</a><br/><a href='2/'>revision 2</a>"
-
-@get('/api/users/test/<deck>/<rev:int>/')
-def list_deck(deck, rev):
- return "<a href='checkinDate'>checkin data</a><br/><a href='checkinComment'>checkin comment</a><br/><a href='deck.fodp'>deck.fodp</a><br/><a href='1/'>slide 1</a>"
-
-@get('/api/users/test/<deck>/<rev:int>/checkinDate')
-def send_checkinDate(deck,rev):
- return '2013-04-01 for '+deck+', rev '+str(rev)
-
-@get('/api/users/test/<deck>/<rev:int>/checkinComment')
-def send_checkinComment(deck,rev):
- return 'Riiiiight on for '+deck+', rev '+str(rev)
-
-@get('/api/users/test/<deck>/<rev:int>/<slide:int>/keywords.json')
-def send_keywords(deck, rev, slide):
- return {'Author': 'Joe User',
- 'Title': 'The great slideshow'}
-
+# config
+#
+# we understand:
+# local_conf["users"], yielding {<username>: {"password": <password>, "description": <description>}, ... }
+# local_conf["thumbnails"], yielding {"soffice": <path>, "convert": <path>, "thumbnail_size": <128x128>}
+# local_conf["tags"], yielding [ <tagname>, ... ]
+#
local_conf=json.loads(open('local.json').read()) if os.path.exists('local.json') else {}
+root='filestore'
+
def validate_auth(user, password):
if "users" in local_conf:
user_dict = local_conf["users"]
@@ -55,55 +28,196 @@ def validate_auth(user, password):
return password == user_dict[user]['password']
return False
+# -------------------------------------------------------------------
+
+def get_users():
+ return {user: data['description'] for user, data in local_conf["users"].items()}
+
+@get('/api/users/')
+def users():
+ return get_users()
+
+@get('/users/')
+@view('users')
+def users():
+ return dict(users=get_users())
+
+# -------------------------------------------------------------------
+
+def get_tags():
+ return local_conf["tags"]
+
+@get('/api/tags/')
+def tags():
+ return json.dumps(get_tags())
+
+@get('/tags/')
+@view('tags')
+def tags():
+ return dict(tags=get_tags())
+
+# -------------------------------------------------------------------
+
+def get_decks(user):
+ path = "%s/%s/" % (root,user)
+ if not os.path.isdir(path):
+ return []
+ return [deck for deck in os.listdir(path) if os.path.isdir(path+deck)]
+
+def get_lastrev(user,deck):
+ path = "%s/%s/%s/" % (root,user,deck)
+ if not os.path.isdir(path):
+ return 0
+ # get last revision
+ return int(sorted([rev for rev in os.listdir(path) if os.path.isdir(path+rev)], key=int)[-1])
+
+@get('/api/users/<user>/')
+def list_decks(user):
+ return json.dumps(get_decks(user))
+
+@get('/users/<user>/')
+@view('decks')
+def list_decks(user):
+ return dict(decks=get_decks(user))
+
+@get('/users/<user>/<deck>/thumbnail.png')
+@get('/api/users/<user>/<deck>/thumbnail.png')
+def get_thumbnail(user, deck):
+ path = "%s/%s/%s/" % (root,user,deck)
+ last_rev = get_lastrev(user,deck)
+ # serve thumbnail from that rev, first slide
+ return static_file('0.png', root=path+str(last_rev)+'/0/', mimetype='image/png')
+
+# -------------------------------------------------------------------
+
+def get_revs(user,deck):
+ path = "%s/%s/%s/" % (root,user,deck)
+ if not os.path.isdir(path):
+ return []
+ return sorted([rev for rev in os.listdir(path) if os.path.isdir(path+rev)], key=int)
+
+@get('/api/users/<user>/<deck>/')
+def list_revs(user,deck):
+ return json.dumps(get_revs(user,deck))
+
+@get('/users/<user>/<deck>/')
+@view('revs')
+def list_revs(user,deck):
+ return dict(revs=get_revs(user,deck))
+
# eventually use ssl here - http://dgtool.blogspot.de/2011/12/ssl-encryption-in-python-bottle.html
-@post('/api/upload')
+@post('/users/<user>/<deck>')
+@post('/api/users/<user>/<deck>')
@auth_basic(validate_auth, realm='upload')
-def upload_file():
- upload_path = '/tmp'
- soffice = 'soffice'
- convert = 'convert'
- thumbnail_size = '128x128'
+def upload_deck(user,deck):
+ path = "%s/%s/%s/" % (root,user,deck)
+ new_rev = get_lastrev(user,deck) + 1
+ new_path = path+str(new_rev)
- # override defaults by config
- if "thumbnails" in local_conf:
- thumbnails_dict = local_conf["thumbnails"]
- upload_path = thumbnails_dict['upload_path'] if 'upload_path' in thumbnails_dict else upload_path
- soffice = thumbnails_dict['soffice'] if 'soffice' in thumbnails_dict else soffice
- convert = thumbnails_dict['convert'] if 'convert' in thumbnails_dict else convert
- thumbnail_size = thumbnails_dict['thumbnail_size'] if 'thumbnail_size' in thumbnails_dict else thumbnail_size
+ if os.path.isdir(new_path):
+ raise HTTPError(body='inconsistent repo, bailing out')
+
+ if request.auth[0] != user:
+ raise HTTPError(body='invalid user or insufficient rights, bailing out')
+
+ os.makedirs(new_path)
+ upload_path = new_path
# TODO: Add all required parameters here
tag = request.forms.get('tag')
content = request.files.get('file')
# TODO: Create proper paths for uploads & Check for upload errors
- out = open(upload_path+'/'+content.filename, 'wb')
+ out = open(upload_path+'/deck.odp', 'wb')
while True:
bits = content.file.read(10240)
if not bits:
break
out.write(bits)
out.close()
- call([soffice, '--headless', '--convert-to', 'pdf', '--outdir', upload_path, upload_path+'/'+content.filename])
- call([convert, '-resize', thumbnail_size, upload_path+'/'+os.path.splitext(content.filename)[0]+'.pdf', upload_path+'/'+os.path.splitext(content.filename)[0]+'.png'])
- return 'Success:'+tag+':'+content.filename
-#-----
+ out = open(upload_path+'/meta.json', 'wb')
+ json.dump({'tag': tag,
+ 'server_version': '1',
+ 'upload_filename': content.filename},
+ out)
-@get('/api/users/test/<deck>/<rev:int>/deck.fodp')
-def send_deck(deck, rev):
- return static_file(deck+'.fodp', root='decks/'+deck+'/'+str(rev), mimetype='text/xml')
+ thumbnail_size = '128x128'
-@get('/api/users/test/<deck>/<rev:int>/<slide:int>/')
-def list_slide(deck,rev,slide):
- return "<a href='thumbnail.png'>thumbnail</a><br/><a href='slide.fodp'>slide "+str(slide)+"</a><br/><a href='keywords.json'>keywords</a>"
+ # override defaults by config
+ if "thumbnails" in local_conf:
+ thumbnails_dict = local_conf["thumbnails"]
+ thumbnail_size = thumbnails_dict['thumbnail_size'] if 'thumbnail_size' in thumbnails_dict else thumbnail_size
+
+ os.system('./convert_deck.sh '+upload_path+' '+thumbnail_size)
+ return 'Success:'+tag+':'+content.filename
-@get('/api/users/test/<deck>/<rev:int>/<slide:int>/slide.fodp')
-def send_slide(deck, rev, slide):
- return static_file(str(slide)+'.fodp', root='decks/'+deck+'/'+str(rev), mimetype='text/xml')
-@get('/api/users/test/<deck>/<rev:int>/<slide:int>/thumbnail.png')
-def send_thumbnail(deck, rev, slide):
- return static_file(str(slide)+'.png', root='decks/'+deck+'/'+str(rev), mimetype='image/png')
+@get('/users/<user>/<deck>/<rev:int>/thumbnail.png')
+@get('/api/users/<user>/<deck>/<rev:int>/thumbnail.png')
+def get_thumbnail(user, deck, rev):
+ path = "%s/%s/%s/%d/" % (root,user,deck,rev)
+ # serve thumbnail from that rev, first slide
+ return static_file('0.png', root=path+'/0/', mimetype='image/png')
+
+@get('/users/<user>/<deck>/<rev:int>/deck.odp')
+@get('/api/users/<user>/<deck>/<rev:int>/deck.odp')
+def send_deck(user, deck, rev):
+ path = "%s/%s/%s/%d/" % (root,user,deck,rev)
+ return static_file(deck+'.odp', root=path, mimetype='text/xml')
+
+# -------------------------------------------------------------------
+
+def get_slides(user,deck,rev):
+ path = "%s/%s/%s/%d/" % (root,user,deck,rev)
+ if not os.path.isdir(path):
+ return []
+ return sorted([slide for slide in os.listdir(path) if os.path.isdir(path+slide)], key=int)
+
+def get_revmeta(user,deck,rev):
+ path = "%s/%s/%s/%d/" % (root,user,deck,rev)
+ comment = open(path+'comment').read() if os.path.exists(path+'comment') else ''
+ meta = json.loads(open(path+'meta.json').read()) if os.path.exists(path+'meta.json') else {}
+ return {'CreationDate': time.strftime("%a, %d %b %Y %H:%M:%S GMT",
+ time.gmtime(os.stat(path).st_ctime)),
+ 'CommitComment': comment,
+ 'Meta': meta}
+
+@get('/api/users/<user>/<deck>/<rev:int>/')
+def list_slides(user, deck, rev):
+ return json.dumps(get_slides(user,deck,rev))
+
+@get('/users/<user>/<deck>/<rev:int>/')
+@view('slides')
+def list_slides(user, deck, rev):
+ return dict(slides=get_slides(user,deck,rev), revmeta=get_revmeta(user,deck,rev))
+
+@get('/users/<user>/<deck>/<rev:int>/<slide:int>/thumbnail.png')
+@get('/api/users/<user>/<deck>/<rev:int>/<slide:int>/thumbnail.png')
+def get_thumbnail(user, deck, rev, slide):
+ path = "%s/%s/%s/%d/%d/" % (root,user,deck,rev,slide)
+ return static_file(str(slide)+'.png', root=path, mimetype='image/png')
+
+@get('/api/users/<user>/<deck>/<rev:int>/meta.json')
+def list_revmeta(user,deck,rev):
+ return get_revmeta(user,deck,rev)
+
+# -------------------------------------------------------------------
+
+def get_slidemeta(user,deck,rev,slide):
+ path = "%s/%s/%s/%d/%d/" % (root,user,deck,rev,slide)
+ return json.loads(open(path+'meta.json').read()) if os.path.exists(path+'meta.json') else {}
+
+@get('/api/users/<user>/<deck>/<rev:int>/<slide:int>/meta.json')
+def list_slidemeta(user, deck, rev, slide):
+ return get_slidemeta(user, deck, rev, slide)
+
+@get('/users/<user>/<deck>/<rev:int>/<slide:int>/slide.odp')
+@get('/api/users/<user>/<deck>/<rev:int>/<slide:int>/slide.odp')
+def send_slide(user, deck, rev, slide):
+ path = "%s/%s/%s/%d/%d/" % (root,user,deck,rev,slide)
+ return static_file(str(slide)+'.odp', root=path, mimetype='text/xml')
+
+# -------------------------------------------------------------------
def main():
debug(True)
diff --git a/views/decks.tpl b/views/decks.tpl
new file mode 100644
index 0000000..f25dc72
--- /dev/null
+++ b/views/decks.tpl
@@ -0,0 +1,9 @@
+%#generate HTML table of all decks
+<table border="1">
+%for deckname in decks:
+ <tr>
+ <td><a href="{{deckname}}/">{{deckname}}</a></td>
+ <td><a href="{{deckname}}/"><img src="{{deckname}}/thumbnail.png"></a></td>
+ </tr>
+%end
+</table>
diff --git a/views/revs.tpl b/views/revs.tpl
new file mode 100644
index 0000000..500605d
--- /dev/null
+++ b/views/revs.tpl
@@ -0,0 +1,9 @@
+%#generate HTML table of all revisions
+<table border="1">
+%for rev in revs:
+ <tr>
+ <td><a href="{{rev}}/">Revision {{rev}}</a></td>
+ <td><a href="{{rev}}/deck.odp"><img src="{{rev}}/thumbnail.png"></a></td>
+ </tr>
+%end
+</table>
diff --git a/views/slides.tpl b/views/slides.tpl
new file mode 100644
index 0000000..8e2e342
--- /dev/null
+++ b/views/slides.tpl
@@ -0,0 +1,17 @@
+%#generate HTML table of all slides in one revision
+<table border="1">
+%for name, content in revmeta.items():
+ <tr>
+ <td>{{name}}</td>
+ <td>{{content}}</td>
+ </tr>
+%end
+</table>
+<table border="1">
+%for slide in slides:
+ <tr>
+ <td><a href="{{slide}}/slide.odp">Slide {{slide}}</a></td>
+ <td><a href="{{slide}}/slide.odp"><img src="{{slide}}/thumbnail.png"></a></td>
+ </tr>
+%end
+</table>
diff --git a/views/tags.tpl b/views/tags.tpl
new file mode 100644
index 0000000..192dc6c
--- /dev/null
+++ b/views/tags.tpl
@@ -0,0 +1,11 @@
+%#generate HTML table of all tags
+<table border="1">
+ <tr>
+ <th>Tag name</th>
+ </tr>
+%for tag in tags:
+ <tr>
+ <td><a href="{{tag}}/">{{tag}}</a></td>
+ </tr>
+%end
+</table>
diff --git a/views/users.tpl b/views/users.tpl
new file mode 100644
index 0000000..b661740
--- /dev/null
+++ b/views/users.tpl
@@ -0,0 +1,13 @@
+%#generate HTML table of all users
+<table border="1">
+ <tr>
+ <th>Username</th>
+ <th>Description</th>
+ </tr>
+%for user, desc in users.items():
+ <tr>
+ <td><a href="{{user}}/">{{user}}</a></td>
+ <td><a href="{{user}}/">{{desc}}</a></td>
+ </tr>
+%end
+</table>