-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathtop_pve_stats.py
149 lines (124 loc) · 4.08 KB
/
top_pve_stats.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
from collections import defaultdict
from datetime import timedelta
import numpy
from pydantic import BaseModel, field_validator
from api_top_db_v2 import TopDBCached
from c_bosses import ALL_FIGHT_NAMES
from c_server_phase import Encounter
from c_path import Directories
from c_player_classes import SPECS_LIST
from h_debug import running_time
IGNORED_SPECS = set([*range(0, 40, 4), 7, 17, 18, 21, 22, 31, 39])
SPECS_DATA_NOT_IGNORED = [
spec_data
for spec_data in SPECS_LIST
if spec_data.index not in IGNORED_SPECS
]
API_EXAMPLES = [
{
"server": "Lordaeron",
"boss": "The Lich King",
"mode": "25H",
},
]
def n_greater_than(data: numpy.ndarray, value: float):
return int((data > value).sum())
def get_percentile(data, percentile):
if percentile == 100:
dps = max(data)
raids = 1
elif percentile == 0:
dps = 0
raids = len(data)
else:
dps = numpy.percentile(data, percentile)
dps = round(dps, 2)
raids = n_greater_than(data, dps)
return {
"dps": dps,
"raids": raids,
}
@running_time
def convert_boss_data(data: dict[int, list[float]]):
BOSS_DATA = {}
for spec_index, values in data.items():
if spec_index in IGNORED_SPECS:
continue
if len(values) < 5:
continue
data_s = numpy.fromiter(values, dtype=numpy.float64)
spec_html = SPECS_LIST[spec_index].html_name
BOSS_DATA[spec_html] = {
"top100": get_percentile(data_s, 100),
"top99": get_percentile(data_s, 99),
"top95": get_percentile(data_s, 95),
"top90": get_percentile(data_s, 90),
"top75": get_percentile(data_s, 75),
"top50": get_percentile(data_s, 50),
"top10": get_percentile(data_s, 10),
"all": get_percentile(data_s, 0),
}
return BOSS_DATA
class PveStatsValidation(BaseModel):
server: str
boss: str
mode: str
model_config = {
"json_schema_extra": {
"examples": API_EXAMPLES,
}
}
@field_validator('server')
@classmethod
def validate_server(cls, server: str):
servers = Directories.top.files_stems()
if server not in servers:
_list = ', '.join(servers)
raise ValueError(f"[server] value value must be from [{_list}]")
return server
@field_validator('boss')
@classmethod
def validate_boss(cls, boss: str):
if boss not in ALL_FIGHT_NAMES:
_list = ', '.join(ALL_FIGHT_NAMES)
raise ValueError(f"[boss] value value must be from [{_list}]")
return boss
@field_validator('mode')
@classmethod
def validate_mode(cls, mode: str):
mode = mode.upper()
modes = ["10N", "10H", "25N", "25H"]
if mode not in modes:
_list = ', '.join(modes)
raise ValueError(f"[boss] value value must be from [{_list}]")
return mode
class PveStats(TopDBCached):
cache: defaultdict[str, dict[str, dict[str, dict]]] = defaultdict(dict)
cooldown = timedelta(minutes=10)
def __init__(self, model: PveStatsValidation) -> None:
super().__init__(model.server)
self.encounter = Encounter(model.boss, model.mode)
self.table_name = self.encounter.table_name
def get_data(self):
if self.db_was_updated():
self.cache[self.server].clear()
server_data = self.cache[self.server]
if self.table_name not in server_data:
server_data[self.table_name] = self._renew_data()
return server_data[self.table_name]
def _renew_data(self):
query = self.encounter.query_stats()
rows_generator = self.cursor.execute(query)
data = defaultdict(list)
for spec, dps in rows_generator:
data[spec].append(dps)
return convert_boss_data(data)
def _test1():
conf = API_EXAMPLES[0]
q = PveStatsValidation(**conf)
z = PveStats(q)
for x, y in z.get_data().items():
print(x)
print(y)
if __name__ == "__main__":
_test1()