2.1.1: Fixed new profile.
This commit is contained in:
@@ -48,6 +48,18 @@ def init_db():
|
|||||||
"rd_phase_death_early_share": "REAL",
|
"rd_phase_death_early_share": "REAL",
|
||||||
"rd_phase_death_mid_share": "REAL",
|
"rd_phase_death_mid_share": "REAL",
|
||||||
"rd_phase_death_late_share": "REAL",
|
"rd_phase_death_late_share": "REAL",
|
||||||
|
"rd_phase_kill_early_share_t": "REAL",
|
||||||
|
"rd_phase_kill_mid_share_t": "REAL",
|
||||||
|
"rd_phase_kill_late_share_t": "REAL",
|
||||||
|
"rd_phase_kill_early_share_ct": "REAL",
|
||||||
|
"rd_phase_kill_mid_share_ct": "REAL",
|
||||||
|
"rd_phase_kill_late_share_ct": "REAL",
|
||||||
|
"rd_phase_death_early_share_t": "REAL",
|
||||||
|
"rd_phase_death_mid_share_t": "REAL",
|
||||||
|
"rd_phase_death_late_share_t": "REAL",
|
||||||
|
"rd_phase_death_early_share_ct": "REAL",
|
||||||
|
"rd_phase_death_mid_share_ct": "REAL",
|
||||||
|
"rd_phase_death_late_share_ct": "REAL",
|
||||||
"rd_firstdeath_team_first_death_rounds": "INTEGER",
|
"rd_firstdeath_team_first_death_rounds": "INTEGER",
|
||||||
"rd_firstdeath_team_first_death_win_rate": "REAL",
|
"rd_firstdeath_team_first_death_win_rate": "REAL",
|
||||||
"rd_invalid_death_rounds": "INTEGER",
|
"rd_invalid_death_rounds": "INTEGER",
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -204,6 +204,18 @@ CREATE TABLE IF NOT EXISTS dm_player_features (
|
|||||||
rd_phase_death_early_share REAL,
|
rd_phase_death_early_share REAL,
|
||||||
rd_phase_death_mid_share REAL,
|
rd_phase_death_mid_share REAL,
|
||||||
rd_phase_death_late_share REAL,
|
rd_phase_death_late_share REAL,
|
||||||
|
rd_phase_kill_early_share_t REAL,
|
||||||
|
rd_phase_kill_mid_share_t REAL,
|
||||||
|
rd_phase_kill_late_share_t REAL,
|
||||||
|
rd_phase_kill_early_share_ct REAL,
|
||||||
|
rd_phase_kill_mid_share_ct REAL,
|
||||||
|
rd_phase_kill_late_share_ct REAL,
|
||||||
|
rd_phase_death_early_share_t REAL,
|
||||||
|
rd_phase_death_mid_share_t REAL,
|
||||||
|
rd_phase_death_late_share_t REAL,
|
||||||
|
rd_phase_death_early_share_ct REAL,
|
||||||
|
rd_phase_death_mid_share_ct REAL,
|
||||||
|
rd_phase_death_late_share_ct REAL,
|
||||||
rd_firstdeath_team_first_death_rounds INTEGER,
|
rd_firstdeath_team_first_death_rounds INTEGER,
|
||||||
rd_firstdeath_team_first_death_win_rate REAL,
|
rd_firstdeath_team_first_death_win_rate REAL,
|
||||||
rd_invalid_death_rounds INTEGER,
|
rd_invalid_death_rounds INTEGER,
|
||||||
|
|||||||
Binary file not shown.
@@ -1202,6 +1202,18 @@ class FeatureService:
|
|||||||
df['rd_phase_death_early_share'] = 0.0
|
df['rd_phase_death_early_share'] = 0.0
|
||||||
df['rd_phase_death_mid_share'] = 0.0
|
df['rd_phase_death_mid_share'] = 0.0
|
||||||
df['rd_phase_death_late_share'] = 0.0
|
df['rd_phase_death_late_share'] = 0.0
|
||||||
|
df['rd_phase_kill_early_share_t'] = 0.0
|
||||||
|
df['rd_phase_kill_mid_share_t'] = 0.0
|
||||||
|
df['rd_phase_kill_late_share_t'] = 0.0
|
||||||
|
df['rd_phase_kill_early_share_ct'] = 0.0
|
||||||
|
df['rd_phase_kill_mid_share_ct'] = 0.0
|
||||||
|
df['rd_phase_kill_late_share_ct'] = 0.0
|
||||||
|
df['rd_phase_death_early_share_t'] = 0.0
|
||||||
|
df['rd_phase_death_mid_share_t'] = 0.0
|
||||||
|
df['rd_phase_death_late_share_t'] = 0.0
|
||||||
|
df['rd_phase_death_early_share_ct'] = 0.0
|
||||||
|
df['rd_phase_death_mid_share_ct'] = 0.0
|
||||||
|
df['rd_phase_death_late_share_ct'] = 0.0
|
||||||
df['rd_firstdeath_team_first_death_rounds'] = 0
|
df['rd_firstdeath_team_first_death_rounds'] = 0
|
||||||
df['rd_firstdeath_team_first_death_win_rate'] = 0.0
|
df['rd_firstdeath_team_first_death_win_rate'] = 0.0
|
||||||
df['rd_invalid_death_rounds'] = 0
|
df['rd_invalid_death_rounds'] = 0
|
||||||
@@ -1268,6 +1280,62 @@ class FeatureService:
|
|||||||
df[c] = df[f'{c}_calc'].fillna(df[c])
|
df[c] = df[f'{c}_calc'].fillna(df[c])
|
||||||
df.drop(columns=[f'{c}_calc'], inplace=True)
|
df.drop(columns=[f'{c}_calc'], inplace=True)
|
||||||
|
|
||||||
|
if 'attacker_side' in df_events.columns:
|
||||||
|
k_side = df_events[df_events['attacker_side'].isin(['CT', 'T'])].copy()
|
||||||
|
if not k_side.empty:
|
||||||
|
k_cnt_side = k_side.groupby(['attacker_steam_id', 'attacker_side', 'phase_bucket']).size().reset_index(name='cnt')
|
||||||
|
k_piv = k_cnt_side.pivot_table(index=['attacker_steam_id', 'attacker_side'], columns='phase_bucket', values='cnt', fill_value=0)
|
||||||
|
k_piv['tot'] = k_piv.sum(axis=1).replace(0, 1)
|
||||||
|
k_piv = k_piv.div(k_piv['tot'], axis=0).drop(columns=['tot'])
|
||||||
|
k_piv = k_piv.reset_index().rename(columns={'attacker_steam_id': 'steam_id_64'})
|
||||||
|
|
||||||
|
for side, suffix in [('T', '_t'), ('CT', '_ct')]:
|
||||||
|
tmp = k_piv[k_piv['attacker_side'] == side].copy()
|
||||||
|
if not tmp.empty:
|
||||||
|
tmp = tmp.rename(columns={
|
||||||
|
'early': f'rd_phase_kill_early_share{suffix}',
|
||||||
|
'mid': f'rd_phase_kill_mid_share{suffix}',
|
||||||
|
'late': f'rd_phase_kill_late_share{suffix}',
|
||||||
|
})
|
||||||
|
df = df.merge(
|
||||||
|
tmp[['steam_id_64', f'rd_phase_kill_early_share{suffix}', f'rd_phase_kill_mid_share{suffix}', f'rd_phase_kill_late_share{suffix}']],
|
||||||
|
on='steam_id_64',
|
||||||
|
how='left',
|
||||||
|
suffixes=('', '_calc')
|
||||||
|
)
|
||||||
|
for c in [f'rd_phase_kill_early_share{suffix}', f'rd_phase_kill_mid_share{suffix}', f'rd_phase_kill_late_share{suffix}']:
|
||||||
|
if f'{c}_calc' in df.columns:
|
||||||
|
df[c] = df[f'{c}_calc'].fillna(df[c])
|
||||||
|
df.drop(columns=[f'{c}_calc'], inplace=True)
|
||||||
|
|
||||||
|
if 'victim_side' in df_events.columns:
|
||||||
|
d_side = df_events[df_events['victim_side'].isin(['CT', 'T'])].copy()
|
||||||
|
if not d_side.empty:
|
||||||
|
d_cnt_side = d_side.groupby(['victim_steam_id', 'victim_side', 'phase_bucket']).size().reset_index(name='cnt')
|
||||||
|
d_piv = d_cnt_side.pivot_table(index=['victim_steam_id', 'victim_side'], columns='phase_bucket', values='cnt', fill_value=0)
|
||||||
|
d_piv['tot'] = d_piv.sum(axis=1).replace(0, 1)
|
||||||
|
d_piv = d_piv.div(d_piv['tot'], axis=0).drop(columns=['tot'])
|
||||||
|
d_piv = d_piv.reset_index().rename(columns={'victim_steam_id': 'steam_id_64'})
|
||||||
|
|
||||||
|
for side, suffix in [('T', '_t'), ('CT', '_ct')]:
|
||||||
|
tmp = d_piv[d_piv['victim_side'] == side].copy()
|
||||||
|
if not tmp.empty:
|
||||||
|
tmp = tmp.rename(columns={
|
||||||
|
'early': f'rd_phase_death_early_share{suffix}',
|
||||||
|
'mid': f'rd_phase_death_mid_share{suffix}',
|
||||||
|
'late': f'rd_phase_death_late_share{suffix}',
|
||||||
|
})
|
||||||
|
df = df.merge(
|
||||||
|
tmp[['steam_id_64', f'rd_phase_death_early_share{suffix}', f'rd_phase_death_mid_share{suffix}', f'rd_phase_death_late_share{suffix}']],
|
||||||
|
on='steam_id_64',
|
||||||
|
how='left',
|
||||||
|
suffixes=('', '_calc')
|
||||||
|
)
|
||||||
|
for c in [f'rd_phase_death_early_share{suffix}', f'rd_phase_death_mid_share{suffix}', f'rd_phase_death_late_share{suffix}']:
|
||||||
|
if f'{c}_calc' in df.columns:
|
||||||
|
df[c] = df[f'{c}_calc'].fillna(df[c])
|
||||||
|
df.drop(columns=[f'{c}_calc'], inplace=True)
|
||||||
|
|
||||||
if 'victim_side' in df_events.columns and 'winner_side' in df_events.columns:
|
if 'victim_side' in df_events.columns and 'winner_side' in df_events.columns:
|
||||||
death_rows = df_events[['match_id', 'round_num', 'event_time', 'victim_steam_id', 'victim_side', 'winner_side']].copy()
|
death_rows = df_events[['match_id', 'round_num', 'event_time', 'victim_steam_id', 'victim_side', 'winner_side']].copy()
|
||||||
death_rows = death_rows[death_rows['victim_side'].isin(['CT', 'T']) & death_rows['winner_side'].isin(['CT', 'T'])]
|
death_rows = death_rows[death_rows['victim_side'].isin(['CT', 'T']) & death_rows['winner_side'].isin(['CT', 'T'])]
|
||||||
|
|||||||
@@ -749,6 +749,10 @@ class StatsService:
|
|||||||
# New: ROUND (Round Dynamics)
|
# New: ROUND (Round Dynamics)
|
||||||
'rd_phase_kill_early_share', 'rd_phase_kill_mid_share', 'rd_phase_kill_late_share',
|
'rd_phase_kill_early_share', 'rd_phase_kill_mid_share', 'rd_phase_kill_late_share',
|
||||||
'rd_phase_death_early_share', 'rd_phase_death_mid_share', 'rd_phase_death_late_share',
|
'rd_phase_death_early_share', 'rd_phase_death_mid_share', 'rd_phase_death_late_share',
|
||||||
|
'rd_phase_kill_early_share_t', 'rd_phase_kill_mid_share_t', 'rd_phase_kill_late_share_t',
|
||||||
|
'rd_phase_kill_early_share_ct', 'rd_phase_kill_mid_share_ct', 'rd_phase_kill_late_share_ct',
|
||||||
|
'rd_phase_death_early_share_t', 'rd_phase_death_mid_share_t', 'rd_phase_death_late_share_t',
|
||||||
|
'rd_phase_death_early_share_ct', 'rd_phase_death_mid_share_ct', 'rd_phase_death_late_share_ct',
|
||||||
'rd_firstdeath_team_first_death_win_rate', 'rd_invalid_death_rate',
|
'rd_firstdeath_team_first_death_win_rate', 'rd_invalid_death_rate',
|
||||||
'rd_pressure_kpr_ratio', 'rd_matchpoint_kpr_ratio', 'rd_trade_response_10s_rate',
|
'rd_pressure_kpr_ratio', 'rd_matchpoint_kpr_ratio', 'rd_trade_response_10s_rate',
|
||||||
'rd_pressure_perf_ratio', 'rd_matchpoint_perf_ratio',
|
'rd_pressure_perf_ratio', 'rd_matchpoint_perf_ratio',
|
||||||
|
|||||||
@@ -349,8 +349,99 @@
|
|||||||
<div class="mt-6 grid grid-cols-1 lg:grid-cols-3 gap-4">
|
<div class="mt-6 grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||||
<div class="bg-gray-50 dark:bg-slate-700/30 rounded-xl p-4 border border-gray-100 dark:border-slate-600">
|
<div class="bg-gray-50 dark:bg-slate-700/30 rounded-xl p-4 border border-gray-100 dark:border-slate-600">
|
||||||
<div class="text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Phase Split</div>
|
<div class="text-xs font-bold text-gray-400 uppercase tracking-wider mb-2">Phase Split</div>
|
||||||
<div class="h-40">
|
{% macro phase_row(title, ke, km, kl, de, dm, dl, ke_key, km_key, kl_key, de_key, dm_key, dl_key) %}
|
||||||
<canvas id="phaseChart"></canvas>
|
{% set ke = ke or 0 %}
|
||||||
|
{% set km = km or 0 %}
|
||||||
|
{% set kl = kl or 0 %}
|
||||||
|
{% set de = de or 0 %}
|
||||||
|
{% set dm = dm or 0 %}
|
||||||
|
{% set dl = dl or 0 %}
|
||||||
|
{% set k_total = ke + km + kl %}
|
||||||
|
{% set d_total = de + dm + dl %}
|
||||||
|
<div class="grid grid-cols-12 gap-2 items-center py-1">
|
||||||
|
<div class="col-span-2 text-[10px] font-bold text-gray-500 dark:text-gray-400">{{ title }}</div>
|
||||||
|
<div class="col-span-5">
|
||||||
|
<div class="w-full h-2 rounded-full overflow-hidden bg-gray-200/60 dark:bg-slate-600/50 flex">
|
||||||
|
{% if k_total > 0 %}
|
||||||
|
<div class="h-full bg-yrtv-500" style="width: {{ (ke / k_total) * 100 }}%"></div>
|
||||||
|
<div class="h-full bg-yrtv-500/70" style="width: {{ (km / k_total) * 100 }}%"></div>
|
||||||
|
<div class="h-full bg-yrtv-500/40" style="width: {{ (kl / k_total) * 100 }}%"></div>
|
||||||
|
{% else %}
|
||||||
|
<div class="h-full w-full bg-gray-300/50 dark:bg-slate-600/40"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="mt-0.5 text-[9px] text-gray-400 font-mono flex justify-between">
|
||||||
|
<span>
|
||||||
|
E {{ '{:.0%}'.format(ke) }}
|
||||||
|
{% if distribution and distribution.get(ke_key) %} (#{{ distribution.get(ke_key).rank }}/{{ distribution.get(ke_key).total }}){% endif %}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
M {{ '{:.0%}'.format(km) }}
|
||||||
|
{% if distribution and distribution.get(km_key) %} (#{{ distribution.get(km_key).rank }}/{{ distribution.get(km_key).total }}){% endif %}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
L {{ '{:.0%}'.format(kl) }}
|
||||||
|
{% if distribution and distribution.get(kl_key) %} (#{{ distribution.get(kl_key).rank }}/{{ distribution.get(kl_key).total }}){% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-5">
|
||||||
|
<div class="w-full h-2 rounded-full overflow-hidden bg-gray-200/60 dark:bg-slate-600/50 flex">
|
||||||
|
{% if d_total > 0 %}
|
||||||
|
<div class="h-full bg-slate-400" style="width: {{ (de / d_total) * 100 }}%"></div>
|
||||||
|
<div class="h-full bg-slate-400/70" style="width: {{ (dm / d_total) * 100 }}%"></div>
|
||||||
|
<div class="h-full bg-slate-400/40" style="width: {{ (dl / d_total) * 100 }}%"></div>
|
||||||
|
{% else %}
|
||||||
|
<div class="h-full w-full bg-gray-300/50 dark:bg-slate-600/40"></div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="mt-0.5 text-[9px] text-gray-400 font-mono flex justify-between">
|
||||||
|
<span>
|
||||||
|
E {{ '{:.0%}'.format(de) }}
|
||||||
|
{% if distribution and distribution.get(de_key) %} (#{{ distribution.get(de_key).rank }}/{{ distribution.get(de_key).total }}){% endif %}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
M {{ '{:.0%}'.format(dm) }}
|
||||||
|
{% if distribution and distribution.get(dm_key) %} (#{{ distribution.get(dm_key).rank }}/{{ distribution.get(dm_key).total }}){% endif %}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
L {{ '{:.0%}'.format(dl) }}
|
||||||
|
{% if distribution and distribution.get(dl_key) %} (#{{ distribution.get(dl_key).rank }}/{{ distribution.get(dl_key).total }}){% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
<div class="text-[10px] text-gray-500 dark:text-gray-400 mb-2 grid grid-cols-12 gap-2">
|
||||||
|
<div class="col-span-2"></div>
|
||||||
|
<div class="col-span-5 flex justify-between">
|
||||||
|
<span>Kills</span><span class="font-mono">E / M / L</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-5 flex justify-between">
|
||||||
|
<span>Deaths</span><span class="font-mono">E / M / L</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-1">
|
||||||
|
{{ phase_row('Total',
|
||||||
|
features.get('rd_phase_kill_early_share', 0), features.get('rd_phase_kill_mid_share', 0), features.get('rd_phase_kill_late_share', 0),
|
||||||
|
features.get('rd_phase_death_early_share', 0), features.get('rd_phase_death_mid_share', 0), features.get('rd_phase_death_late_share', 0),
|
||||||
|
'rd_phase_kill_early_share', 'rd_phase_kill_mid_share', 'rd_phase_kill_late_share',
|
||||||
|
'rd_phase_death_early_share', 'rd_phase_death_mid_share', 'rd_phase_death_late_share'
|
||||||
|
) }}
|
||||||
|
{{ phase_row('T',
|
||||||
|
features.get('rd_phase_kill_early_share_t', 0), features.get('rd_phase_kill_mid_share_t', 0), features.get('rd_phase_kill_late_share_t', 0),
|
||||||
|
features.get('rd_phase_death_early_share_t', 0), features.get('rd_phase_death_mid_share_t', 0), features.get('rd_phase_death_late_share_t', 0),
|
||||||
|
'rd_phase_kill_early_share_t', 'rd_phase_kill_mid_share_t', 'rd_phase_kill_late_share_t',
|
||||||
|
'rd_phase_death_early_share_t', 'rd_phase_death_mid_share_t', 'rd_phase_death_late_share_t'
|
||||||
|
) }}
|
||||||
|
{{ phase_row('CT',
|
||||||
|
features.get('rd_phase_kill_early_share_ct', 0), features.get('rd_phase_kill_mid_share_ct', 0), features.get('rd_phase_kill_late_share_ct', 0),
|
||||||
|
features.get('rd_phase_death_early_share_ct', 0), features.get('rd_phase_death_mid_share_ct', 0), features.get('rd_phase_death_late_share_ct', 0),
|
||||||
|
'rd_phase_kill_early_share_ct', 'rd_phase_kill_mid_share_ct', 'rd_phase_kill_late_share_ct',
|
||||||
|
'rd_phase_death_early_share_ct', 'rd_phase_death_mid_share_ct', 'rd_phase_death_late_share_ct'
|
||||||
|
) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-gray-50 dark:bg-slate-700/30 rounded-xl p-4 border border-gray-100 dark:border-slate-600">
|
<div class="bg-gray-50 dark:bg-slate-700/30 rounded-xl p-4 border border-gray-100 dark:border-slate-600">
|
||||||
@@ -1000,54 +1091,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
const phaseCanvas = document.getElementById('phaseChart');
|
const phaseCanvas = document.getElementById('phaseChart');
|
||||||
if (phaseCanvas) {
|
if (phaseCanvas) {
|
||||||
const ctxPhase = phaseCanvas.getContext('2d');
|
phaseCanvas.remove();
|
||||||
new Chart(ctxPhase, {
|
|
||||||
type: 'bar',
|
|
||||||
data: {
|
|
||||||
labels: ['Early', 'Mid', 'Late'],
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: 'Kills',
|
|
||||||
data: [
|
|
||||||
{{ features.get('rd_phase_kill_early_share', 0) }},
|
|
||||||
{{ features.get('rd_phase_kill_mid_share', 0) }},
|
|
||||||
{{ features.get('rd_phase_kill_late_share', 0) }}
|
|
||||||
],
|
|
||||||
backgroundColor: 'rgba(124, 58, 237, 0.55)'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Deaths',
|
|
||||||
data: [
|
|
||||||
{{ features.get('rd_phase_death_early_share', 0) }},
|
|
||||||
{{ features.get('rd_phase_death_mid_share', 0) }},
|
|
||||||
{{ features.get('rd_phase_death_late_share', 0) }}
|
|
||||||
],
|
|
||||||
backgroundColor: 'rgba(148, 163, 184, 0.55)'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
scales: {
|
|
||||||
y: {
|
|
||||||
beginAtZero: true,
|
|
||||||
suggestedMax: 1,
|
|
||||||
ticks: {
|
|
||||||
callback: (v) => `${Math.round(v * 100)}%`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: { display: true, position: 'bottom' },
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
label: (ctx) => `${ctx.dataset.label}: ${(ctx.parsed.y * 100).toFixed(1)}%`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const weaponTop = JSON.parse({{ (features.get('rd_weapon_top_json', '[]') or '[]') | tojson }});
|
const weaponTop = JSON.parse({{ (features.get('rd_weapon_top_json', '[]') or '[]') | tojson }});
|
||||||
|
|||||||
Reference in New Issue
Block a user