From 1c46d250818d56a3600a7cb70b313e8d47cd9d18 Mon Sep 17 00:00:00 2001 From: Richard Bronkhorst Date: Sun, 11 Jun 2023 20:51:03 +0200 Subject: [PATCH] Update commander.py, Dockerfile and twenty-five other files --- Dockerfile | 4 +- commander.py | 50 ----------- main.py | 15 ++++ models/waypoint.py | 7 -- {models => nullptr}/__init__.py | 0 api.py => nullptr/api.py | 25 ++++-- command_line.py => nullptr/command_line.py | 2 +- nullptr/commander.py | 98 ++++++++++++++++++++++ nullptr/models/__init__.py | 0 {models => nullptr/models}/agent.py | 0 {models => nullptr/models}/base.py | 10 ++- nullptr/models/marketplace.py | 21 +++++ {models => nullptr/models}/sector.py | 0 {models => nullptr/models}/system.py | 2 +- nullptr/models/waypoint.py | 30 +++++++ store.py => nullptr/store.py | 12 +-- util.py => nullptr/util.py | 7 ++ requirements.txt | 1 - 18 files changed, 209 insertions(+), 75 deletions(-) delete mode 100644 commander.py create mode 100644 main.py delete mode 100644 models/waypoint.py rename {models => nullptr}/__init__.py (100%) rename api.py => nullptr/api.py (69%) rename command_line.py => nullptr/command_line.py (97%) create mode 100644 nullptr/commander.py create mode 100644 nullptr/models/__init__.py rename {models => nullptr/models}/agent.py (100%) rename {models => nullptr/models}/base.py (83%) create mode 100644 nullptr/models/marketplace.py rename {models => nullptr/models}/sector.py (100%) rename {models => nullptr/models}/system.py (82%) create mode 100644 nullptr/models/waypoint.py rename store.py => nullptr/store.py (87%) rename util.py => nullptr/util.py (90%) diff --git a/Dockerfile b/Dockerfile index 02b38d9..a82171a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,5 +8,5 @@ RUN pip3 install -r requirements.txt ADD --chown=user . /app RUN chmod +x /app/commander.py VOLUME /data -ENTRYPOINT [ "/app/commander.py"] -CMD ["-s", "/data/store.db"] \ No newline at end of file +ENTRYPOINT [ "/app/main.py"] +CMD ["-s", "/data/"] diff --git a/commander.py b/commander.py deleted file mode 100644 index 6395af7..0000000 --- a/commander.py +++ /dev/null @@ -1,50 +0,0 @@ -from store import Store -from command_line import CommandLine -import argparse -from models.agent import Agent -from api import Api -from util import * - - -class Commander(CommandLine): - def __init__(self, store_dir='data', agent=None): - self.store_dir = store_dir - self.store = Store(store_dir) - self.agent = self.select_agent(agent) - self.api = Api(self.store, self.agent) - self.store.flush() - super().__init__() - - def select_agent(self, agent_str): - if agent_str is not None: - return self.store.get(Agent, agent_str) - else: - agents = self.store.all('', Agent) - agent = next(agents, None) - if agent is None: - symbol = input('agent name: ') - agent = self.store.get(Agent, symbol) - return agent - - def after_cmd(self): - self.store.flush() - - def do_info(self): - pprint(self.api.info(), 100) - - def do_register(self, faction): - self.api.register(faction.upper()) - - def do_systems(self, page=1): - self.api.list_systems(int(page)) - -def main(args): - c = Commander(args.store_dir, args.agent) - c.run() - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('-s', '--store-dir', default='data') - parser.add_argument('-a', '--agent', default=None) - args = parser.parse_args() - main(args) diff --git a/main.py b/main.py new file mode 100644 index 0000000..aa02d37 --- /dev/null +++ b/main.py @@ -0,0 +1,15 @@ +import argparse +from nullptr.commander import Commander + +def main(args): + c = Commander(args.store_dir, args.agent) + c.run() + +# X1-AG74-41076A + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-s', '--store-dir', default='data') + parser.add_argument('-a', '--agent', default=None) + args = parser.parse_args() + main(args) diff --git a/models/waypoint.py b/models/waypoint.py deleted file mode 100644 index b84e5a4..0000000 --- a/models/waypoint.py +++ /dev/null @@ -1,7 +0,0 @@ -from .base import Base - - -class Waypoint(Base): - @classmethod - def ext(self): - return 'way' diff --git a/models/__init__.py b/nullptr/__init__.py similarity index 100% rename from models/__init__.py rename to nullptr/__init__.py diff --git a/api.py b/nullptr/api.py similarity index 69% rename from api.py rename to nullptr/api.py index 7b791df..ac23ebe 100644 --- a/api.py +++ b/nullptr/api.py @@ -1,6 +1,8 @@ import requests -from models.system import System -from util import * +from nullptr.models.system import System +from nullptr.models.waypoint import Waypoint +from nullptr.models.marketplace import Marketplace +from .util import * class ApiError(Exception): def __init__(self, msg, code): @@ -11,6 +13,7 @@ class Api: def __init__(self, store, agent): self.agent = agent self.store = store + self.meta = None self.root = 'https://api.spacetraders.io/v2/' def token(self): @@ -30,7 +33,9 @@ class Api: self.last_result = result if result is None: raise ApiError('http call failed', r.status_code) - elif 'data' not in result: + if 'meta' in result: + self.last_meta = result['meta'] + if 'data' not in result: error_code = sg(result, 'error.code', -1) self.last_error = error_code error_message = sg(result, 'error.message') @@ -56,6 +61,16 @@ class Api: def list_systems(self, page=1): data = self.request('get', 'systems', params={'page': page}) + #pprint(self.last_meta) return self.store.update_list(System, data) - - + + def list_waypoints(self, system): + data = self.request('get', f'systems/{system}/waypoints/') + # pprintz(self.last_meta) + return self.store.update_list(Waypoint, data) + + def marketplace(self, waypoint): + system = waypoint.system() + symbol = f'{waypoint}-market' + data = self.request('get', f'systems/{system}/waypoints/{waypoint}/market') + return self.store.update(Marketplace, symbol, data) diff --git a/command_line.py b/nullptr/command_line.py similarity index 97% rename from command_line.py rename to nullptr/command_line.py index 94f67e5..8ef8014 100644 --- a/command_line.py +++ b/nullptr/command_line.py @@ -35,7 +35,7 @@ class CommandLine: print(f'command not found; {c}') def handle_error(self, cmd, args, e): - logging.error(e, exc_info=str(type(e))=='ApiError') + logging.error(e, exc_info=str(type(e))=='ApiErrorp') def handle_empty(self): pass diff --git a/nullptr/commander.py b/nullptr/commander.py new file mode 100644 index 0000000..807a5d4 --- /dev/null +++ b/nullptr/commander.py @@ -0,0 +1,98 @@ +from nullptr.command_line import CommandLine +from nullptr.store import Store +import argparse +from nullptr.models.agent import Agent +from nullptr.models.system import System +from nullptr.models.waypoint import Waypoint +from nullptr.api import Api +from .util import * +from time import sleep +from threading import Thread + +class Commander(CommandLine): + def __init__(self, store_dir='data', agent=None): + self.store_dir = store_dir + self.store = Store(store_dir) + self.agent = self.select_agent(agent) + self.api = Api(self.store, self.agent) + self.store.flush() + self.stop_auto= False + super().__init__() + + def select_agent(self, agent_str): + if agent_str is not None: + return self.store.get(Agent, agent_str) + else: + agents = self.store.all('', Agent) + agent = next(agents, None) + if agent is None: + symbol = input('agent name: ') + agent = self.store.get(Agent, symbol) + return agent + + def after_cmd(self): + self.store.flush() + + def do_info(self): + pprint(self.api.info(), 100) + + def do_register(self, faction): + self.api.register(faction.upper()) + + def wait_for_stop(self): + input() + self.stop_auto = True + print('stopping...') + + def do_universe(self): + print('universe mode. hit enter to stop') + t = Thread(target=self.wait_for_stop) + t.daemon = True + t.start() + self.all_systems() + print('manual mode') + + def all_specials(self, waypoints): + for w in waypoints: + if 'MARKETPLACE' in w.traits: + self.api.marketplace(w) + sleep(0.5) + + def all_waypoints(self, systems): + for s in systems: + if self.stop_auto: + break + r = self.api.list_waypoints(s) + print(f'system {s}: {len(r)} waypoints') + sleep(0.5) + + def all_systems(self): + self.stop_auto = False + data = self.api.list_systems(1) + pages = total_pages(self.api.last_meta) + print(f'page {1}: {len(data)} results') + self.all_waypoints(data) + + print(f'{pages} more pages of systems') + for p in range(2, pages): + if self.stop_auto: + break + data = self.api.list_systems(p) + print(f'page {p}: {len(data)} systems') + self.all_waypoints(data) + sleep(0.5) + self.store.flush() + + def do_systems(self, page=1): + r = self.api.list_systems(int(page)) + pprint(self.api.last_meta) + + def do_waypoints(self, system_str): + system = self.store.get(System, system_str.upper()) + r = self.api.list_waypoints(system) + pprint(r) + + def do_marketplace(self, waypoint_str): + waypoint = self.store.get(Waypoint, waypoint_str.upper()) + r = self.api.marketplace(waypoint) + diff --git a/nullptr/models/__init__.py b/nullptr/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/agent.py b/nullptr/models/agent.py similarity index 100% rename from models/agent.py rename to nullptr/models/agent.py diff --git a/models/base.py b/nullptr/models/base.py similarity index 83% rename from models/base.py rename to nullptr/models/base.py index 1101d22..8b315ef 100644 --- a/models/base.py +++ b/nullptr/models/base.py @@ -1,6 +1,6 @@ from copy import deepcopy from dataclasses import dataclass -from util import sg +from nullptr.util import sg @dataclass class Base: symbol: str @@ -17,7 +17,13 @@ class Base: val = sg(d, name) if val is not None: setattr(self, attr, val) - + + def setlst(self, attr, d, name, member): + val = sg(d, name) + if val is not None: + lst = [sg(x, member) for x in val] + setattr(self, attr, lst) + def __setattr__(self, name, value): if name != 'dirty': self.dirty = True diff --git a/nullptr/models/marketplace.py b/nullptr/models/marketplace.py new file mode 100644 index 0000000..8b9709e --- /dev/null +++ b/nullptr/models/marketplace.py @@ -0,0 +1,21 @@ + +from .base import Base +from typing import List + +class Marketplace(Base): + imports:List[str] = [] + exports:List[str] = [] + exchange:List[str] = [] + + def update(self, d): + self.setlst('imports', d, 'imports', 'symbol') + self.setlst('exports', d, 'exports', 'symbol') + self.setlst('exchange', d, 'exchange', 'symbol') + + @classmethod + def ext(self): + return 'mkt' + + def path(self): + sector, system, symbol, _ = self.symbol.split('-') + return f'atlas/{sector}/{system[0:1]}/{system}/{symbol}.{self.ext()}' diff --git a/models/sector.py b/nullptr/models/sector.py similarity index 100% rename from models/sector.py rename to nullptr/models/sector.py diff --git a/models/system.py b/nullptr/models/system.py similarity index 82% rename from models/system.py rename to nullptr/models/system.py index ae7ad0b..e4b8b67 100644 --- a/models/system.py +++ b/nullptr/models/system.py @@ -18,4 +18,4 @@ class System(Base): def path(self): sector, symbol = self.symbol.split('-') - return f'atlas/{sector}/{symbol}.{self.ext()}' + return f'atlas/{sector}/{symbol[0:1]}/{symbol}.{self.ext()}' diff --git a/nullptr/models/waypoint.py b/nullptr/models/waypoint.py new file mode 100644 index 0000000..027353d --- /dev/null +++ b/nullptr/models/waypoint.py @@ -0,0 +1,30 @@ +from .base import Base +from nullptr.util import * +from typing import List + +class Waypoint(Base): + x:int = 0 + y:int = 0 + type:str = 'unknown' + traits:List[str]=[] + faction:str = '' + + def update(self, d): + self.seta('x', d) + self.seta('y', d) + self.seta('type', d) + self.seta('faction', d, 'faction.symbol') + if 'traits' in d: + self.traits = [mg(t, 'symbol') for t in d['traits'] ] + + @classmethod + def ext(self): + return 'way' + + def path(self): + sector, system, symbol = self.symbol.split('-') + return f'atlas/{sector}/{system[0:1]}/{system}/{symbol}.{self.ext()}' + + def system(self): + p = self.symbol.split('-') + return f'{p[0]}-{p[1]}' diff --git a/store.py b/nullptr/store.py similarity index 87% rename from store.py rename to nullptr/store.py index 0b3b077..0ec2c3b 100644 --- a/store.py +++ b/nullptr/store.py @@ -1,12 +1,12 @@ -from models.base import Base -from models.waypoint import Waypoint -from models.sector import Sector -from models.system import System -from models.agent import Agent +from nullptr.models.base import Base +from nullptr.models.waypoint import Waypoint +from nullptr.models.sector import Sector +from nullptr.models.system import System +from nullptr.models.agent import Agent from os.path import isfile, dirname, isdir import os import json -from util import * +from .util import * class Store: def __init__(self, data_dir): diff --git a/util.py b/nullptr/util.py similarity index 90% rename from util.py rename to nullptr/util.py index 10a7d0a..2646157 100644 --- a/util.py +++ b/nullptr/util.py @@ -1,4 +1,5 @@ from datetime import datetime +from math import ceil def must_get(d, k): if type(k) == str: @@ -56,6 +57,12 @@ def trim(s, l): s = s[:l] s += ' ' * (l-len(s)) return s + +def total_pages(meta): + total = mg(meta, 'total') + limit = mg(meta, 'limit') + pages = ceil(total / limit) + return pages # >>> parse_timestamp('2023-06-02T20:34:48.293Z') # 1685738088 diff --git a/requirements.txt b/requirements.txt index ac0aa34..f229360 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ requests -sqlalchemy \ No newline at end of file