From 2c96cbb533f5ca229c2eb2e9c99d75cae02e96d6 Mon Sep 17 00:00:00 2001 From: Richard Bronkhorst Date: Fri, 16 Jun 2023 13:03:19 +0200 Subject: [PATCH] Update api.py, command_line.py and five other files --- nullptr/api.py | 14 +++++++-- nullptr/command_line.py | 2 +- nullptr/commander.py | 24 ++++++++++++++-- nullptr/models/base.py | 8 +++++- nullptr/models/contract.py | 59 ++++++++++++++++++++++++++++++++++++++ nullptr/models/ship.py | 30 ++++++------------- nullptr/store.py | 12 +++++--- 7 files changed, 117 insertions(+), 32 deletions(-) create mode 100644 nullptr/models/contract.py diff --git a/nullptr/api.py b/nullptr/api.py index 6ef98a0..a2de79f 100644 --- a/nullptr/api.py +++ b/nullptr/api.py @@ -90,17 +90,27 @@ class Api: system = waypoint.system() symbol = str(waypoint) data = self.request('get', f'systems/{system}/waypoints/{waypoint}/market') - return self.store.update(Marketplace, symbol, data) + return self.store.update(Marketplace, data) def jumps(self, waypoint): data = self.request('get', f'systems/{waypoint.system()}/waypoints/{waypoint}/jump-gate') symbol = str(waypoint) - return self.store.update(Jumpgate, symbol, data) + return self.store.update(Jumpgate, data) def list_ships(self): data = self.request('get', 'my/ships') return self.store.update_list(Ship, data) + def list_contracts(self): + data = self.request('get', 'my/contracts') + return self.store.update_list('Contract', data) + + def negotiate(self, ship): + data = self.request('post', f'my/ships/{ship}/negotiate/contract') + if data is not None and 'contract' in data: + contract = self.store.update('Contract', data['contract']) + return contract + def navigate(self, ship, wp): data = {'waypointSymbol': str(wp)} response = self.request('post', f'my/ships/{ship}/navigate', data) diff --git a/nullptr/command_line.py b/nullptr/command_line.py index c3ab875..389e86d 100644 --- a/nullptr/command_line.py +++ b/nullptr/command_line.py @@ -41,7 +41,7 @@ class CommandLine: print(f'command not found; {c}') def handle_error(self, cmd, args, e): - logging.error(e, exc_info=str(type(e))!='ApiErrorp') + logging.error(e, exc_info=type(e).__name__ !='ApiError') def handle_empty(self): pass diff --git a/nullptr/commander.py b/nullptr/commander.py index 8ba3955..c618ee3 100644 --- a/nullptr/commander.py +++ b/nullptr/commander.py @@ -81,8 +81,14 @@ class Commander(CommandLine): system = self.store.get(System, system_str) r = self.store.all_members(system, 'Waypoint') for w in r: - traits = ','.join(w.traits) - print(w.symbol, traits) + traits = [] + if 'MARKETPLACE' in w.traits: + traits.append('MARKET') + if 'SHIPYARD' in w.traits: + traits.append('SHIPYARD') + if w.type == 'JUMP_GATE': + traits.append('JUMP') + print(w.symbol.split('-')[2], ', '.join(traits)) def do_marketplace(self, waypoint_str): waypoint = self.store.get(Waypoint, waypoint_str.upper()) @@ -119,6 +125,13 @@ class Commander(CommandLine): r = list(self.store.all('Ship')) pprint(r) + def do_contracts(self, arg=''): + if arg.startswith('r'): + r = self.api.list_contracts() + else: + r = list(self.store.all('Contract')) + pprint(r) + def do_ship(self, arg=''): if arg != '': symbol = f'{self.agent.symbol}-{arg}' @@ -128,7 +141,7 @@ class Commander(CommandLine): return else: self.ship = ship - pprint(ship) + pprint(self.ship) def do_pp(self): pprint(self.api.last_result) @@ -151,3 +164,8 @@ class Commander(CommandLine): self.api.orbit(self.ship) pprint(self.ship) + def do_negotiate(self): + if not self.has_ship(): return + r = self.api.negotiate(self.ship) + pprint(r) + diff --git a/nullptr/models/base.py b/nullptr/models/base.py index 20c9b9b..1789d68 100644 --- a/nullptr/models/base.py +++ b/nullptr/models/base.py @@ -4,6 +4,7 @@ from nullptr.util import sg @dataclass class Base: + identifier = 'symbol' symbol: str store: object @@ -17,11 +18,13 @@ class Base: def __eq__(self, other): return self.symbol == other.symbol and type(self) == type(other) - def seta(self, attr, d, name=None): + def seta(self, attr, d, name=None, interp=None): if name is None: name = attr val = sg(d, name) if val is not None: + if interp is not None: + val = interp(val) setattr(self, attr, val) def setlst(self, attr, d, name, member): @@ -37,6 +40,9 @@ class Base: def update(self, d): pass + + def load(self, d): + self.__dict__ = d def dict(self): r = {} diff --git a/nullptr/models/contract.py b/nullptr/models/contract.py new file mode 100644 index 0000000..2e29d44 --- /dev/null +++ b/nullptr/models/contract.py @@ -0,0 +1,59 @@ + +from time import time +from nullptr.util import * +from .base import Base +from typing import List + +class Contract(Base): + identifier = 'id' + type: str + deliveries: List + accepted: bool + fulfilled: bool + expires: int + expires_str: str + pay: int + + @classmethod + def ext(cls): + return 'cnt' + + def path(self): + return f'contracts/{self.symbol}.{self.ext()}' + + def is_expired(self): + return time() > self.expires + + def api_dict(self): + return { + 'id': self.symbol, + 'expiration': self.expires_str, + } + + def update(self, d): + self.seta('expires',d, 'terms.deadline',parse_timestamp) + self.seta('expires_str', d,'terms.deadline') + self.seta('accepted', d, 'accepted') + self.seta('fulfilled', d, 'fulfilled') + self.seta('type', d, 'type') + self.pay = mg(d, 'terms.payment.onAccepted') + mg(d, 'terms.payment.onFulfilled') + deliveries = must_get(d, 'terms.deliver') + self.deliveries = [] + for e in deliveries: + delivery = {} + delivery['trade_symbol'] = must_get(e, 'tradeSymbol') + delivery['units_fulfilled'] = must_get(e, 'unitsFulfilled') + delivery['units_required'] = must_get(e, 'unitsRequired') + delivery['destination'] = must_get(e, 'destinationSymbol') + self.deliveries.append(delivery) + + def f(self, detail=1): + hours = int(max(0, self.expires - time()) / 3600) + accepted = 'A' if self.accepted else '-' + fulfilled = 'F' if self.fulfilled else '-' + result = f'{self.symbol} {hours}h {accepted}{fulfilled}' + if detail > 1: + result += '\n' + for d in self.deliveries: + result += f"({d['units_fulfilled']} / {d['units_required']}) {d['trade_symbol']} to {d['destination']}" + return result diff --git a/nullptr/models/ship.py b/nullptr/models/ship.py index a91eb17..8eeba11 100644 --- a/nullptr/models/ship.py +++ b/nullptr/models/ship.py @@ -3,13 +3,6 @@ from time import time from nullptr.util import * from dataclasses import dataclass -@dataclass -class InventoryItem: - symbol: str - units: int - def __str__(self): - return self.symbol + ': ' + str(self.units) - class Ship(Base): cargo:dict = {} mission_state:dict = {} @@ -38,7 +31,6 @@ class Ship(Base): def update(self, d): self.seta('status', d, 'nav.status') self.seta('location_str', d, 'nav.waypointSymbol') - self.seta('cargo_capacity', d, 'cargo.capacity') self.seta('cargo_units', d, 'cargo.units') self.seta('fuel_capacity', d, 'fuel.capacity') @@ -46,13 +38,9 @@ class Ship(Base): cargo = sg(d, 'cargo.inventory') if cargo is not None: self.load_cargo(cargo) - cooldown = sg(d, 'cooldown.expiration') - if cooldown: - self.cooldown = parse_timestamp(cooldown) - arrival = sg(d, 'nav.route.arrival') - if arrival: - self.arrival = parse_timestamp(arrival) - + self.seta('cooldown', d, 'cooldown.expiration', parse_timestamp) + self.seta('arrival', d, 'nav.route.arrival', parse_timestamp) + def tick(self): if self.status == 'IN_TRANSIT' and self.arrival < time(): self.status = 'IN_ORBIT' @@ -66,26 +54,26 @@ class Ship(Base): def get_cargo(self, typ): if typ not in self.cargo: return 0 - return self.cargo[typ].units + return self.cargo[typ] def load_cargo(self, cargo): result = {} for i in cargo: symbol = must_get(i, 'symbol') units = must_get(i, 'units') - result[symbol] = InventoryItem(symbol, units) + result[symbol] = units self.cargo = result def deliverable_cargo(self, contract): result = [] for d in contract.deliveries: - if self.get_cargo(d.trade_symbol) > 0: - result.append(d.trade_symbol) + if self.get_cargo(d['trade_symbol']) > 0: + result.append(d['trade_symbol']) return result def nondeliverable_cargo(self, contract): - cargo = [c.symbol for c in self.cargo.values()] - deliveries = [d.trade_symbol for d in contract.deliveries] + cargo = self.cargo.keys() + deliveries = [d['trade_symbol'] for d in contract.deliveries] garbage = [c for c in cargo if c not in deliveries] return garbage diff --git a/nullptr/store.py b/nullptr/store.py index 0566168..2a2f0e2 100644 --- a/nullptr/store.py +++ b/nullptr/store.py @@ -7,6 +7,7 @@ from nullptr.models.marketplace import Marketplace from nullptr.models.system_member import SystemMember from nullptr.models.jumpgate import Jumpgate from nullptr.models.ship import Ship +from nullptr.models.contract import Contract from os.path import isfile, dirname, isdir import os from os.path import basename @@ -46,8 +47,8 @@ class Store: typ = self.extensions[ext] obj = self.create(typ, symbol) - data['store'] = self - obj.__dict__ = data + obj.load(data) + obj.store = self return obj def load(self): @@ -92,13 +93,16 @@ class Store: return None return self.data[typ][symbol] - def update(self, typ, symbol, data): + def update(self, typ, data): + if type(typ) == str and typ in self.model_names: + typ = self.model_names[typ] + symbol = mg(data, typ.identifier) obj = self.get(typ, symbol, True) obj.update(data) return obj def update_list(self, typ, lst): - return [self.update(typ, mg(d, 'symbol'), d) for d in lst] + return [self.update(typ, d) for d in lst] def all(self, typ): if type(typ) == str and typ in self.model_names: