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)
Exemple #4
0
    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)
Exemple #6
0
    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