mirror of
https://github.com/norohind/ED-livery-tracker-advanced.git
synced 2025-04-12 07:20:04 +03:00
WIP
This commit is contained in:
parent
79068e6f37
commit
5cd4695ffc
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;"""
|
42
static/js/json2htmltable.js
Normal file
42
static/js/json2htmltable.js
Normal 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;
|
||||
}
|
16
static/js/table_styles.css
Normal file
16
static/js/table_styles.css
Normal 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
127
web.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user