1
0
mirror of https://git.ngram.ca/OpenJam/rc-servers synced 2026-08-23 23:08:52 +00:00

rc_society dashboard display (#131)

### Description

Closes #130

### Please confirm

- [x] I am the legal owner or represent the legal owner of all work submitted (including LLM-generated code, if any)
- [x] I consent to my changes being added to this FOSS project
- [x] I have confirmed that this does not add new errors or warnings with `utils/clippy.sh`
- [ ] This PR used LLMs to generate some or all of the code changes

Reviewed-on: https://git.ngram.ca/OpenJam/rc-servers/pulls/131
This commit is contained in:
NG (Graham)
2026-06-20 21:48:09 +00:00
committed by NGnius
parent d2626bb47f
commit b597e1337d
19 changed files with 741 additions and 109 deletions

View File

@@ -33,7 +33,7 @@ impl FactoryDatabase {
.and_then(|(vehicle, user_opt)| user_opt.map(|user| (vehicle, user))))
}
async fn list_query(&self, query: libfj::robocraft::ListQuery) -> Result<Vec<(crate::schema::factory::vehicle_query::VehicleInfo, crate::schema::factory::vehicle_query::VehicleOwnerInfo)>, sea_orm::DbErr> {
fn build_list_query(&self, query: &libfj::robocraft::ListQuery) -> sea_orm::Selector<sea_orm::SelectTwoModel<crate::schema::factory::vehicle_query::VehicleInfo, crate::schema::factory::vehicle_query::VehicleOwnerInfo>> {
let mut query_builder = crate::schema::factory::vehicle::Entity::find()
.filter(crate::schema::factory::vehicle::Column::RemovedDate.is_null())
.filter(crate::schema::factory::vehicle::Column::BanDate.is_null())
@@ -91,7 +91,7 @@ impl FactoryDatabase {
query_builder = query_builder.filter(crate::schema::factory::vehicle::Column::Cpu.lte(query.maximum_cpu as u32));
}
let query_builder = query_builder
query_builder
.find_also_related(crate::schema::user::Entity)
.select_only()
// WARNING: jank sea_orm query generation (fix)
@@ -115,8 +115,11 @@ impl FactoryDatabase {
.column_as(crate::schema::factory::vehicle::Column::CubeAmounts, "A_cube_amounts")
.column_as(crate::schema::user::Column::PublicId, "B_public_id")
.column_as(crate::schema::user::Column::DisplayName, "B_display_name")
.into_model::<crate::schema::factory::vehicle_query::VehicleInfo, crate::schema::factory::vehicle_query::VehicleOwnerInfo>();
//.into_partial_model::<crate::schema::factory::vehicle_query::VehicleInfo, crate::schema::factory::vehicle_query::VehicleOwnerInfo>();
.into_model::<crate::schema::factory::vehicle_query::VehicleInfo, crate::schema::factory::vehicle_query::VehicleOwnerInfo>()
}
async fn list_query(&self, query: libfj::robocraft::ListQuery) -> Result<Vec<(crate::schema::factory::vehicle_query::VehicleInfo, crate::schema::factory::vehicle_query::VehicleOwnerInfo)>, sea_orm::DbErr> {
let query_builder = self.build_list_query(&query);
let pagination = query_builder.paginate(self.orm.as_ref(), query.page_size as u64);
Ok(pagination.fetch_page((query.page - 1) as u64).await?
.into_iter()
@@ -124,6 +127,11 @@ impl FactoryDatabase {
.collect())
}
async fn count_list_query(&self, query: libfj::robocraft::ListQuery) -> Result<u64, sea_orm::DbErr> {
let query_builder = self.build_list_query(&query);
query_builder.count(self.orm.as_ref()).await
}
fn thumbnail_url(&self, id: i32) -> String {
format!("{}/roboshop/Live/{}", self.cdn, id)
}
@@ -207,6 +215,11 @@ impl VehicleFactoryAdapter for FactoryDatabase {
}).collect())
}
async fn count(&self, query: libfj::robocraft::ListQuery) -> Result<usize, Box<dyn std::error::Error>> {
let count = self.count_list_query(query).await?;
Ok(count as usize)
}
async fn upload(&self, vehicle: VehicleUploadInfo) -> Result<VehicleThumbnailInfo, Box<dyn std::error::Error>> {
let now = chrono::Utc::now();
let entity = crate::schema::factory::vehicle::ActiveModel {

View File

@@ -1,5 +1,5 @@
use sea_orm_migration::MigratorTrait;
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, TransactionTrait, sea_query::ExprTrait};
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, TransactionTrait, sea_query::ExprTrait, sqlx::types::chrono};
pub struct Database {
orm: std::sync::Arc<sea_orm::DatabaseConnection>,
@@ -224,6 +224,32 @@ impl Database {
.await
}
pub async fn garage_count_by_user_id_between(&self, user_id: i32, max_cpu: u64, min_cpu: u64) -> Result<u64, sea_orm::DbErr> {
crate::schema::garage::Entity::find()
.filter(crate::schema::garage::Column::UserId.eq(user_id))
.filter(crate::schema::garage::Column::TotalRobotCpu.lte(max_cpu))
.filter(crate::schema::garage::Column::TotalRobotCpu.gte(min_cpu))
.count(self.orm.as_ref())
.await
}
pub async fn garage_count_by_user_id_factory(&self, user_id: i32) -> Result<u64, sea_orm::DbErr> {
crate::schema::garage::Entity::find()
.filter(crate::schema::garage::Column::UserId.eq(user_id))
.filter(crate::schema::garage::Column::CrfId.is_not_null())
.count(self.orm.as_ref())
.await
}
pub async fn garage_storage_by_user_id(&self, _user_id: i32) -> Result<Option<u64>, sea_orm::DbErr> {
let size = match self.orm.as_ref() {
// TODO implement support in other databases
//sea_orm::DatabaseConnection::SqlxPostgresPoolConnection(_) => None,
_ => None,
};
Ok(size)
}
pub async fn garage_max_slot_by_user_id(&self, user_id: i32) -> Result<i32, sea_orm::DbErr> {
let result = crate::schema::garage::Entity::find()
.select_only()
@@ -373,6 +399,32 @@ impl Database {
.await
}
pub async fn count_sanctions_by_user_id(&self, user_id: i32) -> Result<u64, sea_orm::DbErr> {
crate::schema::sanction::Entity::find()
.filter(crate::schema::sanction::Column::UserId.eq(user_id))
//.order_by_asc(crate::schema::sanction::Column::CreationTime)
.count(self.orm.as_ref())
.await
}
pub async fn count_sanctions_by_user_id_and_ack(&self, user_id: i32, is_acked: bool) -> Result<u64, sea_orm::DbErr> {
crate::schema::sanction::Entity::find()
.filter(crate::schema::sanction::Column::UserId.eq(user_id))
.filter(crate::schema::sanction::Column::Acknowledged.eq(is_acked))
//.order_by_asc(crate::schema::sanction::Column::CreationTime)
.count(self.orm.as_ref())
.await
}
pub async fn count_sanctions_by_user_id_and_descriptor(&self, user_id: i32, desc: crate::schema::sanction::Descriptor) -> Result<u64, sea_orm::DbErr> {
crate::schema::sanction::Entity::find()
.filter(crate::schema::sanction::Column::UserId.eq(user_id))
.filter(crate::schema::sanction::Column::Descriptor.eq(desc))
//.order_by_asc(crate::schema::sanction::Column::CreationTime)
.count(self.orm.as_ref())
.await
}
pub async fn sanctions_by_user_id(&self, user_id: i32) -> Result<Vec<crate::schema::sanction::Model>, sea_orm::DbErr> {
crate::schema::sanction::Entity::find()
.filter(crate::schema::sanction::Column::UserId.eq(user_id))
@@ -382,6 +434,19 @@ impl Database {
.await
}
pub async fn sanction_by_user_id_and_descriptor_and_active(&self, user_id: i32, desc: crate::schema::sanction::Descriptor) -> Result<Option<crate::schema::sanction::Model>, sea_orm::DbErr> {
let now = chrono::Utc::now().timestamp();
let expiry_expr = sea_orm::sea_query::Expr::col(crate::schema::sanction::Column::CreationTime).add(sea_orm::sea_query::Expr::col(crate::schema::sanction::Column::Duration));
crate::schema::sanction::Entity::find()
.filter(crate::schema::sanction::Column::UserId.eq(user_id))
.filter(crate::schema::sanction::Column::Descriptor.eq(desc))
.filter(crate::schema::sanction::Column::Duration.is_not_null())
.filter(expiry_expr.clone().gte(now))
.order_by_asc(crate::schema::sanction::Column::CreationTime)
.one(self.orm.as_ref())
.await
}
pub async fn insert_sanction(&self, entity: crate::schema::sanction::ActiveModel) -> Result<crate::schema::sanction::Model, sea_orm::DbErr> {
entity.insert(self.orm.as_ref()).await
}
@@ -400,6 +465,13 @@ impl Database {
.await
}
pub async fn count_games_by_user_id(&self, user_id: i32) -> Result<u64, sea_orm::DbErr> {
crate::schema::multiplayer_game_player::Entity::find()
.filter(crate::schema::multiplayer_game_player::Column::UserId.eq(user_id))
.count(self.orm.as_ref())
.await
}
pub async fn game_by_user_id_and_completion(&self, user_id: i32, is_complete: bool) -> Result<Option<crate::schema::multiplayer_game::Model>, sea_orm::DbErr> {
Ok(crate::schema::multiplayer_game::Entity::find()
.find_also_related(crate::schema::multiplayer_game_player::Entity)
@@ -610,6 +682,24 @@ impl Database {
.await
}
pub async fn count_friends_by_user_id(&self, user_id: i32, status_in: crate::schema::friend::FriendStatus) -> Result<u64, sea_orm::DbErr> {
crate::schema::friend::Entity::find()
.filter(crate::schema::friend::Column::FriendSource.eq(user_id))
.filter(crate::schema::friend::Column::State.eq(status_in))
//.order_by_asc(crate::schema::friend::Column::CreationTime)
.count(self.orm.as_ref())
.await
}
pub async fn count_friends_target_by_user_id(&self, user_id: i32, status_in: crate::schema::friend::FriendStatus) -> Result<u64, sea_orm::DbErr> {
crate::schema::friend::Entity::find()
.filter(crate::schema::friend::Column::FriendTarget.eq(user_id))
.filter(crate::schema::friend::Column::State.eq(status_in))
//.order_by_asc(crate::schema::friend::Column::CreationTime)
.count(self.orm.as_ref())
.await
}
pub async fn friends_by_user_id(&self, user_id: i32, status_not_in: impl IntoIterator<Item=crate::schema::friend::FriendStatus>) -> Result<Vec<(crate::schema::friend::Model, crate::schema::user::Model)>, sea_orm::DbErr> {
Ok(crate::schema::friend::Entity::find()
.find_also_related(crate::schema::user::Entity)