def clear_character_cache(self, e): conn, cur = db.connect_persistent_db() query_statement = '''DELETE FROM characters''' cur.execute(query_statement) conn.commit() conn.close() statusmsg.push_status("Cleared character cache")
def get_affil_names(conn, cur): # use select distinct to get corp and alliance ids and reslove them alliance_ids = cur.execute('''SELECT DISTINCT alliance_id FROM characters WHERE alliance_id IS NOT 0''').fetchall() corp_ids = cur.execute( "SELECT DISTINCT corp_id FROM characters WHERE corp_id IS NOT 0" ).fetchall() ids = alliance_ids + corp_ids ids = json.dumps(tuple([r[0] for r in ids])) statusmsg.push_status( "Obtaining corporation and alliance names and zKillboard data...") try: names = apis.post_req_ccp("universe/names/", ids) except: Logger.info("Failed request corporation and alliance names.", exc_info=True) raise Exception alliances, corporations = (), () for r in names: if r["category"] == "alliance": alliances = alliances + ((r["id"], r["name"]), ) elif r["category"] == "corporation": corporations = corporations + ((r["id"], r["name"]), ) if alliances: query_string = ('''INSERT INTO alliances (id, name) VALUES (?, ?)''') db.write_many_to_db(conn, cur, query_string, alliances) if corporations: query_string = ( '''INSERT INTO corporations (id, name) VALUES (?, ?)''') db.write_many_to_db(conn, cur, query_string, corporations)
def analyze_chars(char_names): start_time = time.time() wx.CallAfter(app.PySpy.list.DeleteAllItems) try: outlist = analyze.main(char_names) duration = round(time.time() - start_time, 1) reportstats.ReportStats(outlist, duration).start() if outlist is not None: wx.CallAfter(app.PySpy.updateList, outlist, duration) else: statusmsg.push_status( "No valid character names found. Please try again...") except Exception: Logger.error("Failed to collect character information. Clipboard " "content was: " + str(char_names), exc_info=True)
def watch_clpbd(): valid = False recent_value = None while True: clipboard = pyperclip.paste() if clipboard != recent_value: char_names = clipboard.splitlines() for name in char_names: valid = check_name_validity(name) if valid is False: break if valid: statusmsg.push_status("Clipboard change detected...") recent_value = clipboard analyze_chars(clipboard.splitlines()) time.sleep(0.5) # Short sleep between loops to reduce CPU load
def get_char_ids(conn, cur, char_names): char_names = json.dumps( char_names[0:config.MAX_NAMES]) # apis max char is 1000 statusmsg.push_status("Resolving character names to IDs...") try: characters = apis.post_req_ccp("universe/ids/", char_names) characters = characters['characters'] except: return 0 records = () for r in characters: records = records + ((r["id"], r["name"]), ) query_string = ( '''INSERT INTO characters (char_id, char_name) VALUES (?, ?)''') records_added = db.write_many_to_db(conn, cur, query_string, records) return records_added
def updateList(self, outlist, duration=None): # If updateList() gets called before outlist has been provided, do nothing if outlist is None: return self.options.Set("outlist", outlist) hl_blops = self.options.Get("HlBlops", True) self.list.DeleteAllItems() for r in outlist: # Schema depending on output_list() in analyze.py id = r[0] faction_id = r[1] name = r[2] corp = r[3] alliance = str(r[4]) + " (" + str( r[6]) + ")" if r[4] is not None else "-" faction = r[5] if r[5] is not None else "-" week_kills = r[7] zkill = str(r[8]) + " - " + str(r[9]) + " - " + str( r[10]) if r[8] is not None else "n.a." out = [ id, faction_id, name, corp, alliance, faction, week_kills, zkill ] # Check if character belongs to a faction that should be ignored if faction_id != 0: if config.IGNORED_FACTIONS == 2 and faction_id % 2 == 0: continue if config.IGNORED_FACTIONS == 1 and faction_id % 2 != 0: continue self.list.Append(out) if hl_blops and r[7] is not None and ( r[9] > 0 or r[10] > 0): # Highlight BLOPS killer & HIC pilots. self.list.SetItemBackgroundColour(self.list.ItemCount - 1, wx.Colour(255, 189, 90)) if duration is not None: statusmsg.push_status( str(len(outlist)) + " characters analysed in " + str(duration) + " seconds. Double click character to go to zKillboard.")
def post_req_ccp(esi_path, json_data): url = "https://esi.evetech.net/latest/" + esi_path + \ "?datasource=tranquility" try: r = requests.post(url, json_data) except requests.exceptions.ConnectionError: Logger.info("No network connection.", exc_info=True) statusmsg.push_status( "NETWORK ERROR: Check your internet connection and firewall settings." ) time.sleep(5) return "network_error" if r.status_code != 200: server_msg = json.loads(r.text)["error"] Logger.info("CCP Servers at (" + esi_path + ") returned error code: " + str(r.status_code) + ", saying: " + server_msg, exc_info=True) statusmsg.push_status("CCP SERVER ERROR: " + str(r.status_code) + " (" + server_msg + ")") return "server_error" return r.json()
def run(self): url = "http://pyspy.pythonanywhere.com/add_record/" headers = { "Accept-Encoding": "gzip", "User-Agent": "PySpy, Author: White Russsian, https://github.com/WhiteRusssian/PySpy" } payload = { "uuid": self._uuid, "version": self._version, "platform": self._platform, "chars": self._chars, "duration": self._duration, "sh_faction": self._sh_faction, "hl_blops": self._hl_blops, "ig_factions": self._ig_factions, "gui_alpha": self._gui_alpha } try: r = requests.get(url, headers=headers, params=payload) except requests.exceptions.ConnectionError: Logger.info("No network connection.", exc_info=True) statusmsg.push_status( '''NETWORK ERROR: Check your internet connection and firewall settings.''') time.sleep(5) return if r.status_code != 200: status_code = r.status_code reason = r.reason Logger.info( "Could not upload usage statistics. Server message: '" + reason + "'. Code: " + str(status_code) + " [URL: " + r.url + "]", exc_info=True) return
def get_char_affiliations(conn, cur): char_ids = cur.execute("SELECT char_id FROM characters").fetchall() char_ids = json.dumps(tuple([r[0] for r in char_ids])) statusmsg.push_status("Retrieving character affiliation IDs...") try: affiliations = apis.post_req_ccp("characters/affiliation/", char_ids) except: Logger.info("Failed to obtain character affiliations.", exc_info=True) raise Exception records = () for r in affiliations: corp_id = r["corporation_id"] alliance_id = r["alliance_id"] if "alliance_id" in r else 0 faction_id = r["faction_id"] if "faction_id" in r else 0 char_id = r["character_id"] records = records + ((corp_id, alliance_id, faction_id, char_id), ) query_string = ( '''UPDATE characters SET corp_id=?, alliance_id=?, faction_id=? WHERE char_id=?''') db.write_many_to_db(conn, cur, query_string, records)
def analyze_chars(char_names): conn_mem, cur_mem = db.connect_memory_db() conn_dsk, cur_dsk = db.connect_persistent_db() start_time = time.time() wx.CallAfter(app.PySpy.grid.ClearGrid) try: outlist = analyze.main(char_names, conn_mem, cur_mem, conn_dsk, cur_dsk) duration = round(time.time() - start_time, 1) reportstats.ReportStats(outlist, duration).start() if outlist is not None: # Need to use keyword args as sortOutlist can also get called # by event handler which would pass event object as first argument. wx.CallAfter(app.PySpy.sortOutlist, outlist=outlist, duration=duration) else: statusmsg.push_status( "No valid character names found. Please try again...") except Exception: Logger.error("Failed to collect character information. Clipboard " "content was: " + str(char_names), exc_info=True)
def post_proprietary_db(character_ids): ''' Query PySpy's proprietary kill database for the character ids provided as a list or tuple of integers. Returns a JSON containing one line per character id. :param `character_ids`: List or tuple of character ids as integers. :return: JSON dictionary containing certain statistics for each id. ''' url = "http://pyspy.pythonanywhere.com" + "/character_intel/" + "v1/" headers = { "Accept-Encoding": "gzip", "User-Agent": "PySpy, Author: White Russsian, https://github.com/WhiteRusssian/PySpy" } # Character_ids is a list of tuples, which needs to be converted to dict # with list as value. character_ids = {"character_ids": character_ids} try: r = requests.post(url, headers=headers, json=character_ids) except requests.exceptions.ConnectionError: Logger.info("No network connection.", exc_info=True) statusmsg.push_status( "NETWORK ERROR: Check your internet connection and firewall settings." ) time.sleep(5) return "network_error" if r.status_code != 200: server_msg = json.loads(r.text)["error"] Logger.info( "PySpy server returned error code: " + str(r.status_code) + ", saying: " + server_msg, exc_info=True ) statusmsg.push_status( "PYSPY SERVER ERROR: " + str(r.status_code) + " (" + server_msg + ")" ) return "server_error" return r.json()
def run(self): url = ("https://zkillboard.com/api/stats/characterID/" + str(self._char_id) + "/") headers = { "Accept-Encoding": "gzip", "User-Agent": "PySpy, Author: White Russsian, https://github.com/WhiteRusssian/PySpy" } try: r = requests.get(url, headers=headers) except requests.exceptions.ConnectionError: Logger.info("No network connection.", exc_info=True) statusmsg.push_status( '''NETWORK ERROR: Check your internet connection and firewall settings.''') time.sleep(5) return "network error" if r.status_code != 200: server_msg = json.loads(r.text)["error"] Logger.info("zKillboard server returned error for character ID " + str(self._char_id) + ". Error code: " + str(r.status_code), exc_info=True) statusmsg.push_status("ZKILL SERVER ERROR: " + str(r.status_code) + " (" + server_msg + ")") return "server error" try: r = r.json() except AttributeError: kills = 0 blops_kills = 0 hic_losses = 0 self._queue.put([kills, blops_kills, self._char_id]) return try: # Number of total kills of this toon. kills = r["shipsDestroyed"] except KeyError: kills = 0 try: # Number of BLOPS killed by this toon. blops_kills = r["groups"]["898"]["shipsDestroyed"] except KeyError: blops_kills = 0 try: # Number of HICs lost by this toon. hic_losses = r["groups"]["894"]["shipsLost"] except KeyError: hic_losses = 0 try: # Kills over past 7 days week_kills = r["activepvp"]["kills"]["count"] except KeyError: week_kills = 0 self._queue.put( [kills, blops_kills, hic_losses, week_kills, self._char_id]) return
def updateList(self, outlist, duration=None): ''' `updateList()` takes the output of `output_list()` in `analyze.py` (via `sortOutlist()`) or a copy thereof stored in self.option, and uses it to populate the grid widget. Before it does so, it checks each item in outlist against a list of ignored characters, corporations and alliances. Finally, it highlights certain characters and updates the statusbar message. :param `outlist`: A list of rows with character data. :param `duration`: Time in seconds taken to query all relevant databases for each character. ''' # If updateList() gets called before outlist has been provided, do nothing if outlist is None: return # Clean up grid if self.grid.GetNumberRows() > 0: self.grid.DeleteRows(numRows=self.grid.GetNumberRows()) self.grid.AppendRows(len(outlist)) # Add any NPSI fleet related characters to ignored_list npsi_list = self.options.Get("NPSIList", default=[]) ignored_list = self.options.Get("ignoredList", default=[]) highlighted_list = self.options.Get("highlightedList", default=[]) hl_blops = self.options.Get("HlBlops", True) hl_hic = self.options.Get("HlHic", True) hl_cyno = self.options.Get("HlCyno", True) hl_list = self.options.Get("HlList", True) hl_cyno_prob = config.CYNO_HL_PERCENTAGE ignore_count = 0 rowidx = 0 for r in outlist: ignore = False for rec in ignored_list: if r[0] == rec[0] or r[3] == rec[0] or r[5] == rec[0]: ignore = True for rec in npsi_list: if r[0] == rec[0]: ignore = True if ignore: self.grid.HideRow(rowidx) ignore_count += 1 # Schema depending on output_list() in analyze.py id = r[0] # Hidden, used for zKillboard link faction_id = r[1] # Hidden, used for faction ignoring name = r[2] corp_id = r[3] corp_name = r[4] alliance_id = r[5] alliance_name = r[6] faction = r[7] if r[7] is not None else "-" allies = "{:,}".format(int(r[8])) # Add number of allies to alliance name if alliance_name is not None: alliance_name = alliance_name + " (" + allies + ")" else: alliance_name = "-" # zKillboard data is "n.a." unless available week_kills = kills = blops_kills = hic_losses = "n.a." losses = solo_ratio = sec_status = "n.a." if r[13] is not None: week_kills = "{:,}".format(int(r[9])) if int(r[9]) > 0 else "-" kills = "{:,}".format(int(r[10])) blops_kills = "{:,}".format(int( r[11])) if int(r[11]) > 0 else "-" hic_losses = "{:,}".format(int( r[12])) if int(r[12]) > 0 else "-" losses = "{:,}".format(int(r[13])) solo_ratio = "{:.0%}".format(float(r[14])) sec_status = "{:.1f}".format(float(r[15])) # PySpy proprietary data is "n.a." unless available last_loss = last_kill = covert_ship = normal_ship = "n.a." avg_attackers = covert_prob = normal_prob = abyssal_losses = "n.a." cov_prob_float = norm_prob_float = 0 if r[16] is not None: if int(r[16]) > 0: last_loss = str( (datetime.date.today() - datetime.datetime.strptime( str(r[16]), '%Y%m%d').date()).days) + "d" else: last_loss = "n.a." if int(r[17]) > 0: last_kill = str( (datetime.date.today() - datetime.datetime.strptime( str(r[17]), '%Y%m%d').date()).days) + "d" else: last_kill = "n.a." avg_attackers = "{:.1f}".format(float(r[18])) cov_prob_float = r[19] covert_prob = "{:.0%}".format( cov_prob_float) if cov_prob_float > 0 else "-" norm_prob_float = r[20] normal_prob = "{:.0%}".format( norm_prob_float) if norm_prob_float > 0 else "-" covert_ship = r[21] normal_ship = r[22] abyssal_losses = r[23] if int(r[23]) > 0 else "-" out = [ id, "-", faction_id, name, sec_status, corp_id, corp_name, alliance_id, alliance_name, faction, kills, losses, week_kills, solo_ratio, blops_kills, hic_losses, last_loss, last_kill, avg_attackers, covert_prob, normal_prob, covert_ship, normal_ship, abyssal_losses ] # Check if character belongs to a faction that should be ignored if faction_id != 0: if config.IGNORED_FACTIONS == 2 and faction_id % 2 == 0: self.grid.HideRow(rowidx) if config.IGNORED_FACTIONS == 1 and faction_id % 2 != 0: self.grid.HideRow(rowidx) colidx = 0 if hl_blops and r[9] is not None and r[ 11] > 0: # Add BLOPS to Warning Column out[1] = self.appendString(out[1], "BLOPS") if hl_hic and r[9] is not None and r[12] > 0: out[1] = self.appendString(out[1], "HIC") # Add HIC to Warning Column if hl_cyno and (cov_prob_float >= hl_cyno_prob or norm_prob_float >= hl_cyno_prob): # Add CYNO to Warnin Column out[1] = self.appendString(out[1], "CYNO") # Cell text formatting for value in out: color = False self.grid.SetCellValue(rowidx, colidx, str(value)) #self.grid.SetCellAlignment(self.columns[colidx][2], rowidx, colidx) if hl_blops and r[ 9] is not None and r[11] > 0: # Highlight BLOPS chars self.grid.SetCellTextColour(rowidx, colidx, self.hl1_colour) color = True if hl_hic and r[ 9] is not None and r[12] > 0: # Highlight HIC chars self.grid.SetCellTextColour(rowidx, colidx, self.hl1_colour) color = True if hl_cyno and (cov_prob_float >= hl_cyno_prob or norm_prob_float >= hl_cyno_prob ): # Highlight CYNO chars self.grid.SetCellTextColour(rowidx, colidx, self.hl2_colour) color = True for entry in highlighted_list: # Highlight chars from highlight list if hl_list and (entry[1] == out[3] or entry[1] == out[6] or entry[1] == out[8][:-4]): self.grid.SetCellTextColour(rowidx, colidx, self.hl3_colour) color = True if not color: self.grid.SetCellTextColour(rowidx, colidx, self.txt_colour) colidx += 1 rowidx += 1 if duration is not None: statusmsg.push_status( str(len(outlist) - ignore_count) + " characters analysed, in " + str(duration) + " seconds (" + str(ignore_count) + " ignored). Double click " + "character to go to zKillboard.") else: statusmsg.push_status( str(len(outlist) - ignore_count) + " characters analysed (" + str(ignore_count) + " ignored). Double click character to go " + " to zKillboard.")