From 5b81841f420eb948de7f184b588bc75a3227da15 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 31 May 2019 17:18:46 +0200 Subject: Web (mod_python) API --- http/api/.htaccess | 6 ++ http/api/api.py | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 http/api/.htaccess create mode 100644 http/api/api.py (limited to 'http/api') diff --git a/http/api/.htaccess b/http/api/.htaccess new file mode 100644 index 0000000..998bfca --- /dev/null +++ b/http/api/.htaccess @@ -0,0 +1,6 @@ +RewriteEngine On +RewriteRule ^.*$ api.py [QSA,L] + +AddHandler mod_python .py +PythonHandler api +PythonDebug On diff --git a/http/api/api.py b/http/api/api.py new file mode 100644 index 0000000..1c1cb0f --- /dev/null +++ b/http/api/api.py @@ -0,0 +1,165 @@ +# coding=utf-8 + +import base64, copy, json, os, random, sys, warnings +from StringIO import StringIO + +from mod_python import apache, Session + +OLDPATH = copy.copy(sys.path) +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) +from dealconvert import DealConverter +sys.path = OLDPATH + +CACHEPATH = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + '../../cache')) + +def _get_rand_string(length=30): + return ('%0' + str(length) + 'x') % (random.randrange(16**length)) + +def _get_file_id(): + while True: + output_id = _get_rand_string() + output_path = os.path.join(CACHEPATH, output_id) + if not os.path.exists(output_path): + return output_id, output_path + +def _print_response(response, obj): + response.write(json.dumps(obj)) + +def handle_upload(response, request): + if request.method != 'POST': + response.status = apache.HTTP_METHOD_NOT_ALLOWED + return + try: + params = json.load(request) + except ValueError as e: + response.write(str(e)) + response.status = apache.HTTP_BAD_REQUEST + return + + session = Session.Session(response) + if 'tokens' not in session: + session['tokens'] = {} + + response.content_type = 'application/json' + return_obj = { + 'name': None, + 'warnings': [], + 'error': None, + 'files': [] + } + warnings.simplefilter('always') + warnings.showwarning = lambda msg, *args: return_obj['warnings'].append( + unicode(msg)) + + try: + return_obj['name'] = params['name'] + converter = DealConverter() + parser = converter.detect_format(params['name']) + input_file = StringIO(base64.b64decode(params['content'])) + dealset = parser.parse_content(input_file) + input_file.close() + if not len(dealset): + raise RuntimeError('Dealset is empty') + except RuntimeError as e: + return_obj['error'] = unicode(e) + return _print_response(response, return_obj) + + for output_type in params['output']: + output_return = { + 'name': None, + 'link': None, + 'warnings': [], + 'error': None + } + warnings.showwarning = lambda msg, *args: output_return['warnings'].append( + unicode(msg)) + try: + output_name = '.'.join(params['name'].split('.')[:-1] + [output_type]) + output_return['name'] = output_name + output = converter.detect_format(output_name) + output_id, output_path = _get_file_id() + token = _get_rand_string(16) + output_buffer = StringIO() + output.output_content(output_buffer, dealset) + with file(output_path, 'w') as output_file: + json.dump({ + 'token': token, + 'name': output_name, + 'content': base64.b64encode(output_buffer.getvalue()) + }, output_file) + output_buffer.close() + session['tokens'][output_id] = token + output_return['link'] = 'download/%s' % (output_id) + except RuntimeError as e: + output_return['error'] = unicode(e) + return_obj['files'].append(output_return) + session.save() + _print_response(response, return_obj) + + +def handle_download(response, request, uri_parts=[]): + if not len(uri_parts): + response.status = apache.HTTP_BAD_REQUEST + return + if request.method != 'GET': + response.status = apache.HTTP_METHOD_NOT_ALLOWED + return + + session = Session.Session(response) + + if 'tokens' not in session: + response.status = apache.HTTP_NOT_FOUND + return + + output_id = uri_parts[0] + output_path = os.path.join(CACHEPATH, output_id) + if not os.path.exists(output_path): + response.status = apache.HTTP_NOT_FOUND + return + if output_id not in session['tokens']: + response.status = apache.HTTP_NOT_FOUND + return + with file(output_path) as output_file: + output = json.load(output_file) + if output['token'] != session['tokens'][output_id]: + response.status = apache.HTTP_NOT_FOUND + return + content = base64.b64decode(output['content']) + response.content_type = 'application/octet-stream' + response.headers_out.add( + 'Content-Disposition', 'attachment; filename=%s' % (output['name'])) + response.write(content) + +def handler(req): + # MIME type fix for error messages + req.content_type = 'text/plain' + + # we need to recover original request path, from before rewrite + orig_req = req + while True: + if orig_req.prev: + orig_req = orig_req.prev + else: + break + + uri_parts = [part for part in orig_req.uri.split('/') if part.strip()] + uri_parts = uri_parts[uri_parts.index('api')+1:] + + if not len(uri_parts): + req.status = apache.HTTP_BAD_REQUEST + else: + try: + if uri_parts[0] == 'upload': + handle_upload(req, orig_req) + elif uri_parts[0] == 'download': + handle_download(req, orig_req, uri_parts[1:]) + else: + req.status = apache.HTTP_BAD_REQUEST + except Exception as e: + req.status = apache.HTTP_INTERNAL_SERVER_ERROR + req.write(str(e)) + + return apache.OK -- cgit v1.2.3 From a9944c178f13ed6fd876a8757a147ca5949d6423 Mon Sep 17 00:00:00 2001 From: emkael Date: Fri, 31 May 2019 20:51:05 +0200 Subject: Board preview feature --- http/api/api.py | 16 +++++++++ http/dealconvert.js | 22 +++++++++++-- http/img/e-ew.png | Bin 0 -> 5657 bytes http/img/e-ns-ew.png | Bin 0 -> 5952 bytes http/img/e-ns.png | Bin 0 -> 5674 bytes http/img/e.png | Bin 0 -> 5267 bytes http/img/n-ew.png | Bin 0 -> 5577 bytes http/img/n-ns-ew.png | Bin 0 -> 5955 bytes http/img/n-ns.png | Bin 0 -> 5731 bytes http/img/n.png | Bin 0 -> 5247 bytes http/img/s-ew.png | Bin 0 -> 5558 bytes http/img/s-ns-ew.png | Bin 0 -> 5936 bytes http/img/s-ns.png | Bin 0 -> 5688 bytes http/img/s.png | Bin 0 -> 5250 bytes http/img/w-ew.png | Bin 0 -> 5595 bytes http/img/w-ns-ew.png | Bin 0 -> 5901 bytes http/img/w-ns.png | Bin 0 -> 5602 bytes http/img/w.png | Bin 0 -> 5207 bytes http/index.html | 89 ++++++++++++++++++++++++++++++++++++++++++++++++--- 19 files changed, 120 insertions(+), 7 deletions(-) create mode 100644 http/img/e-ew.png create mode 100644 http/img/e-ns-ew.png create mode 100644 http/img/e-ns.png create mode 100644 http/img/e.png create mode 100644 http/img/n-ew.png create mode 100644 http/img/n-ns-ew.png create mode 100644 http/img/n-ns.png create mode 100644 http/img/n.png create mode 100644 http/img/s-ew.png create mode 100644 http/img/s-ns-ew.png create mode 100644 http/img/s-ns.png create mode 100644 http/img/s.png create mode 100644 http/img/w-ew.png create mode 100644 http/img/w-ns-ew.png create mode 100644 http/img/w-ns.png create mode 100644 http/img/w.png (limited to 'http/api') diff --git a/http/api/api.py b/http/api/api.py index 1c1cb0f..be2635f 100644 --- a/http/api/api.py +++ b/http/api/api.py @@ -63,6 +63,22 @@ def handle_upload(response, request): input_file.close() if not len(dealset): raise RuntimeError('Dealset is empty') + if params['display_deals']: + preview_obj = [] + for board in dealset: + deal_preview = { + 'number': board.number, + 'conditions': 'nesw'[board.dealer], + 'hands': [] + } + for pair in ['ns', 'ew']: + if board.vulnerable[pair.upper()]: + deal_preview['conditions'] += '-' + pair + deal_preview['hands'] = board.hands + preview_obj.append(deal_preview) + return_obj['preview'] = preview_obj + else: + return_obj['preview'] = None except RuntimeError as e: return_obj['error'] = unicode(e) return _print_response(response, return_obj) diff --git a/http/dealconvert.js b/http/dealconvert.js index ff25444..e038b58 100644 --- a/http/dealconvert.js +++ b/http/dealconvert.js @@ -52,9 +52,9 @@ $(document).ready(function() { var warningTemplate = $('template#file-output-warning'); var errorTemplate = $('template#file-output-error'); var fileTemplate = $('template#file-output'); - var inputHeader = outputGroup.find('.card-header'); + var inputHeader = outputGroup.find('.file-header'); inputHeader.text(data.name); - var groupBody = outputGroup.find('.card-body'); + var groupBody = outputGroup.find('.file-body'); if (data.error) { inputHeader.addClass('bg-danger'); groupBody.append(errorTemplate.clone().contents().unwrap().text(data.error)); @@ -94,6 +94,24 @@ $(document).ready(function() { ); } } + if (data.preview) { + var boardTemplate = $('#board-preview'); + var hands = ['north', 'east', 'south', 'west']; + var suits = ['spades', 'hearts', 'diamonds', 'clubs']; + for (var b = 0; b < data.preview.length; b++) { + var board = boardTemplate.clone().contents().unwrap(); + board.find('.board-number').text(data.preview[b].number); + board.find('.board-conditions').attr('src', 'img/' + data.preview[b].conditions + '.png'); + for (var h = 0; h < hands.length; h++) { + for (var s = 0; s < suits.length; s++) { + board.find('.board-' + hands[h] + '-' + suits[s]).text(data.preview[b].hands[h][s].join('')); + } + } + outputGroup.find('.file-boards-panel .board-body').append(board); + } + } else { + outputGroup.find('.file-boards-panel').remove(); + } } $('body').append(outputGroup); completed += 1; diff --git a/http/img/e-ew.png b/http/img/e-ew.png new file mode 100644 index 0000000..bbf899f Binary files /dev/null and b/http/img/e-ew.png differ diff --git a/http/img/e-ns-ew.png b/http/img/e-ns-ew.png new file mode 100644 index 0000000..05a3b6c Binary files /dev/null and b/http/img/e-ns-ew.png differ diff --git a/http/img/e-ns.png b/http/img/e-ns.png new file mode 100644 index 0000000..2e94563 Binary files /dev/null and b/http/img/e-ns.png differ diff --git a/http/img/e.png b/http/img/e.png new file mode 100644 index 0000000..0d9304b Binary files /dev/null and b/http/img/e.png differ diff --git a/http/img/n-ew.png b/http/img/n-ew.png new file mode 100644 index 0000000..8cc1807 Binary files /dev/null and b/http/img/n-ew.png differ diff --git a/http/img/n-ns-ew.png b/http/img/n-ns-ew.png new file mode 100644 index 0000000..d45ef7e Binary files /dev/null and b/http/img/n-ns-ew.png differ diff --git a/http/img/n-ns.png b/http/img/n-ns.png new file mode 100644 index 0000000..a94eb42 Binary files /dev/null and b/http/img/n-ns.png differ diff --git a/http/img/n.png b/http/img/n.png new file mode 100644 index 0000000..cb079c4 Binary files /dev/null and b/http/img/n.png differ diff --git a/http/img/s-ew.png b/http/img/s-ew.png new file mode 100644 index 0000000..44c3e71 Binary files /dev/null and b/http/img/s-ew.png differ diff --git a/http/img/s-ns-ew.png b/http/img/s-ns-ew.png new file mode 100644 index 0000000..8cff6f8 Binary files /dev/null and b/http/img/s-ns-ew.png differ diff --git a/http/img/s-ns.png b/http/img/s-ns.png new file mode 100644 index 0000000..36fe441 Binary files /dev/null and b/http/img/s-ns.png differ diff --git a/http/img/s.png b/http/img/s.png new file mode 100644 index 0000000..a0611ac Binary files /dev/null and b/http/img/s.png differ diff --git a/http/img/w-ew.png b/http/img/w-ew.png new file mode 100644 index 0000000..b66f4f9 Binary files /dev/null and b/http/img/w-ew.png differ diff --git a/http/img/w-ns-ew.png b/http/img/w-ns-ew.png new file mode 100644 index 0000000..448abf2 Binary files /dev/null and b/http/img/w-ns-ew.png differ diff --git a/http/img/w-ns.png b/http/img/w-ns.png new file mode 100644 index 0000000..623a641 Binary files /dev/null and b/http/img/w-ns.png differ diff --git a/http/img/w.png b/http/img/w.png new file mode 100644 index 0000000..6a995ad Binary files /dev/null and b/http/img/w.png differ diff --git a/http/index.html b/http/index.html index fb97c5e..82c3dc8 100644 --- a/http/index.html +++ b/http/index.html @@ -9,7 +9,7 @@ - +
-
@@ -171,5 +178,77 @@ + -- cgit v1.2.3