| @@ -0,0 +1,19 @@ | |||||
| Copyright (c) 2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| of this software and associated documentation files (the "Software"), to deal | |||||
| in the Software without restriction, including without limitation the rights to | |||||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |||||
| of the Software, and to permit persons to whom the Software is furnished to do | |||||
| so, subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in all | |||||
| copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| SOFTWARE. | |||||
| @@ -0,0 +1,36 @@ | |||||
| This is a tool that calculates the | |||||
| [Template Influence Factor](https://en.wikipedia.org/wiki/User:The_Earwig/Sandbox/TIF) | |||||
| of a [Wikipedia template](https://en.wikipedia.org/wiki/Help:Template), which | |||||
| is a measure of how | |||||
| [high risk](https://en.wikipedia.org/wiki/Wikipedia:High-risk_templates) it is, | |||||
| for the purposes of anti-vandalism. It runs on | |||||
| [Wikimedia Labs](https://tools.wmflabs.org/earwig-dev/tif). | |||||
| Dependencies | |||||
| ============ | |||||
| * [earwigbot](https://github.com/earwig/earwigbot) >= 0.2 | |||||
| * [flask](http://flask.pocoo.org/) >= 0.10.1 | |||||
| * [flask-mako](https://pythonhosted.org/Flask-Mako/) >= 0.3 | |||||
| * [mako](http://www.makotemplates.org/) >= 1.0.3 | |||||
| * [oursql](http://packages.python.org/oursql/) >= 0.9.3.1 | |||||
| Running | |||||
| ======= | |||||
| - Install all dependencies listed above. | |||||
| - Create an SQL database ... | |||||
| - Create an earwigbot instance in `.earwigbot` (run `earwigbot .earwigbot`). In | |||||
| `.earwigbot/config.yml`, fill out the connection info for the database by | |||||
| adding the following to the `wiki` section: | |||||
| _tifSQL: | |||||
| host: <hostname of database server> | |||||
| db: <name of database> | |||||
| If additional arguments are needed by `oursql.connect()`, like usernames or | |||||
| passwords, they should be added to the `_tifSQL` section. | |||||
| - Start the web server (on Tool Labs, `webservice2 uwsgi-python start`). | |||||
| @@ -0,0 +1,28 @@ | |||||
| #! /usr/bin/env python | |||||
| # -*- coding: utf-8 -*- | |||||
| from flask import Flask, g | |||||
| from flask.ext.mako import MakoTemplates, render_template | |||||
| from tif.util import catch_errors, set_up_hash_caching | |||||
| app = Flask(__name__) | |||||
| MakoTemplates(app) | |||||
| set_up_hash_caching(app) | |||||
| @app.before_request | |||||
| def prepare_request(): | |||||
| g._db = None | |||||
| @app.teardown_appcontext | |||||
| def close_databases(error): | |||||
| if g._db: | |||||
| g._db.close() | |||||
| @app.route("/") | |||||
| @catch_errors(app) | |||||
| def index(): | |||||
| return render_template("index.mako") | |||||
| if __name__ == '__main__': | |||||
| app.run() | |||||
| @@ -0,0 +1 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| @@ -0,0 +1,43 @@ | |||||
| # -*- coding: utf-8 -*- | |||||
| from functools import wraps | |||||
| from hashlib import md5 | |||||
| from os import path | |||||
| from traceback import format_exc | |||||
| from flask.ext.mako import render_template, TemplateError | |||||
| __all__ = ["catch_errors", "set_up_hash_caching"] | |||||
| def catch_errors(app): | |||||
| def callback(func): | |||||
| @wraps(func) | |||||
| def inner(*args, **kwargs): | |||||
| try: | |||||
| return func(*args, **kwargs) | |||||
| except TemplateError as exc: | |||||
| app.logger.error(u"Caught exception:\n{0}".format(exc.text)) | |||||
| return render_template("error.mako", traceback=exc.text) | |||||
| except Exception: | |||||
| app.logger.exception(u"Caught exception:") | |||||
| return render_template("error.mako", traceback=format_exc()) | |||||
| return inner | |||||
| return callback | |||||
| def set_up_hash_caching(app): | |||||
| def callback(app, error, endpoint, values): | |||||
| if endpoint == "static" and "file" in values: | |||||
| fpath = path.join(app.static_folder, values["file"]) | |||||
| mtime = path.getmtime(fpath) | |||||
| cache = app._hash_cache.get(fpath) | |||||
| if cache and cache[0] == mtime: | |||||
| hashstr = cache[1] | |||||
| else: | |||||
| with open(fpath, "rb") as f: | |||||
| hashstr = md5(f.read()).hexdigest() | |||||
| app._hash_cache[fpath] = (mtime, hashstr) | |||||
| return "/static/{0}?v={1}".format(values["file"], hashstr) | |||||
| raise error | |||||
| app._hash_cache = {} | |||||
| app.url_build_error_handlers.append(lambda *args: callback(app, *args)) | |||||