示例#1
0
        def check(label, fieldName, wantStation):
            key = getattr(self, fieldName, None)
            if not key:
                return None

            try:
                place = self.tdb.lookupPlace(key)
            except LookupError:
                raise CommandLineError("Unrecognized {}: {}".format(
                    label, key))
            if not wantStation:
                if isinstance(place, Station):
                    return place.system
                return place

            if isinstance(place, Station):
                return place

            # it's a system, we want a station
            if not place.stations:
                raise CommandLineError(
                    "Station name required for {}: "
                    "{} is a SYSTEM but has no stations.".format(label, key))
            if len(place.stations) > 1:
                raise AmbiguityError(label,
                                     key,
                                     place.stations,
                                     key=lambda key: key.name())

            return place.stations[0]
示例#2
0
def checkStationDoesNotExist(tdb, cmdenv, system, stationName):
    if not system.stations:
        return

    upperName = stationName.upper()
    similarities = set()
    try:
        station = tdb.lookupStation(stationName, system)
        if station.dbname.upper() == upperName:
            raise CommandLineError("Station \"{}\" "
                                   "in system \"{}\" "
                                   "already exists.".format(
                                       stationName,
                                       system.name(),
                                   ))
        similarities.add(station.dbname.upper())
    except LookupError:
        pass
    except AmbiguityError as e:
        for cand in e.anyMatch:
            similarities.add(e.key(cand).upper())

    # Check to see if there are stations with somewhat
    # similar names, but allow the user to get around
    # cases where difflib matches 'X Port' to 'Y Port'.
    stationNames = [stn.dbname.upper() for stn in system.stations]
    cmdenv.DEBUG0("Comparing {} to {}".format(
        upperName,
        list(stationNames),
    ))
    candidates = difflib.get_close_matches(
        upperName,
        stationNames,
        cutoff=0.6,
    )
    for cand in candidates:
        similarities.add(cand)

    if not similarities:
        return

    confCode = makeConfirmationCode(system.ID, similarities)

    if not cmdenv.confirm:
        raise CommandLineError(
            "\"{}\" contains similar station names:\n"
            "  {}\n"
            "\n"
            "If you want to add this station anyway, re-run the "
            "command and add:\n"
            "  --conf {}".format(system.name(), ', '.join(candidates),
                                 confCode))

    if cmdenv.confirm.upper() != confCode:
        raise CommandLineError("Wrong confirmation code.")

    cmdenv.NOTE("Confirmation code accepted.")
def run(results, cmdenv, tdb):
    station = cmdenv.startStation
    if not isinstance(station, Station):
        raise CommandLineError("{} is a system, not a station".format(
            station.name()))
    if station.shipyard == 'N' and not cmdenv.remove:
        raise CommandLineError("{} is flagged as having no shipyard.".format(
            station.name()))

    if cmdenv.add:
        action = addShipVendor
    elif cmdenv.remove:
        action = removeShipVendor
    else:
        return listShipsPresent(tdb, cmdenv, station, results)

    if not cmdenv.ship:
        raise CommandLineError("No ship names specified.")

    cmdenv.NOTE("Using local database ({})", tdb.dbPath)
    ships = {}
    shipNames = chain.from_iterable(name.split(",") for name in cmdenv.ship)
    for shipName in shipNames:
        try:
            ship = tdb.lookupShip(shipName)
        except LookupError:
            raise CommandLineError("Unrecognized Ship: {}".format(shipName))

        # Lets see if that ship sails from the specified port.
        shipPresent = checkShipPresent(tdb, station, ship)
        if cmdenv.add:
            if shipPresent:
                cmdenv.DEBUG0("{} is already listed at {}", ship.name(),
                              station.name())
            else:
                ships[ship.ID] = ship
        else:
            if not shipPresent:
                cmdenv.DEBUG0("{} is not listed at {}", ship.name(),
                              station.name())
            else:
                ships[ship.ID] = ship

    if len(ships) == 0:
        cmdenv.NOTE("Nothing to do.")
        return None

    # We've checked that everything should be good.
    dataToExport = False
    for ship in ships.values():
        if action(tdb, cmdenv, station, ship):
            dataToExport = True

    maybeExportToCSV(tdb, cmdenv)

    return None
示例#4
0
def run(results, cmdenv, tdb):
    place = cmdenv.origPlace
    if isinstance(place, System):
        system = place
        if len(system.stations) != 1:
            raise CommandLineError("'{}' is a system, not a station.".format(
                system.name()))
        cmdenv.startStation = system.stations[0]
    else:
        cmdenv.startStation = place

    if cmdenv.gui or (not cmdenv.editor and not cmdenv.editing):
        if not cmdenv.quiet:
            print(
                "NOTE:\n"
                ". Press CTRL-C here to abort edit, otherwise "
                "just close the update window to save.\n"
                ". '-F' makes the window show in-front of the "
                "E:D Window.\n"
                ". '-A' forces all items to be listed.\n",
                file=sys.stderr)
        guidedUpdate(tdb, cmdenv)
    else:
        # User specified one of the options to use an editor.
        editUpdate(tdb, cmdenv, cmdenv.startStation.ID)

    return None
示例#5
0
    def __init__(self, properties, argv, cmdModule):
        super().__init__(properties=properties)
        self.tdb = None
        self.mfd = None
        self.argv = argv or sys.argv

        if self.detail and self.quiet:
            raise CommandLineError(
                "'--detail' (-v) and '--quiet' (-q) are mutually exclusive.")

        self._cmd = cmdModule or __main__
        self.wantsTradeDB = getattr(cmdModule, 'wantsTradeDB', True)
        self.usesTradeData = getattr(cmdModule, 'usesTradeData', False)

        # We need to relocate to the working directory so that
        # we can load a TradeDB after this without things going
        # pear-shaped
        if not self.cwd and argv[0]:
            cwdPath = pathlib.Path('.').resolve()
            exePath = pathlib.Path(argv[0]).parent.resolve()
            if cwdPath != exePath:
                self.cwd = str(exePath)
                self.DEBUG1(
                    "cwd at launch was: {}, changing to {} to match trade.py",
                    cwdPath, self.cwd)
        if self.cwd:
            os.chdir(self.cwd)
示例#6
0
def checkSystemAndStation(tdb, cmdenv):
    # In add mode, the user has to be more specific.
    stnName = ' '.join(cmdenv.station).strip()

    if not cmdenv.add:
        try:
            station = tdb.lookupPlace(stnName)
        except LookupError:
            raise CommandLineError("Unrecognized Station: {}".format(
                cmdenv.station))
        if not isinstance(station, Station):
            raise CommandLineError(
                "Expecting a STATION, got {}".format(stnName))
        cmdenv.system = station.system.name()
        cmdenv.station = station.dbname

        return station.system, station

    # Clean up the station name and potentially lift the system
    # name out of it.
    stnName = re.sub(r" +", " ", stnName)
    stnName = re.sub(r"[ /]*/[ /]*", "/", stnName)
    while stnName.startswith('/'):
        stnName = stnName[1:]
    slashPos = stnName.find('/')
    if slashPos > 0:
        sysName, stnName = stnName[:slashPos], stnName[slashPos + 1:]
        sysName = sysName.upper()
    else:
        sysName = None

    if not stnName:
        raise CommandLineError("Invalid station name: {}".format(envStnName))

    if not sysName:
        raise CommandLineError("No system name specified")

    cmdenv.system, cmdenv.station = sysName, TradeDB.titleFixup(stnName)
    try:
        system = tdb.lookupSystem(sysName)
    except LookupError:
        raise CommandLineError("Unknown SYSTEM name: \"{}\"".format(sysName))

    # check the station does not exist
    checkStationDoesNotExist(tdb, cmdenv, system, stnName)

    return system, None
def run(results, cmdenv, tdb):
    from tradedb import TradeDB

    # Check that the file doesn't already exist.
    if not cmdenv.force:
        if tdb.dbPath.exists():
            raise CommandLineError(
                    "SQLite3 database '{}' already exists.\n"
                     "Either remove the file first or use the '-f' option."
                        .format(tdb.dbFilename))

    if not tdb.sqlPath.exists():
        raise CommandLineError(
                    "SQL File does not exist: {}"
                        .format(tdb.sqlFilename))

    from cache import buildCache
    buildCache(tdb, cmdenv)

    return None
示例#8
0
def run(results, cmdenv, tdb):
    if cmdenv.lsFromStar and cmdenv.lsFromStar < 0:
        raise CommandLineError("Invalid (negative) --ls option")

    system, station = checkSystemAndStation(tdb, cmdenv)

    systemName = cmdenv.system
    stationName = cmdenv.station

    if cmdenv.add:
        result = addStation(tdb, cmdenv, system, stationName)
        return checkResultAndExportStations(tdb, cmdenv, result)
    elif cmdenv.update:
        result = updateStation(tdb, cmdenv, station)
        return checkResultAndExportStations(tdb, cmdenv, result)
    elif cmdenv.remove:
        result = removeStation(tdb, cmdenv, station)
        return checkResultAndExportStations(tdb, cmdenv, result)

    # Otherwise, it's just a query
    results.summary = ResultRow()
    results.summary.system = station.system
    results.summary.station = station

    avgSell = results.summary.avgSelling = tdb.getAverageSelling()
    avgBuy = results.summary.avgBuying = tdb.getAverageBuying()

    class ItemTrade(object):
        def __init__(self, ID, price, avgAgainst):
            self.ID, self.item = ID, tdb.itemByID[ID]
            self.price = int(price)
            self.avgTrade = avgAgainst.get(ID, 0)

    # Look up all selling and buying by the station
    selling, buying = [], []
    cur = tdb.query(
        """
        SELECT  item_id, demand_price, supply_price
          FROM  StationItem
         WHERE  station_id = ?
                AND (demand_price > 10 or supply_price > 10)
    """, [station.ID])
    for ID, demand_price, supply_price in cur:
        if demand_price > 10 and avgSell.get(ID, 0) > 10:
            buying.append(ItemTrade(ID, demand_price, avgSell))
        if supply_price > 10 and avgBuy.get(ID, 0) > 10:
            selling.append(ItemTrade(ID, supply_price, avgBuy))
    selling.sort(key=lambda item: item.price - item.avgTrade, )
    results.summary.selling = selling[:5]
    buying.sort(key=lambda item: item.avgTrade - item.price, )
    results.summary.buying = buying[:5]

    return results
示例#9
0
    def checkAvoids(self):
        """
            Process a list of avoidances.
        """

        avoidItems = self.avoidItems = []
        avoidPlaces = self.avoidPlaces = []
        avoidances = self.avoid
        if not self.avoid:
            return
        avoidances = self.avoid

        tdb = self.tdb

        # You can use --avoid to specify an item, system or station.
        # and you can group them together with commas or list them
        # individually.
        for avoid in ','.join(avoidances).split(','):
            # Is it an item?
            item, place = None, None
            try:
                item = tdb.lookupItem(avoid)
                avoidItems.append(item)
                if tdb.normalizedStr(item.name()) == tdb.normalizedStr(avoid):
                    continue
            except LookupError:
                pass
            # Or is it a place?
            try:
                place = tdb.lookupPlace(avoid)
                avoidPlaces.append(place)
                if tdb.normalizedStr(place.name()) == tdb.normalizedStr(avoid):
                    continue
                continue
            except LookupError:
                pass

            # If it was none of the above, whine about it
            if not (item or place):
                raise CommandLineError(
                    "Unknown item/system/station: {}".format(avoid))

            # But if it matched more than once, whine about ambiguity
            if item and place:
                raise AmbiguityError('Avoidance', avoid, [item, place.str()])

        self.DEBUG0(
            "Avoiding items {}, places {}",
            [item.name() for item in avoidItems],
            [place.name() for place in avoidPlaces],
        )
示例#10
0
def render(results, cmdenv, tdb):
    if not results or not results.rows:
        raise CommandLineError("No ships available at {}".format(
            results.summary.station.name()))

    maxShipLen = max_len(results.rows, key=lambda row: row.ship.name())

    rowFmt = RowFormat().append(
        ColumnFormat("Ship", '<', maxShipLen,
                     key=lambda row: row.ship.name())).append(
                         ColumnFormat("Cost",
                                      '>',
                                      12,
                                      'n',
                                      key=lambda row: row.ship.cost))

    if not cmdenv.quiet:
        heading, underline = rowFmt.heading()
        print(heading, underline, sep='\n')

    for row in results.rows:
        print(rowFmt.format(row))
示例#11
0
def getEditorPaths(cmdenv, editorName, envVar, windowsFolders, winExe, nixExe):
    cmdenv.DEBUG0("Locating {} editor", editorName)
    try:
        return os.environ[envVar]
    except KeyError:
        pass

    paths = []

    import platform
    system = platform.system()
    if system == 'Windows':
        binary = winExe
        for folder in ["Program Files", "Program Files (x86)"]:
            for version in windowsFolders:
                paths.append("{}\\{}\\{}".format(os.environ['SystemDrive'],
                                                 folder, version))
    else:
        binary = nixExe

    try:
        paths += os.environ['PATH'].split(os.pathsep)
    except KeyError:
        pass

    for path in paths:
        candidate = os.path.join(path, binary)
        try:
            if pathlib.Path(candidate).exists():
                return candidate
        except OSError:
            pass

    raise CommandLineError(
        "ERROR: Unable to locate {} editor.\n"
        "Either specify the path to your editor with --editor "
        "or set the {} environment variable to point to it.".format(
            editorName, envVar))
示例#12
0
def run(results, cmdenv, tdb):
    # check database exists
    if not tdb.dbPath.is_file():
        raise CommandLineError("Database '{}' not found.".format(tdb.dbPath))

    # check export path exists
    if cmdenv.path:
        # the "--path" overwrites the default path of TD
        exportPath = Path(cmdenv.path)
    else:
        exportPath = Path(cmdenv.dataDir)
    if not exportPath.is_dir():
        raise CommandLineError("Save location '{}' not found.".format(
            str(exportPath)))

    # connect to the database
    cmdenv.NOTE("Using database '{}'", tdb.dbPath)
    conn = tdb.getDB()
    conn.row_factory = sqlite3.Row

    # some tables might be ignored
    ignoreList = []

    # extract tables from command line
    if cmdenv.tables:
        bindValues = cmdenv.tables.split(',')
        tableStmt = " AND name COLLATE NOCASE IN ({})".format(",".join(
            "?" * len(bindValues)))
        cmdenv.DEBUG0(tableStmt)
    else:
        bindValues = []
        tableStmt = ''
        if not cmdenv.allTables:
            ignoreList.append("StationItem")

    tableCursor = conn.cursor()
    for row in tableCursor.execute(
            """
                                      SELECT name
                                        FROM sqlite_master
                                       WHERE type = 'table'
                                         AND name NOT LIKE 'sqlite_%'
                                             {cmdTables}
                                       ORDER BY name
                                   """.format(cmdTables=tableStmt),
            bindValues):
        tableName = row['name']
        if tableName in ignoreList:
            # ignore the table
            cmdenv.NOTE("Ignore Table '{table}'", table=tableName)
            continue

        cmdenv.NOTE("Export Table '{table}'", table=tableName)

        # create CSV files
        lineCount, filePath = exportTableToFile(tdb, cmdenv, tableName,
                                                exportPath)
        if cmdenv.deleteEmpty and lineCount == 0:
            # delete file if emtpy
            filePath.unlink()
            cmdenv.DEBUG0("Delete empty file {file}'".format(file=filePath))

    return None
示例#13
0
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