Skip to content

Commit 8e533e3

Browse files
authored
Merge pull request #107 from he1senbrg/seeding
Implemented database seeding as per #61
2 parents 95727ba + 9ad9c88 commit 8e533e3

File tree

5 files changed

+168
-1
lines changed

5 files changed

+168
-1
lines changed

.env.sample

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ POSTGRES_HOST=localhost
88
ROOT_DB_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:5432/${POSTGRES_DB}
99
RUST_ENV=development
1010
ROOT_SECRET=insecuresecret123 # Used to verify origin of attendance mutations
11-
ROOT_PORT=8000
11+
ROOT_PORT=3000
12+
13+
# Seed toggle
14+
SEEDING_ENABLED=false

docs/docs.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ src/
1919
## Database Schema
2020
- [Database](database.md) - Database structure and migrations
2121

22+
### Database Seeding
23+
Database seeding can be enabled by setting `SEEDING_ENABLED` to `true` in the `.env` file. (Disabled by default)
24+
25+
```
26+
# Seed toggle
27+
SEEDING_ENABLED=true
28+
```
29+
2230
## Core Features
2331
### Member Management
2432
- Query members by ID, roll number, or Discord ID

src/database_seeder/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use sqlx::PgPool;
2+
use std::fs;
3+
use std::path::Path;
4+
5+
pub async fn seed_database(pool: &PgPool) {
6+
let sql_path = Path::new("src/database_seeder/seed.sql");
7+
let seed_sql = fs::read_to_string(sql_path).expect("Failed to read seed.sql file");
8+
9+
let statements: Vec<&str> = seed_sql
10+
.split(';')
11+
.filter(|stmt| !stmt.trim().is_empty())
12+
.collect();
13+
14+
for statement in statements {
15+
sqlx::query(statement)
16+
.execute(pool)
17+
.await
18+
.expect("Failed to execute seed statement");
19+
}
20+
}

src/database_seeder/seed.sql

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
-- Member
2+
INSERT INTO member (
3+
roll_no, name, email, sex, year, hostel, mac_address, discord_id, group_id
4+
)
5+
SELECT
6+
'R' || LPAD(i::TEXT, 4, '0'),
7+
CASE
8+
WHEN i % 5 = 0 THEN 'John Doe ' || i
9+
WHEN i % 5 = 1 THEN 'Jane Smith ' || i
10+
WHEN i % 5 = 2 THEN 'Alex Johnson ' || i
11+
WHEN i % 5 = 3 THEN 'Emily Davis ' || i
12+
ELSE 'Chris Brown ' || i
13+
END,
14+
CASE
15+
WHEN i % 5 = 0 THEN 'john.doe' || i || '@example.com'
16+
WHEN i % 5 = 1 THEN 'jane.smith' || i || '@example.com'
17+
WHEN i % 5 = 2 THEN 'alex.johnson' || i || '@example.com'
18+
WHEN i % 5 = 3 THEN 'emily.davis' || i || '@example.com'
19+
ELSE 'chris.brown' || i || '@example.com'
20+
END,
21+
CASE
22+
WHEN i % 2 = 0 THEN 'M'::sex_type
23+
ELSE 'F'::sex_type
24+
END,
25+
(i % 4) + 1,
26+
'Hostel ' || ((i % 5) + 1),
27+
'00:14:22:01:' || LPAD(TO_HEX(i), 2, '0') || ':' || LPAD(TO_HEX(i + 60), 2, '0'),
28+
'discord_user_' || i,
29+
(i % 8) + 1
30+
FROM generate_series(1, 60) AS i
31+
ON CONFLICT (roll_no) DO NOTHING;
32+
33+
34+
-- Attendance
35+
INSERT INTO Attendance (
36+
member_id, date, is_present, time_in, time_out
37+
)
38+
SELECT
39+
m.member_id,
40+
CURRENT_DATE - ((i * 3) % 30),
41+
rnd.is_present,
42+
CASE WHEN rnd.is_present THEN rnd.time_in ELSE NULL END,
43+
CASE WHEN rnd.is_present THEN rnd.time_out ELSE NULL END
44+
FROM generate_series(1, 600) AS i
45+
JOIN (
46+
SELECT generate_series(1, 60) AS idx, member_id
47+
FROM member
48+
) AS m ON (i % 60) + 1 = m.idx
49+
JOIN (
50+
SELECT
51+
TRUE AS is_present,
52+
'08:30'::TIME + (INTERVAL '1 minute' * (random() * 60)) AS time_in,
53+
'17:00'::TIME + (INTERVAL '1 minute' * (random() * 60)) AS time_out
54+
UNION ALL
55+
SELECT FALSE, NULL, NULL
56+
) AS rnd ON TRUE
57+
WHERE (random() < 0.75)
58+
ON CONFLICT (member_id, date) DO NOTHING;
59+
60+
61+
-- AttendanceSummary
62+
INSERT INTO AttendanceSummary (
63+
member_id, year, month, days_attended
64+
)
65+
SELECT
66+
m.member_id,
67+
2025,
68+
(i % 12) + 1,
69+
FLOOR(random() * 26 + 3)::INT
70+
FROM generate_series(1, 400) AS i
71+
JOIN (
72+
SELECT generate_series(1, 60) AS idx, member_id
73+
FROM member
74+
) AS m ON (i % 60) + 1 = m.idx
75+
ON CONFLICT (member_id, year, month) DO NOTHING;
76+
77+
78+
-- StatusUpdateStreak
79+
INSERT INTO StatusUpdateStreak (
80+
member_id, current_streak, max_streak
81+
)
82+
SELECT
83+
member_id,
84+
FLOOR(random() * 10 + 1)::INT,
85+
FLOOR(random() * 30 + 10)::INT
86+
FROM member
87+
ON CONFLICT (member_id) DO NOTHING;
88+
89+
90+
-- Project
91+
INSERT INTO Project (
92+
member_id, title
93+
)
94+
SELECT
95+
(i % 60) + 1,
96+
CASE
97+
WHEN i % 3 = 0 THEN 'Machine Learning Project ' || i
98+
WHEN i % 3 = 1 THEN 'Web Development Project ' || i
99+
ELSE 'Data Analysis Project ' || i
100+
END
101+
FROM generate_series(1, 200) AS i
102+
WHERE NOT EXISTS (
103+
SELECT 1 FROM Project
104+
WHERE member_id = (i % 60) + 1 AND title = CASE
105+
WHEN i % 3 = 0 THEN 'Machine Learning Project ' || i
106+
WHEN i % 3 = 1 THEN 'Web Development Project ' || i
107+
ELSE 'Data Analysis Project ' || i
108+
END
109+
);
110+
111+
112+
-- StatusUpdateHistory
113+
INSERT INTO StatusUpdateHistory (
114+
member_id, date, is_updated
115+
)
116+
SELECT
117+
m.member_id,
118+
CURRENT_DATE - ((i * 2) % 30),
119+
i % 2 = 0
120+
FROM generate_series(1, 500) AS i
121+
JOIN (
122+
SELECT generate_series(1, 60) AS idx, member_id
123+
FROM member
124+
) AS m ON (i % 60) + 1 = m.idx
125+
ON CONFLICT (member_id, date) DO NOTHING;

src/main.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ use tracing::info;
88
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
99

1010
use daily_task::run_daily_task_at_midnight;
11+
use database_seeder::seed_database;
1112
use graphql::{Mutation, Query};
1213
use routes::setup_router;
1314

1415
pub mod daily_task;
16+
pub mod database_seeder;
1517
pub mod graphql;
1618
pub mod models;
1719
pub mod routes;
@@ -23,6 +25,7 @@ struct Config {
2325
secret_key: String,
2426
database_url: String,
2527
port: String,
28+
seeding_enabled: bool,
2629
}
2730

2831
impl Config {
@@ -33,6 +36,9 @@ impl Config {
3336
secret_key: std::env::var("ROOT_SECRET").expect("ROOT_SECRET must be set."),
3437
database_url: std::env::var("ROOT_DB_URL").expect("ROOT_DB_URL must be set."),
3538
port: std::env::var("ROOT_PORT").expect("ROOT_PORT must be set."),
39+
seeding_enabled: std::env::var("SEEDING_ENABLED")
40+
.map(|v| v.to_lowercase() == "true")
41+
.unwrap_or(false),
3642
}
3743
}
3844
}
@@ -45,6 +51,11 @@ async fn main() {
4551
let pool = setup_database(&config.database_url).await;
4652
let schema = build_graphql_schema(pool.clone(), config.secret_key);
4753

54+
if config.seeding_enabled {
55+
info!("Seeding database...");
56+
seed_database(&pool).await;
57+
}
58+
4859
tokio::task::spawn(async {
4960
run_daily_task_at_midnight(pool).await;
5061
});

0 commit comments

Comments
 (0)