1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-04-15 08:40:34 +03:00

outfitting.py: Cleanup flake8 and mypy output

This commit is contained in:
Athanasius 2021-04-06 17:51:26 +01:00
parent 236fb5a5cb
commit c2a2377137

View File

@ -1,7 +1,10 @@
"""Code dealing with ship outfitting."""
import pickle import pickle
import time
from collections import OrderedDict from collections import OrderedDict
from os.path import join from os.path import join
from typing import Optional
from typing import OrderedDict as OrderedDictT
from config import config from config import config
from edmc_data import outfitting_armour_map as armour_map from edmc_data import outfitting_armour_map as armour_map
@ -22,43 +25,64 @@ from edmc_data import outfitting_weaponmount_map as weaponmount_map
from edmc_data import outfitting_weaponoldvariant_map as weaponoldvariant_map from edmc_data import outfitting_weaponoldvariant_map as weaponoldvariant_map
from edmc_data import outfitting_weaponrating_map as weaponrating_map from edmc_data import outfitting_weaponrating_map as weaponrating_map
from edmc_data import ship_name_map from edmc_data import ship_name_map
from EDMCLogging import get_main_logger
logger = get_main_logger()
# Module mass, FSD data etc # Module mass, FSD data etc
moduledata = OrderedDict() moduledata: OrderedDictT = OrderedDict()
# Given a module description from the Companion API returns a description of the module in the form of a
# dict { category, name, [mount], [guidance], [ship], rating, class } using the same terms found in the
# English langauge game. For fitted modules, dict also includes { enabled, priority }.
# ship_name_map tells us what ship names to use for Armour - i.e. EDDN schema names or in-game names.
#
# Returns None if the module is user-specific (i.e. decal, paintjob, kit) or PP-specific in station outfitting.
# (Given the ad-hocery in this implementation a big lookup table might have been simpler and clearer).
def lookup(module, ship_map, entitled=False):
def lookup(module, ship_map, entitled=False) -> Optional[dict]: # noqa: C901, CCR001
"""
Produce a standard dict description of the given module.
Given a module description from the Companion API returns a description of the module in the form of a
dict { category, name, [mount], [guidance], [ship], rating, class } using the same terms found in the
English language game. For fitted modules, dict also includes { enabled, priority }.
ship_name_map tells us what ship names to use for Armour - i.e. EDDN schema names or in-game names.
Given the ad-hocery in this implementation a big lookup table might have been simpler and clearer.
:param module:
:param ship_map:
:param entitled:
:return: None if the module is user-specific (i.e. decal, paintjob, kit) or PP-specific in station outfitting.
"""
# Lazily populate # Lazily populate
if not moduledata: if not moduledata:
moduledata.update(pickle.load(open(join(config.respath_path, 'modules.p'), 'rb'))) moduledata.update(pickle.load(open(join(config.respath_path, 'modules.p'), 'rb')))
# if not module.get('category'): raise AssertionError('%s: Missing category' % module['id']) # only present post 1.3, and not present in ship loadout if not module.get('name'):
if not module.get('name'): raise AssertionError('%s: Missing name' % module['id']) raise AssertionError(f'{module["id"]}')
name = module['name'].lower().split('_') name = module['name'].lower().split('_')
new = { 'id': module['id'], 'symbol': module['name'] } new = {'id': module['id'], 'symbol': module['name']}
# Armour - e.g. Federation_Dropship_Armour_Grade2 # Armour - e.g. Federation_Dropship_Armour_Grade2
if name[-2] == 'armour': if name[-2] == 'armour':
name = module['name'].lower().rsplit('_', 2) # Armour is ship-specific, and ship names can have underscores name = module['name'].lower().rsplit('_', 2) # Armour is ship-specific, and ship names can have underscores
new['category'] = 'standard' new['category'] = 'standard'
new['name'] = armour_map[name[2]] new['name'] = armour_map[name[2]]
new['ship'] = ship_map[name[0]] # Generate error on unknown ship new['ship'] = ship_map[name[0]] # Generate error on unknown ship
new['class'] = '1' new['class'] = '1'
new['rating'] = 'I' new['rating'] = 'I'
# Skip uninteresting stuff - some no longer present in ED 3.1 cAPI data # Skip uninteresting stuff - some no longer present in ED 3.1 cAPI data
elif name[0] in ['bobble', 'decal', 'nameplate', 'paintjob', 'enginecustomisation', 'voicepack', 'weaponcustomisation'] or name[1].startswith('shipkit') : elif (name[0] in [
'bobble',
'decal',
'nameplate',
'paintjob',
'enginecustomisation',
'voicepack',
'weaponcustomisation'
]
or name[1].startswith('shipkit')):
return None return None
# Shouldn't be listing player-specific paid stuff or broker/powerplay-specific modules in outfitting, other than Horizons # Shouldn't be listing player-specific paid stuff or broker/powerplay-specific modules in outfitting,
# other than Horizons
elif not entitled and module.get('sku') and module['sku'] != 'ELITE_HORIZONS_V_PLANETARY_LANDINGS': elif not entitled and module.get('sku') and module['sku'] != 'ELITE_HORIZONS_V_PLANETARY_LANDINGS':
return None return None
@ -67,152 +91,189 @@ def lookup(module, ship_map, entitled=False):
return None return None
# Countermeasures - e.g. Hpt_PlasmaPointDefence_Turret_Tiny # Countermeasures - e.g. Hpt_PlasmaPointDefence_Turret_Tiny
elif name[0]=='hpt' and name[1] in countermeasure_map: elif name[0] == 'hpt' and name[1] in countermeasure_map:
new['category'] = 'utility' new['category'] = 'utility'
new['name'], new['rating'] = countermeasure_map[len(name)>4 and (name[1],name[4]) or name[1]] new['name'], new['rating'] = countermeasure_map[len(name) > 4 and (name[1], name[4]) or name[1]]
new['class'] = weaponclass_map[name[-1]] new['class'] = weaponclass_map[name[-1]]
# Utility - e.g. Hpt_CargoScanner_Size0_Class1 # Utility - e.g. Hpt_CargoScanner_Size0_Class1
elif name[0]=='hpt' and name[1] in utility_map: elif name[0] == 'hpt' and name[1] in utility_map:
new['category'] = 'utility' new['category'] = 'utility'
new['name'] = utility_map[len(name)>4 and (name[1],name[4]) or name[1]] new['name'] = utility_map[len(name) > 4 and (name[1], name[4]) or name[1]]
if not name[2].startswith('size') or not name[3].startswith('class'): raise AssertionError('%s: Unknown class/rating "%s/%s"' % (module['id'], name[2], name[3])) if not name[2].startswith('size') or not name[3].startswith('class'):
raise AssertionError(f'{module["id"]}: Unknown class/rating "{name[2]}/{name[3]}"')
new['class'] = str(name[2][4:]) new['class'] = str(name[2][4:])
new['rating'] = rating_map[name[3][5:]] new['rating'] = rating_map[name[3][5:]]
# Hardpoints - e.g. Hpt_Slugshot_Fixed_Medium # Hardpoints - e.g. Hpt_Slugshot_Fixed_Medium
elif name[0]=='hpt': elif name[0] == 'hpt':
# Hack 'Guardian' and 'Mining' prefixes # Hack 'Guardian' and 'Mining' prefixes
if len(name) > 3 and name[3] in weaponmount_map: if len(name) > 3 and name[3] in weaponmount_map:
prefix = name.pop(1) prefix = name.pop(1)
name[1] = '%s_%s' % (prefix, name[1]) name[1] = f'{prefix}_{name[1]}'
if name[1] not in weapon_map: raise AssertionError('%s: Unknown weapon "%s"' % (module['id'], name[0]))
if name[2] not in weaponmount_map: raise AssertionError('%s: Unknown weapon mount "%s"' % (module['id'], name[2])) if name[1] not in weapon_map:
if name[3] not in weaponclass_map: raise AssertionError('%s: Unknown weapon class "%s"' % (module['id'], name[3])) raise AssertionError(f'{module["id"]}: Unknown weapon "{name[0]}"')
if name[2] not in weaponmount_map:
raise AssertionError(f'{module["id"]}: Unknown weapon mount "{name[2]}"')
if name[3] not in weaponclass_map:
raise AssertionError(f'{module["id"]}: Unknown weapon class "{name[3]}"')
new['category'] = 'hardpoint' new['category'] = 'hardpoint'
if len(name)>4: if len(name) > 4:
if name[4] in weaponoldvariant_map: # Old variants e.g. Hpt_PulseLaserBurst_Turret_Large_OC if name[4] in weaponoldvariant_map: # Old variants e.g. Hpt_PulseLaserBurst_Turret_Large_OC
new['name'] = weapon_map[name[1]] + ' ' + weaponoldvariant_map[name[4]] new['name'] = weapon_map[name[1]] + ' ' + weaponoldvariant_map[name[4]]
new['rating'] = '?' new['rating'] = '?'
elif '_'.join(name[:4]) not in weaponrating_map: elif '_'.join(name[:4]) not in weaponrating_map:
raise AssertionError('%s: Unknown weapon rating "%s"' % (module['id'], module['name'])) raise AssertionError(f'{module["id"]}: Unknown weapon rating "{module["name"]}"')
else: # PP faction-specific weapons e.g. Hpt_Slugshot_Fixed_Large_Range
new['name'] = weapon_map[(name[1],name[4])] else:
new['rating'] = weaponrating_map['_'.join(name[:4])] # assumes same rating as base weapon # PP faction-specific weapons e.g. Hpt_Slugshot_Fixed_Large_Range
new['name'] = weapon_map[(name[1], name[4])]
new['rating'] = weaponrating_map['_'.join(name[:4])] # assumes same rating as base weapon
elif module['name'].lower() not in weaponrating_map: elif module['name'].lower() not in weaponrating_map:
raise AssertionError('%s: Unknown weapon rating "%s"' % (module['id'], module['name'])) raise AssertionError(f'{module["id"]}: Unknown weapon rating "{module["name"]}"')
else: else:
new['name'] = weapon_map[name[1]] new['name'] = weapon_map[name[1]]
new['rating'] = weaponrating_map[module['name'].lower()] # no obvious rule - needs lookup table new['rating'] = weaponrating_map[module['name'].lower()] # no obvious rule - needs lookup table
new['mount'] = weaponmount_map[name[2]] new['mount'] = weaponmount_map[name[2]]
if name[1] in missiletype_map: # e.g. Hpt_DumbfireMissileRack_Fixed_Small if name[1] in missiletype_map:
# e.g. Hpt_DumbfireMissileRack_Fixed_Small
new['guidance'] = missiletype_map[name[1]] new['guidance'] = missiletype_map[name[1]]
new['class'] = weaponclass_map[name[3]] new['class'] = weaponclass_map[name[3]]
elif name[0]!='int': elif name[0] != 'int':
raise AssertionError('%s: Unknown prefix "%s"' % (module['id'], name[0])) raise AssertionError(f'{module["id"]}: Unknown prefix "{name[0]}"')
# Miscellaneous Class 1 - e.g. Int_PlanetApproachSuite, Int_StellarBodyDiscoveryScanner_Advanced, Int_DockingComputer_Standard # Miscellaneous Class 1
# e.g. Int_PlanetApproachSuite, Int_StellarBodyDiscoveryScanner_Advanced, Int_DockingComputer_Standard
elif name[1] in misc_internal_map: elif name[1] in misc_internal_map:
new['category'] = 'internal' new['category'] = 'internal'
new['name'], new['rating'] = misc_internal_map[name[1]] new['name'], new['rating'] = misc_internal_map[name[1]]
new['class'] = '1' new['class'] = '1'
elif len(name) > 2 and (name[1],name[2]) in misc_internal_map:
elif len(name) > 2 and (name[1], name[2]) in misc_internal_map:
# Reported category is not necessarily helpful. e.g. "Int_DockingComputer_Standard" has category "utility" # Reported category is not necessarily helpful. e.g. "Int_DockingComputer_Standard" has category "utility"
new['category'] = 'internal' new['category'] = 'internal'
new['name'], new['rating'] = misc_internal_map[(name[1],name[2])] new['name'], new['rating'] = misc_internal_map[(name[1], name[2])]
new['class'] = '1' new['class'] = '1'
# Standard & Internal
else: else:
if name[1] == 'dronecontrol': # e.g. Int_DroneControl_Collection_Size1_Class1 # Standard & Internal
if name[1] == 'dronecontrol': # e.g. Int_DroneControl_Collection_Size1_Class1
name.pop(0) name.pop(0)
elif name[-1] == 'free': # Starter Sidewinder or Freagle modules - just treat them like vanilla modules
elif name[-1] == 'free': # Starter Sidewinder or Freagle modules - just treat them like vanilla modules
name.pop() name.pop()
if name[1] in standard_map: # e.g. Int_Engine_Size2_Class1, Int_ShieldGenerator_Size8_Class5_Strong if name[1] in standard_map: # e.g. Int_Engine_Size2_Class1, Int_ShieldGenerator_Size8_Class5_Strong
new['category'] = 'standard' new['category'] = 'standard'
new['name'] = standard_map[len(name)>4 and (name[1],name[4]) or name[1]] new['name'] = standard_map[len(name) > 4 and (name[1], name[4]) or name[1]]
elif name[1] in internal_map: # e.g. Int_CargoRack_Size8_Class1
elif name[1] in internal_map: # e.g. Int_CargoRack_Size8_Class1
new['category'] = 'internal' new['category'] = 'internal'
if name[1] == 'passengercabin': if name[1] == 'passengercabin':
new['name'] = cabin_map[name[3][5:]] new['name'] = cabin_map[name[3][5:]]
else:
new['name'] = internal_map[len(name)>4 and (name[1],name[4]) or name[1]]
else:
raise AssertionError('%s: Unknown module "%s"' % (module['id'], name[1]))
if len(name) < 4 and name[1] == 'unkvesselresearch': # Hack! No size or class. else:
(new['class'], new['rating']) = ('1', 'E') new['name'] = internal_map[len(name) > 4 and (name[1], name[4]) or name[1]]
elif len(name) < 4 and name[1] == 'resourcesiphon': # Hack! 128066402 has no size or class.
(new['class'], new['rating']) = ('1', 'I')
elif len(name) < 4 and name[1] in ['guardianpowerdistributor', 'guardianpowerplant']: # Hack! No class.
(new['class'], new['rating']) = (str(name[2][4:]), 'A')
elif len(name) < 4 and name[1] in ['guardianfsdbooster']: # Hack! No class.
(new['class'], new['rating']) = (str(name[2][4:]), 'H')
else: else:
if len(name) < 3: raise AssertionError('%s: length < 3]' % (name)) raise AssertionError(f'{module["id"]}: Unknown module "{name[1]}"')
if not name[2].startswith('size') or not name[3].startswith('class'): raise AssertionError('%s: Unknown class/rating "%s/%s"' % (module['id'], name[2], name[3]))
if len(name) < 4 and name[1] == 'unkvesselresearch': # Hack! No size or class.
(new['class'], new['rating']) = ('1', 'E')
elif len(name) < 4 and name[1] == 'resourcesiphon': # Hack! 128066402 has no size or class.
(new['class'], new['rating']) = ('1', 'I')
elif len(name) < 4 and name[1] in ['guardianpowerdistributor', 'guardianpowerplant']: # Hack! No class.
(new['class'], new['rating']) = (str(name[2][4:]), 'A')
elif len(name) < 4 and name[1] in ['guardianfsdbooster']: # Hack! No class.
(new['class'], new['rating']) = (str(name[2][4:]), 'H')
else:
if len(name) < 3:
raise AssertionError(f'{name}: length < 3]')
if not name[2].startswith('size') or not name[3].startswith('class'):
raise AssertionError(f'{module["id"]}: Unknown class/rating "{name[2]}/{name[3]}"')
new['class'] = str(name[2][4:]) new['class'] = str(name[2][4:])
new['rating'] = (name[1]=='buggybay' and planet_rating_map or new['rating'] = (name[1] == 'buggybay' and planet_rating_map or
name[1]=='fighterbay' and fighter_rating_map or name[1] == 'fighterbay' and fighter_rating_map or
name[1]=='corrosionproofcargorack' and corrosion_rating_map or name[1] == 'corrosionproofcargorack' and corrosion_rating_map or
rating_map)[name[3][5:]] rating_map)[name[3][5:]]
# Disposition of fitted modules # Disposition of fitted modules
if 'on' in module and 'priority' in module: if 'on' in module and 'priority' in module:
new['enabled'], new['priority'] = module['on'], module['priority'] # priority is zero-based new['enabled'], new['priority'] = module['on'], module['priority'] # priority is zero-based
# Entitlements # Entitlements
if not module.get('sku'): if not module.get('sku'):
pass pass
else: else:
new['entitlement'] = module['sku'] new['entitlement'] = module['sku']
# Extra module data # Extra module data
if module['name'].endswith('_free'): if module['name'].endswith('_free'):
key = module['name'][:-5].lower() # starter modules - treated like vanilla modules key = module['name'][:-5].lower() # starter modules - treated like vanilla modules
else: else:
key = module['name'].lower() key = module['name'].lower()
if __debug__: if __debug__:
m = moduledata.get(key, {}) m = moduledata.get(key, {})
if not m: if not m:
print('No data for module %s' % key) print(f'No data for module {key}')
elif new['name'] == 'Frame Shift Drive': elif new['name'] == 'Frame Shift Drive':
assert 'mass' in m and 'optmass' in m and 'maxfuel' in m and 'fuelmul' in m and 'fuelpower' in m, m assert 'mass' in m and 'optmass' in m and 'maxfuel' in m and 'fuelmul' in m and 'fuelpower' in m, m
else: else:
assert 'mass' in m, m assert 'mass' in m, m
new.update(moduledata.get(module['name'].lower(), {})) new.update(moduledata.get(module['name'].lower(), {}))
# check we've filled out mandatory fields # check we've filled out mandatory fields
for thing in ['id', 'symbol', 'category', 'name', 'class', 'rating']: # Don't consider mass etc as mandatory for thing in ['id', 'symbol', 'category', 'name', 'class', 'rating']: # Don't consider mass etc as mandatory
if not new.get(thing): raise AssertionError('%s: failed to set %s' % (module['id'], thing)) if not new.get(thing):
raise AssertionError(f'{module["id"]}: failed to set {thing}')
if new['category'] == 'hardpoint' and not new.get('mount'): if new['category'] == 'hardpoint' and not new.get('mount'):
raise AssertionError('%s: failed to set %s' % (module['id'], 'mount')) raise AssertionError(f'{module["id"]}: failed to set mount')
return new return new
def export(data, filename): def export(data, filename) -> None:
"""Export given data to provided filename."""
querytime = config.get_int('querytime', default=int(time.time()))
assert data['lastSystem'].get('name') assert data['lastSystem'].get('name')
assert data['lastStarport'].get('name') assert data['lastStarport'].get('name')
header = 'System,Station,Category,Name,Mount,Guidance,Ship,Class,Rating,FDevID,Date\n' header = 'System,Station,Category,Name,Mount,Guidance,Ship,Class,Rating,FDevID,Date\n'
rowheader = '%s,%s' % (data['lastSystem']['name'], data['lastStarport']['name']) rowheader = f'{data["lastSystem"]["name"]},{data["lastStarport"]["name"]}'
h = open(filename, 'wt') with open(filename, 'wt') as h:
h.write(header) h.write(header)
for v in list(data['lastStarport'].get('modules', {}).values()): for v in list(data['lastStarport'].get('modules', {}).values()):
try: try:
m = lookup(v, ship_name_map) m = lookup(v, ship_name_map)
if m: if m:
h.write('%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n' % (rowheader, m['category'], m['name'], m.get('mount',''), m.get('guidance',''), m.get('ship',''), m['class'], m['rating'], m['id'], data['timestamp'])) h.write(f'{rowheader}, {m["category"]}, {m["name"]}, {m.get("mount","")},'
except AssertionError as e: f'{m.get("guidance","")}, {m.get("ship","")}, {m["class"]}, {m["rating"]},'
if __debug__: print('Outfitting: %s' % e) # Silently skip unrecognized modules f'{m["id"]}, {data["timestamp"]}\n')
except:
if __debug__: raise except AssertionError as e:
h.close() # Log unrecognised modules
logger.debug('Outfitting', exc_info=e)