def skip_flight(self, params): """ params: {"flight_id": int, "flight_time": int, "az_damage": dict/int} """ flight = self.db.fetchRow("select * from flights where id=:flight_id", params) if flight['status'] != 'prepare': return api_fail( f"Полет находится в статусе {flight['status']} и не может быть пропущен" ) build = get_build_data(self, params) # Все нормально - можем фрахтовать node_ids = [item['node_id'] for item in build.values()] # "Возвращаем" полет self.db.query("update flights set status='returned' where id=:flight_id", params, need_commit=True) # Фрахтуем узлы if node_ids: self.db.query("update nodes set status='free' where id in (" + ", ".join(map(str, node_ids)) + ")", None, need_commit=True) # Выдаем компаниям KPI за фрахт узлов kpi_insert = [{ "company": item['company'], "node_id": item['node_id'], "reason": "фрахт", "amount": 5 } for item in build.values()] self.db.insert("kpi_changes", kpi_insert) logger.info("Полет {flight_id} пропущен".format(**params)) return api_ok()
def mcc_add_flight(self, params): """ params = {"departure": "2018-08-16 15:00:00", "dock": 2, "company": "pre"} """ params['flight_id'] = self.db.insert('flights', params) logger.info( "Создан полет: отбытие {departure}, док {dock}, компания {pre}".format( **params)) return api_ok(flight=params)
def unload_luggage(self, params): """ params = {flight_id: int, code: "beacon"/"mine"/"module", <company>: str (только для шахт), <planet_id>: str (только для модулей) }""" if 'company' in params and params.get('code') != 'mine': del (params['company']) if 'planet_id' in params and params.get('code') != 'module': del (params['planet_id']) if not params.get('planet_id') and params.get('code') == 'module': return api_fail( "Для выгрузки модуля необходимо указать код планеты высадки!") if 'company' not in params and params.get('code') == 'mine': return api_fail( "Для выгрузки шахты необходимо указать компанию-владельца!") where = self.db.construct_where(params) sql = "select * from flight_luggage where " + where # Проверяем, есть ли такой груз row = self.db.fetchRow(sql, params) if not row: return api_fail("Такого груза нет на борту") if int(row['amount']) == 1: self.db.query("delete from flight_luggage where " + where, params, need_commit=True) else: self.db.query("update flight_luggage set amount = amount-1 where " + where, params, need_commit=True) return api_ok()
def mcc_remove(self, params): """ params = {"flight_id": 1, "user_id": 2} """ deleted = self.db.query( "delete from flight_crews where flight_id = :flight_id and user_id = :user_id", params, need_commit=True) return api_ok(deleted=self.db.cursor.rowcount)
def set_build_correction(self, params): """ params: {flight_id: int, node_type_code: str, correction: str}""" existing = self.db.fetchAll( """ select * from builds where flight_id = :flight_id""", params, 'node_type_code') if not existing: return api_fail( "Полет {flight_id) не существует, либо для него не зарезервировано ни одного узла!" .format(**params)) flight = self.db.fetchRow("select * from flights where id=:flight_id", params) if flight.get('status') == 'freight': return api_fail( "Ваш полет уже зафрахтован. Дальнейшая синхронизация невозможна") update_row = existing.get(params['node_type_code']) if not update_row: return api_fail( "Для полета не зарезервирован узел {node_type_code}".format( **params)) update_row['correction_func'] = params['correction'] update_row['correction'] = get_func_vector(params['correction']) update_row['total'] = xor([update_row['vector'], update_row['correction']]) update_row['params'] = calc_node_params_with_desync( update_row['total'], node_id=update_row['node_id']) update_row['params_json'] = json.dumps(update_row['params']) update_row['slots'] = count_elements_for_functext(params['correction']) update_row['slots_json'] = json.dumps(update_row['slots']) if update_row['slots_json'] == 'null': update_row['slots_json'] = '{}' self.db.update( 'builds', update_row, 'flight_id = :flight_id and node_type_code=:node_type_code') del (params['correction']) return api_ok(node_sync=get_build_data(self, params))
def create_tech(self, params): """ params = {name: str, description: str, level: int, is_available: 0/1, point_cost: {str: float}, effects: [ {node_code: str, parameter_code: str, value: float},], inventors: [str] }""" tech_id = self.db.insert('technologies', params) point_costs = [ {"tech_id": tech_id, "resource_code": key, "amount": value} for key, value in params['point_cost'].items() ] self.db.insert('tech_point_cost', point_costs) tech_inventors = [ {"tech_id": tech_id, "company": company} for company in params.get("inventors", []) ] self.db.insert('tech_inventors', tech_inventors) tech_effects = [ {**{"tech_id": tech_id}, **effect} for effect in params['effects'] ] self.db.insert('tech_effects', tech_effects) params['id'] = tech_id return api_ok(tech=params)
def decomm_node(self, params): """ params={node_id: int, is_auto: 0/1, reason: str} """ node = self.db.fetchRow( """select m.company, n.az_level, n.status from nodes n join models m on m.id = n.model_id where n.id=:node_id""", params) if node['status'] in ['decomm', 'lost']: return api_fail( "Узел имеет статус {status} и уже не может быть списан".format( **node)) self.db.query("update nodes set status='decomm' where id=:node_id", params, need_commit=True) if not params.get('is_auto'): self.db.insert( "kpi_changes", { "company": node['company'], "reason": "Списание узла вручную", "node_id": params['node_id'], 'amount': -15 }) stop_pump(self, {"section": "nodes", "entity_id": params['node_id']}) if params.get('is_auto'): params[ 'reason'] = f"Автоматическое списание, уровень АЗ {node['az_level']}" logger.info("Списан узел {node_id}, причина: {reason}".format(**params)) return api_ok(reason=params.get('reason', ''))
def mcc_assign_flight(self, params): """ params = {"flight_id": int, "company": mat/mst/gd/pre/kkg/ideo} """ company = params.get('company', '') if company not in ('mat', 'mst', 'gd', 'pre', 'kkg', 'ideo'): return api_fail("Неверный код компании") params['id'] = params['flight_id'] self.db.update('flights', params, "id=:id") return api_ok(updated=self.db.affected_rows())
def mcc_set_crew(self, params): """ params = {"flight_id": 1, "role": "pilot", "user_id": 2} """ self.db.query("""delete from flight_crews where flight_id = :flight_id and (role=:role or user_id=:user_id)""", params, need_commit=True) self.db.insert('flight_crews', params) return api_ok()
def boost_use(self, params): """ params = {"password": str} """ boost = self.db.fetchRow('select code, used_datetime from boosts where password=:password', params) if not boost: return api_fail("Пароль неверен") if boost['used_datetime']: return api_fail("Этот буст уже был использован") self.db.query("update boosts set used_datetime=now() where password=:password", params) return api_ok()
def mcc_set_all_crew(self, params): """ params = {"flight_id": int, "crew": [{"role": "pilot", "user_id": 1}, {"role": "radist", "user_id": 2}] }""" if not self.db.fetchRow('select * from flights where id=:flight_id', params): return api_fail("Полет с указанным номером не существует") self.db.query("delete from flight_crews where flight_id=:flight_id", params, need_commit=True) if params['crew']: for item in params['crew']: item['flight_id'] = params['flight_id'] self.db.insert('flight_crews', params['crew']) return api_ok(crew=params['crew'])
def mcc_add_passenger(self, params): """ params = {"flight_id": 1, "user_id": 2} """ cnt = self.db.fetchOne( 'select count(*) from flight_crews where flight_id=:flight_id and role="_other"', params) if cnt >= 5: return api_fail("В полет разрешается брать не более 5 пассажиров!") params['role'] = "_other" self.db.query("""delete from flight_crews where flight_id = :flight_id and user_id=:user_id""", params, need_commit=True) self.db.insert('flight_crews', params) return api_ok()
def flight_died(self, params): """ params = {flight_id: int} """ # Находим все узлы того полета node_ids = self.db.fetchColumn( "select node_id from builds where flight_id=:flight_id", params) nodelist = "(" + ", ".join(node_ids) + ")" # Ставим всем узлам статус "утерян" self.db.query( f"update nodes set status_code='lost' where id in {nodelist}", None, need_commit=True) # Отключаем их насосы в экономике self.db.query( f"update pumps set date_end = Now() where section='nodes' and entity_id in {nodelist}", need_commit=True) return api_ok()
def add_node_upkeep_pump(self, node_id=None, model=None): if not model: model = self.db.fetchRow("""select m.id, m.name, m.company from models m join nodes n on m.id = n.model_id where n.id = :node_id""", {"node_id": node_id}) upkeep_price = get_model_upkeep_price(self, {"model_id": model['id']}) insufficient = get_insufficient_for_node(company=model['company'], model_id=model['id'], upkeep_price=upkeep_price) if insufficient: return api_fail("Вашего дохода не хватает для создания узла: " + dict2str(insufficient)) pump = { "company": model['company'], "section": "nodes", "entity_id": node_id, "comment": "Поддержка узла {} модели {}".format(node_id, model['name']), "is_income": 0, "resources": upkeep_price } add_pump(self, pump) return api_ok(pump=pump)
def load_luggage(self, params): """ params = {flight_id: int, code: "beacon"/"mine"/"module", <company>: str (только для шахт), <planet_id>: str (только для модулей) }""" if 'company' in params and params.get('code') != 'mine': del (params['company']) if 'planet_id' in params and params.get('code') != 'module': del (params['planet_id']) if not params.get('planet_id') and params.get('code') == 'module': return api_fail( "Для загрузки модуля необходимо указать код планеты высадки!") if 'company' not in params and params.get('code') == 'mine': return api_fail( "Для загрузки шахты необходимо указать компанию-владельца!") where = self.db.construct_where(params) # Пытаемся добавить к существующим update_sql = 'update flight_luggage set amount=amount +1 where ' + where self.db.query(update_sql, params, need_commit=True) if self.db.affected_rows() == 0: self.db.insert('flight_luggage', params) return api_ok()
def flight_returned(self, params): """ params = { "flight_id": int, "flight_time": int, // время полета в секундах "az_damage": { "march_engine": 32.5, "warp_engine": 12.9, } / float }""" self.db.query("update flights set status='returned' where id = :flight_id", params, need_commit=True) # Находим все узлы того полета nodes = self.db.fetchDict( "select node_type_code, node_id from builds where flight_id=:flight_id", params, 'node_type_code', 'node_id') for node_type_code, node_id in nodes.items(): if node_type_code == 'hull': az_damage = roundTo(params['flight_time'] / (5 * 60)) else: if isinstance(params['az_damage'], dict): az_damage = roundTo( params['az_damage'].get(node_type_code, 0) + params['flight_time'] / (8 * 60)) else: az_damage = roundTo(params['az_damage'] + params['flight_time'] / (8 * 60)) self.db.query( f"update nodes set status='free', az_level = az_level - {az_damage} where id={node_id}", None, need_commit=True) logger.info("Полет {flight_id} вернулся".format(**params)) nodes_to_decomm = self.db.fetchColumn( "select id from nodes where az_level <= 15 and status='free'") for node_id in nodes_to_decomm: decomm_node(self, {"node_id": node_id, "is_auto": 1}) return api_ok(nodes_to_decomm=nodes_to_decomm)
def set_build_correction(self, params): """ params: {flight_id: int, node_type_code: str, correction: str}""" existing = self.db.fetchAll( """ select * from builds where flight_id = :flight_id""", params, 'node_type_code') if not existing: return api_fail( "Полет {flight_id) не существует, либо для него не зарезервировано ни одного узла!" .format(**params)) update_row = existing.get(params['node_type_code']) if not update_row: return api_fail( "Для полета не зарезервирован узел {node_type_code}".format( **params)) update_row['correction_func'] = params['correction'] update_row['correction'] = get_func_vector(params['correction']) update_row['total'] = xor([update_row['vector'], update_row['correction']]) update_row = get_node_params_with_desync(update_row['total'], node_id=update_row['node_id']) self.db.update( 'bulids', update_row, 'flight_id = :flight_id and node_type_code=:node_type_code') return api_ok(node_sync=update_row)
def stop_pump(self, params): """ params {pump_id: int} """ self.db.query("update pumps set date_end=Now() where id=:pump_id", params, need_commit=True) return api_ok()
def stop_pump(self, params): """ params {<id>: int, <company>: str, <section>: mines/nodes/models/crises/markets, <entity_id>: int/str} """ self.db.query("update pumps set date_end=Now() where "+self.db.construct_where(params), params, need_commit=True) return api_ok()
def develop_model(self, params): """ params = { "node_type_code": "radar", "company": "mat", "size": "large", "tech_balls": {"1": 10, "2": 5}, "name": "Азаза", "description": "", "password": "" } """ existing_model_name = self.db.fetchRow("select * from models where name=:name", params) if existing_model_name: return api_fail("Модель с названием {name} уже создана".format(**params)) # Читаем используемые технологии techs = read_techs(self, params) # Чистим неиспользуемые техи params['tech_balls'] = { int(key): value for key, value in params['tech_balls'].items() if int(key) in techs.keys() } # Считаем, сколько апкипа жрет модель model_upkeep = calc_model_upkeep(self, params['tech_balls']) # проверяем, хватает ли доходов insufficient = get_insufficient_for_both(company=params['company'], upkeep_price=model_upkeep) if insufficient: return api_fail("Для создания модели и стартового узла по этой модели вам не хватает ресурсов: "+ dict2str(insufficient)) # Вычисляем параметры model_params = calc_model_params(self, params) # Находим цену в KPI model_kpi_price = sum([ ball * techs.get(tech_id, {}).get('level', 0) ** 2 for tech_id, ball in params['tech_balls'].items() ]) model_level = get_model_level_by_technologies([ [techs[key].get('level'), value] for key, value in params['tech_balls'].items() ]) model_data = add_model(self, { "name": params['name'], "description": params.get("description", ''), "level": model_level, "kpi_price": model_kpi_price, "size": params['size'], "node_type_code": params['node_type_code'], "company": params['company'], 'params': model_params, "upkeep": model_upkeep, }) model_id = model_data['data']['id'] # Создаем временный насос model_pump = add_pump(self, { "company": params['company'], 'section': 'models', 'entity_id': model_id, 'comment': f'Разработка модели {model_id} {params["name"]}', "is_income": 0, "resources": {code: value / 2 for code, value in model_upkeep.items()} }) # Устанавливаем насосу время окончания self.db.query(f"""update pumps set date_end = Now() + interval {MODEL_WOW_PERIOD_HOURS} hour where id=:id""", model_pump['data'], need_commit=True) # Создаем стартовый узел if not params.get('password'): params['password'] = str(randint(1000, 9999)) if params['company'] != 'pre' else '' create_node(self, {"model_id": model_id, "password": params['password']}) return api_ok(params=params)
def freight_flight(self, params): """ params {flight_id: int} """ flight = self.db.fetchRow("select * from flights where id=:flight_id", params) if flight['status'] == 'freight': return api_fail(f"Ваш полет уже зафрахтован") if flight['status'] != 'prepare': return api_fail( f"Полет находится в статусе {flight['status']} и не может быть зафрахтован" ) build = get_build_data(self, params) # Проверить, что корабль состоит из всех нужных узлов. nodes_not_reserved = set(NODE_NAMES.keys()) - set(build.keys()) if nodes_not_reserved: return api_fail( "Невозможно совершить фрахт. В корабле не хватает следующих узлов: " + ", ".join([NODE_NAMES[item] for item in nodes_not_reserved])) # проверить, что общий объем неотрицательный hull_volume = build['hull']['params']['volume']['value'] inner_nodes_volume = sum([ item['params']['volume']['value'] for item in build.values() if item['node_type_code'] != 'hull' ]) luggage_volume = self.db.fetchOne( """ select sum(fl.amount* vl.volume) from flight_luggage fl join v_luggages vl on fl.code = vl.code and (fl.company = vl.company or (fl.company is null and vl.company is null)) where fl.flight_id = :flight_id""", params) or 0 if hull_volume - inner_nodes_volume - luggage_volume < 0: return api_fail( f"Внутренний объем вашего корпуса {hull_volume}. Его недостаточно для размещения всех узлов " + f"(суммарный объем {inner_nodes_volume}) и багажа (суммарный объем {luggage_volume})" ) # Проверить, что всех слотов синхронизации хватает slots = defaultdict(int) for row in build.values(): for slot_type, qty in row['slots'].items(): slots[slot_type] += qty * (1 if row['node_type_code'] == 'hull' else -1) not_enough_slots = {key: val for key, val in slots.items() if val < 0} if not_enough_slots: return api_fail( "В корпусе недостаточно слотов для выбранных инженером формул синхронизации. " + "Недостающие слоты: " + dict2str(not_enough_slots)) # Все нормально - можем фрахтовать node_ids = [item['node_id'] for item in build.values()] # Фрахтуем полет self.db.query("update flights set status='freight' where id=:flight_id", params, need_commit=True) # Фрахтуем узлы self.db.query("update nodes set status='freight' where id in (" + ", ".join(map(str, node_ids)) + ")", None, need_commit=True) # Выдаем компаниям KPI за фрахт узлов kpi_insert = [{ "company": item['company'], "node_id": item['node_id'], "reason": "фрахт", "amount": 5 } for item in build.values()] self.db.insert("kpi_changes", kpi_insert) # Формируем инфу по щитам для CouchDb dock = flight['dock'] shield_value = round( build['shields']['params']['desinfect_level']['value']) url = f"https://api.alice.magellan2018.ru/ships/set_shields/{dock}/{shield_value}" requests.get(url, auth=HTTPBasicAuth(os.environ['ADMIN_USER'], os.environ['ADMIN_PASSWORD'])) return api_ok()
def mcc_add_flight(self, params): """ params = {"departure": "2018-08-16 15:00:00", "dock": 2, "company": "pre"} """ params['flight_id'] = self.db.insert('flights', params) return api_ok(flight=params)