Skip to content

Commit

Permalink
v0.3 release
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiteRusssian committed Aug 1, 2018
1 parent 1a27ce4 commit 218bb2b
Show file tree
Hide file tree
Showing 15 changed files with 1,072 additions and 184 deletions.
40 changes: 29 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ PySpy was born out of frustration over the *death* of [Pirate's Little Helper](h
a great [EVE Online](https://www.eveonline.com/) third-party tool for gathering useful information on character names from the in-game *local chat* window.

PySpy connects to [CCP's ESI API](https://esi.evetech.net/ui/) and the
[zKillboard API](https://github.com/zKillboard/zKillboard/wiki) and is available on Windows and macOS.
[zKillboard API](https://github.com/zKillboard/zKillboard/wiki) and is available on Windows, macOS and Linux.

## How to use PySpy

Expand All @@ -28,48 +28,62 @@ PySpy connects to [CCP's ESI API](https://esi.evetech.net/ui/) and the
3. Wait until PySpy is done and inspect the results.
4. Double-click a name to open the respective zKillboard in your browser.

**Note**: PySpy will save its window location, size, column sizes and transparency (slider on bottom right) and any other settings automatically and restore them the next time you launch it. PySpy will stay on top of the EVE client so long as the game runs in *window mode*.
**Note**: PySpy will save its window location, size, column sizes, sorting order and transparency (slider on bottom right) and any other settings automatically and restore them the next time you launch it. If selected in the _View Menu_, PySpy will stay on top of the EVE client so long as the game runs in *window mode*.

## Information Provided by PySpy

### New Dark Mode
<p align="center">
<img alt="PySpy in action" src="https://github.com/WhiteRusssian/PySpy/blob/master/assets/v0.2_screenshot.png?raw=true">
<img alt="PySpy in action" src="https://github.com/WhiteRusssian/PySpy/blob/master/assets/v0.3_dark_screenshot.png?raw=true">
</p>

### Traditional Normal Mode
<p align="center">
<img alt="PySpy in action" src="https://github.com/WhiteRusssian/PySpy/blob/master/assets/v0.3_light_screenshot.png?raw=true">
</p>

* **Character**: Character name.
* **Security**: Concord security status.
* **Corporation**: Corporation of character.
* **Alliance**: Alliance of character's Corporation, if any.
* **Faction**: Faction of character, if any.
* **Kills**: Total number of kills.
* **Losses**: Total number of losses.
* **Last Wk**: Number of kills over past 7 days.
* **K**: Total number of kills.
* **B**: Number of Black Ops Battleships (BLOPS) killed.
* **H**: Number of lost Heavy Interdiction Cruisers (HIC).
* **Solo**: Ratio of solo kills over total kills.
* **BLOPS**: Number of Black Ops Battleships (BLOPS) killed.
* **HICs**: Number of lost Heavy Interdiction Cruisers (HIC).

**Note**: Characters that have killed BLOPS or have lost HICs are high-lighted *orange*. This can now be changed in the *View* menu.

**Current Limitations**: To avoid undue strain on zKillboard's API, PySpy will run the *Last Wk* and *K-B-H* analyses only for the first 30 characters in the list.
**Current Limitations**: To avoid undue strain on zKillboard's API, PySpy will run the *Kills*, *Losses*, *Last Wk*, *Solo*, *BLOPS* and *HICs* analyses only for the first 30 characters in the list.

## Installation

You can download the latest release for your operating system [here](https://github.com/WhiteRusssian/PySpy/releases/latest).

PySpy comes as a single-file executable both in Windows and macOS. On both platforms, you can run PySpy from any folder location you like.

**Note**: PySpy automatically checks for updates on launch.
On Linux, you can run PySpy like any other Python3 script. PySpy was developed on Python 3.6.5 but should run on any other Python3 version, so long as you install the required packages listed in [requirements.txt](https://github.com/WhiteRusssian/PySpy/blob/master/requirements.txt).

**Note**: PySpy automatically checks for updates on launch and will notify you if a new version is available.

## Uninstalling PySpy

Delete the PySpy executable and remove the following files manually:

* **Windows**: PySpy saves preference and log files in a folder called `PySpy` located at `%LocalAppData%`.
* **macOS**: PySpy creates `pyspy.log` under `~/Library/Logs` and `pyspy.cfg` as well as `pyspy.pickle` under `~/Library/Preferences`.
* **Linux**: PySpy creates `pyspy.log` under `~/Library/Logs` and `pyspy.cfg` as well as `pyspy.pickle` under `~/.config/pyspy`.

## Future Features

Below is a non-exhaustive list of additional features I plan to add to PySpy as and when the ESI and zKillboard APIs support them:

* **Standings**: Only show characters that are non-blue, i.e. neutral or hostile.
* **Cynos**: Indicate if a character has in the past lost ships fitted with regular or covert cynos. I am currently putting together the underlying database of killmails and expect to add this feature in August.
* **Average Attackers**: Average number of attackers across all kills a character has been involved in.
* **Abyssal Losses**: Show number of losses in abyssal sites.
* **Custom Highlighting**: Choose a list of characters, corproations or alliances to highlight.
* **Standings**: Only show characters that are non-blue, i.e. neutral or hostile.
* **Highlight New Pilots**: Highlight any pilots that have entered system since last PySpy run.
* **Improved GUI**: The current GUI is very basic and while it works, I do appreciate that it is not ideal for people who cannot use it on a second screen but actually have to overlay it on-top of their EVE client.

Please feel free to add a [feature request](https://github.com/WhiteRusssian/PySpy/issues/new?template=pyspy-feature-request.md) for any improvements you would like to see in future releases.
Expand All @@ -94,3 +108,7 @@ PySpy is licensed under the [MIT](LICENSE.txt) License.
## CCP Copyright Notice

EVE Online and the EVE logo are the registered trademarks of CCP hf. All rights are reserved worldwide. All other trademarks are the property of their respective owners. EVE Online, the EVE logo, EVE and all associated logos and designs are the intellectual property of CCP hf. All artwork, screenshots, characters, vehicles, storylines, world facts or other recognizable features of the intellectual property relating to these trademarks are likewise the intellectual property of CCP hf. CCP is in no way responsible for the content on or functioning of this website, nor can it be liable for any damage arising from the use of this website.

## Collection of Usage Statistics

To help improve PySpy further, PySpy reports usage statistics comprising certain anonymous information such as the number of characters analysed, duration of each analysis, operating system, version of PySpy, and any active GUI features. For full disclosure, a randomly generated identifier is being sent with each data set to allow me to track how many people are actually using PySpy over any given period. If you would like to see for yourself what is being collected, have a look at the source code of module `reportstats.py`.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.2
v0.3
10 changes: 8 additions & 2 deletions __main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,19 @@ def check_name_validity(char_name):

def analyze_chars(char_names):
start_time = time.time()
wx.CallAfter(app.PySpy.list.DeleteAllItems)
wx.CallAfter(app.PySpy.grid.ClearGrid)
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)
# 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..."
Expand Down
6 changes: 2 additions & 4 deletions aboutdialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# Example call: Logger.info("Something badhappened", exc_info=True) ****


def OnAboutBox(e):
def showAboutBox(parent, event=None):
# __main__.app.PySpy.ToggleWindowStyle(wx.STAY_ON_TOP)

description = """
Expand All @@ -43,13 +43,11 @@ def OnAboutBox(e):
info = wx.adv.AboutDialogInfo()

info.SetIcon(wx.Icon(config.ABOUT_ICON, wx.BITMAP_TYPE_PNG))
info.SetName('PySpy')
info.SetName("AboutDialog")
info.SetVersion(config.CURRENT_VER)
info.SetDescription(description)
info.SetCopyright('(C) 2018 White Russsian')
info.SetWebSite('https://github.com/WhiteRusssian/PySpy')
info.SetLicence(license)

wx.adv.AboutBox(info)

# __main__.app.PySpy.SetWindowStyle(wx.STAY_ON_TOP)
17 changes: 12 additions & 5 deletions analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def main(char_names):
zkill_stats = q_main.get()
query_string = (
'''UPDATE characters SET kills=?, blops_kills=?, hic_losses=?,
week_kills=?
week_kills=?, losses=?, solo_ratio=?, sec_status=?
WHERE char_id=?'''
)
db.write_many_to_db(conn, cur, query_string, zkill_stats)
Expand Down Expand Up @@ -160,17 +160,24 @@ def run(self):
blops_kills = str(s[1])
hic_losses = str(s[2])
week_kills = str(s[3])
id = str(s[4])
zkill_stats.append([kills, blops_kills, hic_losses, week_kills, id])
losses = str(s[4])
solo_ratio = str(s[5])
sec_status = str(s[6])
id = str(s[7])
zkill_stats.append([
kills, blops_kills, hic_losses, week_kills, losses, solo_ratio,
sec_status, id
])
self._q_main.put(zkill_stats)
return


def output_list(cur):
query_string = (
'''SELECT
ch.char_id, ch.faction_id, ch.char_name, co.name, al.name, fa.name,
ac.numid, ch.week_kills, ch.kills, ch.blops_kills, hic_losses
ch.char_id, ch.faction_id, ch.char_name, co.id, co.name, al.id,
al.name, fa.name, ac.numid, ch.week_kills, ch.kills, ch.blops_kills,
ch.hic_losses, ch.losses, ch.solo_ratio, ch.sec_status
FROM characters AS ch
LEFT JOIN alliances AS al ON ch.alliance_id = al.id
LEFT JOIN corporations AS co ON ch.corp_id = co.id
Expand Down
36 changes: 28 additions & 8 deletions apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,31 +95,51 @@ def run(self):
hic_losses = 0
self._queue.put([kills, blops_kills, self._char_id])
return

try:
# Number of total kills of this toon.
# Number of total kills
kills = r["shipsDestroyed"]
except KeyError:
except (KeyError, TypeError):
kills = 0

try:
# Number of BLOPS killed by this toon.
# Number of BLOPS killed
blops_kills = r["groups"]["898"]["shipsDestroyed"]
except KeyError:
except (KeyError, TypeError):
blops_kills = 0

try:
# Number of HICs lost by this toon.
# Number of HICs lost
hic_losses = r["groups"]["894"]["shipsLost"]
except KeyError:
except (KeyError, TypeError):
hic_losses = 0

try:
# Kills over past 7 days
week_kills = r["activepvp"]["kills"]["count"]
except KeyError:
except (KeyError, TypeError):
week_kills = 0

try:
# Number of total losses
losses = r["shipsLost"]
except (KeyError, TypeError):
losses = 0

try:
# Ratio of solo kills to total kills
solo_ratio = int(r["soloKills"]) / int(r["shipsDestroyed"])
except (KeyError, TypeError):
solo_ratio = 0

try:
# Security status
sec_status = r["info"]["secStatus"]
except (KeyError, TypeError):
sec_status = 0

self._queue.put(
[kills, blops_kills, hic_losses, week_kills, self._char_id]
[kills, blops_kills, hic_losses, week_kills, losses, solo_ratio,
sec_status, self._char_id]
)
return
Binary file added assets/v0.3_dark_screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/v0.3_light_screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 39 additions & 4 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
import logging.config
import logging
import os
import platform
import sys
import uuid

import requests
import wx # required for colour codes in DARK_MODE

import optstore
# cSpell Checker - Correct Words****************************************
# // cSpell:words MEIPASS, datefmt, russsian, pyinstaller, posix, pyspy
# // cSpell:words zkill, amarr, caldari, gallente, minmatar
# // cSpell:words zkill, amarr, caldari, gallente, minmatar, isfile
# **********************************************************************
Logger = logging.getLogger(__name__)
# Example call: Logger.info("Something badhappened", exc_info=True) ****
Expand Down Expand Up @@ -52,7 +54,10 @@ def resource_path(relative_path):
elif __file__:
ABOUT_ICON = resource_path("assets/pyspy_mid.png")
application_path = os.path.dirname(__file__)
PREF_PATH = os.path.join(application_path, "tmp")
if platform.system() == "Linux":
PREF_PATH = os.path.expanduser("~/.config/pyspy")
else:
PREF_PATH = os.path.join(application_path, "tmp")
if not os.path.exists(PREF_PATH):
os.makedirs(PREF_PATH)
LOG_PATH = PREF_PATH
Expand All @@ -72,7 +77,7 @@ def resource_path(relative_path):
with open(resource_path('VERSION'), 'r') as ver_file:
CURRENT_VER = ver_file.read().replace('\n', '')

# Clean up old GUI_CFG_FILES
# Clean up old GUI_CFG_FILES and OPTIONS_OBJECT keys
if os.path.isfile(GUI_CFG_FILE) and not os.path.isfile(OPTIONS_FILE):
try:
os.remove(GUI_CFG_FILE)
Expand All @@ -83,16 +88,46 @@ def resource_path(relative_path):
os.remove(GUI_CFG_FILE)
except:
pass
for key in OPTIONS_OBJECT.ListKeys():
if key != "uuid":
OPTIONS_OBJECT.Del(key)

# Unique identifier for usage statistics reporting
if OPTIONS_OBJECT.Get("uuid", "not set") == "not set":
OPTIONS_OBJECT.Set("uuid", str(uuid.uuid4()))

# Store version information
OPTIONS_OBJECT.Set("version", CURRENT_VER)

# Various constants
MAX_NAMES = 500 # The max number of char names to be processed
ZKILL_DELAY = 0.05 # API rate limit is 10/second, pushing it a little...
ZKILL_CALLS = 30
ZKILL_CALLS = 40
GUI_TITLE = "PySpy " + CURRENT_VER
FONT_SCALE_MIN = 7 # 7 equates to 70%
FONT_SCALE_MAX = 13


# Colour Scheme
DARK_MODE = {
"BG": wx.Colour(0, 0, 0),
"TXT": wx.Colour(166, 105, 33),
"LNE": wx.Colour(15, 15, 15),
"LBL": wx.Colour(160, 160, 160),
"HL1": wx.Colour(187, 55, 46),
"HL2": wx.Colour(38, 104, 166),
"HL3": wx.Colour(30, 30, 30)
}

NORMAL_MODE = {
"BG": wx.Colour(-1, -1, -1),
"TXT": wx.Colour(45, 45, 45),
"LNE": wx.Colour(240, 240, 240),
"LBL": wx.Colour(32, 32, 32),
"HL1": wx.Colour(187, 55, 46),
"HL2": wx.Colour(0, 170, 0),
"HL3": wx.Colour(0, 0, 170)
}

# Note, Amarr and Caldari are allied and have IDs ending on uneven integers.
# Likewise, Gallente and Minmatar, also allied, have even IDs.
Expand Down
26 changes: 18 additions & 8 deletions db.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@


def connect_db():
'''Create in memory database
@returns: connection and curser objects as conn and cur'''
'''
Create in memory database
@returns: connection and curser objects as conn and cur
'''
conn = sqlite3.connect(":memory:")
conn.isolation_level = None
cur = conn.cursor()
Expand All @@ -28,13 +31,16 @@ def connect_db():


def prepare_tables(conn, cur):
'''Create a few tables, unless they already exist. Do not close the
'''
Create a few tables, unless they already exist. Do not close the
connection as it will continue to be used by the calling
function.'''
function.
'''
cur.execute(
'''CREATE TABLE IF NOT EXISTS characters (char_name TEXT, char_id INT,
corp_id INT, alliance_id INT, faction_id INT, kills INT,
blops_kills INT, hic_losses INT, week_kills INT)'''
blops_kills INT, hic_losses INT, week_kills INT, losses INT,
solo_ratio NUMERIC, sec_status NUMERIC)'''
)
cur.execute(
'''CREATE TABLE IF NOT EXISTS corporations (id INT, name TEXT)'''
Expand All @@ -45,18 +51,22 @@ def prepare_tables(conn, cur):
cur.execute(
'''CREATE TABLE IF NOT EXISTS factions (id INT, name TEXT)'''
)
# Populate this table with the 4 faction warfare factions
cur.executemany(
'''INSERT INTO factions (id, name) VALUES (?, ?)''',
config.FACTION_IDS
)
)
conn.commit()


def write_many_to_db(conn, cur, query_string, records, keepalive=True):
'''Take a database connection and write records to it. Afterwards,
'''
Take a database connection and write records to it. Afterwards,
leave the connection alive, unless keepalive=False and return the
number of records added to the database.
@returns: records_added'''
@returns: records_added
'''
try:
cur.executemany(query_string, records)
conn.commit()
Expand Down
Loading

0 comments on commit 218bb2b

Please sign in to comment.