This commit is contained in:
norohind 2021-12-11 20:04:03 +03:00
parent 79068e6f37
commit 5cd4695ffc
Signed by: norohind
GPG Key ID: 01C3BECC26FB59E1
6 changed files with 232 additions and 38 deletions

View File

@ -20,6 +20,10 @@ class AbstractModel(abc.ABC):
def insert_livery(self, livery_list: list) -> None:
raise NotImplemented
@abc.abstractmethod
def insert_livery_timestamp(self, livery_list: list) -> None:
raise NotImplemented
@abc.abstractmethod
def get_diff_action_id(self, action_id: int) -> list:
raise NotImplemented

View File

@ -1,4 +1,3 @@
import json
import typing
import psycopg2.extensions
@ -46,20 +45,16 @@ class PostgresModel(AbstractModel):
print(f'Connection to {self.db.dsn} closed successfully')
@errors_catcher
def get_activity_changes(self, platform: str, leaderboard_type: str, limit: int, low_timestamp, high_timestamp)\
-> list:
def get_activity_changes(self, limit: int, low_timestamp, high_timestamp) -> list:
with self.db:
with self.db.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cursor:
cursor.execute(postgres_sql_requests.select_activity_pretty_names, {
'LB_type': utils.LeaderboardTypes(leaderboard_type.lower()).value,
'platform': utils.Platform(platform.upper()).value,
'limit': limit,
'high_timestamp': high_timestamp,
'low_timestamp': low_timestamp
})
with self.db.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cursor:
cursor.execute(postgres_sql_requests.select_activity_pretty_names, {
'limit': limit,
'high_timestamp': high_timestamp,
'low_timestamp': low_timestamp
})
result: list = cursor.fetchall()
result: list = cursor.fetchall()
return result
@ -92,7 +87,7 @@ class PostgresModel(AbstractModel):
with self.db:
with self.db.cursor() as cursor:
cursor.executemany(
postgres_sql_requests.insert_leader_board,
postgres_sql_requests.insert_livery,
livery_list)
@errors_catcher
@ -124,7 +119,7 @@ class PostgresModel(AbstractModel):
with self.db:
with self.db.cursor() as cursor:
cursor.executemany(
postgres_sql_requests.insert_leader_board_timestamp,
postgres_sql_requests.insert_livery_timestamp,
livery_list)
@errors_catcher
@ -137,9 +132,8 @@ class PostgresModel(AbstractModel):
:return:
"""
with self.db:
with self.db.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cursor:
cursor.execute(postgres_sql_requests.select_diff_by_action_id, {'action_id': action_id})
result: list = cursor.fetchall()
with self.db.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cursor:
cursor.execute(postgres_sql_requests.select_diff_by_action_id, {'action_id': action_id})
result: list = cursor.fetchall()
return result

View File

@ -15,47 +15,56 @@ from livery
order by action_id desc
limit 1;"""
insert_leader_board = """insert into livery (action_id, name, cur_price, orig_price, image_url)
insert_livery = """insert into livery (action_id, name, cur_price, orig_price, image_url)
values
(%(action_id)s, %(name)s, %(cur_price)s, %(orig_price)s, %(image)s);"""
insert_leader_board_timestamp = """insert into livery (action_id, name, cur_price, orig_price, image_url, timestamp)
insert_livery_timestamp = """insert into livery (action_id, name, cur_price, orig_price, image_url, timestamp)
values
(%(action_id)s, %(name)s, %(cur_price)s, %(orig_price)s, %(image)s, %(timestamp)s);"""
select_activity_pretty_names = """select
sum_score::bigint as "TotalExperience",
to_char(timestamp, 'YYYY-MM-DD HH24:MI:SS') as "Timestamp UTC",
action_id::bigint as "ActionId",
sum_score_old::bigint as "TotalExperienceOld",
(sum_score - sum_score_old)::bigint as "Diff"
items_count_new as "New Items count",
items_count_old as "Old Items count",
(items_count_new - items_count_old)::bigint as "Count Diff",
sum_cur_price::bigint as "Sum Total",
sum_cur_price_old::bigint as "Sum Total Old",
(sum_cur_price - sum_cur_price_old)::bigint as "Price Sum Diff"
from
(
select
sum_score,
min(timestamp) as timestamp,
sum_cur_price,
min(timestamp) as timestamp,
min(items_count) as items_count_new,
action_id,
lag (sum_score, 1) over (order by sum_score) sum_score_old
lag (sum_cur_price, 1) over (order by sum_cur_price) sum_cur_price_old,
lag (items_count, 1) over (order by items_count) items_count_old
from (
select sum(cur_price) as sum_score, min(timestamp) as timestamp, action_id
select
sum(cur_price) as sum_cur_price,
count(distinct name) as items_count,
min(timestamp) as timestamp,
action_id
from livery
group by action_id
) as foo
group by sum_score, action_id
group by sum_cur_price, action_id, items_count
order by timestamp desc
) as foo1
where (sum_score - sum_score_old) <> 0
where (sum_cur_price - sum_cur_price_old) != 0
limit %(limit)s;"""
select_diff_by_action_id = """select
new_livery.name as new_name,
old_livery.name as old_name,
new_livery.orig_price as new_orig_price,
new_livery.cur_price as new_cur_price,
old_livery.orig_price as old_orig_price,
old_livery.cur_price as old_cur_price,
new_livery.cur_price - old_livery.cur_price
coalesce(new_livery.name, 'Deleted Item') as "New Name",
coalesce(old_livery.name, 'New Item') as "Old Name",
new_livery.orig_price as "New Original Price",
new_livery.cur_price as "New Current Price",
old_livery.orig_price as "Old Original Price",
old_livery.cur_price as "Old Current Price",
new_livery.cur_price - old_livery.cur_price as "Current Price diff"
from (
select *
from livery
@ -66,4 +75,6 @@ full join
from livery
where action_id = %(action_id)s - 1
) old_livery
on new_livery.name = old_livery.name;"""
on new_livery.name = old_livery.name
where (new_livery.cur_price - old_livery.cur_price) <> 0 or (new_livery.cur_price - old_livery.cur_price) is null
order by new_livery.cur_price - old_livery.cur_price desc;"""

View File

@ -0,0 +1,42 @@
// Thanks to https://stackoverflow.com/a/21065846
var _table_ = document.createElement('table'),
_tr_ = document.createElement('tr'),
_th_ = document.createElement('th'),
_td_ = document.createElement('td');
// Builds the HTML Table out of myList json data from Ivy restful service.
function buildHtmlTable(arr) {
var table = _table_.cloneNode(false),
columns = addAllColumnHeaders(arr, table);
for (var i = 0, maxi = arr.length; i < maxi; ++i) {
var tr = _tr_.cloneNode(false);
for (var j = 0, maxj = columns.length; j < maxj; ++j) {
var td = _td_.cloneNode(false);
cellValue = arr[i][columns[j]];
td.appendChild(document.createTextNode(arr[i][columns[j]]));
tr.appendChild(td);
}
table.appendChild(tr);
}
return table;
}
// Adds a header row to the table and returns the set of columns.
// Need to do union of keys from all records as some records may not contain
// all records
function addAllColumnHeaders(arr, table) {
var columnSet = [],
tr = _tr_.cloneNode(false);
for (var i = 0, l = arr.length; i < l; i++) {
for (var key in arr[i]) {
if (arr[i].hasOwnProperty(key) && columnSet.indexOf(key) === -1) {
columnSet.push(key);
var th = _th_.cloneNode(false);
th.appendChild(document.createTextNode(key));
tr.appendChild(th);
}
}
}
table.appendChild(tr);
return columnSet;
}

View File

@ -0,0 +1,16 @@
table {
margin-bottom: 20px;
border: 1px solid #dddddd;
border-collapse: collapse;
}
table th {
font-weight: bold;
padding: 5px;
background: #efefef;
border: 1px solid #dddddd;
}
table td {
border: 1px solid #dddddd;
padding: 5px;
text-align:center;
}

127
web.py Normal file
View File

@ -0,0 +1,127 @@
from model import model
import json
import falcon
import os
"""
/leaderboard/{leaderboard_type}/platform/{platform}?[limit=<int>
&after=<timestamp (as "Timestamp UTC" column format)>&after=<timestamp (as "Timestamp UTC" column format)>]
leaderboard_type - one of
powerplay
cqc
trade
exploration
aegis
bgs
combat
platform - one of
XBOX
PS4
PC
"""
model.open_model()
activity_table_html_template = """<!DOCTYPE HTML>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<script src="/js/json2htmltable.js"></script>
<script type="text/javascript">
window.addEventListener("load", () => {
document.body.appendChild(buildHtmlTable({items})); // build table
var table = document.querySelector("body > table")
var header = table.rows[0]
for (var i = 0, cell; cell = header.cells[i]; i++){
if (cell.innerText.includes('{target_column_name}')){
var target_column_id = i;
break;
}
}
if (target_column_id == null) { // don't to anything if no action_id in the table
return;
}
for (var i = 1, row; row = table.rows[i]; i++) { // append to action_id filed onclick action
row.cells[target_column_id].innerHTML = '<td><a href="{link}">{original_value}</a></td>'.replace('{link}', '{target_new_url}' + table.rows[i].cells[target_column_id].innerText).replace('{original_value}', table.rows[i].cells[target_column_id].innerText);
}
})
</script>
<link type="text/css" rel="stylesheet" href="/js/table_styles.css">
</head>
<body>
</body>
</html>"""
class Activity:
def on_get(self, req: falcon.request.Request, resp: falcon.response.Response) -> None:
resp.content_type = falcon.MEDIA_JSON
args_activity_changes = {
'limit': req.params.get('limit', 10),
'high_timestamp': req.params.get('before', '3307-12-12'),
'low_timestamp': req.params.get('after', '0001-01-01')
}
try:
resp.text = json.dumps(model.get_activity_changes(**args_activity_changes))
except Exception as e:
print(f'Exception occurred during executing Activity request:\n{e}')
raise falcon.HTTPInternalServerError(description=str(e))
class ActivityHtml:
def on_get(self, req: falcon.request.Request, resp: falcon.response.Response) -> None:
Activity().on_get(req, resp)
table_in_json: str = resp.text
resp.content_type = falcon.MEDIA_HTML
resp.text = activity_table_html_template.replace(
'{items}', table_in_json
).replace('{target_column_name}', 'ActionId').replace('{target_new_url}', '/diff/')
# what? f-strings? .format? never heard about them
class ActivityDiff:
def on_get(self, req: falcon.request.Request, resp: falcon.response.Response, action_id: int) -> None:
"""
Give squads tags and diff in their experience for specified action_id - 1 (smart -1)
:param action_id:
:param req:
:param resp:
:return:
"""
resp.content_type = falcon.MEDIA_JSON
resp.text = json.dumps(model.get_diff_action_id(action_id))
class ActivityDiffHtml:
def on_get(self, req: falcon.request.Request, resp: falcon.response.Response, action_id: int) -> None:
resp.content_type = falcon.MEDIA_HTML
resp.text = activity_table_html_template.replace(
'{items}', json.dumps(model.get_diff_action_id(action_id)))
app = falcon.App()
app.add_route('/api/livery', Activity())
app.add_route('/api/diff/{action_id}', ActivityDiff())
app.add_route('/livery', ActivityHtml())
app.add_route('/diff/{action_id}', ActivityDiffHtml())
application = app # for uwsgi
if __name__ == '__main__':
import waitress
app.add_static_route('/js', os.path.join(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static'), 'js'))
app.add_static_route('/', os.path.join(os.path.dirname(os.path.abspath(__file__)), 'static'))
waitress.serve(app, host='127.0.0.1', port=9486)