From b9fbbbbbd6aacb5a7a49297957677798084be482 Mon Sep 17 00:00:00 2001 From: abhishekbhakat Date: Fri, 19 Dec 2025 13:52:48 +0530 Subject: [PATCH] feat: add diet entity and user lifestyle habits with data seeding support --- backend/seed.yaml | 10 ++++++++++ backend/src/db.rs | 3 ++- backend/src/models/user/diet.rs | 31 +++++++++++++++++++++++++++++++ backend/src/models/user/mod.rs | 2 ++ backend/src/models/user/user.rs | 24 +++++++++++++++++++++++- backend/src/seed.rs | 27 ++++++++++++++++++++++++++- 6 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 backend/src/models/user/diet.rs diff --git a/backend/seed.yaml b/backend/seed.yaml index f88eae6..8058ca9 100644 --- a/backend/seed.yaml +++ b/backend/seed.yaml @@ -9,6 +9,16 @@ roles: - name: reader description: Read-only access to analytics +diets: + - name: normal + description: Standard balanced diet without specific restrictions + - name: vegetarian + description: No meat or fish; includes dairy and eggs + - name: vegan + description: Purely plant-based; no animal products + - name: jain + description: Strictly vegetarian; no root vegetables (onions, potatoes, garlic, etc.) + biomarker_categories: - name: blood description: Blood cell counts and hemogram markers diff --git a/backend/src/db.rs b/backend/src/db.rs index db23b07..9167eb2 100644 --- a/backend/src/db.rs +++ b/backend/src/db.rs @@ -5,7 +5,7 @@ use sea_orm::sea_query::SqliteQueryBuilder; use crate::config::Config; use crate::models::bio::{biomarker, biomarker_category, biomarker_entry, biomarker_reference_rule}; -use crate::models::user::{role, session, user}; +use crate::models::user::{diet, role, session, user}; /// Connect to the SQLite database. pub async fn connect(config: &Config) -> Result { @@ -27,6 +27,7 @@ pub async fn run_migrations(db: &DatabaseConnection) -> Result<(), DbErr> { // Create table statements (order matters for foreign keys) let statements = vec![ schema.create_table_from_entity(role::Entity), + schema.create_table_from_entity(diet::Entity), schema.create_table_from_entity(user::Entity), schema.create_table_from_entity(session::Entity), schema.create_table_from_entity(biomarker_category::Entity), diff --git a/backend/src/models/user/diet.rs b/backend/src/models/user/diet.rs new file mode 100644 index 0000000..76fa764 --- /dev/null +++ b/backend/src/models/user/diet.rs @@ -0,0 +1,31 @@ +//! Diet entity - predefined diet types for user selection. + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Serialize, Deserialize)] +#[sea_orm(table_name = "diets")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + + #[sea_orm(unique)] + pub name: String, + + #[sea_orm(column_type = "Text", nullable)] + pub description: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::user::Entity")] + Users, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Users.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/backend/src/models/user/mod.rs b/backend/src/models/user/mod.rs index 03ed789..01c3e95 100644 --- a/backend/src/models/user/mod.rs +++ b/backend/src/models/user/mod.rs @@ -1,9 +1,11 @@ //! User and session entities for authentication. +pub mod diet; pub mod role; pub mod session; pub mod user; +pub use diet::Entity as Diet; pub use role::Entity as Role; pub use session::Entity as Session; pub use user::Entity as User; diff --git a/backend/src/models/user/user.rs b/backend/src/models/user/user.rs index 1f2b3cc..56e091e 100644 --- a/backend/src/models/user/user.rs +++ b/backend/src/models/user/user.rs @@ -31,6 +31,16 @@ pub struct Model { /// Date of birth pub birthdate: Option, + // Lifestyle / Habits + /// Whether the user smokes + pub smoking: Option, + + /// Whether the user consumes alcohol + pub alcohol: Option, + + /// Foreign key to diet types + pub diet_id: Option, + pub created_at: DateTime, pub updated_at: DateTime, } @@ -46,6 +56,13 @@ pub enum Relation { to = "super::role::Column::Id" )] Role, + + #[sea_orm( + belongs_to = "super::diet::Entity", + from = "Column::DietId", + to = "super::diet::Column::Id" + )] + Diet, } impl Related for Entity { @@ -60,5 +77,10 @@ impl Related for Entity { } } -impl ActiveModelBehavior for ActiveModel {} +impl Related for Entity { + fn to() -> RelationDef { + Relation::Diet.def() + } +} +impl ActiveModelBehavior for ActiveModel {} diff --git a/backend/src/seed.rs b/backend/src/seed.rs index 51c3853..137ab41 100644 --- a/backend/src/seed.rs +++ b/backend/src/seed.rs @@ -7,7 +7,7 @@ use std::fs; use std::path::Path; use crate::models::bio::{biomarker, biomarker_category, biomarker_reference_rule}; -use crate::models::user::role; +use crate::models::user::{diet, role}; // ============================================================================ // Seed Data Structures @@ -16,6 +16,7 @@ use crate::models::user::role; #[derive(Debug, Deserialize)] pub struct SeedData { pub roles: Vec, + pub diets: Vec, pub biomarker_categories: Vec, } @@ -36,6 +37,12 @@ pub struct BiomarkerCategorySeed { pub description: Option, } +#[derive(Debug, Deserialize)] +pub struct DietSeed { + pub name: String, + pub description: Option, +} + #[derive(Debug, Deserialize)] pub struct BiomarkerSeed { pub name: String, @@ -350,6 +357,24 @@ pub async fn sync_seed_data(db: &DatabaseConnection, seed: &SeedData) -> anyhow: } } + // Sync diet types + for diet_seed in &seed.diets { + let existing = diet::Entity::find() + .filter(diet::Column::Name.eq(&diet_seed.name)) + .one(db) + .await?; + + if existing.is_none() { + let new_diet = diet::ActiveModel { + name: Set(diet_seed.name.clone()), + description: Set(diet_seed.description.clone()), + ..Default::default() + }; + new_diet.insert(db).await?; + tracing::info!("Inserted diet: {}", diet_seed.name); + } + } + // Sync biomarker categories for cat_seed in &seed.biomarker_categories { let existing = biomarker_category::Entity::find()