@@ -2,6 +2,7 @@ | |||||
import time | import time | ||||
from flask import g | |||||
import requests | import requests | ||||
from ..exceptions import ZKillboardError | from ..exceptions import ZKillboardError | ||||
@@ -20,6 +21,45 @@ class ZKillboard: | |||||
self._base_url = "https://zkillboard.com/api" | self._base_url = "https://zkillboard.com/api" | ||||
self._last_query = 0 | self._last_query = 0 | ||||
def _extend_killmail(self, kill): | |||||
"""Extend a killmail object to match the old ZKill format. | |||||
Requires ESI API calls to fill in entity names. If we can't do them, | |||||
we'll just set the names to be empty. | |||||
""" | |||||
esi = g.eve.esi() | |||||
victim = kill["victim"] | |||||
if "character_id" in victim: | |||||
char_info = esi.v4.characters(victim["character_id"]).get() | |||||
victim["character_name"] = char_info["name"] | |||||
else: | |||||
victim["character_id"] = 0 | |||||
victim["character_name"] = "" | |||||
if "corporation_id" in victim: | |||||
corp_info = esi.v3.corporations(victim["corporation_id"]).get() | |||||
victim["corporation_name"] = corp_info["corporation_name"] | |||||
else: | |||||
victim["corporation_id"] = 0 | |||||
victim["corporation_name"] = "" | |||||
if "alliance_id" in victim: | |||||
alliance_info = esi.v2.alliances(victim["alliance_id"]).get() | |||||
victim["alliance_name"] = alliance_info["alliance_name"] | |||||
else: | |||||
victim["alliance_id"] = 0 | |||||
victim["alliance_name"] = "" | |||||
if "faction_id" in victim: | |||||
factions = esi.v1.universe.factions.get() | |||||
matches = [fac["faction_name"] for fac in factions | |||||
if fac["faction_id"] == victim["faction_id"]] | |||||
victim["faction_name"] = matches[0] if matches else "" | |||||
else: | |||||
victim["faction_id"] = 0 | |||||
victim["faction_name"] = "" | |||||
def query(self, *args): | def query(self, *args): | ||||
"""Make an API query using the given arguments.""" | """Make an API query using the given arguments.""" | ||||
query = "/" + "".join(str(arg) + "/" for arg in args) | query = "/" + "".join(str(arg) + "/" for arg in args) | ||||
@@ -47,10 +87,15 @@ class ZKillboard: | |||||
self._last_query = time.time() | self._last_query = time.time() | ||||
return result | return result | ||||
def iter_killmails(self, *args): | |||||
def iter_killmails(self, *args, extended=False): | |||||
"""Return an iterator over killmails using the given API arguments. | """Return an iterator over killmails using the given API arguments. | ||||
Automagically follows pagination as far as possible. (Be careful.) | Automagically follows pagination as far as possible. (Be careful.) | ||||
If extended is True, we will provide extra information for each kill | |||||
(names of entities instead of just IDs), which requires ESI API calls. | |||||
This matches the original API format of ZKill before it was, er, | |||||
"simplified". | |||||
""" | """ | ||||
page = 1 | page = 1 | ||||
while True: | while True: | ||||
@@ -60,7 +105,11 @@ class ZKillboard: | |||||
result = self.query(*args) | result = self.query(*args) | ||||
if result: | if result: | ||||
yield from result | |||||
if extended: | |||||
for kill in result: | |||||
yield self._extend_killmail(kill) | |||||
else: | |||||
yield from result | |||||
page += 1 | page += 1 | ||||
else: | else: | ||||
break | break |
@@ -94,10 +94,10 @@ class CampaignDB: | |||||
def add_kill(self, kill): | def add_kill(self, kill): | ||||
"""Insert a killmail into the database.""" | """Insert a killmail into the database.""" | ||||
try: | try: | ||||
datetime.strptime(kill["killTime"], "%Y-%m-%d %H:%M:%S") | |||||
datetime.strptime(kill["killmail_time"], "%Y-%m-%d %H:%M:%S") | |||||
except ValueError: | except ValueError: | ||||
raise RuntimeError("Invalid kill_date=%s for kill_id=%d" % ( | raise RuntimeError("Invalid kill_date=%s for kill_id=%d" % ( | ||||
kill["killTime"], kill["killID"])) | |||||
kill["killmail_time"], kill["killmail_id"])) | |||||
query = """INSERT OR REPLACE INTO kill ( | query = """INSERT OR REPLACE INTO kill ( | ||||
kill_id, kill_date, kill_system, kill_victim_shipid, | kill_id, kill_date, kill_system, kill_victim_shipid, | ||||
@@ -108,12 +108,13 @@ class CampaignDB: | |||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""" | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""" | ||||
victim = kill["victim"] | victim = kill["victim"] | ||||
args = ( | args = ( | ||||
int(kill["killID"]), kill["killTime"], int(kill["solarSystemID"]), | |||||
int(victim["shipTypeID"]), int(victim["characterID"]), | |||||
victim["characterName"], int(victim["corporationID"]), | |||||
victim["corporationName"], int(victim["allianceID"]), | |||||
victim["allianceName"], int(victim["factionID"]), | |||||
victim["factionName"], float(kill["zkb"]["totalValue"])) | |||||
int(kill["killmail_id"]), kill["killmail_time"], | |||||
int(kill["solar_system_id"]), int(victim["ship_type_id"]), | |||||
int(victim["character_id"]), victim["character_name"], | |||||
int(victim["corporation_id"]), victim["corporation_name"], | |||||
int(victim["alliance_id"]), victim["alliance_name"], | |||||
int(victim["faction_id"]), victim["faction_name"], | |||||
float(kill["zkb"]["totalValue"])) | |||||
with self._conn as conn: | with self._conn as conn: | ||||
conn.execute(query, args) | conn.execute(query, args) | ||||
@@ -33,7 +33,7 @@ def _build_filter(qualifiers, arg): | |||||
def _store_kill(cname, opnames, kill): | def _store_kill(cname, opnames, kill): | ||||
"""Store the given kill and its associations into the database.""" | """Store the given kill and its associations into the database.""" | ||||
kid = kill["killID"] | |||||
kid = kill["killmail_id"] | |||||
if g.campaign_db.has_kill(kid): | if g.campaign_db.has_kill(kid): | ||||
current = g.campaign_db.get_kill_associations(cname, kid) | current = g.campaign_db.get_kill_associations(cname, kid) | ||||
opnames -= set(current) | opnames -= set(current) | ||||
@@ -59,12 +59,16 @@ def _update_killboard_operations(cname, opnames, min_kill_id): | |||||
"no-attackers", "orderDirection", "desc"] | "no-attackers", "orderDirection", "desc"] | ||||
max_kill_id = min_kill_id | max_kill_id = min_kill_id | ||||
for kill in g.eve.zkill.iter_killmails(*args): | |||||
kid = kill["killID"] | |||||
for kill in g.eve.zkill.iter_killmails(*args, extended=True): | |||||
kid = kill["killmail_id"] | |||||
if min_kill_id > 0 and kid == min_kill_id: | if min_kill_id > 0 and kid == min_kill_id: | ||||
# TODO: This fails if ZKill receives kills out of order. | |||||
# Should look ahead for kills within, say, 12 hours of the | |||||
# min_kill_id, and extend code below to ignore kills aleady | |||||
# included instead of re-adding. | |||||
break | break | ||||
ktime = kill["killTime"] | |||||
ktime = kill["killmail_time"] | |||||
logger.debug("Evaluating kill date=%s id=%d for campaign=%s " | logger.debug("Evaluating kill date=%s id=%d for campaign=%s " | ||||
"operations=%s", ktime, kid, cname, ",".join(opnames)) | "operations=%s", ktime, kid, cname, ",".join(opnames)) | ||||
max_kill_id = max(max_kill_id, kid) | max_kill_id = max(max_kill_id, kid) | ||||