@@ -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)) |