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