def from_corp_name(corp_name, corp_assets=None): pos_mod_dict = {} pos_list = [] corp_assets = corp_assets or Asset.from_entity_name(corp_name) assets = [a for a in corp_assets if Pos.is_pos_mod(a)] pos_mods = [m for m in assets if m.group.name != 'Control Tower'] mod_locations = item_locations([m.item_id for m in pos_mods]) pos_assets = { p.item_id: p for p in assets if p.group.name == 'Control Tower' } pos_locations = item_locations( [p.item_id for p in pos_assets.values()]) for mod in pos_mods: mod.xyz = mod_locations[mod.item_id] mods = pos_mod_dict.setdefault(nearest(mod.xyz, pos_locations), []) mods.append(mod) corp_id = name_to_id(corp_name, 'corporation') poses_request = esi.op['get_corporations_corporation_id_starbases']( corporation_id=corp_id) poses_response = esi_client.request(poses_request) if not poses_response.status == 200: raise HTTPError(poses_response.data['error']) poses = {s.starbase_id: s for s in poses_response.data} for pos_id, pos in poses.iteritems(): pos.update(pos_assets[pos.starbase_id].__dict__) pos['xyz'] = pos_locations[pos.starbase_id] pos_object = Pos.from_id(corp_id=corp_id, mods=pos_mod_dict.get(pos_id, []), **pos) pos_list.append(pos_object) return pos_list
def from_corporation(cls, corporation_name, assets=None): structure_list = [] corporation_id = name_to_id(corporation_name, 'corporation') assets = assets or Asset.from_entity_id(corporation_id, 'corporations') endpoint = 'get_corporations_corporation_id_structures' structures_request = esi.op[endpoint](corporation_id=corporation_id) structures_response = esi_client.request(structures_request) structures = structures_response.data endpoint = 'get_corporation_corporation_id_mining_extractions' detonations_request = esi.op[endpoint](corporation_id=corporation_id) detonations_response = esi_client.request(detonations_request) detonations = detonations_response.data detonations = { d['structure_id']: d['chunk_arrival_time'] for d in detonations } structure_keys = [ 'structure_id', 'corporation_id', 'system_id', 'type_id', 'services', 'fuel_expires', 'state', 'state_timer_end' ] for s in structures: sid = s['structure_id'] kwargs = {k: v for k, v in s.items() if k in structure_keys} kwargs['type_name'] = ids_to_names([s['type_id']])[s['type_id']] kwargs['detonation'] = detonations.get(sid) structure_contents = [a for a in assets if a.location_id == sid] if structure_contents: kwargs['fitting'] = Fitting.from_assets(structure_contents) kwargs['fuel'] = [ a for a in structure_contents if a.location_flag == 'StructureFuel' ] structure_list.append(cls(**kwargs)) return structure_list
def pos_assets(): corp_id = name_to_id(CORPORATION_NAME, 'corporation') asset_xml = xml_api('/corp/AssetList.xml.aspx', params={'corporationID': corp_id}, xpath='.//rowset[@name="assets"]/row[@singleton="1"]') pos = {} mods = {} for row in asset_xml: typeID = int(row.get('typeID')) # Filter out things that aren't POS mods if typeID not in pos_mods: continue # Decorate POS mods with SDE data annotate_element(row, pos_mods[typeID]) itemID = int(row.get('itemID')) if row.get('groupName') == 'Control Tower': pos_stuff = pos else: pos_stuff = mods pos_stuff[itemID] = row.attrib for contents in row.findall('.//rowset[@name="contents"]/row'): typeID = int(contents.get('typeID')) # Decorate POS mod contents with SDE data if typeID in moon_goo: annotate_element(contents, moon_goo[typeID]) elif typeID in fuel_types: annotate_element(contents, fuel_types[typeID]) else: continue parents_contents = pos_stuff[itemID].setdefault('contents', []) parents_contents.append(contents.attrib) locations = item_locations(pos.keys() + mods.keys()) for itemID, location in locations.iteritems(): try: pos[itemID].update(location) except KeyError: mods[itemID].update(location) pos_locations = {i: (pos[i]['x'], pos[i]['y'], pos[i]['z']) for i in pos} for i, d in mods.iteritems(): try: location = (d['x'], d['y'], d['z']) except KeyError: print '{} ({}) has no coordinates'.format(d['typeName'], i) continue nearest_pos = nearest(location, pos_locations) parent_pos_mods = pos[nearest_pos].setdefault('mods', []) if d['groupName'] == 'Silo': if pos[nearest_pos]['raceName'] == 'Amarr': d['capacity'] = Decimal(d['capacity']) * Decimal(1.5) if pos[nearest_pos]['raceName'] == 'Gallente': d['capacity'] = Decimal(d['capacity']) * Decimal(2) parent_pos_mods.append(d) return pos
def from_corporation(cls, corporation_name, token=access_token, assets={}): corporation_id = name_to_id(corporation_name, 'corporation') structures = esi_api('Corporation.get_corporations_corporation_id_structures', token=token, corporation_id=corporation_id) structure_keys = ['structure_id', 'system_id', 'services', 'fuel_expires'] for s in structures: sid = s['structure_id'] kwargs = {k:v for k,v in s.items() if k in structure_keys} kwargs['token'] = token if 'children' in assets.get(sid).keys(): kwargs['fitting'] = Fitting.from_assets(assets[sid]['children']) kwargs['structure_type_id'] = assets[sid]['typeID'] kwargs['structure_type_name'] = assets[sid]['typeName'] else: kwargs['structure_type_id'] = s['type_id'] kwargs['structure_type_name'] = ids_to_names([s['type_id']])[s['type_id']] yield cls(**kwargs)
def check_citadels(): """ Check citadels for fuel and services status """ corporation_id = name_to_id(CORPORATION_NAME, 'corporation') structures = esi_api( 'Corporation.get_corporations_corporation_id_structures', token=access_token, corporation_id=corporation_id) detonations = esi_api( 'Industry.get_corporation_corporation_id_mining_extractions', token=access_token, corporation_id=corporation_id) detonations = {d['structure_id']: d for d in detonations} now = datetime.datetime.utcnow().replace(tzinfo=pytz.utc) too_soon = datetime.timedelta(days=TOO_SOON) messages = [] for structure in structures: structure_id = structure['structure_id'] message = [] # Grab structure name try: structure_info = esi_api( 'Universe.get_universe_structures_structure_id', token=access_token, structure_id=structure_id) except HTTPForbidden, e: messages.append( 'Found a citadel ({}) in {} that doesn\'t allow {} to dock!'. format(structure_id, structure['system_id'], CORPORATION_NAME)) continue name = structure_info.get('name') # List online/offline services online_services = [] offline_services = [] for service in structure.get('services') or []: if service['state'] == 'online': online_services.append(service.get('name')) if service['state'] == 'offline': offline_services.append(service.get('name')) online = ', '.join([service for service in online_services]) offline = ', '.join([service for service in offline_services]) # Check when fuel expires fuel_expires = structure.get('fuel_expires', None) # Check for upcoming detonations try: detonation = detonations[structure_id]['chunk_arrival_time'] if detonation - now < too_soon: message.append('Ready to detonate {}'.format(detonation)) except KeyError: pass # Build message for fuel running out and offline services if fuel_expires and (fuel_expires - now.date() < too_soon): message.append('Runs out of fuel on {}'.format(fuel_expires)) if online_services: message.append('Online Services: {}'.format(online)) if offline_services: message.append('Offline Services: {}'.format(offline)) elif offline_services: message.append('Offline services: {}'.format(offline)) if message: messages.append('\n'.join(['{}'.format(name)] + message))
def check_pos(corp_name, corp_assets=None): """ Check POS for fuel and status Returns: list: list of alert strings """ messages = [] corp_id = name_to_id(CONFIG['CORPORATION_NAME'], 'corporation') pos_list = Pos.from_corp_name(corp_name, corp_assets) if not pos_list: return messages alliance_id_request = esi.op['get_corporations_corporation_id']( corporation_id=corp_id) alliance_id = esi_client.request(alliance_id_request).data.get( 'alliance_id', None) sovs = sov_systems(alliance_id) for pos in pos_list: # TODO: All this could be done in the Pos object for easier testing # But POS are going away ;) sov = pos.system_id in sovs has_stront = False has_fuel = False has_defensive_mods = False for fuel in pos.fuels: multiplier = .75 if sov else 1.0 rate = pos_fuel[pos.type_id][fuel.type_id] * multiplier if fuel.type_id == 16275: has_stront = True if pos.state == 'offline': continue reinforce_hours = int(fuel.quantity / rate) if reinforce_hours < CONFIG['STRONT_HOURS']: message = '{} has {} hours of stront'.format( pos.moon_name, reinforce_hours) messages.append(message) else: has_fuel = True if pos.state == 'offline': continue how_soon = datetime.timedelta(fuel.quantity / (rate * 24)) if how_soon < CONFIG['TOO_SOON']: days = 'day' if how_soon == 1 else 'days' message = '{} has {} {} of fuel'.format( pos.moon_name, how_soon, days) messages.append(message) for mod in pos.mods: if mod.group.name == 'Shield Hardening Array': has_defensive_mods = True if pos.state != 'online': if has_fuel and pos.state == 'offline' and not has_defensive_mods: continue message = '{} is {}'.format(pos.moon_name, pos.state) if pos.reinforced_until: state_predicates = {'reinforced': 'until'} message += ' {} {}'.format( state_predicates.get(pos.state, 'since'), pos.reinforced_until) messages.append(message) return messages
#!/usr/bin/env python from config import CONFIG from util import notify_slack, name_to_id from citadels import check_citadels from pos import check_pos if __name__ == '__main__': CONFIG['CORP_ID'] = name_to_id(CONFIG['CORPORATION_NAME'], 'corporation') messages = [] try: messages += check_citadels() messages += check_pos() except Exception, e: if CONFIG['DEBUG']: raise if e.message: messages = [e.message] else: raise if messages: messages.insert( 0, ' Upcoming {} Structure Maintenence Tasks'.format( CONFIG['CORPORATION_NAME'])) notify_slack(sorted(messages))
def check_pos(): pos_list_xml = xml_api('/corp/StarbaseList.xml.aspx') poses = pos_assets() messages = [] corp_id = name_to_id(CORPORATION_NAME, 'corporation') alliance_id = esi_api('Corporation.get_corporations_corporation_id', corporation_id=corp_id).get('alliance_id', None) sovs = sov_systems(alliance_id) for pos in pos_list_xml.findall('.//rowset[@name="starbases"]/row'): pos_id = int(pos.get('itemID')) type_id = int(pos.get('typeID')) location_id = int(pos.get('locationID')) states = ('Unanchored', 'Offline', 'Onlining', 'Reinforced', 'Online') state = states[int(pos.get('state'))] if state == 'Unanchored': print 'POS {} is unanchored, skipping'.format(pos_id) continue location_name = esi_api('Universe.get_universe_systems_system_id', system_id=location_id).get('name') moon_id = int(pos.get('moonID')) moon_name = esi_api('Universe.get_universe_moons_moon_id', moon_id=moon_id).get('name') sov = location_id in sovs poses[pos_id]['locationName'] = location_name poses[pos_id]['moonName'] = moon_name poses[pos_id]['moonID'] = moon_id has_stront = False has_fuel = False has_defensive_mods = False # TODO: handle purposefully offlined POS by checking for fuel for fuel in poses[pos_id].get('contents', []): fuel_type_id = int(fuel.get('typeID')) quantity = int(fuel.get('quantity')) multiplier = .75 if sov else 1.0 rate = pos_fuel[type_id][fuel_type_id] * multiplier fuel['hourly_rate'] = rate if fuel_type_id == 16275: has_stront = True if state == 'Offline': continue reinforce_hours = int(quantity / rate) message = '{} has {} hours of stront'.format( moon_name, reinforce_hours) if reinforce_hours < STRONT_HOURS: messages.append(message) else: has_fuel = True if state == 'Offline': continue how_soon = int(quantity / (rate * 24)) days = 'day' if how_soon == 1 else 'days' message = '{} has {} {} of fuel'.format( moon_name, how_soon, days) if how_soon < TOO_SOON: messages.append(message) for mod in poses[pos_id].get('mods', []): # Note this is currently only useful for # silos that are being filled (e.g. mining), # not emptied (e.g. reaction inputt) if mod['typeName'] == 'Silo': try: goo = mod['contents'][0] except KeyError: goo = None if goo: capacity = Decimal(mod['capacity']) name = goo['typeName'] volume = Decimal(goo['volume']) quantity = int(goo['quantity']) total_volume = volume * quantity rate = volume * 100 * 24 remaining_capacity = capacity - total_volume days_remaining = int(remaining_capacity / rate) days = 'day' if days_remaining == 1 else 'days' message = "{} has {} {} of {} capacity left ({} current units)".format( moon_name, days_remaining, days, name, quantity) if days_remaining < TOO_SOON: messages.append(message) if mod['groupName'] == 'Shield Hardening Array': has_defensive_mods = True if state != 'Online': if has_fuel and state == 'Offline' and not has_defensive_mods: continue statetime = pos.get('stateTimestamp') message = '{} is {}'.format(moon_name, state) if statetime: state_predicates = {'Reinforced': 'until'} message += ' {} {}'.format( state_predicates.get(state, 'since'), statetime) messages.append(message) return messages