Basic functional implemented: can update squadron with specified id

This commit is contained in:
norohind 2022-04-10 14:32:48 +03:00
parent 5955f2f093
commit 605d4a3e43
Signed by: norohind
GPG Key ID: 01C3BECC26FB59E1
15 changed files with 376 additions and 1 deletions

53
DB.py Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,6 @@
class FAPIDownForMaintenance(Exception):
pass
class FAPIUnknownStatusCode(Exception):
pass

45
FAPI/Mappings.py Normal file
View 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
View 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
View 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
View File

@ -0,0 +1 @@
from . import Queries

17
SQLRequests.py Normal file
View 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')

27
main.py
View File

@ -22,4 +22,29 @@ Workflow to update existing squadron:
else:
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()

View File

@ -0,0 +1 @@
insert into operations_info (squad_id) values (:squad_id) RETURNING operation_id;

1
sql/delete_squadron.sql Normal file
View File

@ -0,0 +1 @@
insert into squadrons_deleted (operation_id, squad_id) VALUES (:operation_id, :squad_id);

43
sql/insert_info.sql Normal file
View 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
View 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
View File

@ -0,0 +1 @@
select count(*) from squadrons_deleted where squad_id = :squad_id;