def guidedUpdate(tdb, cmdenv): dbFilename = tdb.dbFilename stationID = cmdenv.startStation.ID tmpPath = getTemporaryPath(cmdenv) cur = tdb.query( """ SELECT JULIANDAY('now') - JULIANDAY(MIN(modified)), JULIANDAY('now') - JULIANDAY(MAX(modified)) FROM StationItem WHERE station_id = ? """, [stationID]) oldest, newest = cur.fetchone() if oldest and newest: cmdenv.NOTE( "Current data {:.2f}-{:.2f} days old.", oldest, newest, ) from commands.update_gui import render try: render(tdb, cmdenv, tmpPath) cmdenv.DEBUG0("Got results, importing") cache.importDataFromFile(tdb, cmdenv, tmpPath) saveCopyOfChanges(cmdenv, dbFilename, stationID) tmpPath.unlink() tmpPath = None except Exception as e: print("ERROR:", e) print() print("*** YOUR UPDATES WILL BE SAVED AS {} ***".format("prices.last")) if tmpPath: saveTemporaryFile(tmpPath) if "EXCEPTIONS" in os.environ: raise e
def guidedUpdate(tdb, cmdenv): dbFilename = tdb.dbFilename stationID = cmdenv.startStation.ID tmpPath = getTemporaryPath(cmdenv) cur = tdb.query(""" SELECT JULIANDAY('now') - JULIANDAY(MIN(modified)), JULIANDAY('now') - JULIANDAY(MAX(modified)) FROM StationItem WHERE station_id = ? """, [stationID]) oldest, newest = cur.fetchone() if oldest and newest: cmdenv.NOTE( "Current data {:.2f}-{:.2f} days old.", oldest, newest, ) from commands.update_gui import render try: render(tdb, cmdenv, tmpPath) cmdenv.DEBUG0("Got results, importing") cache.importDataFromFile(tdb, cmdenv, tmpPath) saveCopyOfChanges(cmdenv, dbFilename, stationID) tmpPath.unlink() tmpPath = None except Exception as e: print("ERROR:", e) print() print("*** YOUR UPDATES WILL BE SAVED AS {} ***".format( "prices.last" )) if tmpPath: saveTemporaryFile(tmpPath) if "EXCEPTIONS" in os.environ: raise e
def import_prices(self): """ Download and import data price data """ tdb, tdenv = self.tdb, self.tdenv # It takes a while to download these files, so we want # to record the start time before we download. What we # care about is when we downloaded relative to when the # files were previously generated. startTime = time.time() prevImportDate, lastRunDays = self.load_timestamp() self.prevImportDate = prevImportDate self.download_prices(lastRunDays) if tdenv.download: return # Scan the file for the latest data. firstDate = None newestDate = prevImportDate numNewLines = 0 minLen = len(prevImportDate) + 10 dateRe = ImportPlugin.dateRe lastStn = None updatedStations = set() tdenv.DEBUG0("Reading prices data") with open(self.filename, "rUb") as fh: # skip the shebang. firstLine = fh.readline().decode(encoding="utf-8") self.check_shebang(firstLine, False) importDate = self.importDate lineNo = 0 while True: lineNo += 1 try: line = next(fh) except StopIteration: break try: line = line.decode(encoding="utf-8") except UnicodeDecodeError as e: try: line = line.decode(encoding="latin1") line = line.encode("utf-8") line = line.decode() except UnicodeDecodeError: raise DecodingError( "{} line {}: " "Invalid (unrecognized, non-utf8) character " "sequence: {}\n{}".format( self.filename, lineNo, str(e), line, ) ) from None raise DecodingError( "{} line {}: " "Invalid (latin1, non-utf8) character " "sequence:\n{}".format( self.filename, lineNo, line, ) ) if line.startswith('@'): lastStn = line[2:line.find('#')].strip() continue if not line.startswith(' ') or len(line) < minLen: continue m = dateRe.search(line) if not m: continue date = m.group(1) + ' ' + m.group(2) if not firstDate or date < firstDate: firstDate = date if date > prevImportDate: updatedStations.add(lastStn) numNewLines += 1 if date > newestDate: newestDate = date if date > importDate: raise PluginException( "Station {} has suspicious date: {} " "(newer than the import?)" .format(lastStn, date) ) if numNewLines == 0: tdenv.NOTE("No new price entries found.") forceParse = self.getOption("force") or self.getOption("skipdl") if numNewLines > 0 or forceParse: if tdenv.detail: print( "Date of last import : {}\n" "Timestamp of import : {}\n" "Oldest update in file : {}\n" "Newest update in file : {}\n" "Number of new entries : {}\n" .format( prevImportDate, importDate, firstDate, newestDate, numNewLines, )) numStationsUpdated = len(updatedStations) if not tdenv.quiet and numStationsUpdated: if len(updatedStations) > 12 and tdenv.detail < 2: updatedStations = list(updatedStations)[:10] + ["..."] tdenv.NOTE( "{} {} updated:\n{}", numStationsUpdated, "stations" if numStationsUpdated > 1 else "station", ', '.join(updatedStations) ) cache.importDataFromFile( tdb, tdenv, pathlib.Path(self.filename), ) self.save_timestamp(importDate, startTime)
def run(self): tdb, tdenv = self.tdb, self.tdenv # first check for EDCD if self.getOption("edcd"): # Call the EDCD plugin try: import plugins.edcd_plug as EDCD except: raise plugins.PluginException("EDCD plugin not found.") tdenv.NOTE("Calling the EDCD plugin.") edcdPlugin = EDCD.ImportPlugin(tdb, tdenv) edcdPlugin.options["csvs"] = True edcdPlugin.run() tdenv.NOTE("Going back to EDAPI.\n") # now load the mapping tables itemMap = mapping.FDEVMappingItems(tdb, tdenv) shipMap = mapping.FDEVMappingShips(tdb, tdenv) # Connect to the API, authenticate, and pull down the commander # /profile. if self.getOption("test"): tdenv.WARN("#############################") tdenv.WARN("### EDAPI in test mode. ###") tdenv.WARN("#############################") apiED = namedtuple('EDAPI', ['profile','text']) try: proPath = pathlib.Path(self.getOption("test")) except TypeError: raise plugins.PluginException( "Option 'test' must be a file name" ) if proPath.exists(): with proPath.open() as proFile: proData = json.load(proFile) if isinstance(proData, list): # since 4.3.0: list(profile, market, shipyard) testProfile = proData[0] for data in proData[1:]: if int(data["id"]) == int(testProfile["lastStarport"]["id"]): testProfile["lastStarport"].update(data) else: testProfile = proData api = apiED( profile = testProfile, text = '{{"mode":"test","file":"{}"}}'.format(str(proPath)) ) else: raise plugins.PluginException( "JSON-file '{}' not found.".format(str(proPath)) ) else: api = EDAPI( cookiefile=str(self.cookiePath), login=self.getOption('login'), debug=tdenv.debug, ) self.edAPI = api # save profile if requested if self.getOption("save"): saveName = 'tmp/profile.' + time.strftime('%Y%m%d_%H%M%S') + '.json' with open(saveName, 'w', encoding="utf-8") as saveFile: if isinstance(api.text, list): # since 4.3.0: list(profile, market, shipyard) saveFile.write("[{}]".format(",".join(api.text))) else: saveFile.write(api.text) print('API response saved to: {}'.format(saveName)) # Sanity check that the commander is docked. Otherwise we will get a # mismatch between the last system and last station. if not api.profile['commander']['docked']: print('Commander not docked. Aborting!') return False # Figure out where we are. sysName = api.profile['lastSystem']['name'] stnName = api.profile['lastStarport']['name'] print('@{}/{}'.format(sysName.upper(), stnName)) # Reload the cache. tdenv.DEBUG0("Checking the cache") tdb.close() tdb.reloadCache() tdb.load( maxSystemLinkLy=tdenv.maxSystemLinkLy, ) tdb.close() # Check to see if this system is in the database try: system = tdb.lookupSystem(sysName) except LookupError: raise plugins.PluginException( "System '{}' unknown.".format(sysName) ) # Check to see if this station is in the database try: station = tdb.lookupStation(stnName, system) except LookupError: station = None # New or update station data station = self.askForStationData(system, stnName=stnName, station=station) # If a shipyard exists, make the ship lists shipCost = {} shipList = [] eddn_ships = [] if ((station.shipyard == "Y") and ('ships' in api.profile['lastStarport']) ): if 'shipyard_list' in api.profile['lastStarport']['ships']: if len(api.profile['lastStarport']['ships']['shipyard_list']): for ship in api.profile['lastStarport']['ships']['shipyard_list'].values(): shipName = shipMap.mapID(ship['id'], ship['name']) shipCost[shipName] = ship['basevalue'] shipList.append(shipName) eddn_ships.append(ship['name']) if 'unavailable_list' in api.profile['lastStarport']['ships']: for ship in api.profile['lastStarport']['ships']['unavailable_list']: shipName = shipMap.mapID(ship['id'], ship['name']) shipCost[shipName] = ship['basevalue'] shipList.append(shipName) eddn_ships.append(ship['name']) if self.getOption("csvs"): addRows = delRows = 0 db = tdb.getDB() if station.shipyard == "N": # delete all ships if there is no shipyard delRows = db.execute( """ DELETE FROM ShipVendor WHERE station_id = ? """, [station.ID] ).rowcount if len(shipList): # and now update the shipyard list # we go through all ships to decide if a ship needs to be # added or deleted from the shipyard for shipID in tdb.shipByID: shipName = tdb.shipByID[shipID].dbname if shipName in shipList: # check for ship discount, costTD = 100% # python builtin round() uses "Round half to even" # but we need commercial rounding, so we do it ourself costTD = tdb.shipByID[shipID].cost costED = int((shipCost[shipName]+5)/10)*10 if costTD != costED: prozED = int(shipCost[shipName]*100/costTD+0.5)-100 tdenv.NOTE( "CostDiff {}: {} != {} ({}%)", shipName, costTD, costED, prozED ) # add the ship to the shipyard shipSQL = ( "INSERT OR IGNORE" " INTO ShipVendor(station_id, ship_id)" " VALUES(?, ?)" ) tdenv.DEBUG0(shipSQL.replace("?", "{}"), station.ID, shipID) addRows += db.execute(shipSQL, [station.ID, shipID]).rowcount else: # delete the ship from the shipyard shipSQL = ( "DELETE FROM ShipVendor" " WHERE station_id = ?" " AND ship_id = ?" ) tdenv.DEBUG0(shipSQL.replace("?", "{}"), station.ID, shipID) delRows += db.execute(shipSQL, [station.ID, shipID]).rowcount db.commit() if (addRows + delRows) > 0: if addRows > 0: tdenv.NOTE( "Added {} ships in '{}' shipyard.", addRows, station.name() ) if delRows > 0: tdenv.NOTE( "Deleted {} ships in '{}' shipyard.", delRows, station.name() ) lines, csvPath = csvexport.exportTableToFile( tdb, tdenv, "ShipVendor", ) tdenv.DEBUG0("{} updated.", csvPath) # If a market exists, make the item lists itemList = [] eddn_market = [] if ((station.market == "Y") and ('commodities' in api.profile['lastStarport']) ): for commodity in api.profile['lastStarport']['commodities']: if commodity['categoryname'] in cat_ignore: continue if commodity.get('legality', '') != '': # ignore if present and not empty continue locName = commodity.get('locName', commodity['name']) itmName = itemMap.mapID(commodity['id'], locName) def commodity_int(key): try: ret = int(float(commodity[key])+0.5) except (ValueError, KeyError): ret = 0 return ret itmSupply = commodity_int('stock') itmDemand = commodity_int('demand') itmSupplyLevel = commodity_int('stockBracket') itmDemandLevel = commodity_int('demandBracket') itmBuyPrice = commodity_int('buyPrice') itmSellPrice = commodity_int('sellPrice') if itmSupplyLevel == 0 or itmBuyPrice == 0: # If there is not stockBracket or buyPrice, ignore stock itmBuyPrice = 0 itmSupply = 0 itmSupplyLevel = 0 tdSupply = "-" tdDemand = "{}{}".format( itmDemand, bracket_levels[itmDemandLevel] ) else: # otherwise don't care about demand itmDemand = 0 itmDemandLevel = 0 tdDemand = "?" tdSupply = "{}{}".format( itmSupply, bracket_levels[itmSupplyLevel] ) # ignore items without supply or demand bracket (TD only) if itmSupplyLevel > 0 or itmDemandLevel > 0: itemTD = ( itmName, itmSellPrice, itmBuyPrice, tdDemand, tdSupply, ) itemList.append(itemTD) # Populate EDDN if self.getOption("eddn"): itemEDDN = { "name": commodity['name'], "meanPrice": commodity_int('meanPrice'), "buyPrice": commodity_int('buyPrice'), "stock": commodity_int('stock'), "stockBracket": commodity['stockBracket'], "sellPrice": commodity_int('sellPrice'), "demand": commodity_int('demand'), "demandBracket": commodity['demandBracket'], } if len(commodity['statusFlags']) > 0: itemEDDN["statusFlags"] = commodity['statusFlags'] eddn_market.append(itemEDDN) if itemList: # Create the import file. with open(self.filename, 'w', encoding="utf-8") as f: # write System/Station line f.write("@ {}/{}\n".format(sysName, stnName)) # write Item lines (category lines are not needed) for itemTD in itemList: f.write("\t\t%s %s %s %s %s\n" % itemTD) tdenv.ignoreUnknown = True cache.importDataFromFile( tdb, tdenv, pathlib.Path(self.filename), ) # Import EDDN if self.getOption("eddn"): con = EDDN( api.profile['commander']['name'], self.getOption("name"), 'EDAPI Trade Dangerous Plugin', __version__ ) if self.getOption("test"): con._debug = True else: con._debug = False if eddn_market: print('Posting commodities to EDDN...') con.publishCommodities( sysName, stnName, eddn_market ) if eddn_ships: print('Posting shipyard to EDDN...') con.publishShipyard( sysName, stnName, eddn_ships ) if ((station.outfitting == "Y") and ('modules' in api.profile['lastStarport']) ): eddn_modules = [] for module in api.profile['lastStarport']['modules'].values(): # see: https://github.com/EDSM-NET/EDDN/wiki addModule = False if module['name'].startswith(('Hpt_', 'Int_')) or module['name'].find('_Armour_') > 0: if module.get('sku', None) in ( None, 'ELITE_HORIZONS_V_PLANETARY_LANDINGS' ): if module['name'] != 'Int_PlanetApproachSuite': addModule = True if addModule: eddn_modules.append(module['name']) elif self.getOption("test"): tdenv.NOTE("Ignored module ID: {}, name: {}", module['id'], module['name']) if eddn_modules: print('Posting outfitting to EDDN...') con.publishOutfitting( sysName, stnName, sorted(eddn_modules) ) # We did all the work return False
def import_prices(self): """ Download and import data price data """ tdb, tdenv = self.tdb, self.tdenv # It takes a while to download these files, so we want # to record the start time before we download. What we # care about is when we downloaded relative to when the # files were previously generated. startTime = time.time() prevImportDate, lastRunDays = self.load_timestamp() self.prevImportDate = prevImportDate self.download_prices(lastRunDays) if tdenv.download: return # Scan the file for the latest data. firstDate = None newestDate = prevImportDate numNewLines = 0 minLen = len(prevImportDate) + 10 dateRe = ImportPlugin.dateRe lastStn = None updatedStations = set() tdenv.DEBUG0("Reading prices data") with open(self.filename, "rUb") as fh: # skip the shebang. firstLine = fh.readline().decode(encoding="utf-8") self.check_shebang(firstLine, False) importDate = self.importDate lineNo = 0 while True: lineNo += 1 try: line = next(fh) except StopIteration: break try: line = line.decode(encoding="utf-8") except UnicodeDecodeError as e: try: line = line.decode(encoding="latin1") line = line.encode("utf-8") line = line.decode() except UnicodeDecodeError: raise DecodingError( "{} line {}: " "Invalid (unrecognized, non-utf8) character " "sequence: {}\n{}".format( self.filename, lineNo, str(e), line, )) from None raise DecodingError("{} line {}: " "Invalid (latin1, non-utf8) character " "sequence:\n{}".format( self.filename, lineNo, line, )) if line.startswith('@'): lastStn = line[2:line.find('#')].strip() continue if not line.startswith(' ') or len(line) < minLen: continue m = dateRe.search(line) if not m: continue date = m.group(1) + ' ' + m.group(2) if not firstDate or date < firstDate: firstDate = date if date > prevImportDate: updatedStations.add(lastStn) numNewLines += 1 if date > newestDate: newestDate = date if date > importDate: raise PluginException( "Station {} has suspicious date: {} " "(newer than the import?)".format( lastStn, date)) if numNewLines == 0: tdenv.NOTE("No new price entries found.") forceParse = self.getOption("force") or self.getOption("skipdl") if numNewLines > 0 or forceParse: if tdenv.detail: print("Date of last import : {}\n" "Timestamp of import : {}\n" "Oldest update in file : {}\n" "Newest update in file : {}\n" "Number of new entries : {}\n".format( prevImportDate, importDate, firstDate, newestDate, numNewLines, )) numStationsUpdated = len(updatedStations) if not tdenv.quiet and numStationsUpdated: if len(updatedStations) > 12 and tdenv.detail < 2: updatedStations = list(updatedStations)[:10] + ["..."] tdenv.NOTE("{} {} updated:\n{}", numStationsUpdated, "stations" if numStationsUpdated > 1 else "station", ', '.join(updatedStations)) cache.importDataFromFile( tdb, tdenv, pathlib.Path(self.filename), ) self.save_timestamp(importDate, startTime)
def run(self): tdb, tdenv = self.tdb, self.tdenv # first check for EDCD if self.getOption("edcd"): # Call the EDCD plugin try: import plugins.edcd_plug as EDCD except: raise plugins.PluginException("EDCD plugin not found.") tdenv.NOTE("Calling the EDCD plugin.") edcdPlugin = EDCD.ImportPlugin(tdb, tdenv) edcdPlugin.options["csvs"] = True edcdPlugin.run() tdenv.NOTE("Going back to EDAPI.\n") # now load the mapping tables itemMap = mapping.FDEVMappingItems(tdb, tdenv) shipMap = mapping.FDEVMappingShips(tdb, tdenv) # Connect to the API, authenticate, and pull down the commander # /profile. if self.getOption("test"): tdenv.WARN("#############################") tdenv.WARN("### EDAPI in test mode. ###") tdenv.WARN("#############################") apiED = namedtuple('EDAPI', ['profile','text']) try: proPath = pathlib.Path(self.getOption("test")) except TypeError: raise plugins.PluginException( "Option 'test' must be a file name" ) if proPath.exists(): with proPath.open() as proFile: api = apiED( profile = json.load(proFile), text = '{{"mode":"test","file":"{}"}}'.format(str(proPath)) ) else: raise plugins.PluginException( "JSON-file '{}' not found.".format(str(proPath)) ) else: api = EDAPI(cookiefile=str(self.cookiePath)) self.edAPI = api # Sanity check that the commander is docked. Otherwise we will get a # mismatch between the last system and last station. if not api.profile['commander']['docked']: print('Commander not docked. Aborting!') return False # save profile if requested if self.getOption("save"): saveName = 'tmp/profile.' + time.strftime('%Y%m%d_%H%M%S') + '.json' with open(saveName, 'w', encoding="utf-8") as saveFile: saveFile.write(api.text) print('API response saved to: {}'.format(saveName)) # Figure out where we are. sysName = api.profile['lastSystem']['name'] stnName = api.profile['lastStarport']['name'] print('@{}/{}'.format(sysName.upper(), stnName)) # Reload the cache. tdenv.DEBUG0("Checking the cache") tdb.close() tdb.reloadCache() tdb.load( maxSystemLinkLy=tdenv.maxSystemLinkLy, ) tdb.close() # Check to see if this system is in the database try: system = tdb.lookupSystem(sysName) except LookupError: raise plugins.PluginException( "System '{}' unknown.".format(sysName) ) # Check to see if this station is in the database try: station = tdb.lookupStation(stnName, system) except LookupError: station = None # New or update station data station = self.askForStationData(system, stnName=stnName, station=station) # If a shipyard exists, make the ship lists shipList = [] eddn_ships = [] if ((station.shipyard == "Y") and ('ships' in api.profile['lastStarport']) ): if 'shipyard_list' in api.profile['lastStarport']['ships']: for ship in api.profile['lastStarport']['ships']['shipyard_list'].values(): shipList.append(shipMap.mapID(ship['id'], ship['name'])) eddn_ships.append(ship['name']) if 'unavailable_list' in api.profile['lastStarport']['ships']: for ship in api.profile['lastStarport']['ships']['unavailable_list']: shipList.append(shipMap.mapID(ship['id'], ship['name'])) eddn_ships.append(ship['name']) if self.getOption("csvs"): exportCSV = False db = tdb.getDB() # first delete all ships if ((len(shipList) > 0) or (station.shipyard == "N")): # but only if there should be no shipyard or there is a new list delRows = db.execute( """ DELETE FROM ShipVendor WHERE station_id = ? """, [station.ID] ).rowcount if delRows > 0: exportCSV = True tdenv.NOTE( "Deleted {} ships in '{}' shipyard.", delRows, station.name() ) # and now add the shipyard list for ship in shipList: ship_lookup = tdb.lookupShip(ship) db.execute( """ INSERT INTO ShipVendor(ship_id, station_id) VALUES(?, ?) """, [ship_lookup.ID, station.ID] ) exportCSV = True db.commit() if exportCSV: tdenv.NOTE( "Added {} ships in '{}' shipyard.", len(shipList), station.name() ) lines, csvPath = csvexport.exportTableToFile( tdb, tdenv, "ShipVendor", ) tdenv.DEBUG0("{} updated.", csvPath) # If a market exists, make the item lists itemList = [] eddn_market = [] if ((station.market == "Y") and ('commodities' in api.profile['lastStarport']) ): for commodity in api.profile['lastStarport']['commodities']: if commodity['categoryname'] in cat_ignore: continue itmName = itemMap.mapID(commodity['id'], commodity['name']) def commodity_int(key): try: ret = int(float(commodity[key])+0.5) except (ValueError, KeyError): ret = 0 return ret itmSupply = commodity_int('stock') itmDemand = commodity_int('demand') itmSupplyLevel = commodity_int('stockBracket') itmDemandLevel = commodity_int('demandBracket') itmBuyPrice = commodity_int('buyPrice') itmSellPrice = commodity_int('sellPrice') demandLevel = True supplyLevel = True if itmBuyPrice == 0: # If there is not buyPrice, ignore stock supplyLevel = False itmSupply = 0 itmSupplyLevel = 0 tdSupply = "-" tdDemand = "{}{}".format( itmDemand, bracket_levels[itmDemandLevel] ) else: # otherwise don't care about demand demandLevel = False itmDemand = 0 itmDemandLevel = 0 tdDemand = "?" tdSupply = "{}{}".format( itmSupply, bracket_levels[itmSupplyLevel] ) itemTD = ( itmName, itmSellPrice, itmBuyPrice, tdDemand, tdSupply, ) itemList.append(itemTD) # Populate EDDN if self.getOption("eddn"): itemEDDN = { "name": commodity['name'], "meanPrice": commodity_int('meanPrice'), "buyPrice": commodity_int('buyPrice'), "stock": commodity_int('stock'), "stockBracket": commodity['stockBracket'], "sellPrice": commodity_int('sellPrice'), "demand": commodity_int('demand'), "demandBracket": commodity['demandBracket'], } if len(commodity['statusFlags']) > 0: itemEDDN["statusFlags"] = commodity['statusFlags'] eddn_market.append(itemEDDN) if itemList: # Create the import file. with open(self.filename, 'w', encoding="utf-8") as f: # write System/Station line f.write("@ {}/{}\n".format(sysName, stnName)) # write Item lines (category lines are not needed) for itemTD in itemList: f.write("\t\t%s %s %s %s %s\n" % itemTD) tdenv.ignoreUnknown = True cache.importDataFromFile( tdb, tdenv, pathlib.Path(self.filename), ) # Import EDDN if self.getOption("eddn"): con = EDDN( api.profile['commander']['name'], self.getOption("name"), 'EDAPI Trade Dangerous Plugin', __version__ ) if self.getOption("test"): con._debug = True else: con._debug = False if eddn_market: print('Posting commodities to EDDN...') con.publishCommodities( sysName, stnName, eddn_market ) if eddn_ships: print('Posting shipyard to EDDN...') con.publishShipyard( sysName, stnName, eddn_ships ) if ((station.outfitting == "Y") and ('modules' in api.profile['lastStarport']) ): eddn_modules = [] for module in api.profile['lastStarport']['modules'].values(): # see: https://github.com/jamesremuscat/EDDN/wiki addModule = False if module['name'].startswith(('Hpt_', 'Int_')) or module['name'].find('_Armour_') > 0: if module.get('sku', None) in ( None, 'ELITE_HORIZONS_V_PLANETARY_LANDINGS' ): if module['name'] != 'Int_PlanetApproachSuite': addModule = True if addModule: eddn_modules.append(module['name']) elif self.getOption("test"): tdenv.NOTE("Ignored module ID: {}, name: {}", module['id'], module['name']) if eddn_modules: print('Posting outfitting to EDDN...') con.publishOutfitting( sysName, stnName, sorted(eddn_modules) ) # We did all the work return False
def editUpdate(tdb, cmdenv, stationID): """ Dump the price data for a specific station to a file and launch the user's text editor to let them make changes to the file. If the user makes changes, re-load the file, update the database and regenerate the master .prices file. """ cmdenv.DEBUG0("'update' mode with editor. editor:{} station:{}", cmdenv.editor, cmdenv.origin) editor, editorArgs = cmdenv.editor, [] if cmdenv.editing == 'sublime': cmdenv.DEBUG0("Sublime mode") editor = editor or \ getEditorPaths(cmdenv, "sublime", "SUBLIME_EDITOR", ["Sublime Text 3", "Sublime Text 2"], "sublime_text.exe", "subl" ) editorArgs += [ "--wait" ] elif cmdenv.editing == 'npp': cmdenv.DEBUG0("Notepad++ mode") editor = editor or \ getEditorPaths(cmdenv, "notepad++", "NOTEPADPP_EDITOR", ["Notepad++"], "notepad++.exe", "notepad++" ) if not cmdenv.quiet: print("NOTE: " "You'll need to exit Notepad++ when you are done.") elif cmdenv.editing == "vim": cmdenv.DEBUG0("VI iMproved mode") if not editor: # Hack... Hackity hack, hack, hack. # This has a disadvantage in that: we don't check for just "vim" (no .exe) under Windows vimDirs = [ "Git\\share\\vim\\vim{}".format(vimVer) for vimVer in range(70,75) ] editor = getEditorPaths(cmdenv, "vim", "EDITOR", vimDirs, "vim.exe", "vim" ) elif cmdenv.editing == "notepad": cmdenv.DEBUG0("Notepad mode") editor = editor or "notepad.exe" # herp try: envArgs = os.environ["EDITOR_ARGS"] if envArgs: editorArgs += envArgs.split(' ') except KeyError: pass # Create a temporary text file with a list of the price data. tmpPath = getTemporaryPath(cmdenv) absoluteFilename = None dbFilename = tdb.dbFilename try: elementMask = prices.Element.basic | prices.Element.supply if cmdenv.timestamps: elementMask |= prices.Element.timestamp if cmdenv.all: elementMask |= prices.Element.blanks # Open the file and dump data to it. with tmpPath.open("w", encoding='utf-8') as tmpFile: # Remember the filename so we know we need to delete it. absoluteFilename = str(tmpPath.resolve()) prices.dumpPrices(dbFilename, elementMask, file=tmpFile, stationID=stationID, defaultZero=cmdenv.forceNa, debug=cmdenv.debug ) # Stat the file so we can determine if the user writes to it. # Use the most recent create/modified timestamp. preStat = tmpPath.stat() preStamp = max(preStat.st_mtime, preStat.st_ctime) # Launch the editor editorCommandLine = [ editor ] + editorArgs + [ absoluteFilename ] cmdenv.DEBUG0("Invoking [{}]", ' '.join(editorCommandLine)) try: result = subprocess.call(editorCommandLine) except FileNotFoundError: raise CommandLineError( "Unable to launch requested editor: {}" .format(editorCommandLine) ) if result != 0: raise CommandLineError( "NO DATA IMPORTED: " "Your editor exited with a 'failed' exit code ({})" .format(result) ) # Did they update the file? Some editors destroy the file and rewrite it, # other files just write back to it, and some OSes do weird things with # these timestamps. That's why we have to use both mtime and ctime. postStat = tmpPath.stat() postStamp = max(postStat.st_mtime, postStat.st_ctime) if postStamp == preStamp: import random print("- No changes detected - doing nothing. {}".format( random.choice([ "Brilliant!", "I'll get my coat.", "I ain't seen you.", "You ain't seen me", "... which was nice", "Bingo!", "Scorchio!", "Boutros, boutros, ghali!", "I'm Ed Winchester!", "Suit you, sir! Oh!" ]) )) else: cache.importDataFromFile(tdb, cmdenv, tmpPath) saveCopyOfChanges(cmdenv, dbFilename, stationID) tmpPath.unlink() tmpPath = None except Exception as e: print("ERROR:", e) print() print("*** YOUR UPDATES WILL BE SAVED AS {} ***".format( "prices.last" )) # Save a copy if absoluteFilename and tmpPath: saveTemporaryFile(tmpPath) if "EXCEPTIONS" in os.environ: raise e
def run(results, cmdenv, tdb): # If we're using a plugin, initialize that first. if cmdenv.plug: if cmdenv.pluginOptions: cmdenv.pluginOptions = chain.from_iterable( opt.split(',') for opt in cmdenv.pluginOptions) try: pluginClass = plugins.load(cmdenv.plug, "ImportPlugin") except plugins.PluginException as e: raise CommandLineError("Plugin Error: " + str(e)) # Initialize the plugin plugin = pluginClass(tdb, cmdenv) # Run the plugin. If it returns False, then it did everything # that needs doing and we can stop now. # If it returns True, it is returning control to the module. if not plugin.run(): return None tdb.reloadCache() tdb.close() if cmdenv.filename: if re.match(r"^https?://", cmdenv.filename, re.IGNORECASE): cmdenv.url, cmdenv.filename = cmdenv.filename, None if cmdenv.url: cmdenv.filename = cmdenv.filename or "import.prices" transfers.download(cmdenv, cmdenv.url, cmdenv.filename) if cmdenv.download: return None # If the filename specified was "-" or None, then go ahead # and present the user with an open file dialog. if not cmdenv.filename and hasTkInter: tk = tkinter.Tk() tk.withdraw() filetypes = ( ("TradeDangerous '.prices' Files", "*.prices"), ("All Files", "*.*"), ) filename = tkfd.askopenfilename( title="Select the file to import", initialfile="TradeDangerous.prices", filetypes=filetypes, initialdir='.', ) if not filename: raise SystemExit("Aborted") cmdenv.filename = filename # check the file exists. if cmdenv.filename != "-": fh = None filePath = Path(cmdenv.filename) if not filePath.is_file(): raise CommandLineError("File not found: {}".format(str(filePath))) else: filePath = "stdin" fh = sys.stdin if cmdenv.plug: if not plugin.finish(): cache.regeneratePricesFile() return None cache.importDataFromFile(tdb, cmdenv, filePath, pricesFh=fh, reset=cmdenv.reset) return None
def editUpdate(tdb, cmdenv, stationID): """ Dump the price data for a specific station to a file and launch the user's text editor to let them make changes to the file. If the user makes changes, re-load the file, update the database and regenerate the master .prices file. """ cmdenv.DEBUG0("'update' mode with editor. editor:{} station:{}", cmdenv.editor, cmdenv.origin) editor, editorArgs = cmdenv.editor, [] if cmdenv.editing == 'sublime': cmdenv.DEBUG0("Sublime mode") editor = editor or \ getEditorPaths(cmdenv, "sublime", "SUBLIME_EDITOR", ["Sublime Text 3", "Sublime Text 2"], "sublime_text.exe", "subl" ) editorArgs += ["--wait"] elif cmdenv.editing == 'npp': cmdenv.DEBUG0("Notepad++ mode") editor = editor or \ getEditorPaths(cmdenv, "notepad++", "NOTEPADPP_EDITOR", ["Notepad++"], "notepad++.exe", "notepad++" ) if not cmdenv.quiet: print("NOTE: " "You'll need to exit Notepad++ when you are done.") elif cmdenv.editing == "vim": cmdenv.DEBUG0("VI iMproved mode") if not editor: # Hack... Hackity hack, hack, hack. # This has a disadvantage in that: we don't check for just "vim" (no .exe) under Windows vimDirs = [ "Git\\share\\vim\\vim{}".format(vimVer) for vimVer in range(70, 75) ] editor = getEditorPaths(cmdenv, "vim", "EDITOR", vimDirs, "vim.exe", "vim") elif cmdenv.editing == "notepad": cmdenv.DEBUG0("Notepad mode") editor = editor or "notepad.exe" # herp try: envArgs = os.environ["EDITOR_ARGS"] if envArgs: editorArgs += envArgs.split(' ') except KeyError: pass # Create a temporary text file with a list of the price data. tmpPath = getTemporaryPath(cmdenv) absoluteFilename = None dbFilename = tdb.dbFilename try: elementMask = prices.Element.basic | prices.Element.supply if cmdenv.timestamps: elementMask |= prices.Element.timestamp if cmdenv.all: elementMask |= prices.Element.blanks # Open the file and dump data to it. with tmpPath.open("w", encoding='utf-8') as tmpFile: # Remember the filename so we know we need to delete it. absoluteFilename = str(tmpPath.resolve()) prices.dumpPrices(dbFilename, elementMask, file=tmpFile, stationID=stationID, defaultZero=cmdenv.forceNa, debug=cmdenv.debug) # Stat the file so we can determine if the user writes to it. # Use the most recent create/modified timestamp. preStat = tmpPath.stat() preStamp = max(preStat.st_mtime, preStat.st_ctime) # Launch the editor editorCommandLine = [editor] + editorArgs + [absoluteFilename] cmdenv.DEBUG0("Invoking [{}]", ' '.join(editorCommandLine)) try: result = subprocess.call(editorCommandLine) except FileNotFoundError: raise CommandLineError( "Unable to launch requested editor: {}".format( editorCommandLine)) if result != 0: raise CommandLineError( "NO DATA IMPORTED: " "Your editor exited with a 'failed' exit code ({})".format( result)) # Did they update the file? Some editors destroy the file and rewrite it, # other files just write back to it, and some OSes do weird things with # these timestamps. That's why we have to use both mtime and ctime. postStat = tmpPath.stat() postStamp = max(postStat.st_mtime, postStat.st_ctime) if postStamp == preStamp: import random print("- No changes detected - doing nothing. {}".format( random.choice([ "Brilliant!", "I'll get my coat.", "I ain't seen you.", "You ain't seen me", "... which was nice", "Bingo!", "Scorchio!", "Boutros, boutros, ghali!", "I'm Ed Winchester!", "Suit you, sir! Oh!" ]))) else: cache.importDataFromFile(tdb, cmdenv, tmpPath) saveCopyOfChanges(cmdenv, dbFilename, stationID) tmpPath.unlink() tmpPath = None except Exception as e: print("ERROR:", e) print() print("*** YOUR UPDATES WILL BE SAVED AS {} ***".format("prices.last")) # Save a copy if absoluteFilename and tmpPath: saveTemporaryFile(tmpPath) if "EXCEPTIONS" in os.environ: raise e
def run(self): tdb, tdenv = self.tdb, self.tdenv # Connect to the API, authenticate, and pull down the commander # /profile. api = EDAPI(cookiefile=str(self.cookiePath)) # Sanity check that the commander is docked. Otherwise we will get a # mismatch between the last system and last station. if not api.profile['commander']['docked']: print('Commander not docked. Aborting!') return False # Figure out where we are. system = api.profile['lastSystem']['name'] station = api.profile['lastStarport']['name'] place = '@{}/{}'.format(system.upper(), station) print(place) # Reload the cache. tdenv.DEBUG0("Checking the cache") tdb.close() tdb.reloadCache() tdb.load( maxSystemLinkLy=tdenv.maxSystemLinkLy, ) tdb.close() # Check to see if this system is in the Stations file try: station_lookup = tdb.lookupPlace(place) except LookupError: station_lookup = None print(station_lookup) # The station isn't known. Add it. if not station_lookup: print('Station unknown.') print('Adding:', place) lsFromStar = input( "Distance from star (enter for 0): " ) or 0 lsFromStar = int(lsFromStar) blackMarket = input( "Black market present (Y, N or enter for ?): " ) or '?' maxPadSize = input( "Max pad size (S, M, L or enter for ?): " ) or '?' outfitting = input( "Outfitting present (Y, N or enter for ?): " ) or '?' rearm = input( "Rearm present (Y, N or enter for ?): " ) or '?' refuel = input( "Refuel present (Y, N or enter for ?): " ) or '?' repair = input( "Repair present (Y, N or enter for ?): " ) or '?' # This is unreliable, so default to unknown. if 'commodities' in api.profile['lastStarport']: market = 'Y' else: market = '?' # This is also unreliable, so default to unknown. if 'ships' in api.profile['lastStarport']: shipyard = 'Y' else: shipyard = '?' system_lookup = tdb.lookupSystem(system) if tdb.addLocalStation( system=system_lookup, name=station, lsFromStar=lsFromStar, blackMarket=blackMarket, maxPadSize=maxPadSize, market=market, shipyard=shipyard, outfitting=outfitting, rearm=rearm, refuel=refuel, repair=repair ): lines, csvPath = csvexport.exportTableToFile( tdb, tdenv, "Station" ) tdenv.NOTE("{} updated.", csvPath) station_lookup = tdb.lookupPlace(place) station_lookup = tdb.lookupStation(station, system) else: # See if we need to update the info for this station. lsFromStar = station_lookup.lsFromStar blackMarket = station_lookup.blackMarket maxPadSize = station_lookup.maxPadSize market = station_lookup.market shipyard = station_lookup.shipyard outfitting = station_lookup.outfitting rearm = station_lookup.rearm refuel = station_lookup.refuel repair = station_lookup.repair if lsFromStar == 0: lsFromStar = input( "Update distance from star (enter for 0): " ) or 0 lsFromStar = int(lsFromStar) if blackMarket is '?': blackMarket = input( "Update black market present (Y, N or enter for ?): " ) or '?' if maxPadSize is '?': maxPadSize = input( "Update max pad size (S, M, L or enter for ?): " ) or '?' if outfitting is '?': outfitting = input( "Update outfitting present (Y, N or enter for ?): " ) or '?' if rearm is '?': rearm = input( "Update rearm present (Y, N or enter for ?): " ) or '?' if refuel is '?': refuel = input( "Update refuel present (Y, N or enter for ?): " ) or '?' if repair is '?': repair = input( "Update repair present (Y, N or enter for ?): " ) or '?' # This is unreliable, so default to unchanged. if 'commodities' in api.profile['lastStarport']: market = 'Y' # This is also unreliable, so default to unchanged. if 'ships' in api.profile['lastStarport']: shipyard = 'Y' if ( lsFromStar != station_lookup.lsFromStar or blackMarket != station_lookup.blackMarket or maxPadSize != station_lookup.maxPadSize or market != station_lookup.market or shipyard != station_lookup.shipyard or outfitting != station_lookup.outfitting or rearm != station_lookup.rearm or refuel != station_lookup.refuel or repair != station_lookup.repair ): if tdb.updateLocalStation( station=station_lookup, lsFromStar=lsFromStar, blackMarket=blackMarket, maxPadSize=maxPadSize, market=market, shipyard=shipyard, outfitting=outfitting, rearm=rearm, refuel=refuel, repair=repair ): lines, csvPath = csvexport.exportTableToFile( tdb, tdenv, "Station", ) tdenv.NOTE("{} updated.", csvPath) # If a shipyard exists, update the ship vendor list. if 'ships' in api.profile['lastStarport']: ships = list( api.profile['lastStarport']['ships']['shipyard_list'].keys() ) for ship in api.profile['lastStarport']['ships']['unavailable_list']: ships.append(ship['name']) db = tdb.getDB() for ship in ships: ship_lookup = tdb.lookupShip(ship_names[ship]) db.execute( """ REPLACE INTO ShipVendor (ship_id, station_id) VALUES (?, ?) """, [ship_lookup.ID, station_lookup.ID] ) db.commit() tdenv.NOTE("Updated {} ships in {} shipyard.", len(ships), place) lines, csvPath = csvexport.exportTableToFile( tdb, tdenv, "ShipVendor", ) # Some sanity checking on the market. if 'commodities' not in api.profile['lastStarport']: print( 'The API did not return a commodity market for this station.' ) print( 'If you think this is wrong, try again. The API will ' 'occasionally skip the market.' ) return False # Create the import file. with open(self.filename, 'w', encoding="utf-8") as f: f.write("@ {}/{}\n".format(system, station)) eddn_market = [] for commodity in api.profile['lastStarport']['commodities']: if commodity['categoryname'] in cat_ignore: continue if commodity['categoryname'] in cat_correct: commodity['categoryname'] = cat_correct[commodity['categoryname']] if commodity['name'] in comm_correct: commodity['name'] = comm_correct[commodity['name']] # Populate EDDN if self.getOption("eddn"): eddn_market.append( { "name": commodity['name'], "buyPrice": int(commodity['buyPrice']), "supply": int(commodity['stock']), "supplyLevel": EDDN._levels[int(commodity['stockBracket'])], "sellPrice": int(commodity['sellPrice']), "demand": int(commodity['demand']), "demandLevel": EDDN._levels[int(commodity['demandBracket'])] } ) f.write("\t+ {}\n".format(commodity['categoryname'])) def commodity_int(key): try: commodity[key] = int(commodity[key]) except (ValueError, KeyError): commodity[key] = 0 commodity_int('stock') commodity_int('demand') commodity_int('demandBracket') commodity_int('stockBracket') # If stock is zero, list it as unavailable. if not commodity['stock']: commodity['stock'] = '-' else: demand = bracket_levels[commodity['stockBracket']] commodity['stock'] = str(commodity['stock'])+demand # If demand is zero, zero out the sell price. if not (commodity['demand'] and commodity['demandBracket']): commodity['demand'] = '?' commodity['sellPrice'] = 0 else: demand = bracket_levels[commodity['demandBracket']] commodity['demand'] = str(commodity['demand'])+demand f.write( "\t\t{} {} {} {} {}\n".format( commodity['name'], commodity['sellPrice'], commodity['buyPrice'], commodity['demand'], commodity['stock'], )) tdenv.ignoreUnknown = True cache.importDataFromFile( tdb, tdenv, pathlib.Path(self.filename), ) # Import EDDN if self.getOption("eddn"): print('Posting prices to EDDN...') con = EDDN( api.profile['commander']['name'], 'EDAPI Trade Dangerous Plugin', __version__ ) con._debug = False con.publishCommodities( system, station, eddn_market ) # We did all the work return False
def run(results, cmdenv, tdb): # If we're using a plugin, initialize that first. if cmdenv.plug: if cmdenv.pluginOptions: cmdenv.pluginOptions = chain.from_iterable( opt.split(',') for opt in cmdenv.pluginOptions ) try: pluginClass = plugins.load(cmdenv.plug, "ImportPlugin") except plugins.PluginException as e: raise CommandLineError("Plugin Error: "+str(e)) # Initialize the plugin plugin = pluginClass(tdb, cmdenv) # Run the plugin. If it returns False, then it did everything # that needs doing and we can stop now. # If it returns True, it is returning control to the module. if not plugin.run(): return None tdb.reloadCache() tdb.close() if cmdenv.filename: if re.match(r"^https?://", cmdenv.filename, re.IGNORECASE): cmdenv.url, cmdenv.filename = cmdenv.filename, None if cmdenv.url: cmdenv.filename = cmdenv.filename or "import.prices" transfers.download(cmdenv, cmdenv.url, cmdenv.filename) if cmdenv.download: return None # If the filename specified was "-" or None, then go ahead # and present the user with an open file dialog. if not cmdenv.filename and hasTkInter: tk = tkinter.Tk() tk.withdraw() filetypes = ( ("TradeDangerous '.prices' Files", "*.prices"), ("All Files", "*.*"), ) filename = tkfd.askopenfilename( title="Select the file to import", initialfile="TradeDangerous.prices", filetypes=filetypes, initialdir='.', ) if not filename: raise SystemExit("Aborted") cmdenv.filename = filename # check the file exists. if cmdenv.filename != "-": fh = None filePath = Path(cmdenv.filename) if not filePath.is_file(): raise CommandLineError("File not found: {}".format( str(filePath) )) else: filePath = "stdin" fh = sys.stdin if cmdenv.plug: if not plugin.finish(): cache.regeneratePricesFile() return None cache.importDataFromFile(tdb, cmdenv, filePath, pricesFh=fh, reset=cmdenv.reset) return None