A copyright violation detector running on Wikimedia Cloud Services https://tools.wmflabs.org/copyvios/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

198 lines
6.3 KiB

  1. #! /usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from functools import wraps
  4. from hashlib import md5
  5. from json import dumps
  6. from logging import DEBUG, INFO, getLogger
  7. from logging.handlers import TimedRotatingFileHandler
  8. from multiprocessing import Value
  9. from os import path
  10. from time import asctime
  11. from traceback import format_exc
  12. from urllib import quote_plus, quote
  13. from earwigbot.bot import Bot
  14. from earwigbot.wiki.copyvios import globalize
  15. from flask import Flask, g, make_response, request, redirect, session
  16. from flask_mako import MakoTemplates, render_template, TemplateError
  17. from copyvios.api import format_api_error, handle_api_request
  18. from copyvios.checker import do_check
  19. from copyvios.cookies import parse_cookies
  20. from copyvios.misc import cache, get_notice
  21. from copyvios.settings import process_settings
  22. from copyvios.sites import update_sites
  23. from copyvios.auth import oauth_login_start, oauth_login_end, clear_login_session
  24. app = Flask(__name__)
  25. MakoTemplates(app)
  26. hand = TimedRotatingFileHandler("logs/app.log", when="midnight", backupCount=7)
  27. hand.setLevel(DEBUG)
  28. app.config.from_pyfile("config.py", True)
  29. app.logger.addHandler(hand)
  30. app.logger.info(u"Flask server started " + asctime())
  31. app._hash_cache = {}
  32. def catch_errors(func):
  33. @wraps(func)
  34. def inner(*args, **kwargs):
  35. try:
  36. return func(*args, **kwargs)
  37. except TemplateError as exc:
  38. app.logger.error(u"Caught exception:\n{0}".format(exc.text))
  39. return render_template("error.mako", traceback=exc.text)
  40. except Exception:
  41. app.logger.exception(u"Caught exception:")
  42. return render_template("error.mako", traceback=format_exc())
  43. return inner
  44. @app.before_first_request
  45. def setup_app():
  46. cache.bot = Bot(".earwigbot", 100)
  47. cache.langs, cache.projects = [], []
  48. cache.last_sites_update = 0
  49. cache.background_data = {}
  50. cache.last_background_updates = {}
  51. oauth_config = cache.bot.config.wiki.get('copyvios', {}).get('oauth', {})
  52. if oauth_config.get('consumer_token') is None:
  53. raise ValueError("No OAuth consumer token is configured (config.wiki.copyvios.oauth.consumer_token).")
  54. if oauth_config.get('consumer_secret') is None:
  55. raise ValueError("No OAuth consumer secret is configured (config.wiki.copyvios.oauth.consumer_secret).")
  56. globalize(num_workers=8)
  57. @app.before_request
  58. def prepare_request():
  59. g._db = None
  60. g.cookies = parse_cookies(
  61. request.script_root or "/", request.environ.get("HTTP_COOKIE"))
  62. g.new_cookies = []
  63. @app.after_request
  64. def add_new_cookies(response):
  65. for cookie in g.new_cookies:
  66. response.headers.add("Set-Cookie", cookie)
  67. return response
  68. @app.after_request
  69. def write_access_log(response):
  70. msg = u"%s %s %s %s -> %s"
  71. app.logger.debug(msg, asctime(), request.method, request.path,
  72. request.values.to_dict(), response.status_code)
  73. return response
  74. @app.teardown_appcontext
  75. def close_databases(error):
  76. if g._db:
  77. g._db.close()
  78. def external_url_handler(error, endpoint, values):
  79. if endpoint == "static" and "file" in values:
  80. fpath = path.join(app.static_folder, values["file"])
  81. mtime = path.getmtime(fpath)
  82. cache = app._hash_cache.get(fpath)
  83. if cache and cache[0] == mtime:
  84. hashstr = cache[1]
  85. else:
  86. with open(fpath, "rb") as f:
  87. hashstr = md5(f.read()).hexdigest()
  88. app._hash_cache[fpath] = (mtime, hashstr)
  89. return "/static/{0}?v={1}".format(values["file"], hashstr)
  90. raise error
  91. app.url_build_error_handlers.append(external_url_handler)
  92. @app.route("/")
  93. @catch_errors
  94. def index():
  95. notice = get_notice()
  96. update_sites()
  97. query = do_check()
  98. if query.submitted and query.error == "not logged in":
  99. return redirect("/login?next=" + quote("/?" + request.query_string), 302)
  100. return render_template(
  101. "index.mako", notice=notice, query=query, result=query.result,
  102. turnitin_result=query.turnitin_result)
  103. @app.route("/login", methods=["GET", "POST"])
  104. @catch_errors
  105. def login():
  106. try:
  107. redirect_url = oauth_login_start() if request.method == "POST" else None
  108. if redirect_url:
  109. return redirect(redirect_url, 302)
  110. except Exception as e:
  111. app.log_exception(e)
  112. print e.message
  113. kwargs = {"error": e.message}
  114. else:
  115. if session.get("username") is not None:
  116. return redirect("/", 302)
  117. kwargs = {"error": request.args.get("error")}
  118. return render_template("login.mako", **kwargs)
  119. @app.route("/logout", methods=["GET", "POST"])
  120. @catch_errors
  121. def logout():
  122. if request.method == "POST":
  123. clear_login_session()
  124. return redirect("/", 302)
  125. else:
  126. return render_template("logout.mako")
  127. @app.route("/oauth-callback")
  128. @catch_errors
  129. def oauth_callback():
  130. try:
  131. next_url = oauth_login_end()
  132. except Exception as e:
  133. app.log_exception(e)
  134. return redirect("/login?error=" + quote_plus(e.message), 302)
  135. else:
  136. return redirect(next_url, 302)
  137. @app.route("/settings", methods=["GET", "POST"])
  138. @catch_errors
  139. def settings():
  140. status = process_settings() if request.method == "POST" else None
  141. update_sites()
  142. default = cache.bot.wiki.get_site()
  143. kwargs = {"status": status, "default_lang": default.lang,
  144. "default_project": default.project}
  145. return render_template("settings.mako", **kwargs)
  146. @app.route("/api")
  147. @catch_errors
  148. def api():
  149. return render_template("api.mako", help=True)
  150. @app.route("/api.json")
  151. @catch_errors
  152. def api_json():
  153. if not request.args:
  154. return render_template("api.mako", help=True)
  155. format = request.args.get("format", "json")
  156. if format in ["json", "jsonfm"]:
  157. update_sites()
  158. try:
  159. result = handle_api_request()
  160. except Exception as exc:
  161. result = format_api_error("unhandled_exception", exc)
  162. else:
  163. errmsg = u"Unknown format: '{0}'".format(format)
  164. result = format_api_error("unknown_format", errmsg)
  165. if format == "jsonfm":
  166. return render_template("api.mako", help=False, result=result)
  167. resp = make_response(dumps(result))
  168. resp.mimetype = "application/json"
  169. resp.headers["Access-Control-Allow-Origin"] = "*"
  170. return resp
  171. if __name__ == '__main__':
  172. app.run()