commit 233c2946de68a71493f96a8f2b31c52a8ee224bd Author: Andrew Williams Date: Wed Nov 2 13:52:41 2011 +0000 Initial import diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5e9904 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +env/ +test.py +build/ +*.pyc diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..93d110f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +-e git+https://github.com/nikdoof/eveapi.git#egg=eveapi +ordereddict +jinja2 +argparse diff --git a/scripts/evestandings b/scripts/evestandings new file mode 100755 index 0000000..e343f40 --- /dev/null +++ b/scripts/evestandings @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +import sys +import os +from argparse import ArgumentParser +from ConfigParser import ConfigParser + +import standings + +def load_config(file='~/.evestandings.conf'): + config = ConfigParser() + file = os.path.expanduser(file) + if os.path.exists(file): + config.read(file) + outconfig = object() + for name, val in config.items('standings'): + setattr(outconfig, name, val) + return outconfig + else: + return {} + +def output_html(keyid, vcode, character, type): + sys.stdout.write(Standings(keyid, vcode, character, type=type)._get_html()) + +if __name__ == '__main__': + + parser = ArgumentParser(prog='EVEStandings', description='Outputs a EVE corporation/alliance standings to a HTML page') + parser.add_argument('-k', '--keyid', help='Key ID of the API key') + parser.add_argument('-v', '--vcode', help='vCode of the API key') + parser.add_argument('-c', '--character', help='Character whos corporation you wish to output') + parser.add_argument('-t', '--type', help='Type of standings list, either corp or alliance') + parser.add_argument('-C', '--config', help='Path to your configuration file') + parser.add_argument('-f', '--output', help='Output the resulting HTML to a file') + parser.add_argument('--template', help='Location of a customized template to use instead of the default') + parser.add_argument('--version', action='version', version='%(prog)s ' + standings.__version__) + + ns = parser.parse_args() + + # + if 'keyid' in ns or 'vcode' in ns: + conf = ns + else: + if 'config' in ns: + conf = load_config(ns['config']) + else: + conf = load_config() + + if not conf.keyid or not conf.vcode: + sys.stderr.write('Key ID or vCode is missing, please provide both on the command line or in the config file\n') + parser.print_help() + sys.exit(os.EX_USAGE) + + print ns + obj = standings.Standings(conf.keyid, conf.vcode, conf.character) + html = obj._get_html(conf.template) + + if conf.output: + f = open(os.path.expanduser(conf.output), 'w') + f.write(html) + f.close() + else: + sys.stdout.write(html) + + sys.exit(0) diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..4ddf579 --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +from distutils.core import setup +from standings import __version__ + +setup(name = "standings", + version = __version__, + description = "EVE API Standings Page Generator", + author = "Andrew Willaims", + author_email = "andy@tensixtyone.com", + url = "https://dev.pleaseignore.com/", + keywords = "eveapi", + packages = ['standings',], + scripts = ['scripts/evestandings'], + package_data={'standings': ['templates/*.html']}, + + classifiers = [ + 'License :: OSI Approved :: BSD License', + 'Development Status :: 3 - Alpha', + ] +) diff --git a/standings/__init__.py b/standings/__init__.py new file mode 100644 index 0000000..5bad0cf --- /dev/null +++ b/standings/__init__.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +import sys +try: + from collections import OrderedDict +except ImportError: + from ordereddict import OrderedDict + +from eveapi import EVEAPIConnection, Error +from jinja2 import Environment, PackageLoader + +from standings.cache import DbCacheHandler + +__version__ = '0.1' + +STANDINGS_ALLIANCE = 0 +STANDINGS_CORPORATION = 1 + +class Standings: + """ + Grabs the latest Standings from the EVE API and outputs them into + a nice template format + """ + + def __init__(self, keyid, vcode, characterid, dbpath='/tmp/standingscache.sqlite3', type=STANDINGS_ALLIANCE): + self.eveapi = EVEAPIConnection(cacheHandler=DbCacheHandler(dbpath)).auth(keyID=keyid, vCode=vcode) + self.character = characterid + self.standings_type = type + + @property + def _get_alliance_id_list(self): + if not hasattr(self, '_allianceids'): + self._allianceids = set([x.allianceID for x in EVEAPIConnection().eve.AllianceList().alliances]) + return self._allianceids + + def _check_if_corp(self, corpid): + try: + res = EVEAPIConnection().corp.CorporationSheet(corporationID=corpid) + except Error: + return False + return True + + def _get_standings(self): + res = self.eveapi.corp.ContactList(characterID=self.character) + + standings = OrderedDict() + for x in ['excellent', 'good', 'neutral', 'bad', 'terrible']: standings[x] = [] + + def parse_list(list, output): + for row in list: + level = float(row['standing']) + if level > 5 and level <= 10: + type = 'excellent' + elif level > 0 and level <= 5: + type = 'good' + elif level < 0 and level >= -5: + type = 'bad' + elif level < -5 and level >= -10: + type = 'terrible' + else: + # Neutral? + type = 'neutral' + + if int(row['contactID']) in self._get_alliance_id_list: + rowtype = 'alli' + elif self._check_if_corp(int(row['contactID'])): + rowtype = 'corp' + else: + rowtype = 'char' + + output[type].append((rowtype, row['contactID'], row['contactName'], row['standing'])) + + # Order standings for each group + for x in ['excellent', 'good', 'neutral', 'bad', 'terrible']: + standings[x] = sorted(standings[x], key=lambda v: -int(v[3])) + + + if self.standings_type == STANDINGS_ALLIANCE: + parse_list(res.allianceContactList, standings) + else: + parse_list(res.corporateContactList, standings) + + return standings + + def _get_name(self): + res = self.eveapi.corp.CorporationSheet() + if hasattr(res, 'allianceName'): return res.allianceName + return res.corporationName + + def _get_html(self, template='standings_list.html'): + if not template: template = 'standings_list.html' + env = Environment(loader=PackageLoader('standings', 'templates')) + template = env.get_template(template) + return template.render(standings=self._get_standings(), name=self._get_name()) diff --git a/standings/cache.py b/standings/cache.py new file mode 100644 index 0000000..4e46c0f --- /dev/null +++ b/standings/cache.py @@ -0,0 +1,83 @@ +from hashlib import sha1 +import logging +import sqlite3 + +SQLITE_PATH = '/tmp/eveapicache.sqlite3' + +class DbCacheHandler: + """ + Database backed cache handler for Entity's eveapi module + """ + + def __init__(self, conn=SQLITE_PATH): + self._conn_url = conn + + @property + def log(self): + if not hasattr(self, '_log'): + self._log = logging.getLogger(self.__class__.__name__) + return self._log + + @property + def conn(self): + if not hasattr(self, '_conn') or not self._conn: + self._conn = sqlite3.connect(self._conn_url) + self.setup() + return self._conn + + @property + def cursor(self): + if not hasattr(self, '_cursor') or not self._cursor: + self._cursor = self.conn.cursor() + return self._cursor + + def setup(self): + if not hasattr(self, '_setupchecked'): + self.cursor.execute('CREATE TABLE IF NOT EXISTS api_cache(docid TEXT PRIMARY KEY, xml TEXT, cacheduntil TEXT)') + self._setupchecked = True + + def disconnect(self): + if hasattr(self, '_cursor'): + self._cursor.close() + self._cursor = None + if hasattr(self, '_conn'): + self._conn.close() + self._conn = None + + @staticmethod + def _gen_docid(host, path, params): + return sha1("%s%s?%s" % (host, path, params)).hexdigest() + + def retrieve(self, host, path, params): + docid = self._gen_docid(host, path, params) + self.log.debug("Retrieving document: %s" % docid) + try: + self.cursor.execute("SELECT xml FROM api_cache WHERE docid = ? and datetime(cacheduntil, 'unixepoch') >= current_timestamp", (docid,)) + res = self.cursor.fetchone() + self.disconnect() + except sqlite3.Error as e: + self.log.error("Error retrieving document: %s", e.args[0]) + else: + if res: + self.log.debug("Found %s documents for ID %s" % (len(res), docid)) + return res[0] + return None + + def store(self, host, path, params, doc, obj): + docid = self._gen_docid(host, path, params) + self.log.debug("Storing document: %s (%s)" % (docid, path)) + try: + self.cursor.execute('REPLACE INTO api_cache (docid, xml, cacheduntil) VALUES (?, ?, ?)', (docid, doc, obj.cachedUntil)) + self.conn.commit() + self.disconnect() + except sqlite3.Error as e: + self.log.error("Error storing document: %s", e.args[0]) + + def purge_stale(self): + self.log.info("Purging stale cached documents") + try: + self.cursor.execute("DELETE FROM api_cache WHERE datetime(cacheduntil, 'unixepoch') >= current_timestamp") + self.conn.commit() + self.disconnect() + except sqlite3.Error as e: + self.log.error("Error purging document cache: %s", e.args[0]) diff --git a/standings/templates/base.html b/standings/templates/base.html new file mode 100644 index 0000000..9d80c9e --- /dev/null +++ b/standings/templates/base.html @@ -0,0 +1,13 @@ + + + + {% block head %}{% block title %}{% endblock %}{% endblock %} + + +
+
+ {% block content %}

It seems something has gone wrong, please contact a admin

{% endblock %} +
+
+ + diff --git a/standings/templates/standings_list.html b/standings/templates/standings_list.html new file mode 100644 index 0000000..ed15d4b --- /dev/null +++ b/standings/templates/standings_list.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block head %} + {{ name }} Standings + +{% endblock %} + +{% block content %} +{% for type in standings %} +
+

{{ type }}

+

+ {% for type, id, entname, standing in standings[type] %} + + {% endfor %} +

+
+{% endfor %} +{% endblock %}