@@ -11,9 +11,11 @@ use lru_time_cache::LruCache;
11
11
use serde:: { Deserialize , Serialize } ;
12
12
use tide:: Request ;
13
13
14
- use crate :: models:: { AccountInfo , AllStatistics , TankSnapshot } ;
14
+ use crate :: models:: { AccountInfo , AllStatistics , TankSnapshot , Vehicle } ;
15
+ use crate :: statistics:: wilson_score_interval_90;
15
16
use crate :: wargaming:: WargamingApi ;
16
17
use crate :: web:: state:: State ;
18
+ use std:: cmp:: Ordering ;
17
19
18
20
lazy_static ! {
19
21
static ref ACCOUNT_INFO_CACHE : Arc <Mutex <LruCache <i32 , Arc <AccountInfo >>>> =
@@ -40,7 +42,7 @@ pub struct PlayerViewModel {
40
42
pub query : Query ,
41
43
pub warn_no_previous_account_info : bool ,
42
44
pub statistics : AllStatistics ,
43
- pub tank_snapshots : Vec < TankSnapshot > ,
45
+ pub rows : Vec < TankRow > ,
44
46
}
45
47
46
48
impl PlayerViewModel {
@@ -84,9 +86,27 @@ impl PlayerViewModel {
84
86
} else {
85
87
Vec :: new ( )
86
88
} ;
87
- let mut tank_snapshots =
88
- Self :: subtract_tank_snapshots ( actual_tanks. to_vec ( ) , previous_tanks) ;
89
- tank_snapshots. sort_by_key ( |snapshot| -snapshot. all_statistics . battles ) ;
89
+ let tank_snapshots = Self :: subtract_tank_snapshots ( actual_tanks. to_vec ( ) , previous_tanks) ;
90
+
91
+ let mut rows: Vec < TankRow > = Vec :: new ( ) ;
92
+ for snapshot in tank_snapshots. into_iter ( ) {
93
+ let stats = & snapshot. all_statistics ;
94
+ let vehicle = state. get_vehicle ( snapshot. tank_id ) . await ?;
95
+ let win_rate = stats. wins as f64 / stats. battles as f64 ;
96
+ let true_win_rate = wilson_score_interval_90 ( stats. battles , stats. wins ) ;
97
+ rows. push ( TankRow {
98
+ win_rate,
99
+ true_win_rate,
100
+ damage_per_battle : stats. damage_dealt as f64 / stats. battles as f64 ,
101
+ survival_rate : stats. survived_battles as f64 / stats. battles as f64 ,
102
+ all_statistics : snapshot. all_statistics ,
103
+ gold_per_battle : 10.0 + vehicle. tier as f64 * win_rate,
104
+ true_gold_per_battle : 10.0 + vehicle. tier as f64 * true_win_rate. 0 ,
105
+ vehicle,
106
+ } ) ;
107
+ }
108
+ Self :: sort_vehicles ( & mut rows, query. sort_by ) ;
109
+
90
110
let warn_no_previous_account_info = previous_account_info. is_none ( ) ;
91
111
let statistics = actual_statistics
92
112
. sub ( & previous_account_info. map_or_else ( Default :: default, |info| info. statistics . all ) ) ;
@@ -103,7 +123,7 @@ impl PlayerViewModel {
103
123
query,
104
124
warn_no_previous_account_info,
105
125
statistics,
106
- tank_snapshots ,
126
+ rows ,
107
127
total_tanks,
108
128
} )
109
129
}
@@ -180,6 +200,59 @@ impl PlayerViewModel {
180
200
. collect :: < Vec < TankSnapshot > > ( )
181
201
}
182
202
203
+ fn sort_vehicles ( rows : & mut Vec < TankRow > , sort_by : SortBy ) {
204
+ match sort_by {
205
+ SortBy :: Battles => rows. sort_unstable_by_key ( |row| -row. all_statistics . battles ) ,
206
+ SortBy :: Wins => rows. sort_unstable_by_key ( |row| -row. all_statistics . wins ) ,
207
+ SortBy :: Nation => rows. sort_unstable_by_key ( |row| row. vehicle . nation ) ,
208
+ SortBy :: DamageDealt => {
209
+ rows. sort_unstable_by_key ( |row| -row. all_statistics . damage_dealt )
210
+ }
211
+ SortBy :: DamagePerBattle => rows. sort_unstable_by ( |left, right| {
212
+ right
213
+ . damage_per_battle
214
+ . partial_cmp ( & left. damage_per_battle )
215
+ . unwrap_or ( Ordering :: Equal )
216
+ } ) ,
217
+ SortBy :: Tier => rows. sort_unstable_by_key ( |row| -row. vehicle . tier ) ,
218
+ SortBy :: VehicleType => rows. sort_unstable_by_key ( |row| row. vehicle . type_ ) ,
219
+ SortBy :: WinRate => rows. sort_unstable_by ( |left, right| {
220
+ right
221
+ . win_rate
222
+ . partial_cmp ( & left. win_rate )
223
+ . unwrap_or ( Ordering :: Equal )
224
+ } ) ,
225
+ SortBy :: TrueWinRate => rows. sort_unstable_by ( |left, right| {
226
+ right
227
+ . true_win_rate
228
+ . 0
229
+ . partial_cmp ( & left. true_win_rate . 0 )
230
+ . unwrap_or ( Ordering :: Equal )
231
+ } ) ,
232
+ SortBy :: Gold => rows. sort_unstable_by ( |left, right| {
233
+ right
234
+ . gold_per_battle
235
+ . partial_cmp ( & left. gold_per_battle )
236
+ . unwrap_or ( Ordering :: Equal )
237
+ } ) ,
238
+ SortBy :: TrueGold => rows. sort_unstable_by ( |left, right| {
239
+ right
240
+ . true_gold_per_battle
241
+ . partial_cmp ( & left. true_gold_per_battle )
242
+ . unwrap_or ( Ordering :: Equal )
243
+ } ) ,
244
+ SortBy :: SurvivedBattles => {
245
+ rows. sort_unstable_by_key ( |row| -row. all_statistics . survived_battles )
246
+ }
247
+ SortBy :: SurvivalRate => rows. sort_unstable_by ( |left, right| {
248
+ right
249
+ . survival_rate
250
+ . partial_cmp ( & left. survival_rate )
251
+ . unwrap_or ( Ordering :: Equal )
252
+ } ) ,
253
+ }
254
+ }
255
+
183
256
/// Inserts account if it doesn't exist. The rest is updated by [`crate::crawler`].
184
257
async fn insert_account_or_ignore (
185
258
state : & State ,
@@ -208,6 +281,17 @@ impl PlayerViewModel {
208
281
}
209
282
}
210
283
284
+ pub struct TankRow {
285
+ pub vehicle : Arc < Vehicle > ,
286
+ pub all_statistics : AllStatistics ,
287
+ pub win_rate : f64 ,
288
+ pub true_win_rate : ( f64 , f64 ) ,
289
+ pub damage_per_battle : f64 ,
290
+ pub survival_rate : f64 ,
291
+ pub gold_per_battle : f64 ,
292
+ pub true_gold_per_battle : f64 ,
293
+ }
294
+
211
295
#[ derive( Deserialize , Serialize , Clone , Copy ) ]
212
296
pub struct Query {
213
297
#[ serde( default = "default_period" , with = "humantime_serde" ) ]
@@ -244,17 +328,16 @@ impl Query {
244
328
#[ derive( Serialize , Deserialize , Clone , Copy , PartialEq ) ]
245
329
#[ serde( rename_all = "kebab-case" ) ]
246
330
pub enum SortBy {
247
- Vehicle ,
248
331
Battles ,
249
332
Tier ,
250
333
Nation ,
251
334
VehicleType ,
252
- WonBattles ,
335
+ Wins ,
253
336
WinRate ,
254
337
TrueWinRate ,
255
338
Gold ,
256
339
TrueGold ,
257
- Damage ,
340
+ DamageDealt ,
258
341
DamagePerBattle ,
259
342
SurvivedBattles ,
260
343
SurvivalRate ,
0 commit comments