123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- import logging
- from base64 import b64encode
- from copy import deepcopy
- from email.message import EmailMessage
- from json import dumps
- from os import environ, urandom
- from smtplib import SMTP
- from flask import Flask, jsonify, render_template, request
- from flask_cors import CORS
- from scriptshifter import EMAIL_FROM, EMAIL_TO, SMTP_HOST, SMTP_PORT
- from scriptshifter.exceptions import ApiError
- from scriptshifter.tables import list_tables, load_table
- from scriptshifter.trans import transliterate
- logger = logging.getLogger(__name__)
- logging.basicConfig(level=environ.get("TXL_LOGLEVEL", logging.INFO))
- def create_app():
- flask_env = environ.get("TXL_APP_MODE", "production")
- app = Flask(__name__)
- app.config.update({
- "ENV": flask_env,
- "SECRET_KEY": environ.get("TXL_FLASK_SECRET", b64encode(urandom(64))),
- "JSON_AS_ASCII": False,
- "JSONIFY_PRETTYPRINT_REGULAR": True,
- })
- CORS(app)
- return app
- app = create_app()
- @app.errorhandler(ApiError)
- def handle_exception(e: ApiError):
- if e.status_code >= 500:
- warnings = [
- "An internal error occurred.",
- "If the error persists, contact the technical support team."
- ]
- else:
- warnings = [
- "ScriptShifter API replied with status code "
- f"{e.status_code}: {e.msg}"
- ]
- if e.status_code >= 400:
- warnings.append(
- "Please review your input before repeating this request.")
- body = {
- "warnings": warnings,
- "output": "",
- }
- if logging.DEBUG >= logging.root.level:
- body["debug"] = {
- "form_data": request.json or request.form,
- }
- return (body, e.status_code)
- @app.route("/", methods=["GET"])
- def index():
- return render_template(
- "index.html",
- languages=list_tables(),
- feedback_form=SMTP_HOST is not None)
- @app.route("/health", methods=["GET"])
- def health_check():
- return "I'm alive!\n"
- @app.route("/languages", methods=["GET"])
- def list_languages():
- return jsonify(list_tables())
- @app.route("/table/<lang>")
- def dump_table(lang):
- """
- Dump parsed transliteration table for a language.
- """
- tbl = deepcopy(load_table(lang))
- for sec_name in ("roman_to_script", "script_to_roman"):
- if sec_name in tbl:
- for hname, fn_defs in tbl[sec_name].get("hooks", {}).items():
- tbl[sec_name]["hooks"][hname] = [
- (fn.__name__, kw) for (fn, kw) in fn_defs]
- return jsonify(tbl)
- @app.route("/options/<lang>", methods=["GET"])
- def get_options(lang):
- """
- Get extra options for a table.
- """
- tbl = load_table(lang)
- return jsonify(tbl.get("options", []))
- @app.route("/trans", methods=["POST"])
- def transliterate_req():
- lang = request.json["lang"]
- in_txt = request.json["text"]
- capitalize = request.json.get("capitalize", False)
- t_dir = request.json.get("t_dir", "s2r")
- if t_dir not in ("s2r", "r2s"):
- return f"Invalid direction: {t_dir}", 400
- if not len(in_txt):
- return ("No input text provided! ", 400)
- options = request.json.get("options", {})
- logger.debug(f"Extra options: {options}")
- try:
- out, warnings = transliterate(in_txt, lang, t_dir, capitalize, options)
- except (NotImplementedError, ValueError) as e:
- raise ApiError(str(e), 400)
- except Exception as e:
- raise ApiError(str(e), 500)
- return {"output": out, "warnings": warnings}
- @app.route("/feedback", methods=["POST"])
- def feedback():
- """
- Allows users to provide feedback to improve a specific result.
- """
- t_dir = request.json.get("t_dir", "s2r")
- options = request.json.get("options", {})
- contact = request.json.get("contact")
- msg = EmailMessage()
- msg["subject"] = "Scriptshifter feedback report"
- msg["from"] = EMAIL_FROM
- msg["to"] = EMAIL_TO
- if contact:
- msg["cc"] = contact
- msg.set_content(f"""
- *Scriptshifter feedback report from {contact or 'anonymous'}*\n\n
- *Language:* {request.json['lang']}\n
- *Direction:* {
- 'Roman to Script' if t_dir == 'r2s'
- else 'Script to Roman'}\n
- *Source:* {request.json['src']}\n
- *Result:* {request.json['result']}\n
- *Expected result:* {request.json['expected']}\n
- *Applied options:* {dumps(options)}\n
- *Notes:*\n
- {request.json['notes']}""")
- # TODO This uses a test SMTP server:
- # python -m smtpd -n -c DebuggingServer localhost:1025
- smtp = SMTP(SMTP_HOST, SMTP_PORT)
- smtp.send_message(msg)
- smtp.quit()
- return {"message": "Feedback message sent."}
|