Conversation
db/src/models/game.rs
Outdated
| "" | ||
| }; | ||
|
|
||
| let query = format!(r#" |
There was a problem hiding this comment.
We never use raw sql, but diesel functions. That said this might be hard to achieve without raw sql but give it a try
There was a problem hiding this comment.
I tried! It cannot do multiple group bys one of which is a case statement...
Yes, those are rated. I'll add a note.
Those are basically Black or White has a small advantage between 0 and 100. I'll think about labels. But the number is not the same, either white has higher rating in a game or black.
Yup yup will add a clarification! |
32c74c2 to
248951b
Compare
|
I really dislike the huge type and function names like SiteStatisticsWinrateByRatingDifferenceResponse, totally a taste thing tho dunno if felix agrees. |
I'm happy to optimize names and code, but need hints for the direction 🙂 |
|
I guess I could move all the aggregate functions out of Game or User objects directly into the response file. Not that it actually reduces the amount of code but at least keeps the objects free of complex functions. (Might be not possible though, all the diesel stuff should be in db?) I will reduce the names too. Other than that I don't see a lot of potential for the code to be more concise. It does a lot of aggregations to display a ton of data... |
I will get back to you on this when I've had a more thorough think. But yeah all the diesel stuff needs to stay in the db so don't move that out. |
apis/src/pages/statistics.rs
Outdated
|
|
||
| #[component] | ||
| pub fn Statistics() -> impl IntoView { | ||
| let (selected_speed, set_selected_speed) = signal("All speeds".to_string()); |
There was a problem hiding this comment.
1.We have types for speed and gametype don't use strings like "All speeds" if the type can't easily do what you want it to do you can add methods to the type for that. This probably happens elsewhere as well, wherever you use a string you need to use a type, the type get to_stringed when going into the db and get converted back into types in the responses and handed back as types to the frontend.
2.Unless you have a good reason to have separate read and write signals (like you are using a library that needs separate signals) use RwSignal instead with RwSignal::new(...) and that way you have both get and set on the same signal. The codebase uses RwSignals everywhere and separate read and write rarely.
| let label_for_fallback = label.clone(); | ||
| let label_for_error = label.clone(); | ||
|
|
||
| let user_resource = LocalResource::new(move || { |
There was a problem hiding this comment.
Maybe instead of looping over n users here and doing another db call for each, the response for this type should contain the complete user data (get a vector of users from the user model based on what the game model returns and that way it's two queries not n+1)
| <button | ||
| type="button" | ||
| class=move || { | ||
| if selected_period.get() == period { |
There was a problem hiding this comment.
In this case this is fine because SiteStatisticsTimePeriod is just a plain enum but if were anything more complex you want to use .with and that way you do the comparison without cloning the value.
selected_period.with(|p| p == period)| {speeds | ||
| .iter() | ||
| .map(|speed| { | ||
| let speed_clone = speed.clone(); |
There was a problem hiding this comment.
switching to types instead of strings here will avoid all the needing to clone multiple times.
In general if you find yourself needing to do a bunch of let x,x1,x2 = y.clone()... consider using let x = StoredValue::new(y) and wherever you need that value you call x.get_value()
| .map(|add_val| { | ||
| view! { | ||
| <div class="text-m text-gray-500 dark:text-gray-300 ml-1"> | ||
| {format!("{:.1}", add_val)} "%" |
There was a problem hiding this comment.
Clippy will complain about this, use the add_value inside the format string where it's possible. Once you are done with the pr check yourself by running cargo clippy and follow it's suggestions.
248951b to
5e98a88
Compare
|
I'm yet to tackle issues raised by Ion, so don't look at it yet :D |
2880d42 to
9085c08
Compare
|
I think I fixed all issues and rebased to the current code so marking ready for review. |
|
Some of the files touched are formatters' work. I can revert those if necessary. |
Please check that it still compiles after a rebase. In this case it doesn't as there are duplicate definitions |
9085c08 to
1318693
Compare
Oops, deleted the duplicates. |
klautcomputing
left a comment
There was a problem hiding this comment.
I only skimmed this, there is some stuff I would like to change. Most importantly there should not be changes to the engine, the raw SQL queries (this is why we use an ORM) and the VeryLongJavaLikeNames.
I will give this a better look tomorrow with Ion and leave you some more detailed comments.
engine/src/game_type.rs
Outdated
| MLP, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Copy, Default)] |
There was a problem hiding this comment.
I don't think you should touch the engine in this PR :)
There was a problem hiding this comment.
I just did what Ion asked me to do... it's just some extra methods on the enum, it doesn't touch any substantial logic. I could move this new logic into a new shared type or something, but that mean breaking DRY. Anyhow, any specific suggestions appreciated.
There was a problem hiding this comment.
I might not have expressed myself clearly enough, but when I said add methods to the types, I didn't also mean extend an enum with an extra variant for example, like GameSpeed:AllSpeeds. I was trying to say use what's already there from that type instead of using special strings to achieve your goal.
There was a problem hiding this comment.
The engine has no need for GameTypeFilter or the SQL stuff, why would we keep it in the engine then? Also it doesn't make the code DRYer, the enum could live somewhere else. If you compile the the engine's code now, it is actually dead code...
There was a problem hiding this comment.
I might not have expressed myself clearly enough, but when I said add methods to the types, I didn't also mean extend an enum with an extra variant for example, like GameSpeed:AllSpeeds. I was trying to say use what's already there from that type instead of using special strings to achieve your goal.
If the speeds should be passed as a type between the db through responses to the component as you suggested then I'm not sure how to do it without adding to the type itself. I need AllSpeeds for almost all aggregates where you see UNION ALL. I'd really appreciate specific suggestions instead of just "you can't do this" comments.
(This also extends to the raw SQL comments: I checked the documentation and different GPTs, there's no way to have multiple group bys by calculated columns. They themselves admit that in their readme: https://github.com/diesel-rs/diesel?tab=readme-ov-file#raw-sql . If you have a specific way in mind on how to do it, please let me know, I'm happy to rewrite it. )
|
I understand the raw SQL concern but it would be significantly more complex code and instead of pulling aggregates it would load thousand of games into memory without any substantial benefit. The long names I shortened some already, if you have any specific you don't like I can F2 no problem :) |
More complex maybe but diesel can express the queries you wrote and writing them in diesel wouldn't mean loading the games in memory, it can load exactly what the raw sql is loading. |
|
One thing that is missing is this /statistics page is never exposed as a link anywhere internally. There should be buttons in the dropdowns leading to the page. |
klautcomputing
left a comment
There was a problem hiding this comment.
@IongIer and I checked out the feature and we have some feedback for you on how it work.
I am going to look at the code a bit more when it's working nicely :)
| #[derive( | ||
| Debug, Serialize, PartialEq, Eq, Deserialize, Clone, Copy, Hash, PartialOrd, Ord, Default, | ||
| )] | ||
| pub enum SiteStatisticsTimePeriod { |
There was a problem hiding this comment.
| pub enum SiteStatisticsTimePeriod { | |
| pub enum TimePeriod { |
|
|
||
| view! { | ||
| <h1 class="text-xl font-bold text-center"> | ||
| "Player ratings distribution" |
There was a problem hiding this comment.
This just means the users in the db don't have enough games probably and there's exactly one user for each bucket (buckets are step 10). Try to create 200-300 games per user?
There was a problem hiding this comment.
All users have 50+ games, also then the scale doesn't make sense because there can't be fractional users :)
I am gonna retry with more users and games and see whether I can get different results.
There was a problem hiding this comment.
All users have 50+ games, also then the scale doesn't make sense because there can't be fractional users :)
I am gonna retry with more users and games and see whether I can get different results.
| } | ||
|
|
||
| #[component] | ||
| pub fn SpeedSelector(speeds: Vec<GameSpeed>, selected_speed: RwSignal<GameSpeed>) -> impl IntoView { |
There was a problem hiding this comment.
I think it doesn't make sense to repeat all the buttons if they are all linked.

Also clicking one of them makes the whole layout change and jump. I think the right thing to do here is to have the speedselectors and the other selectors (time period, include bot, ...) all be in one floating header so that you always have access to them and don't need to scroll around.
engine/src/game_type.rs
Outdated
|
|
||
| #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Copy, Default)] | ||
| pub enum GameTypeFilter { | ||
| Base, |
There was a problem hiding this comment.
Not sure where to leave this comment so here it goes: Base games should not display elo statistics as we have no elo for base games :)
| fallback=|| { | ||
| view! { | ||
| <div class="text-center py-8 text-gray-500 dark:text-gray-400"> | ||
| "No data available" |
There was a problem hiding this comment.
yeah I guess I can try to make a common floating selector and that would actually allow to get rid of the "Games by type and speed" table, the speed selector will just update the single stat cards. And will fix the headers when data is missing.
1318693 to
dce4f5f
Compare
dce4f5f to
0178ae7
Compare
|
I made filters collapsible and floating and moved stuff out of the engine, plus cleaned up some minor things. I don't see a clean way to get rid of AllSpeeds and raw SQL, but, again, if you do, please let me know what it looks like. |
I think it's ok if it's not exposed at first tbh, I can make a further diff for that. I'd like to see what the page looks like on live data. |





Just a very simple rough first try for the site stats page.
If you guys can try on live data and post the screenshot would be great. Not in the pages directory yet akin to puzzles and whatnot.
I have some ideas on what else can be added but I need think how to get some more fake data in my local db...