def export_outfitting(data):
    # *Do* send empty modules list - implies station has no outfitting
    modules = []
    for v in data["lastStarport"].get("modules", {}).itervalues():
        try:
            module = outfitting.lookup(v)
            if module:
                modules.append(module)
        except AssertionError as e:
            if __debug__:
                print "Outfitting: %s" % e  # Silently skip unrecognized modules
        except:
            if __debug__:
                raise

    send(
        data["commander"]["name"],
        {
            "$schemaRef": "http://schemas.elite-markets.net/eddn/outfitting/1",
            "message": {
                "systemName": data["lastSystem"]["name"].strip(),
                "stationName": data["lastStarport"]["name"].strip(),
                "modules": modules,
            },
        },
    )
Example #2
0
    def __init__(self, data, eddb):
        self.eddb = eddb
        self.modules = list()
        self.name = data['name']
        self.hullmass = masslist[self.name]
        self.fuel = data['fuel']['main']['level']
        self.maxfuel = data['fuel']['main']['capacity']

        self.rfuel = data['fuel']['reserve']['level']
        self.rmaxfuel = data['fuel']['reserve']['capacity']

        self.cargo = data['cargo']['items']
        self.cargomass = data['cargo']['qty']
        self.cargocap = data['cargo']['capacity']

        for slot in sorted(data['modules']):
            v = data['modules'][slot]
            if 'module' not in v:
                continue
            module = outfitting.lookup(v['module'], companion.ship_map)
            if module == None:
                continue
            module = self.eddb.getmodule(module, self.name)
            if module == None:
                continue
            self.modules.append(module)
Example #3
0
def export_outfitting(data):
    # *Do* send empty modules list - implies station has no outfitting
    schemakeys = [
        'category', 'name', 'mount', 'guidance', 'ship', 'class', 'rating'
    ]
    modules = []
    for v in data['lastStarport'].get('modules', {}).itervalues():
        try:
            module = outfitting.lookup(v, ship_map)
            if module:
                modules.append(
                    {k: module[k]
                     for k in schemakeys
                     if k in module})  # just the relevant keys
        except AssertionError as e:
            if __debug__:
                print 'Outfitting: %s' % e  # Silently skip unrecognized modules
        except:
            if __debug__: raise

    send(
        data['commander']['name'], {
            '$schemaRef': 'http://schemas.elite-markets.net/eddn/outfitting/1',
            'message': {
                'systemName': data['lastSystem']['name'].strip(),
                'stationName': data['lastStarport']['name'].strip(),
                'modules': modules,
            }
        })
Example #4
0
def cmdr_data(data, is_beta):

    system = data['lastSystem']['name']

    if not this.system['text']:
        this.system['text'] = system
        this.system['image'] = ''
        if not system or system in FAKE:
            this.system['url'] = None
            this.lastlookup = True
        else:
            this.system[
                'url'] = 'https://www.edsm.net/show-system?systemName=%s' % urllib2.quote(
                    system)
            this.lastlookup = False
        this.system.update_idletasks()

    if config.getint(
            'edsm_out') and not is_beta and not this.multicrew and credentials(
                data['commander']['name']):
        # Send flightlog to EDSM if FSDJump failed to do so
        if not this.lastlookup:
            try:
                this.writelog(data['commander']['name'], int(time.time()),
                              system, None, data['ship']['id'])
            except Exception as e:
                if __debug__: print_exc()
                return unicode(e)

        # Update credits and ship info and send to EDSM
        try:
            if data['commander'].get('credits') is not None:
                setcredits(data['commander']['name'],
                           data['commander']['credits'],
                           data['commander'].get('debt', 0))
            ship = companion.ship(data)
            if ship != this.lastship:
                cargo = 0
                fuel = 0
                for v in data['ship']['modules'].itervalues():
                    module = outfitting.lookup(v['module'], companion.ship_map)
                    if not module:
                        pass
                    elif 'Fuel Tank' in module['name']:
                        fuel += 2**int(module['class'])
                    elif 'Cargo Rack' in module['name']:
                        cargo += 2**int(module['class'])

                updateship(
                    data['commander']['name'], data['ship']['id'],
                    data['ship']['name'].lower(), {
                        'cargoCapacity': cargo,
                        'fuelMainCapacity': fuel,
                        'linkToCoriolis': coriolis.url(data, is_beta),
                        'linkToEDShipyard': edshipyard.url(data, is_beta),
                    })
                this.lastship = ship
        except Exception as e:
            # Not particularly important so silent on failure
            if __debug__: print_exc()
Example #5
0
def addmodules(data):

    if not data['lastStarport'].get('modules'): return

    outfile = 'outfitting.csv'
    modules = {}
    fields = [
        'id', 'symbol', 'category', 'name', 'mount', 'guidance', 'ship',
        'class', 'rating', 'entitlement'
    ]

    # slurp existing
    if isfile(outfile):
        with open(outfile) as csvfile:
            reader = csv.DictReader(csvfile, restval='')
            for row in reader:
                modules[int(
                    row['id']
                )] = row  # index by int for easier lookup and sorting
    size_pre = len(modules)

    for key, module in data['lastStarport'].get('modules').iteritems():
        # sanity check
        if int(key) != module.get('id'):
            raise AssertionError('id: %s!=%s' % (key, module['id']))
        try:
            new = outfitting.lookup(module, companion.ship_map, True)
        except:
            print '%d, %s:' % (module['id'], module['name'])
            print_exc(0)
            new = None
        if new:
            old = modules.get(int(key))
            if old:
                # check consistency with existing data
                for thing in fields:
                    if not old.get(thing) and new.get(thing):
                        size_pre -= 1
                    elif str(new.get(thing, '')) != old.get(thing):
                        raise AssertionError(
                            '%s: %s "%s"!="%s"' %
                            (key, thing, new.get(thing), old.get(thing)))
            modules[int(key)] = new

    if len(modules) > size_pre:

        if isfile(outfile):
            if isfile(outfile + '.bak'):
                os.unlink(outfile + '.bak')
            os.rename(outfile, outfile + '.bak')

        with open(outfile, 'wb') as csvfile:
            writer = csv.DictWriter(csvfile, fields, extrasaction='ignore')
            writer.writeheader()
            for key in sorted(modules):
                writer.writerow(modules[key])

        print 'Added %d new modules' % (len(modules) - size_pre)
Example #6
0
def addmodules(data):
    if not data['lastStarport'].get('modules'):
        return

    outfile = 'outfitting.csv'
    modules = {}
    fields = ('id', 'symbol', 'category', 'name', 'mount', 'guidance', 'ship', 'class', 'rating', 'entitlement')

    # slurp existing
    if isfile(outfile):
        with open(outfile) as csvfile:
            reader = csv.DictReader(csvfile, restval='')
            for row in reader:
                modules[int(row['id'])] = row  # index by int for easier lookup and sorting

    size_pre = len(modules)

    for key, module in data['lastStarport'].get('modules').items():
        # sanity check
        key = int(key)
        if key != module.get('id'):
            raise ValueError('id: {} != {}'.format(key, module['id']))

        try:
            new = outfitting.lookup(module, companion.ship_map, True)

        except Exception:
            print('{}, {}:'.format(module['id'], module['name']))
            print_exc(0)
            new = None

        if new:
            old = modules.get(key)
            if old:
                # check consistency with existing data
                for thing in fields:
                    if not old.get(thing) and new.get(thing):
                        size_pre -= 1

                    elif str(new.get(thing, '')) != old.get(thing):
                        raise ValueError('{}: {} {!r}!={!r}'.format(key, thing, new.get(thing), old.get(thing)))

            modules[key] = new

    if not len(modules) > size_pre:
        return

    if isfile(outfile):
        __make_backup(outfile)

    with open(outfile, 'w') as csvfile:
        writer = csv.DictWriter(csvfile, fields, extrasaction='ignore')
        writer.writeheader()

        for key in sorted(modules):
            writer.writerow(modules[key])

    print('Added {} new modules'.format(len(modules) - size_pre))
Example #7
0
def addmodules(data):

    if not data['lastStarport'].get('modules'): return

    outfile = 'outfitting.csv'
    modules = {}
    fields = ['id', 'symbol', 'category', 'name', 'mount', 'guidance', 'ship', 'class', 'rating', 'entitlement']

    # slurp existing
    if isfile(outfile):
        with open(outfile) as csvfile:
            reader = csv.DictReader(csvfile, restval='')
            for row in reader:
                modules[int(row['id'])] = row	# index by int for easier lookup and sorting
    size_pre = len(modules)

    for key,module in data['lastStarport'].get('modules').iteritems():
        # sanity check
        if int(key) != module.get('id'): raise AssertionError('id: %s!=%s' % (key, module['id']))
        try:
            new = outfitting.lookup(module, companion.ship_map, True)
        except:
            print '%d, %s:' % (module['id'], module['name'])
            print_exc(0)
            new = None
        if new:
            old = modules.get(int(key))
            if old:
                # check consistency with existing data
                for thing in fields:
                    if not old.get(thing) and new.get(thing):
                        size_pre -= 1
                    elif str(new.get(thing,'')) != old.get(thing):
                        raise AssertionError('%s: %s "%s"!="%s"' % (key, thing, new.get(thing), old.get(thing)))
            modules[int(key)] = new

    if len(modules) > size_pre:

        if isfile(outfile):
            if isfile(outfile+'.bak'):
                os.unlink(outfile+'.bak')
            os.rename(outfile, outfile+'.bak')

        with open(outfile, 'wb') as csvfile:
            writer = csv.DictWriter(csvfile, fields, extrasaction='ignore')
            writer.writeheader()
            for key in sorted(modules):
                writer.writerow(modules[key])

        print 'Added %d new modules' % (len(modules) - size_pre)
Example #8
0
def addmodules(data):

    if not data['lastStarport'].get('modules'): return

    outfile = 'outfitting.csv'
    modules = {}

    # slurp existing
    if isfile(outfile):
        with open(outfile) as csvfile:
            reader = csv.DictReader(csvfile)
            for row in reader:
                key = int(row.pop('id'))	# index by int for easier lookup and sorting
                modules[key] = row
    size_pre = len(modules)

    for key,module in data['lastStarport'].get('modules').iteritems():
        # sanity check
        if int(key) != module.get('id'): raise AssertionError('id: %s!=%s' % (key, module['id']))
        new = outfitting.lookup(module, ship_map)
        if new:
            old = modules.get(int(key))
            if old:
                # check consistency with existing data
                for thing in ['category', 'name', 'mount', 'guidance', 'ship', 'class', 'rating']:
                    if new.get(thing,'') != old.get(thing): raise AssertionError('%s: %s "%s"!="%s"' % (key, thing, new.get(thing), old.get(thing)))
            else:
                modules[int(key)] = new

    if len(modules) > size_pre:

        if isfile(outfile):
            if isfile(outfile+'.bak'):
                os.unlink(outfile+'.bak')
            os.rename(outfile, outfile+'.bak')

        with open(outfile, 'wb') as csvfile:
            writer = csv.DictWriter(csvfile, ['id', 'category', 'name', 'mount', 'guidance', 'ship', 'class', 'rating'])
            writer.writeheader()
            for key in sorted(modules):
                row = modules[key]
                row['id'] = key
                writer.writerow(row)

        print 'Added %d new modules' % (len(modules) - size_pre)
def export_outfitting(data):
    # *Do* send empty modules list - implies station has no outfitting
    modules = []
    for v in data['lastStarport'].get('modules', {}).itervalues():
        try:
            module = outfitting.lookup(v)
            if module:
                modules.append(module)
        except AssertionError as e:
            if __debug__: print 'Outfitting: %s' % e	# Silently skip unrecognized modules
        except:
            if __debug__: raise

    send(data['commander']['name'], {
        '$schemaRef' : 'http://schemas.elite-markets.net/eddn/outfitting/1',
        'message'    : {
            'systemName'  : data['lastSystem']['name'].strip(),
            'stationName' : data['lastStarport']['name'].strip(),
            'modules'     : modules,
        }
    })
Example #10
0
def export_outfitting(data):
    # *Do* send empty modules list - implies station has no outfitting
    modules = []
    for v in data['lastStarport'].get('modules', {}).itervalues():
        try:
            module = outfitting.lookup(v, ship_map)
            if module:
                modules.append(module)
        except AssertionError as e:
            if __debug__: print 'Outfitting: %s' % e	# Silently skip unrecognized modules
        except:
            if __debug__: raise

    send(data['commander']['name'], {
        '$schemaRef' : 'http://schemas.elite-markets.net/eddn/outfitting/1',
        'message'    : {
            'systemName'  : data['lastSystem']['name'].strip(),
            'stationName' : data['lastStarport']['name'].strip(),
            'modules'     : modules,
        }
    })
Example #11
0
def export_outfitting(data):
    # *Do* send empty modules list - implies station has no outfitting
    schemakeys = ['category', 'name', 'mount', 'guidance', 'ship', 'class', 'rating']
    modules = []
    for v in data['lastStarport'].get('modules', {}).itervalues():
        try:
            module = outfitting.lookup(v, ship_map)
            if module:
                modules.append({ k: module[k] for k in schemakeys if k in module })	# just the relevant keys
        except AssertionError as e:
            if __debug__: print 'Outfitting: %s' % e	# Silently skip unrecognized modules
        except:
            if __debug__: raise

    send(data['commander']['name'], {
        '$schemaRef' : 'http://schemas.elite-markets.net/eddn/outfitting/1',
        'message'    : {
            'systemName'  : data['lastSystem']['name'].strip(),
            'stationName' : data['lastStarport']['name'].strip(),
            'modules'     : modules,
        }
    })
Example #12
0
    add(modules, 'hpt_mining_subsurfdispmisle_turret_small', {'mass': 2})
    add(modules, 'hpt_mining_subsurfdispmisle_fixed_medium', {'mass': 4})
    add(modules, 'hpt_mining_subsurfdispmisle_turret_medium', {'mass': 4})
    add(modules, 'hpt_mining_abrblstr_fixed_small', {'mass': 2})
    add(modules, 'hpt_mining_abrblstr_turret_small', {'mass': 2})
    add(modules, 'hpt_mining_seismchrgwarhd_fixed_medium', {'mass': 4})
    add(modules, 'hpt_mining_seismchrgwarhd_turret_medium', {'mass': 4})
    add(modules, 'hpt_mrascanner_size0_class1', {'mass': 1.3})
    add(modules, 'hpt_mrascanner_size0_class2', {'mass': 1.3})
    add(modules, 'hpt_mrascanner_size0_class3', {'mass': 1.3})
    add(modules, 'hpt_mrascanner_size0_class4', {'mass': 1.3})
    add(modules, 'hpt_mrascanner_size0_class5', {'mass': 1.3})

    modules = OrderedDict([(k, modules[k]) for k in sorted(modules)
                           ])  # sort for easier diffing
    cPickle.dump(modules, open('modules.p', 'wb'))

    # Check data is present for all modules
    with open('outfitting.csv') as csvfile:
        reader = csv.DictReader(csvfile, restval='')
        for row in reader:
            try:
                module = outfitting.lookup(
                    {
                        'id': row['id'],
                        'name': row['symbol']
                    }, companion.ship_map)
            except:
                print row['symbol']
                print_exc()
Example #13
0
def export(data, filename=None):

    querytime = config.getint('querytime') or int(time.time())

    loadout = OrderedDict([	# Mimic Coriolis export ordering
        ('$schema',    'http://cdn.coriolis.io/schemas/ship-loadout/2.json#'),
        ('name',       ship_map.get(data['ship']['name'].lower(), data['ship']['name'])),
        ('ship',       ship_map.get(data['ship']['name'].lower(), data['ship']['name'])),
        ('components', OrderedDict([
            ('standard',   OrderedDict([(x,None) for x in standard_map.values()])),
            ('hardpoints', []),
            ('utility',    []),
            ('internal',   []),
        ])),
    ])
    maxpri = 0
    mass = 0.0
    fsd = None

    # Correct module ordering relies on the fact that "Slots" in the data  are correctly ordered alphabetically.
    # Correct hardpoint ordering additionally relies on the fact that "Huge" < "Large" < "Medium" < "Small"
    for slot in sorted(data['ship']['modules']):

        v = data['ship']['modules'][slot]
        try:
            for s in slot_map:
                if slot.lower().startswith(s):
                    category = slot_map[s]
                    break
            else:
                # Uninteresting slot - e.g. DecalX or PaintJob
                if __debug__ and not slot.lower().startswith('bobble') and not slot.lower().startswith('decal') and not slot.lower().startswith('paintjob') and not slot.lower().startswith('planetaryapproachsuite'):
                    print 'Coriolis: Unknown slot %s' % slot
                continue

            if not v:
                # Need to add nulls for empty slots. Assumes that standard slots can't be empty.
                loadout['components'][category].append(None)
                continue

            module = outfitting.lookup(v['module'], ship_map)
            if not module:
                raise AssertionError('Unknown module %s' % v)	# Shouldn't happen
            mass += module.get('mass', 0)

            thing = OrderedDict([
                ('class',int(module['class'])),
                ('rating',   module['rating']),
                ('enabled',  module['enabled']),
                ('priority', module['priority']+1),	# make 1-based
            ])
            maxpri = max(maxpri, thing['priority'])

            if category == 'standard':
                # Standard items are indexed by "group" rather than containing a "group" member
                if module['name'] in bulkheads:
                    loadout['components'][category]['bulkheads'] = module['name']	# Bulkheads are just strings
                else:
                    loadout['components'][category][standard_map[module['name']]] = thing
                    if module['name'] == 'Frame Shift Drive':
                        fsd = module	# save for range calculation
            else:
                # All other items have a "group" member, some also have a "name"
                if module['name'] in fixup_map:
                    thing['group'], name = fixup_map[module['name']]
                    if name: thing['name'] = name
                else:
                    thing['group'] = module['name']

                if 'mount' in module:
                    thing['mount'] = weaponmount_map[module['mount']]
                if 'guidance' in module:
                    thing['missile'] = module['guidance'][0]	# not mentioned in schema

                loadout['components'][category].append(thing)

        except AssertionError as e:
            # Silently skip unrecognized modules
            if __debug__: print 'Coriolis: %s' % e
            if category != 'standard':
                loadout['components'][category].append(None)
        except:
            if __debug__: raise

    # Cargo Hatch status is not available in the data - fake something up
    loadout['components']['standard']['cargoHatch'] = OrderedDict([
        ('enabled',  True),
        ('priority', maxpri),
    ])

    # Add mass and range
    assert data['ship']['name'].lower() in companion.ship_map, data['ship']['name']
    assert companion.ship_map[data['ship']['name'].lower()] in ships, companion.ship_map[data['ship']['name'].lower()]
    try:
        # https://github.com/cmmcleod/coriolis/blob/master/app/js/shipyard/module-shipyard.js#L184
        hullMass = ships[companion.ship_map[data['ship']['name'].lower()]]['hullMass']
        mass += hullMass
        multiplier = pow(min(data['ship']['fuel']['main']['capacity'], fsd['maxfuel']) / fsd['fuelmul'], 1.0 / fsd['fuelpower']) * fsd['optmass']

        loadout['stats'] = OrderedDict([
            ('hullMass',      hullMass),
            ('fuelCapacity',  data['ship']['fuel']['main']['capacity']),
            ('cargoCapacity', data['ship']['cargo']['capacity']),
            ('ladenMass',     mass + data['ship']['fuel']['main']['capacity'] + data['ship']['cargo']['capacity']),
            ('unladenMass',   mass),
            ('unladenRange',  round(multiplier / (mass + min(data['ship']['fuel']['main']['capacity'], fsd['maxfuel'])), 2)),	# fuel for one jump
            ('fullTankRange', round(multiplier / (mass + data['ship']['fuel']['main']['capacity']), 2)),
            ('ladenRange',    round(multiplier / (mass + data['ship']['fuel']['main']['capacity'] + data['ship']['cargo']['capacity']), 2)),
        ])
    except:
        if __debug__: raise

    # Construct description
    string = json.dumps(loadout, indent=2, separators=(',', ': '))

    if filename:
        with open(filename, 'wt') as h:
            h.write(string)
        return

    # Look for last ship of this type
    ship = companion.ship_map.get(data['ship']['name'].lower(), data['ship']['name'])	# Use in-game name
    regexp = re.compile(re.escape(ship) + '\.\d\d\d\d\-\d\d\-\d\dT\d\d\.\d\d\.\d\d\.json')
    oldfiles = sorted([x for x in os.listdir(config.get('outdir')) if regexp.match(x)])
    if oldfiles:
        with open(join(config.get('outdir'), oldfiles[-1]), 'rU') as h:
            if h.read() == string:
                return	# same as last time - don't write

    # Write
    filename = join(config.get('outdir'), '%s.%s.json' % (ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime))))
    with open(filename, 'wt') as h:
        h.write(string)
Example #14
0
def export(data, filename=None):

    querytime = config.getint('querytime') or int(time.time())

    loadout = OrderedDict([  # Mimic Coriolis export ordering
        ('$schema', 'http://cdn.coriolis.io/schemas/ship-loadout/2.json#'),
        ('name',
         ship_map.get(data['ship']['name'].lower(), data['ship']['name'])),
        ('ship',
         ship_map.get(data['ship']['name'].lower(), data['ship']['name'])),
        ('components',
         OrderedDict([
             ('standard',
              OrderedDict([(x, None) for x in standard_map.values()])),
             ('hardpoints', []),
             ('utility', []),
             ('internal', []),
         ])),
    ])
    maxpri = 0
    mass = 0.0
    fsd = None

    # Correct module ordering relies on the fact that "Slots" in the data  are correctly ordered alphabetically.
    # Correct hardpoint ordering additionally relies on the fact that "Huge" < "Large" < "Medium" < "Small"
    for slot in sorted(data['ship']['modules']):

        v = data['ship']['modules'][slot]
        try:
            for s in slot_map:
                if slot.lower().startswith(s):
                    category = slot_map[s]
                    break
            else:
                # Uninteresting slot - e.g. DecalX or PaintJob
                if __debug__ and not slot.lower().startswith(
                        'bobble') and not slot.lower().startswith(
                            'decal') and not slot.lower().startswith(
                                'paintjob') and not slot.lower().startswith(
                                    'planetaryapproachsuite'):
                    print 'Coriolis: Unknown slot %s' % slot
                continue

            if not v:
                # Need to add nulls for empty slots. Assumes that standard slots can't be empty.
                loadout['components'][category].append(None)
                continue

            module = outfitting.lookup(v['module'], ship_map)
            if not module:
                raise AssertionError('Unknown module %s' %
                                     v)  # Shouldn't happen
            mass += module.get('mass', 0)

            thing = OrderedDict([
                ('class', int(module['class'])),
                ('rating', module['rating']),
                ('enabled', module['enabled']),
                ('priority', module['priority'] + 1),  # make 1-based
            ])
            maxpri = max(maxpri, thing['priority'])

            if category == 'standard':
                # Standard items are indexed by "group" rather than containing a "group" member
                if module['name'] in bulkheads:
                    loadout['components'][category]['bulkheads'] = module[
                        'name']  # Bulkheads are just strings
                else:
                    loadout['components'][category][standard_map[
                        module['name']]] = thing
                    if module['name'] == 'Frame Shift Drive':
                        fsd = module  # save for range calculation
            else:
                # All other items have a "group" member, some also have a "name"
                if module['name'] in fixup_map:
                    thing['group'], name = fixup_map[module['name']]
                    if name: thing['name'] = name
                elif module['name'] in scanners:
                    thing['group'] = 'Scanner'
                    thing['name'] = module['name']
                elif module['name'] in countermeasures:
                    thing['group'] = 'Countermeasure'
                    thing['name'] = module['name']
                else:
                    thing['group'] = module['name']

                if 'mount' in module:
                    thing['mount'] = weaponmount_map[module['mount']]
                if 'guidance' in module:
                    thing['missile'] = module['guidance'][
                        0]  # not mentioned in schema

                loadout['components'][category].append(thing)

        except AssertionError as e:
            # Silently skip unrecognized modules
            if __debug__: print 'Coriolis: %s' % e
            if category != 'standard':
                loadout['components'][category].append(None)
        except:
            if __debug__: raise

    # Cargo Hatch status is not available in the data - fake something up
    loadout['components']['standard']['cargoHatch'] = OrderedDict([
        ('enabled', True),
        ('priority', maxpri),
    ])

    # Add mass and range
    assert data['ship']['name'].lower(
    ) in companion.ship_map, data['ship']['name']
    assert companion.ship_map[data['ship']['name'].lower(
    )] in ships, companion.ship_map[data['ship']['name'].lower()]
    try:
        # https://github.com/cmmcleod/coriolis/blob/master/app/js/shipyard/module-shipyard.js#L184
        hullMass = ships[companion.ship_map[data['ship']
                                            ['name'].lower()]]['hullMass']
        mass += hullMass
        multiplier = pow(
            min(data['ship']['fuel']['main']['capacity'], fsd['maxfuel']) /
            fsd['fuelmul'], 1.0 / fsd['fuelpower']) * fsd['optmass']

        loadout['stats'] = OrderedDict([
            ('hullMass', hullMass),
            ('fuelCapacity', data['ship']['fuel']['main']['capacity']),
            ('cargoCapacity', data['ship']['cargo']['capacity']),
            ('ladenMass', mass + data['ship']['fuel']['main']['capacity'] +
             data['ship']['cargo']['capacity']),
            ('unladenMass', mass),
            ('unladenRange',
             round(
                 multiplier /
                 (mass + min(data['ship']['fuel']['main']['capacity'],
                             fsd['maxfuel'])), 2)),  # fuel for one jump
            ('fullTankRange',
             round(
                 multiplier /
                 (mass + data['ship']['fuel']['main']['capacity']), 2)),
            ('ladenRange',
             round(
                 multiplier /
                 (mass + data['ship']['fuel']['main']['capacity'] +
                  data['ship']['cargo']['capacity']), 2)),
        ])
    except:
        if __debug__: raise

    # Construct description
    string = json.dumps(loadout, indent=2, separators=(',', ': '))

    if filename:
        with open(filename, 'wt') as h:
            h.write(string)
        return

    # Look for last ship of this type
    ship = companion.ship_map.get(data['ship']['name'].lower(),
                                  data['ship']['name'])  # Use in-game name
    regexp = re.compile(
        re.escape(ship) + '\.\d\d\d\d\-\d\d\-\d\dT\d\d\.\d\d\.\d\d\.json')
    oldfiles = sorted(
        [x for x in os.listdir(config.get('outdir')) if regexp.match(x)])
    if oldfiles:
        with open(join(config.get('outdir'), oldfiles[-1]), 'rU') as h:
            if h.read() == string:
                return  # same as last time - don't write

    # Write
    filename = join(
        config.get('outdir'), '%s.%s.json' %
        (ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime))))
    with open(filename, 'wt') as h:
        h.write(string)
Example #15
0
def export(data):

    def class_rating(module):
        if 'guidance' in module:
            return module['class'] + module['rating'] + '/' + module.get('mount', 'F')[0] + module['guidance'][0] + ' '
        elif 'mount' in module:
            return module['class'] + module['rating'] + '/' + module['mount'][0] + ' '
        else:
            return module['class'] + module['rating'] + ' '

    querytime = config.getint('querytime') or int(time.time())

    ship = ship_map.get(data['ship']['name'], data['ship']['name'])

    loadout = defaultdict(list)

    for slot in sorted(data['ship']['modules']):

        v = data['ship']['modules'][slot]
        if not v or not v.get('module'):
            continue
        try:
            module = outfitting.lookup(v['module'])
            if not module: continue
        except AssertionError as e:
            if __debug__: print 'Loadout: %s' % e
            continue	# Silently skip unrecognized modules
        except:
            if __debug__: raise

        cr = class_rating(module)

        # Specials
        if module['name'] in ['Fuel Tank', 'Cargo Rack']:
            name = '%s (Capacity: %d)' % (module['name'], 2**int(module['class']))
        else:
            name = module['name']

        for s in slot_map:
            if slot.startswith(s):
                loadout[slot_map[s]].append(cr + name)
                break
        else:
            if slot.startswith('Slot'):
                loadout[slot[-1]].append(cr + name)
            elif __debug__: print 'Loadout: Unknown slot %s' % slot

    # Construct description
    string = '[%s]\n' % ship
    for slot in ['L', 'M', 'S', 'U', None, 'BH', 'RB', 'TM', 'FH', 'EC', 'PC', 'SS', 'FS', None, '9', '8', '7', '6', '5', '4', '3', '2', '1']:
        if not slot:
            string += '\n'
        elif slot in loadout:
            for name in loadout[slot]:
                string += '%s: %s\n' % (slot, name)
    string += '---\nCargo : %d T\nFuel  : %d T\n' % (data['ship']['cargo']['capacity'], data['ship']['fuel']['capacity'])

    # Look for last ship of this type
    regexp = re.compile(re.escape(ship) + '\.\d\d\d\d\-\d\d\-\d\dT\d\d\.\d\d\.\d\d\.txt')
    oldfiles = sorted([x for x in os.listdir(config.get('outdir')) if regexp.match(x)])
    if oldfiles:
        with open(join(config.get('outdir'), oldfiles[-1]), 'rU') as h:
            if h.read() == string:
                return	# same as last time - don't write

    # Write
    filename = join(config.get('outdir'), '%s.%s.txt' % (ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime))))
    with open(filename, 'wt') as h:
        h.write(string)
Example #16
0
def export(data, filename=None):
    def class_rating(module):
        if 'guidance' in module:
            return module['class'] + module['rating'] + '/' + module.get(
                'mount', 'F')[0] + module['guidance'][0] + ' '
        elif 'mount' in module:
            return module['class'] + module['rating'] + '/' + module['mount'][
                0] + ' '
        else:
            return module['class'] + module['rating'] + ' '

    querytime = config.getint('querytime') or int(time.time())

    loadout = defaultdict(list)
    mass = 0.0
    fsd = None

    for slot in sorted(data['ship']['modules']):

        v = data['ship']['modules'][slot]
        try:
            if not v: continue

            module = outfitting.lookup(v['module'], ship_map)
            if not module: continue

            cr = class_rating(module)
            mass += module.get('mass', 0)

            # Specials
            if module['name'] in ['Fuel Tank', 'Cargo Rack']:
                name = '%s (Capacity: %d)' % (module['name'], 2**int(
                    module['class']))
            else:
                name = module['name']

            if name == 'Frame Shift Drive':
                fsd = module  # save for range calculation

            for s in slot_map:
                if slot.lower().startswith(s):
                    loadout[slot_map[s]].append(cr + name)
                    break
            else:
                if slot.lower().startswith('slot'):
                    loadout[slot[-1]].append(cr + name)
                elif __debug__ and not slot.lower().startswith(
                        'planetaryapproachsuite'):
                    print 'EDShipyard: Unknown slot %s' % slot

        except AssertionError as e:
            if __debug__: print 'EDShipyard: %s' % e
            continue  # Silently skip unrecognized modules
        except:
            if __debug__: raise

    # Construct description
    string = '[%s]\n' % ship_map.get(data['ship']['name'].lower(),
                                     data['ship']['name'])
    for slot in [
            'H', 'L', 'M', 'S', 'U', None, 'BH', 'RB', 'TM', 'FH', 'EC', 'PC',
            'SS', 'FS', None, '9', '8', '7', '6', '5', '4', '3', '2', '1'
    ]:
        if not slot:
            string += '\n'
        elif slot in loadout:
            for name in loadout[slot]:
                string += '%s: %s\n' % (slot, name)
    string += '---\nCargo : %d T\nFuel  : %d T\n' % (
        data['ship']['cargo']['capacity'],
        data['ship']['fuel']['main']['capacity'])

    # Add mass and range
    assert data['ship']['name'].lower(
    ) in companion.ship_map, data['ship']['name']
    assert companion.ship_map[data['ship']['name'].lower(
    )] in ships, companion.ship_map[data['ship']['name'].lower()]
    try:
        # https://github.com/cmmcleod/coriolis/blob/master/app/js/shipyard/module-shipyard.js#L184
        mass += ships[companion.ship_map[data['ship']
                                         ['name'].lower()]]['hullMass']
        string += 'Mass  : %.1f T empty\n        %.1f T full\n' % (
            mass, mass + data['ship']['fuel']['main']['capacity'] +
            data['ship']['cargo']['capacity'])
        multiplier = pow(
            min(data['ship']['fuel']['main']['capacity'], fsd['maxfuel']) /
            fsd['fuelmul'], 1.0 / fsd['fuelpower']) * fsd['optmass']
        string += 'Range : %.2f LY unladen\n        %.2f LY laden\n' % (
            multiplier /
            (mass + data['ship']['fuel']['main']['capacity']), multiplier /
            (mass + data['ship']['fuel']['main']['capacity'] +
             data['ship']['cargo']['capacity']))
    except:
        if __debug__: raise

    if filename:
        with open(filename, 'wt') as h:
            h.write(string)
        return

    # Look for last ship of this type
    ship = companion.ship_map.get(data['ship']['name'].lower(),
                                  data['ship']['name'])  # Use in-game name
    regexp = re.compile(
        re.escape(ship) + '\.\d\d\d\d\-\d\d\-\d\dT\d\d\.\d\d\.\d\d\.txt')
    oldfiles = sorted(
        [x for x in os.listdir(config.get('outdir')) if regexp.match(x)])
    if oldfiles:
        with open(join(config.get('outdir'), oldfiles[-1]), 'rU') as h:
            if h.read() == string:
                return  # same as last time - don't write

    # Write
    filename = join(
        config.get('outdir'), '%s.%s.txt' %
        (ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime))))
    with open(filename, 'wt') as h:
        h.write(string)
def export(data):

    querytime = config.getint('querytime') or int(time.time())

    ship = companion.ship_map.get(data['ship']['name'].lower(),
                                  data['ship']['name'])

    loadout = OrderedDict([  # Mimic Coriolis export ordering
        ('$schema', 'http://cdn.coriolis.io/schemas/ship-loadout/2.json#'),
        ('name',
         ship_map.get(data['ship']['name'].lower(), data['ship']['name'])),
        ('ship',
         ship_map.get(data['ship']['name'].lower(), data['ship']['name'])),
        ('components',
         OrderedDict([
             ('standard',
              OrderedDict([(x, None) for x in standard_map.values()])),
             ('hardpoints', []),
             ('utility', []),
             ('internal', []),
         ])),
    ])
    maxpri = 0

    # Correct module ordering relies on the fact that "Slots" in the data  are correctly ordered alphabetically.
    # Correct hardpoint ordering additionally relies on the fact that "Huge" < "Large" < "Medium" < "Small"
    for slot in sorted(data['ship']['modules']):

        v = data['ship']['modules'][slot]
        try:
            if not v:
                # Need to add nulls for empty slots. Assumes that standard slots can't be empty.
                for s in slot_map:
                    if slot.lower().startswith(s):
                        loadout['components'][slot_map[s]].append(None)
                        break
                continue

            module = outfitting.lookup(v['module'])
            if not module: continue

            category = loadout['components'][category_map[module['category']]]
            thing = OrderedDict([
                ('class', int(module['class'])),
                ('rating', module['rating']),
                ('enabled', module['enabled']),
                ('priority', module['priority'] + 1),  # make 1-based
            ])
            maxpri = max(maxpri, thing['priority'])

            if module['name'] in bulkheads:
                # Bulkheads are just strings
                category['bulkheads'] = module['name']
            elif module['category'] == 'standard':
                # Standard items are indexed by "group" rather than containing a "group" member
                category[standard_map[module['name']]] = thing
            else:
                # All other items have a "group" member, some also have a "name"
                if module['name'] in scanners:
                    thing['group'] = 'Scanner'
                    thing['name'] = module['name']
                elif module['name'] in countermeasures:
                    thing['group'] = 'Countermeasure'
                    thing['name'] = module['name']
                elif module['name'] in fixup_map:
                    thing['group'], name = fixup_map[module['name']]
                    if name: thing['name'] = name
                else:
                    thing['group'] = module['name']

                if 'mount' in module:
                    thing['mount'] = weaponmount_map[module['mount']]
                if 'guidance' in module:
                    thing['missile'] = module['guidance'][
                        0]  # not mentioned in schema

                category.append(thing)

        except AssertionError as e:
            if __debug__: print 'Loadout: %s' % e
            continue  # Silently skip unrecognized modules
        except:
            if __debug__: raise

    # Cargo Hatch status is not available in the data - fake something up
    loadout['components']['standard']['cargoHatch'] = OrderedDict([
        ('enabled', True),
        ('priority', maxpri),
    ])

    # Construct description
    string = json.dumps(loadout, indent=2)

    # Look for last ship of this type
    regexp = re.compile(
        re.escape(ship) + '\.\d\d\d\d\-\d\d\-\d\dT\d\d\.\d\d\.\d\d\.json')
    oldfiles = sorted(
        [x for x in os.listdir(config.get('outdir')) if regexp.match(x)])
    if oldfiles:
        with open(join(config.get('outdir'), oldfiles[-1]), 'rU') as h:
            if h.read() == string:
                return  # same as last time - don't write

    # Write
    filename = join(
        config.get('outdir'), '%s.%s.json' %
        (ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime))))
    with open(filename, 'wt') as h:
        h.write(string)
def export(data):

    querytime = config.getint("querytime") or int(time.time())

    ship = companion.ship_map.get(data["ship"]["name"].lower(), data["ship"]["name"])

    loadout = OrderedDict(
        [  # Mimic Coriolis export ordering
            ("$schema", "http://cdn.coriolis.io/schemas/ship-loadout/2.json#"),
            ("name", ship_map.get(data["ship"]["name"].lower(), data["ship"]["name"])),
            ("ship", ship_map.get(data["ship"]["name"].lower(), data["ship"]["name"])),
            (
                "components",
                OrderedDict(
                    [
                        ("standard", OrderedDict([(x, None) for x in standard_map.values()])),
                        ("hardpoints", []),
                        ("utility", []),
                        ("internal", []),
                    ]
                ),
            ),
        ]
    )
    maxpri = 0

    # Correct module ordering relies on the fact that "Slots" in the data  are correctly ordered alphabetically.
    # Correct hardpoint ordering additionally relies on the fact that "Huge" < "Large" < "Medium" < "Small"
    for slot in sorted(data["ship"]["modules"]):

        v = data["ship"]["modules"][slot]
        try:
            if not v:
                # Need to add nulls for empty slots. Assumes that standard slots can't be empty.
                for s in slot_map:
                    if slot.lower().startswith(s):
                        loadout["components"][slot_map[s]].append(None)
                        break
                continue

            module = outfitting.lookup(v["module"])
            if not module:
                continue

            category = loadout["components"][category_map[module["category"]]]
            thing = OrderedDict(
                [
                    ("class", int(module["class"])),
                    ("rating", module["rating"]),
                    ("enabled", module["enabled"]),
                    ("priority", module["priority"] + 1),  # make 1-based
                ]
            )
            maxpri = max(maxpri, thing["priority"])

            if module["name"] in bulkheads:
                # Bulkheads are just strings
                category["bulkheads"] = module["name"]
            elif module["category"] == "standard":
                # Standard items are indexed by "group" rather than containing a "group" member
                category[standard_map[module["name"]]] = thing
            else:
                # All other items have a "group" member, some also have a "name"
                if module["name"] in scanners:
                    thing["group"] = "Scanner"
                    thing["name"] = module["name"]
                elif module["name"] in countermeasures:
                    thing["group"] = "Countermeasure"
                    thing["name"] = module["name"]
                elif module["name"] in fixup_map:
                    thing["group"], name = fixup_map[module["name"]]
                    if name:
                        thing["name"] = name
                else:
                    thing["group"] = module["name"]

                if "mount" in module:
                    thing["mount"] = weaponmount_map[module["mount"]]
                if "guidance" in module:
                    thing["missile"] = module["guidance"][0]  # not mentioned in schema

                category.append(thing)

        except AssertionError as e:
            if __debug__:
                print "Loadout: %s" % e
            continue  # Silently skip unrecognized modules
        except:
            if __debug__:
                raise

    # Cargo Hatch status is not available in the data - fake something up
    loadout["components"]["standard"]["cargoHatch"] = OrderedDict([("enabled", True), ("priority", maxpri)])

    # Construct description
    string = json.dumps(loadout, indent=2)

    # Look for last ship of this type
    regexp = re.compile(re.escape(ship) + "\.\d\d\d\d\-\d\d\-\d\dT\d\d\.\d\d\.\d\d\.json")
    oldfiles = sorted([x for x in os.listdir(config.get("outdir")) if regexp.match(x)])
    if oldfiles:
        with open(join(config.get("outdir"), oldfiles[-1]), "rU") as h:
            if h.read() == string:
                return  # same as last time - don't write

    # Write
    filename = join(
        config.get("outdir"), "%s.%s.json" % (ship, time.strftime("%Y-%m-%dT%H.%M.%S", time.localtime(querytime)))
    )
    with open(filename, "wt") as h:
        h.write(string)
Example #19
0
def export(data, filename=None):

    def class_rating(module):
        if 'guidance' in module:
            return module['class'] + module['rating'] + '/' + module.get('mount', 'F')[0] + module['guidance'][0] + ' '
        elif 'mount' in module:
            return module['class'] + module['rating'] + '/' + module['mount'][0] + ' '
        else:
            return module['class'] + module['rating'] + ' '

    querytime = config.getint('querytime') or int(time.time())

    loadout = defaultdict(list)
    mass = 0.0
    fsd = None

    for slot in sorted(data['ship']['modules']):

        v = data['ship']['modules'][slot]
        try:
            if not v: continue

            module = outfitting.lookup(v['module'], ship_map)
            if not module: continue

            cr = class_rating(module)
            mass += module.get('mass', 0)

            # Specials
            if module['name'] in ['Fuel Tank', 'Cargo Rack']:
                name = '%s (Capacity: %d)' % (module['name'], 2**int(module['class']))
            else:
                name = module['name']

            if name == 'Frame Shift Drive':
                fsd = module	# save for range calculation

            for s in slot_map:
                if slot.lower().startswith(s):
                    loadout[slot_map[s]].append(cr + name)
                    break
            else:
                if slot.lower().startswith('slot'):
                    loadout[slot[-1]].append(cr + name)
                elif __debug__ and not slot.lower().startswith('planetaryapproachsuite'):
                    print 'EDShipyard: Unknown slot %s' % slot

        except AssertionError as e:
            if __debug__: print 'EDShipyard: %s' % e
            continue	# Silently skip unrecognized modules
        except:
            if __debug__: raise

    # Construct description
    string = '[%s]\n' % ship_map.get(data['ship']['name'].lower(), data['ship']['name'])
    for slot in ['H', 'L', 'M', 'S', 'U', None, 'BH', 'RB', 'TM', 'FH', 'EC', 'PC', 'SS', 'FS', None, '9', '8', '7', '6', '5', '4', '3', '2', '1']:
        if not slot:
            string += '\n'
        elif slot in loadout:
            for name in loadout[slot]:
                string += '%s: %s\n' % (slot, name)
    string += '---\nCargo : %d T\nFuel  : %d T\n' % (data['ship']['cargo']['capacity'], data['ship']['fuel']['main']['capacity'])

    # Add mass and range
    assert data['ship']['name'].lower() in companion.ship_map, data['ship']['name']
    assert companion.ship_map[data['ship']['name'].lower()] in ships, companion.ship_map[data['ship']['name'].lower()]
    try:
        # https://github.com/cmmcleod/coriolis/blob/master/app/js/shipyard/module-shipyard.js#L184
        mass += ships[companion.ship_map[data['ship']['name'].lower()]]['hullMass']
        string += 'Mass  : %.1f T empty\n        %.1f T full\n' % (mass, mass + data['ship']['fuel']['main']['capacity']+ data['ship']['cargo']['capacity'])
        multiplier = pow(min(data['ship']['fuel']['main']['capacity'], fsd['maxfuel']) / fsd['fuelmul'], 1.0 / fsd['fuelpower']) * fsd['optmass']
        string += 'Range : %.2f LY unladen\n        %.2f LY laden\n' % (
            multiplier / (mass + data['ship']['fuel']['main']['capacity']),
            multiplier / (mass + data['ship']['fuel']['main']['capacity'] + data['ship']['cargo']['capacity']))
    except:
        if __debug__: raise

    if filename:
        with open(filename, 'wt') as h:
            h.write(string)
        return

    # Look for last ship of this type
    ship = companion.ship_map.get(data['ship']['name'].lower(), data['ship']['name'])	# Use in-game name
    regexp = re.compile(re.escape(ship) + '\.\d\d\d\d\-\d\d\-\d\dT\d\d\.\d\d\.\d\d\.txt')
    oldfiles = sorted([x for x in os.listdir(config.get('outdir')) if regexp.match(x)])
    if oldfiles:
        with open(join(config.get('outdir'), oldfiles[-1]), 'rU') as h:
            if h.read() == string:
                return	# same as last time - don't write

    # Write
    filename = join(config.get('outdir'), '%s.%s.txt' % (ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime))))
    with open(filename, 'wt') as h:
        h.write(string)
Example #20
0
                    }
                elif grp == 'gfsb':
                    modules[key] = {
                        'mass'      : m['mass'],
                        'jumpboost' : m['jumpboost'],
                    }
                else:
                    modules[key] = { 'mass': m.get('mass', 0) }	# Some modules don't have mass

    # Pre 3.3 modules
    add(modules, 'int_stellarbodydiscoveryscanner_standard',     { 'mass': 2 })
    add(modules, 'int_stellarbodydiscoveryscanner_intermediate', { 'mass': 2 })
    add(modules, 'int_stellarbodydiscoveryscanner_advanced',     { 'mass': 2 })

    # Missing
    add(modules, 'hpt_mining_subsurfdispmisle_fixed_small',      { 'mass': 2 })
    add(modules, 'hpt_mining_subsurfdispmisle_fixed_medium',     { 'mass': 4 })

    modules = OrderedDict([(k,modules[k]) for k in sorted(modules)])	# sort for easier diffing
    cPickle.dump(modules, open('modules.p', 'wb'))

    # Check data is present for all modules
    with open('outfitting.csv') as csvfile:
        reader = csv.DictReader(csvfile, restval='')
        for row in reader:
            try:
                module = outfitting.lookup({ 'id': row['id'], 'name': row['symbol'] }, companion.ship_map)
            except:
                print row['symbol']
                print_exc()
Example #21
0
def export(data, filename=None):
    def class_rating(module: __Module):
        mod_class = module['class']
        mod_rating = module['rating']
        mod_mount = module.get('mount')
        mod_guidance = module.get('guidance')

        ret = '{mod_class}{rating}'.format(mod_class=mod_class, rating=mod_rating)
        if 'guidance' in module:  # Missiles
            ret += "/{mount}{guidance}".format(
                mount=mod_mount[0] if mod_mount is not None else 'F',
                guidance=mod_guidance[0],
            )

        elif 'mount' in module:  # Hardpoints
            ret += "/{mount}".format(mount=mod_mount)

        elif 'Cabin' in module['name']:  # Passenger cabins
            ret += "/{name}".format(name=module['name'][0])

        return ret + ' '

    querytime = config.getint('querytime') or int(time.time())

    loadout = defaultdict(list)
    mass = 0.0
    fuel = 0
    cargo = 0
    fsd = None
    jumpboost = 0

    for slot in sorted(data['ship']['modules']):

        v = data['ship']['modules'][slot]
        try:
            if not v:
                continue

            module: __Module = outfitting.lookup(v['module'], ship_map)
            if not module:
                continue

            cr = class_rating(module)
            mods = v.get('modifications') or v.get('WorkInProgress_modifications') or {}
            if mods.get('OutfittingFieldType_Mass'):
                mass += (module.get('mass', 0) * mods['OutfittingFieldType_Mass']['value'])

            else:
                mass += module.get('mass', 0)

            # Specials
            if 'Fuel Tank' in module['name']:
                fuel += 2**int(module['class'])
                name = '{} (Capacity: {})'.format(module['name'], 2**int(module['class']))

            elif 'Cargo Rack' in module['name']:
                cargo += 2**int(module['class'])
                name = '{} (Capacity: {})'.format(module['name'], 2**int(module['class']))

            else:
                name = module['name']

            if name == 'Frame Shift Drive':
                fsd = module  # save for range calculation

                if mods.get('OutfittingFieldType_FSDOptimalMass'):
                    fsd['optmass'] *= mods['OutfittingFieldType_FSDOptimalMass']['value']

                if mods.get('OutfittingFieldType_MaxFuelPerJump'):
                    fsd['maxfuel'] *= mods['OutfittingFieldType_MaxFuelPerJump']['value']

            jumpboost += module.get('jumpboost', 0)

            for s in slot_map:
                if slot.lower().startswith(s):
                    loadout[slot_map[s]].append(cr + name)
                    break

            else:
                if slot.lower().startswith('slot'):
                    loadout[slot[-1]].append(cr + name)

                elif __debug__ and not slot.lower().startswith('planetaryapproachsuite'):
                    print('EDShipyard: Unknown slot {}'.format(slot))

        except AssertionError as e:
            if __debug__:
                print('EDShipyard: {}'.format(e))

            continue  # Silently skip unrecognized modules

        except Exception:
            if __debug__:
                raise

    # Construct description
    ship = ship_map.get(data['ship']['name'].lower(), data['ship']['name'])

    if data['ship'].get('shipName') is not None:
        _ships = '{}, {}'.format(ship, data['ship']['shipName'])
    else:
        _ships = ship

    string = '[{}]\n'.format(_ships)

    SLOT_TYPES = (
        'H', 'L', 'M', 'S', 'U', None, 'BH', 'RB', 'TM', 'FH', 'EC', 'PC', 'SS', 'FS', None, 'MC', None, '9', '8',
        '7', '6', '5', '4', '3', '2', '1'
    )
    for slot in SLOT_TYPES:
        if not slot:
            string += '\n'

        elif slot in loadout:
            for name in loadout[slot]:
                string += '{}: {}\n'.format(slot, name)

    string += '---\nCargo : {} T\nFuel  : {} T\n'.format(cargo, fuel)

    # Add mass and range
    assert data['ship']['name'].lower() in companion.ship_map, data['ship']['name']
    assert companion.ship_map[data['ship']['name'].lower()] in ships, companion.ship_map[data['ship']['name'].lower()]

    try:
        mass += ships[companion.ship_map[data['ship']['name'].lower()]]['hullMass']
        string += 'Mass  : {:.2f} T empty\n        {:.2f} T full\n'.format(mass, mass + fuel + cargo)

        multiplier = pow(min(fuel, fsd['maxfuel']) / fsd['fuelmul'], 1.0 / fsd['fuelpower']) * fsd['optmass']

        string += 'Range : {:.2f} LY unladen\n        {:.2f} LY laden\n'.format(
            multiplier / (mass + fuel) + jumpboost,
            multiplier / (mass + fuel + cargo) + jumpboost
        )

    except Exception:
        if __debug__:
            raise

    if filename:
        with open(filename, 'wt') as h:
            h.write(string)

        return

    # Look for last ship of this type
    ship = companion.ship_file_name(data['ship'].get('shipName'), data['ship']['name'])
    regexp = re.compile(re.escape(ship) + r'\.\d{4}-\d\d-\d\dT\d\d\.\d\d\.\d\d\.txt')
    oldfiles = sorted([x for x in os.listdir(config.get('outdir')) if regexp.match(x)])
    if oldfiles:
        with open(join(config.get('outdir'), oldfiles[-1]), 'rU') as h:
            if h.read() == string:
                return  # same as last time - don't write

    # Write
    filename = join(config.get('outdir'), '{}.{}.txt'.format(
        ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime)))
    )

    with open(filename, 'wt') as h:
        h.write(string)