Browse Source

start over

raylu 12 years ago
commit
224f66a77a
12 changed files with 367 additions and 0 deletions
  1. 2 0
      .gitignore
  2. 0 0
      .gitmodules
  3. 29 0
      config.py
  4. 12 0
      config.yaml.example
  5. 15 0
      db/__init__.py
  6. 42 0
      db/importer.py
  7. 59 0
      db/schema.sql
  8. 66 0
      importer.py
  9. 57 0
      web/server.py
  10. 37 0
      web/static/css/base.less
  11. 18 0
      web/templates/base.html
  12. 30 0
      web/templates/home.html

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+__pycache__
+config.yaml

+ 0 - 0
.gitmodules


+ 29 - 0
config.py

@@ -0,0 +1,29 @@
+import yaml
+
+class Config:
+	def __init__(self, cdict):
+		attrs = set(self.attrs) # copy and "unfreeze"
+		for k, v in cdict.items():
+			attrs.remove(k) # check if the key is allowed, mark it as present
+			setattr(self, k, v)
+		if len(attrs) != 0:
+			raise KeyError('missing required bot config keys: %s' % attrs)
+
+class WebConfig(Config):
+	attrs = frozenset([
+		'port',
+		'host',
+		'cookie_secret',
+	])
+
+class DBConfig(Config):
+	attrs = frozenset([
+		'host',
+		'user',
+		'password',
+		'database',
+	])
+
+__doc = yaml.load(open('config.yaml', 'r'))
+web = WebConfig(__doc['web'])
+db = DBConfig(__doc['db'])

+ 12 - 0
config.yaml.example

@@ -0,0 +1,12 @@
+web:
+    port: 8888
+    host: 'localhost:8888'
+    cookie_secret: 'dis is super sekrit'
+
+db:
+    host: localhost
+    user: ykill
+    password: ykill
+    database: ykill
+
+# vim: set et ft=yaml:

+ 15 - 0
db/__init__.py

@@ -0,0 +1,15 @@
+import oursql
+
+from config import db as dbconfig
+
+conn = oursql.connect(host=dbconfig.host, db=dbconfig.database, user=dbconfig.user, passwd=dbconfig.password, autoreconnect=True)
+
+def ykill_cursor():
+	return conn.cursor(oursql.DictCursor)
+
+def execute(cursor, sql, *values):
+	cursor.execute(sql, values)
+
+def query(cursor, sql, *values):
+	execute(cursor, sql, *values)
+	return cursor.fetchall()

+ 42 - 0
db/importer.py

@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+import json
+from pprint import pprint
+
+import __init__ as db
+
+with db.ykill_cursor() as c:
+	db.execute(c, 'INSERT INTO kills (kill_id, solar_system_id, kill_time, moon_id) VALUES(?, ?, ?, ?)',
+			kill['killID'], kill['solarSystemID'], kill['killTime'], kill['moonID'])
+
+	victim = kill['victim']
+	parambatch = [(
+		kill['killID'], 1, victim['characterID'], victim['characterName'], victim['shipTypeID'],
+		victim['allianceID'], victim['allianceName'], victim['corporationID'], victim['corporationName'], victim['factionID'], victim['factionName'],
+		victim['damageTaken'], None, None, None,
+	)]
+	for attacker in kill['attackers']:
+		parambatch.append((
+			kill['killID'], 0, attacker['characterID'], attacker['characterName'], attacker['shipTypeID'],
+			attacker['allianceID'], attacker['allianceName'], attacker['corporationID'], attacker['corporationName'], attacker['factionID'], attacker['factionName'],
+			attacker['damageDone'], attacker['finalBlow'], attacker['securityStatus'], attacker['weaponTypeID'],
+		))
+	c.executemany('''
+		INSERT INTO characters (
+			kill_id, victim, character_id, character_name, ship_type_id,
+			alliance_id, alliance_name, corporation_id, corporation_name, faction_id, faction_name,
+			damage, final_blow, security_status, weapon_type_id
+		) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+		''', parambatch
+	)
+
+	parambatch = []
+	for item in kill['items']:
+		parambatch.append((kill['killID'], item['typeID'], item['flag'],
+			item['qtyDropped'], item['qtyDestroyed'], item['singleton']))
+	c.executemany('''
+		INSERT INTO items (
+			kill_id, type_id, flag, dropped, destroyed, singleton
+		) VALUES(?, ?, ?, ?, ?, ?)
+		''', parambatch
+	)

+ 59 - 0
db/schema.sql

@@ -0,0 +1,59 @@
+DROP TABLE IF EXISTS `characters`;
+DROP TABLE IF EXISTS `items`;
+DROP TABLE IF EXISTS `item_costs`;
+DROP TABLE IF EXISTS `kill_costs`;
+DROP TABLE IF EXISTS `kills`;
+
+CREATE TABLE `kills` (
+	`kill_id` int unsigned NOT NULL,
+	`solar_system_id` int NOT NULL,
+	`kill_time` datetime NOT NULL,
+	`moon_id` int unsigned NOT NULL,
+	PRIMARY KEY (`kill_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+
+CREATE TABLE `characters` (
+	`id` int unsigned NOT NULL AUTO_INCREMENT,
+	`kill_id` int unsigned NOT NULL,
+	`victim` tinyint(1) NOT NULL,
+	`character_id` int unsigned NOT NULL,
+	`character_name` varchar(64) NOT NULL,
+	`ship_type_id` int NOT NULL,
+	`alliance_id` int unsigned NOT NULL,
+	`alliance_name` varchar(64) NOT NULL,
+	`corporation_id` int unsigned NOT NULL,
+	`corporation_name` varchar(64) NOT NULL,
+	`faction_id` int NOT NULL,
+	`faction_name` varchar(64) NOT NULL,
+	`damage` int DEFAULT NULL,
+	`final_blow` tinyint(1) DEFAULT NULL,
+	`security_status` float DEFAULT NULL,
+	`weapon_type_id` int DEFAULT NULL,
+	PRIMARY KEY (`id`),
+	CONSTRAINT `fk_char_km` FOREIGN KEY (`kill_id`) REFERENCES `kills` (`kill_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+
+CREATE TABLE `items` (
+	`id` int unsigned NOT NULL AUTO_INCREMENT,
+	`kill_id` int unsigned NOT NULL,
+	`type_id` int NOT NULL,
+	`flag` tinyint(3) unsigned NOT NULL,
+	`dropped` int unsigned NOT NULL,
+	`destroyed` int unsigned NOT NULL,
+	`singleton` tinyint(4) NOT NULL,
+	PRIMARY KEY (`id`),
+	CONSTRAINT `fk_item_km` FOREIGN KEY (`kill_id`) REFERENCES `kills` (`kill_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+
+CREATE TABLE `item_costs` (
+	`type_id` int NOT NULL UNIQUE,
+	`cost` bigint unsigned NOT NULL,
+	PRIMARY KEY (`type_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
+
+CREATE TABLE `kill_costs` (
+	`kill_id` int unsigned NOT NULL UNIQUE,
+	`cost` bigint unsigned NOT NULL,
+	PRIMARY KEY (`kill_id`),
+	CONSTRAINT `fk_kill_cost_km` FOREIGN KEY (`kill_id`) REFERENCES `kills` (`kill_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

+ 66 - 0
importer.py

@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+
+from http.client import HTTPSConnection
+import json
+import operator
+from pprint import pprint
+
+import db
+
+def insert_kill(c, kill):
+    db.execute(c, 'INSERT INTO kills (kill_id, solar_system_id, kill_time, moon_id) VALUES(?, ?, ?, ?)',
+                    kill['killID'], kill['solarSystemID'], kill['killTime'], kill['moonID'])
+
+    victim = kill['victim']
+    parambatch = [(
+            kill['killID'], 1, victim['characterID'], victim['characterName'], victim['shipTypeID'],
+            victim['allianceID'], victim['allianceName'], victim['corporationID'], victim['corporationName'], victim['factionID'], victim['factionName'],
+            victim['damageTaken'], None, None, None,
+    )]
+    for attacker in kill['attackers']:
+            parambatch.append((
+                    kill['killID'], 0, attacker['characterID'], attacker['characterName'], attacker['shipTypeID'],
+                    attacker['allianceID'], attacker['allianceName'], attacker['corporationID'], attacker['corporationName'], attacker['factionID'], attacker['factionName'],
+                    attacker['damageDone'], attacker['finalBlow'], attacker['securityStatus'], attacker['weaponTypeID'],
+            ))
+    c.executemany('''
+            INSERT INTO characters (
+                    kill_id, victim, character_id, character_name, ship_type_id,
+                    alliance_id, alliance_name, corporation_id, corporation_name, faction_id, faction_name,
+                    damage, final_blow, security_status, weapon_type_id
+            ) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+            ''', parambatch
+    )
+
+    parambatch = []
+    for item in kill['items']:
+            parambatch.append((kill['killID'], item['typeID'], item['flag'],
+                    item['qtyDropped'], item['qtyDestroyed'], item['singleton']))
+    c.executemany('''
+            INSERT INTO items (
+                    kill_id, type_id, flag, dropped, destroyed, singleton
+            ) VALUES(?, ?, ?, ?, ?, ?)
+            ''', parambatch
+    )
+
+    db.conn.commit()
+
+def main():
+    conn = HTTPSConnection('zkillboard.com', timeout=10)
+    with db.ykill_cursor() as c:
+        groups = db.query(c, 'SELECT groupID FROM eve.invGroups WHERE categoryID = ?', 6)
+        groups = list(map(operator.itemgetter('groupID'), groups))
+        for i in range(0, len(groups), 10):
+            query_groups = map(str, groups[i:i+10])
+            conn.request('GET', '/api/groupID/{}/api-only'.format(','.join(query_groups)))
+            response = conn.getresponse()
+            if response.status != 200:
+                raise Exception('got {} {} from zkb'.format(response.status, response.reason))
+            kills = json.loads(response.read().decode('utf-8'))
+            response.close()
+            print('inserting', len(kills), 'kills')
+            for kill in kills:
+                insert_kill(c, kill)
+
+if __name__ == '__main__':
+    main()

+ 57 - 0
web/server.py

@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+
+import json
+from lesscss import lessc
+import tornado.gen
+import tornado.httpclient
+import tornado.ioloop
+import tornado.web
+import operator
+import os
+
+import config
+
+class BaseHandler(tornado.web.RequestHandler):
+	def render(self, *args, **kwargs):
+		kwargs['host'] = config.web.host
+		return super(BaseHandler, self).render(*args, **kwargs)
+
+	def render_string(self, *args, **kwargs):
+		s = super(BaseHandler, self).render_string(*args, **kwargs)
+		return s.replace(b'\n', b'') # this is like Django's {% spaceless %}
+
+class MainHandler(BaseHandler):
+	@tornado.web.asynchronous
+	@tornado.gen.coroutine
+	def get(self):
+		http_client = tornado.httpclient.AsyncHTTPClient()
+		kills_url = 'https://zkillboard.com/api/kills/corporationID/98182803/limit/1'
+		losses_url = 'https://zkillboard.com/api/losses/corporationID/98182803/limit/1'
+		kills_res, losses_res = yield [http_client.fetch(kills_url), http_client.fetch(losses_url)]
+		kills = json.loads(kills_res.body.decode('utf-8'))
+		losses = json.loads(losses_res.body.decode('utf-8'))
+		kills = sorted(kills + losses, key=operator.itemgetter('killTime'), reverse=True)
+		self.render('home.html', kills=kills)
+
+
+class CSSHandler(tornado.web.RequestHandler):
+	def get(self, css_path):
+		css_path = os.path.join(os.path.dirname(__file__), 'static', css_path) + '.less'
+		with open(css_path, 'r') as f:
+			self.set_header('Content-Type', 'text/css')
+			css = lessc.compile(f.read())
+			self.write(css)
+
+if __name__ == '__main__':
+	tornado.web.Application(
+		handlers=[
+			(r'/', MainHandler),
+			(r"/(css/.+)\.css", CSSHandler),
+		],
+		template_path=os.path.join(os.path.dirname(__file__), 'templates'),
+		static_path=os.path.join(os.path.dirname(__file__), 'static'),
+		cookie_secret=config.web.cookie_secret,
+		debug=True,
+	).listen(config.web.port)
+	print('Listening on :%d' % config.web.port)
+	tornado.ioloop.IOLoop.instance().start()

+ 37 - 0
web/static/css/base.less

@@ -0,0 +1,37 @@
+* {
+	box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	-webkit-box-sizing: border-box;
+}
+
+body {
+	margin: 0;
+	padding: 0;
+	height: 100%;
+	background: #000;
+	color: #ddd;
+	font-family: sans-serif;
+}
+
+a {
+	text-decoration: none;
+	color: #dde;
+}
+
+form {
+	input {
+		background: #fff;
+		border: 1px solid #cdd;
+		padding: 5px 10px;
+	}
+}
+
+.clear {
+	clear: both;
+}
+
+#wrapper {
+	width: 900px;
+	margin: 50px auto;
+	background: #111;
+}

+ 18 - 0
web/templates/base.html

@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<link rel="stylesheet" type="text/css" href="/css/base.css" />
+	<script src="//ajax.googleapis.com/ajax/libs/mootools/1.4.5/mootools.js"></script>
+</head>
+<body>
+	<div id="wrapper">
+		<div id="topbar">
+			<div id="title"></div>
+			<div id="nav">
+			</div>
+		</div>
+
+		{% block main %}{% end %}
+	</div>
+</body>
+</html>

+ 30 - 0
web/templates/home.html

@@ -0,0 +1,30 @@
+{% extends "base.html" %}
+
+{% block main %}
+
+<div class="kill_list">
+	{% for kill in kills %}
+		<a href="/kill/{{ kill['killID'] }}">
+			{{ kill['killTime'] }}
+			{{ kill['solarSystemID'] }}
+			<img src="//image.eveonline.com/Type/{{ kill['victim']['shipTypeID'] }}_64.png">
+			<img src="//image.eveonline.com/Character/{{ kill['victim']['characterID'] }}_64.jpg">
+			<img src="//image.eveonline.com/Corporation/{{ kill['victim']['corporationID'] }}_64.png">
+			<img src="//image.eveonline.com/Alliance/{{ kill['victim']['allianceID'] }}_64.png">
+			{{ kill['victim']['characterName'] }}
+			{{ kill['victim']['corporationName'] }}
+			{{ kill['victim']['allianceName'] }}
+			{% for attacker in kill['attackers'] %}
+				{% if attacker['finalBlow'] %}
+					{% set killer = attacker %}
+					{% break %}
+				{% end %}
+			{% end %}
+			{{ killer['characterName'] }}
+			{{ killer['corporationName'] }}
+			{{ killer['allianceName'] }}
+		</a>
+	{% end %}
+</div>
+
+{% end %}