mirror of
https://github.com/norohind/jubilant-system-core.git
synced 2025-06-09 03:42:11 +03:00
Basic functional implemented: can update squadron with specified id
This commit is contained in:
parent
5955f2f093
commit
605d4a3e43
53
DB.py
Normal file
53
DB.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import sqlite3
|
||||||
|
from SQLRequests import SQLRequests
|
||||||
|
|
||||||
|
db = sqlite3.connect('jubilant-system.sqlite')
|
||||||
|
db.row_factory = lambda c, r: dict(zip([col[0] for col in c.description], r))
|
||||||
|
|
||||||
|
|
||||||
|
def allocate_operation_id(squad_id: int) -> int:
|
||||||
|
return db.execute(SQLRequests.create_operation_id, {'squad_id': squad_id}).fetchone()['operation_id']
|
||||||
|
|
||||||
|
|
||||||
|
def insert_info_news(news_dict: dict | None, info_dict: dict) -> int:
|
||||||
|
"""
|
||||||
|
Saved both news and info endpoint's data
|
||||||
|
|
||||||
|
:param news_dict:
|
||||||
|
:param info_dict:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
with db:
|
||||||
|
operation_id = allocate_operation_id(info_dict['squad_id'])
|
||||||
|
info_dict['operation_id'] = operation_id
|
||||||
|
|
||||||
|
db.execute(SQLRequests.insert_info, info_dict)
|
||||||
|
|
||||||
|
if news_dict is not None:
|
||||||
|
news_dict['type_of_news'] = 'public_statements'
|
||||||
|
news_dict['operation_id'] = operation_id
|
||||||
|
db.execute(SQLRequests.insert_news, news_dict)
|
||||||
|
|
||||||
|
return operation_id
|
||||||
|
|
||||||
|
|
||||||
|
def delete_squadron(squad_id: int, suppress_deleted=True) -> None:
|
||||||
|
"""
|
||||||
|
A function to make record in squadrons_deleted table
|
||||||
|
|
||||||
|
:param squad_id: squad_id to mark as deleted
|
||||||
|
:param suppress_deleted: if IntegrityError exception due to this squad already
|
||||||
|
exists in squadrons_deleted table should be suppressed
|
||||||
|
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
with db:
|
||||||
|
operation_id = allocate_operation_id(squad_id)
|
||||||
|
db.execute(SQLRequests.delete_squadron, {'squad_id': squad_id, 'operation_id': operation_id})
|
||||||
|
|
||||||
|
except sqlite3.IntegrityError as e:
|
||||||
|
if not suppress_deleted:
|
||||||
|
raise e
|
39
FAPI/BearerManager.py
Normal file
39
FAPI/BearerManager.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from enum import Enum
|
||||||
|
from loguru import logger
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class BearerManager:
|
||||||
|
class Endpoints(Enum):
|
||||||
|
RANDOM = '/random_token'
|
||||||
|
|
||||||
|
def __init__(self, demb_capi_auth: str, base_address: str):
|
||||||
|
|
||||||
|
self.base_address = base_address
|
||||||
|
self.demb_capi_auth = demb_capi_auth
|
||||||
|
|
||||||
|
def get_random_bearer(self) -> str:
|
||||||
|
"""Gets bearer token from capi.demb.design (companion-api project)
|
||||||
|
|
||||||
|
:return: bearer token as str
|
||||||
|
"""
|
||||||
|
|
||||||
|
bearer_request: requests.Response = self._request(self.Endpoints.RANDOM)
|
||||||
|
|
||||||
|
try:
|
||||||
|
bearer: str = bearer_request.json()['access_token']
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(f'Unable to parse capi.demb.design answer\nrequested: {bearer_request.url!r}\n'
|
||||||
|
f'code: {bearer_request.status_code!r}\nresponse: {bearer_request.content!r}', exc_info=e)
|
||||||
|
raise e
|
||||||
|
|
||||||
|
return bearer
|
||||||
|
|
||||||
|
def _request(self, _endpoint: Endpoints) -> requests.Response:
|
||||||
|
endpoint = self.base_address + _endpoint.value
|
||||||
|
return requests.get(url=endpoint, headers={'auth': self.demb_capi_auth})
|
||||||
|
|
||||||
|
|
||||||
|
bearer_manager = BearerManager(os.environ['DEMB_CAPI_AUTH'], 'https://capi.demb.design')
|
3
FAPI/Converters.py
Normal file
3
FAPI/Converters.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
def dehexify(hex_str: str) -> str:
|
||||||
|
"""Converts string with hex chars to string"""
|
||||||
|
return bytes.fromhex(hex_str).decode('utf-8')
|
6
FAPI/Exceptions.py
Normal file
6
FAPI/Exceptions.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
class FAPIDownForMaintenance(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class FAPIUnknownStatusCode(Exception):
|
||||||
|
pass
|
45
FAPI/Mappings.py
Normal file
45
FAPI/Mappings.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
info_request_mapping = {
|
||||||
|
"id": "squad_id",
|
||||||
|
"name": "name",
|
||||||
|
"tag": "tag",
|
||||||
|
"ownerId": "owner_id",
|
||||||
|
"ownerName": "owner_name",
|
||||||
|
"platform": "platform",
|
||||||
|
"created": "created",
|
||||||
|
"created_ts": "created_ts",
|
||||||
|
"acceptingNewMembers": "accepting_new_members",
|
||||||
|
"powerId": "power_id",
|
||||||
|
"powerName": "power_name",
|
||||||
|
"superpowerId": "superpower_id",
|
||||||
|
"superpowerName": "superpower_name",
|
||||||
|
"factionId": "faction_id",
|
||||||
|
"factionName": "faction_name",
|
||||||
|
"deleteAfter": "delete_after",
|
||||||
|
"userTags": "user_tags",
|
||||||
|
"memberCount": "member_count",
|
||||||
|
"onlineCount": "online_count",
|
||||||
|
"pendingCount": "pending_count",
|
||||||
|
"publicComms": "public_comms",
|
||||||
|
"publicCommsOverride": "public_comms_override",
|
||||||
|
"publicCommsAvailable": "public_comms_available"
|
||||||
|
}
|
||||||
|
|
||||||
|
news_request_mapping = {
|
||||||
|
'id': 'news_id'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def perform_mapping(mapping: dict, dict_to_map: dict) -> dict:
|
||||||
|
for key in (list(dict_to_map.keys())):
|
||||||
|
if key in mapping:
|
||||||
|
dict_to_map[mapping[key]] = dict_to_map.pop(key)
|
||||||
|
|
||||||
|
return dict_to_map
|
||||||
|
|
||||||
|
|
||||||
|
def perform_info_mapping(info_data: dict) -> dict:
|
||||||
|
return perform_mapping(info_request_mapping, info_data)
|
||||||
|
|
||||||
|
|
||||||
|
def perform_news_mapping(news_data: dict) -> dict:
|
||||||
|
return perform_mapping(news_request_mapping, news_data)
|
88
FAPI/Queries.py
Normal file
88
FAPI/Queries.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
from .Mappings import perform_info_mapping, perform_news_mapping
|
||||||
|
from .Exceptions import *
|
||||||
|
from .Requester import *
|
||||||
|
from loguru import logger
|
||||||
|
from . import Converters
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
def get_squad_news(squad_id) -> dict | None:
|
||||||
|
"""
|
||||||
|
Returns news of squadron with specified id or None if squadron doesn't exists or there is no news
|
||||||
|
|
||||||
|
:param squad_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
news_request: requests.Response = request(BASE_URL + NEWS_ENDPOINT, params={'squadronId': squad_id})
|
||||||
|
if news_request.status_code != 200: # must not happen
|
||||||
|
logger.warning(f'Got not 200 status code on requesting news, content: {news_request.content}, '
|
||||||
|
f'code: {news_request.status_code}')
|
||||||
|
|
||||||
|
squad_news: dict = news_request.json()['squadron']
|
||||||
|
|
||||||
|
if isinstance(squad_news, list): # check squadron 2517 for example 0_0
|
||||||
|
# squadron have no public statements
|
||||||
|
return None
|
||||||
|
|
||||||
|
elif 'id' not in squad_news.keys(): # squadron doesn't FDEV
|
||||||
|
return None
|
||||||
|
|
||||||
|
else:
|
||||||
|
if 'public_statements' in squad_news.keys() and len(squad_news['public_statements']) > 0:
|
||||||
|
return perform_news_mapping(squad_news['public_statements'][0])
|
||||||
|
|
||||||
|
|
||||||
|
def get_squad_info(squad_id: int) -> dict | None:
|
||||||
|
"""Returns information about squadron with specified id or None if squadrons doesn't exists on FDEV side
|
||||||
|
|
||||||
|
:param squad_id: id of squad to update/insert
|
||||||
|
:return: dict with state of squadron or None if squad not found
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
How it should works?
|
||||||
|
Request squad's info
|
||||||
|
|
||||||
|
if squad exists FDEV
|
||||||
|
return squadron state
|
||||||
|
|
||||||
|
if squad doesn't exists FDEV
|
||||||
|
return None
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
if DB.db.execute(SQLRequests.squad_deleted, {'squad_id': squad_id}) == 1:
|
||||||
|
# we have it deleted in our DB
|
||||||
|
logger.debug(f'squad {squad_id} is marked as deleted in our DB, returning False')
|
||||||
|
return None
|
||||||
|
"""
|
||||||
|
|
||||||
|
squad_request: requests.Response = request(BASE_URL + INFO_ENDPOINT, params={'squadronId': squad_id})
|
||||||
|
|
||||||
|
if squad_request.status_code == 200: # squad exists FDEV
|
||||||
|
squad_request_json: dict = squad_request.json()['squadron']
|
||||||
|
squad_request_json['ownerName'] = Converters.dehexify(squad_request_json['ownerName']) # normalize value
|
||||||
|
squad_request_json['userTags'] = json.dumps(squad_request_json['userTags'])
|
||||||
|
squad_request_json = perform_info_mapping(squad_request_json)
|
||||||
|
|
||||||
|
"""
|
||||||
|
with DB.db:
|
||||||
|
operation_id = DB.allocate_operation_id(squad_id)
|
||||||
|
squad_request_json['operation_id'] = operation_id
|
||||||
|
DB.db.execute(SQLRequests.insert_info, squad_request_json)
|
||||||
|
"""
|
||||||
|
|
||||||
|
return squad_request_json
|
||||||
|
|
||||||
|
elif squad_request.status_code == 404: # squad doesn't exists FDEV
|
||||||
|
"""
|
||||||
|
if not suppress_absence:
|
||||||
|
with DB.db:
|
||||||
|
operation_id = DB.allocate_operation_id(squad_id)
|
||||||
|
DB.db.execute(SQLRequests.delete_squadron, {'operation_id': operation_id, 'squad_id': squad_id})
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
else: # any other codes (except 418, that one handles in authed_request), never should happen
|
||||||
|
logger.warning(f'Unknown squad info status_code: {squad_request.status_code}, content: {squad_request.content}')
|
||||||
|
raise FAPIUnknownStatusCode(f'Status code: {squad_request.status_code}, content: {squad_request.content}')
|
30
FAPI/Requester.py
Normal file
30
FAPI/Requester.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import requests
|
||||||
|
from .BearerManager import bearer_manager
|
||||||
|
from loguru import logger
|
||||||
|
from . import Exceptions
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Functions to perform queries to FDEV
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
BASE_URL = 'https://api.orerve.net/2.0/website/squadron/'
|
||||||
|
INFO_ENDPOINT = 'info'
|
||||||
|
NEWS_ENDPOINT = 'news/list'
|
||||||
|
|
||||||
|
|
||||||
|
def request(url: str, method: str = 'get', **kwargs) -> requests.Response:
|
||||||
|
_request: requests.Response = requests.request(
|
||||||
|
method=method,
|
||||||
|
url=url,
|
||||||
|
headers={'Authorization': f'Bearer {bearer_manager.get_random_bearer()}'},
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
if _request.status_code == 418: # FAPI is on maintenance
|
||||||
|
logger.warning(f'{method.upper()} {_request.url} returned 418, content dump:\n{_request.content}')
|
||||||
|
|
||||||
|
raise Exceptions.FAPIDownForMaintenance
|
||||||
|
|
||||||
|
return _request
|
1
FAPI/__init__.py
Normal file
1
FAPI/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from . import Queries
|
17
SQLRequests.py
Normal file
17
SQLRequests.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
from os import path
|
||||||
|
|
||||||
|
|
||||||
|
def read_request(name: str) -> str:
|
||||||
|
req_path = path.join('sql', name+'.sql')
|
||||||
|
with open(req_path, mode='r') as file:
|
||||||
|
return ''.join(file.readlines())
|
||||||
|
|
||||||
|
|
||||||
|
class SQLRequests:
|
||||||
|
build_squadrons_current_data = read_request('build_squadrons_current_data')
|
||||||
|
schema = read_request('schema')
|
||||||
|
squad_deleted = read_request('squad_deleted')
|
||||||
|
create_operation_id = read_request('create_operation_id')
|
||||||
|
insert_info = read_request('insert_info')
|
||||||
|
delete_squadron = read_request('delete_squadron')
|
||||||
|
insert_news = read_request('insert_news')
|
25
main.py
25
main.py
@ -23,3 +23,28 @@ Workflow to update existing squadron:
|
|||||||
else:
|
else:
|
||||||
insert squad_id, operations_id to squadrons_deleted
|
insert squad_id, operations_id to squadrons_deleted
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import FAPI
|
||||||
|
import DB
|
||||||
|
|
||||||
|
|
||||||
|
def update_squad(squad_id: int, suppress_absence=False) -> None:
|
||||||
|
squad_info = FAPI.Queries.get_squad_info(squad_id)
|
||||||
|
if squad_info is None:
|
||||||
|
# Squad not found FDEV
|
||||||
|
if not suppress_absence:
|
||||||
|
DB.delete_squadron(squad_id)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Then we got valid squad_info dict
|
||||||
|
news_info = FAPI.Queries.get_squad_news(squad_id)
|
||||||
|
print(DB.insert_info_news(news_info, squad_info))
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
update_squad(2530)
|
||||||
|
update_squad(47999)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
1
sql/create_operation_id.sql
Normal file
1
sql/create_operation_id.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
insert into operations_info (squad_id) values (:squad_id) RETURNING operation_id;
|
1
sql/delete_squadron.sql
Normal file
1
sql/delete_squadron.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
insert into squadrons_deleted (operation_id, squad_id) VALUES (:operation_id, :squad_id);
|
43
sql/insert_info.sql
Normal file
43
sql/insert_info.sql
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
INSERT INTO squadrons_historical_data values (
|
||||||
|
:operation_id,
|
||||||
|
:name,
|
||||||
|
:tag,
|
||||||
|
:owner_id,
|
||||||
|
:owner_name,
|
||||||
|
:platform,
|
||||||
|
:created,
|
||||||
|
:created_ts,
|
||||||
|
:accepting_new_members,
|
||||||
|
:power_id,
|
||||||
|
:power_name,
|
||||||
|
:superpower_id,
|
||||||
|
:superpower_name,
|
||||||
|
:faction_id,
|
||||||
|
:faction_name,
|
||||||
|
:delete_after,
|
||||||
|
:credits_balance,
|
||||||
|
:credits_in,
|
||||||
|
:credits_out,
|
||||||
|
:user_tags,
|
||||||
|
:member_count,
|
||||||
|
:online_count,
|
||||||
|
:pending_count,
|
||||||
|
:full,
|
||||||
|
:public_comms,
|
||||||
|
:public_comms_override,
|
||||||
|
:public_comms_available,
|
||||||
|
:current_season_trade_score,
|
||||||
|
:previous_season_trade_score,
|
||||||
|
:current_season_combat_score,
|
||||||
|
:previous_season_combat_score,
|
||||||
|
:current_season_exploration_score,
|
||||||
|
:previous_season_exploration_score,
|
||||||
|
:current_season_cqc_score,
|
||||||
|
:previous_season_cqc_score,
|
||||||
|
:current_season_bgs_score,
|
||||||
|
:previous_season_bgs_score,
|
||||||
|
:current_season_powerplay_score,
|
||||||
|
:previous_season_powerplay_score,
|
||||||
|
:current_season_aegis_score,
|
||||||
|
:previous_season_aegis_score
|
||||||
|
);
|
22
sql/insert_news.sql
Normal file
22
sql/insert_news.sql
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
insert into squadrons_news_historical (
|
||||||
|
operation_id,
|
||||||
|
type_of_news,
|
||||||
|
news_id,
|
||||||
|
"date",
|
||||||
|
category,
|
||||||
|
motd,
|
||||||
|
author,
|
||||||
|
cmdr_id,
|
||||||
|
user_id
|
||||||
|
) values
|
||||||
|
(
|
||||||
|
:operation_id,
|
||||||
|
:type_of_news,
|
||||||
|
:news_id,
|
||||||
|
:date,
|
||||||
|
:category,
|
||||||
|
:motd,
|
||||||
|
:author,
|
||||||
|
:cmdr_id,
|
||||||
|
:user_id
|
||||||
|
);
|
1
sql/squad_deleted.sql
Normal file
1
sql/squad_deleted.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
select count(*) from squadrons_deleted where squad_id = :squad_id;
|
Loading…
x
Reference in New Issue
Block a user