From c5863fcc84806291883ad0a13d3ef5d4f7306f6b Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Sun, 20 Dec 2020 13:45:34 -0500 Subject: [PATCH] Swagger docs, fix links, character limits --- main.py | 180 ++++++++++++++++++++++++++++++++++++++-------- requirements.txt | 2 + static/index.html | 49 +++++++++++-- 3 files changed, 196 insertions(+), 35 deletions(-) diff --git a/main.py b/main.py index ec9c9e3..4b0daa3 100644 --- a/main.py +++ b/main.py @@ -2,12 +2,16 @@ from app.init import boot import argparse from flask import Flask, render_template, jsonify, request, abort, send_from_directory from app.language import languages +from flask_swagger import swagger +from flask_swagger_ui import get_swaggerui_blueprint parser = argparse.ArgumentParser(description='LibreTranslate - Free and Open Source Translation API') parser.add_argument('host', type=str, help='Hostname', default="127.0.0.1") parser.add_argument('port', type=int, help='Port', default=5000) +parser.add_argument('--char-limit', default=-1, + help='Character limit') args = parser.parse_args() boot() @@ -27,46 +31,166 @@ def server_error(e): def index(): return send_from_directory('static', 'index.html') - @app.route("/languages") def langs(): - return jsonify([{'code': l.code, 'name': l.name } for l in languages]) + """ + Retrieve list of supported languages + --- + tags: + - translate + responses: + 200: + description: List of languages + content: + application/json: + schema: + type: array + items: + type: object + properties: + code: + type: string + description: Language code + name: + type: string + description: Human-readable language name (in English) + charLimit: + type: string + description: Character input limit for this language (-1 indicates no limit) + """ + return jsonify([{'code': l.code, 'name': l.name, 'charLimit': args.char_limit } for l in languages]) + +# Add cors +@app.after_request +def after_request(response): + response.headers.add('Access-Control-Allow-Origin','*') + response.headers.add('Access-Control-Allow-Headers', "Authorization, Content-Type") + response.headers.add('Access-Control-Expose-Headers', "Authorization") + response.headers.add('Access-Control-Allow-Methods', "GET, POST") + response.headers.add('Access-Control-Allow-Credentials', "true") + response.headers.add('Access-Control-Max-Age', 60 * 60 * 24 * 20) + return response + @app.route("/translate", methods=['POST']) def translate(): + """ + Translate text from a language to another + --- + tags: + - translate + parameters: + - in: formData + name: q + schema: + type: string + example: Hello world! + required: true + description: Text to translate + - in: formData + name: source + schema: + type: string + example: en + required: true + description: Source language code + - in: formData + name: target + schema: + type: string + example: es + required: true + description: Target language code + responses: + 200: + description: Translated text + content: + application/json: + schema: + type: object + properties: + translatedText: + type: string + description: Translated text + 400: + description: Invalid request + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Error message + 500: + description: Translation error + content: + application/json: + schema: + type: object + properties: + error: + type: string + description: Error message + """ - if request.is_json: - json = request.get_json() - q = json.get('q') - source_lang = json.get('source') - target_lang = json.get('target') - else: - q = request.values.get("q") - source_lang = request.values.get("source") - target_lang = request.values.get("target") + if request.is_json: + json = request.get_json() + q = json.get('q') + source_lang = json.get('source') + target_lang = json.get('target') + else: + q = request.values.get("q") + source_lang = request.values.get("source") + target_lang = request.values.get("target") - if not q: - abort(400, description="Invalid request: missing q parameter") - if not source_lang: - abort(400, description="Invalid request: missing source parameter") - if not target_lang: - abort(400, description="Invalid request: missing target parameter") + if not q: + abort(400, description="Invalid request: missing q parameter") + if not source_lang: + abort(400, description="Invalid request: missing source parameter") + if not target_lang: + abort(400, description="Invalid request: missing target parameter") + + if args.char_limit != -1: + q = q[:args.char_limit] + + src_lang = next(iter([l for l in languages if l.code == source_lang]), None) + tgt_lang = next(iter([l for l in languages if l.code == target_lang]), None) + + if src_lang is None: + abort(400, description="%s is not supported" % source_lang) + if tgt_lang is None: + abort(400, description="%s is not supported" % target_lang) + + translator = src_lang.get_translation(tgt_lang) + try: + return jsonify({"translatedText": translator.translate(q) }) + except Exception as e: + abort(500, description="Cannot translate text: %s" % str(e)) - src_lang = next(iter([l for l in languages if l.code == source_lang]), None) - tgt_lang = next(iter([l for l in languages if l.code == target_lang]), None) +swag = swagger(app) +swag['info']['version'] = "1.0" +swag['info']['title'] = "LibreTranslate" - if src_lang is None: - abort(400, description="%s is not supported" % source_lang) - if tgt_lang is None: - abort(400, description="%s is not supported" % target_lang) +@app.route("/spec") +def spec(): + return jsonify(swag) - translator = src_lang.get_translation(tgt_lang) - try: - return jsonify({"translatedText": translator.translate(q) }) - except Exception as e: - abort(500, description="Cannot translate text: %s" % str(e)) +SWAGGER_URL = '/docs' # URL for exposing Swagger UI (without trailing '/') +API_URL = 'http://petstore.swagger.io/v2/swagger.json' # Our API url (can of course be a local resource) +# Call factory function to create our blueprint +swaggerui_blueprint = get_swaggerui_blueprint( + SWAGGER_URL, + "", + config={ # Swagger UI config overrides + 'app_name': "LibreTranslate", + "spec": swag + } +) + +app.register_blueprint(swaggerui_blueprint) if __name__ == "__main__": app.run(host=args.host) diff --git a/requirements.txt b/requirements.txt index 478da92..1448d30 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ argostranslate==1.0.3 Flask==1.1.2 +flask-swagger==0.2.14 +flask-swagger-ui==3.36.0 diff --git a/static/index.html b/static/index.html index a753ddb..6aea9ae 100644 --- a/static/index.html +++ b/static/index.html @@ -21,22 +21,27 @@ overflow: auto; font-family: monospace; min-height: 280px; + width: 100%; + overflow: auto; } .progress.translate{ position: absolute; } + .card.horizontal .card-stacked{ + overflow: auto; + }