Skip to content
This repository was archived by the owner on Oct 10, 2025. It is now read-only.

Commit 7d1bd33

Browse files
committed
Add stage and stage count information
1 parent 22fe935 commit 7d1bd33

11 files changed

Lines changed: 84 additions & 32 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,4 @@ image = "0.25.5"
2323
flate2 = "1.1.0"
2424
itertools = "0.14.0"
2525
async-broadcast = "0.7.2"
26-
csv = "1.3.1"
27-
log = "0.4.26"
26+
csv = "1.3.1"

db/ev0001.sqlite

-268 KB
Binary file not shown.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
alter table events add column stage integer not null default 1;
2+
alter table events add column stage_count integer not null default 1;
3+

db/qxdb.sqlite

0 Bytes
Binary file not shown.

src/event.rs

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub type EventId = i64;
3434
pub struct EventRecord {
3535
pub id: EventId,
3636
pub name: String,
37+
pub stage: i64,
38+
pub stage_count: i64,
3739
pub place: String,
3840
pub start_time: QxDateTime,
3941
pub owner: String,
@@ -45,6 +47,8 @@ impl EventRecord {
4547
Self {
4648
id: 0,
4749
name: "".to_string(),
50+
stage: 1,
51+
stage_count: 1,
4852
place: "".to_string(),
4953
start_time,
5054
// time_zone: "Europe/Prague".to_string(),
@@ -74,26 +78,33 @@ pub async fn load_event_info_for_api_token(qx_api_token: &QxApiToken, db: &State
7478
.fetch_one(pool)
7579
.await
7680
.map_err(|e| {
77-
warn!("Unauthorized request for api token: {}", qx_api_token.0);
81+
warn!("Unauthorized request for api token: {}, error: {}", qx_api_token.0, e);
7882
Custom(Status::Unauthorized, e.to_string())
7983
})?;
8084
Ok(event)
8185
}
8286
pub(crate) async fn save_event(event: &EventRecord, db: &State<DbPool>) -> anyhow::Result<EventId> {
8387
let id = if event.id > 0 {
84-
query("UPDATE events SET name=?, place=?, start_time=? WHERE id=?")
88+
query("UPDATE events SET name=?, place=?, stage=?, stage_count=?, start_time=? WHERE id=?")
8589
.bind(&event.name)
8690
.bind(&event.place)
91+
.bind(&event.stage)
92+
.bind(&event.stage_count)
8793
.bind(event.start_time.0)
8894
// .bind(&event.time_zone)
8995
.bind(event.id)
9096
.execute(&db.0)
9197
.await.map_err(|e| anyhow!("{e}"))?;
9298
event.id
9399
} else {
94-
let id: (i64, ) = query_as("INSERT INTO events(name, place, start_time, api_token, owner) VALUES (?, ?, ?, ?, ?) RETURNING id")
100+
let id: (i64, ) = query_as(
101+
"INSERT INTO events(name, place, stage, stage_count, start_time, api_token, owner)
102+
VALUES (?, ?, ?, ?, ?, ?, ?) RETURNING id"
103+
)
95104
.bind(&event.name)
96105
.bind(&event.place)
106+
.bind(&event.stage)
107+
.bind(&event.stage_count)
97108
.bind(event.start_time.0)
98109
.bind(&event.api_token.0)
99110
.bind(&event.owner)
@@ -109,6 +120,8 @@ struct EventFormValues<'v> {
109120
id: EventId,
110121
name: &'v str,
111122
place: &'v str,
123+
stage: i64,
124+
stage_count: i64,
112125
start_time: &'v str,
113126
// #[field(validate = len(1..))]
114127
// owner: &'v str,
@@ -124,22 +137,26 @@ async fn post_event<'r>(form: Form<Contextual<'r, EventFormValues<'r>>>, session
124137
let vals = form.value.as_ref().ok_or(Custom(Status::BadRequest, "Form data invalid".to_string()))?;
125138
let start_time = QxDateTime::parse_from_iso(vals.start_time)
126139
.map_err(|e| Custom(Status::BadRequest, format!("Unrecognized date-time string: {}, error: {e}", vals.start_time)))?;
127-
let event = if vals.id > 0 {
128-
let event = load_event_info(vals.id, db).await?;
140+
let event = if vals.id == 0 {
129141
EventRecord {
142+
id: 0,
130143
name: vals.name.to_string(),
144+
stage: vals.stage,
145+
stage_count: vals.stage_count,
131146
place: vals.place.to_string(),
132147
start_time,
133-
..event
148+
owner: user.email.clone(),
149+
api_token: QxApiToken(vals.api_token.to_string()),
134150
}
135151
} else {
152+
let event = load_event_info(vals.id, db).await?;
136153
EventRecord {
137-
id: 0,
138154
name: vals.name.to_string(),
139155
place: vals.place.to_string(),
156+
stage: vals.stage,
157+
stage_count: vals.stage_count,
140158
start_time,
141-
owner: user.email.clone(),
142-
api_token: QxApiToken(vals.api_token.to_string()),
159+
..event
143160
}
144161
};
145162
if event.owner != user.email {
@@ -167,13 +184,6 @@ pub async fn get_user_info(session_id: &QxSessionId, state: &State<SharedQxState
167184
Ok(user_info)
168185
}
169186

170-
// pub async fn event_owner_opt(event_id: EventId, session_id: MaybeSessionId, state: &State<SharedQxState>, gdb: &State<DbPool>) -> anyhow::Result<Option<UserInfo>> {
171-
// let event = load_event(event_id, gdb).await?;
172-
// let user = user_info_opt(session_id.0.as_ref(), state).await?
173-
// .and_then(|user| if user.email == event.owner {Some(user)} else {None});
174-
// Ok(user)
175-
// }
176-
177187
pub fn is_event_owner(event: &EventRecord, user: Option<&UserInfo>) -> bool {
178188
if let Some(user) = user {
179189
user.email == event.owner
@@ -263,6 +273,8 @@ async fn get_api_event_current(api_token: QxApiToken, db: &State<DbPool>) -> Res
263273
#[derive(Serialize, Deserialize, Clone, Debug)]
264274
pub struct PostedEvent {
265275
pub name: String,
276+
pub stage: i64,
277+
pub stage_count: i64,
266278
pub place: String,
267279
pub start_time: DateTime<FixedOffset>,
268280
}
@@ -272,6 +284,8 @@ async fn post_api_event_current(api_token: QxApiToken, posted_event: Json<Posted
272284
return Err(string_to_custom_error("Event not found"));
273285
};
274286
event_info.name = posted_event.name.clone();
287+
event_info.stage = posted_event.stage;
288+
event_info.stage_count = posted_event.stage_count;
275289
event_info.place = posted_event.place.clone();
276290
event_info.start_time = posted_event.start_time.into();
277291
debug!("Post event info, start00: {}", event_info.start_time.to_iso_string());
@@ -465,12 +479,14 @@ pub async fn import_runs(edb: &SqlitePool) -> anyhow::Result<()> {
465479
Ok(())
466480
}
467481

482+
pub(crate) const TEST_API_TOKEN: &str = "plelababamak";
483+
468484
#[get("/event/create-demo")]
469485
async fn create_demo_event(state: &State<SharedQxState>, gdb: &State<DbPool>) -> Result<Redirect, Custom<String>> {
470486
let mut event_info = EventRecord::new("fanda.vacek@gmail.com");
471487
event_info.name = String::from("Demo event");
472488
event_info.place = String::from("Deep forest 42");
473-
event_info.api_token = QxApiToken(String::from("plelababamak"));
489+
event_info.api_token = QxApiToken(String::from(TEST_API_TOKEN));
474490
let event_id = save_event(&event_info, gdb).await.map_err(|e| Custom(Status::BadRequest, e.to_string()))?;
475491
{
476492
// upload demo start list

src/main.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use rocket::response::{status};
1414
use rocket::response::status::{Custom};
1515
use rocket_dyn_templates::{Template, context, handlebars};
1616
use rocket::serde::Serialize;
17-
use rocket_dyn_templates::handlebars::{Handlebars, Helper};
1817
use serde::{Deserialize};
1918
use sqlx::SqlitePool;
2019
use crate::auth::{UserInfo, QX_SESSION_ID};
@@ -23,6 +22,7 @@ use crate::db::{DbPool, DbPoolFairing};
2322
use crate::qxdatetime::{dtstr, obtime, obtimems};
2423
use crate::util::anyhow_to_custom_error;
2524
use async_broadcast::{broadcast};
25+
use rocket_dyn_templates::handlebars::{Handlebars, Helper};
2626
use sqlx::sqlite::{SqliteArgumentValue};
2727

2828
#[cfg(test)]
@@ -215,6 +215,22 @@ fn rocket() -> _ {
215215
}
216216
Ok(())
217217
}));
218+
handlebars.register_helper("gt",
219+
Box::new(|h: &Helper, _r: &Handlebars, _: &handlebars::Context, _rc: &mut handlebars::RenderContext, out: &mut dyn handlebars::Output| -> handlebars::HelperResult {
220+
let left = h.param(0).and_then(|v| v.value().as_i64()).unwrap_or(0);
221+
let right = h.param(1).and_then(|v| v.value().as_i64()).unwrap_or(0);
222+
223+
if h.inverse().is_some() {
224+
if left <= right {
225+
out.write("1")?;
226+
}
227+
} else {
228+
if left > right {
229+
out.write("1")?;
230+
}
231+
}
232+
Ok(())
233+
}));
218234
}))
219235
.attach(DbPoolFairing())
220236
.mount("/", FileServer::from("./static"))

src/tests.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::event::START_LIST_IOFXML3_FILE;
1+
use crate::event::{START_LIST_IOFXML3_FILE, TEST_API_TOKEN};
22
use std::fs::OpenOptions;
33
use std::io::{Read};
44
use rocket::local::blocking::Client;
@@ -10,7 +10,6 @@ use crate::{util};
1010
use crate::changes::DataId;
1111
use crate::runs::RunsRecord;
1212

13-
const API_TOKEN: &str = "plelababamak";
1413
const EVENT_ID: EventId = 1;
1514

1615
fn create_test_server() -> Client {
@@ -27,7 +26,7 @@ fn update_event_data() {
2726
let client = create_test_server();
2827

2928
let resp = client.get("/api/event/current")
30-
.header(Header::new("qx-api-token", API_TOKEN))
29+
.header(Header::new("qx-api-token", TEST_API_TOKEN))
3130
.dispatch();
3231
assert_eq!(resp.status(), Status::Ok);
3332
assert_eq!(resp.content_type(), Some(ContentType::JSON));
@@ -37,24 +36,28 @@ fn update_event_data() {
3736
let dt = QxDateTime::now().trimmed_to_sec();
3837
let post_event = PostedEvent {
3938
name: "Foo".to_string(),
39+
stage: 2,
40+
stage_count: 3,
4041
place: "Bar".to_string(),
4142
start_time: dt.0,
4243
};
4344
let resp = client.post("/api/event/current")
44-
.header(Header::new("qx-api-token", API_TOKEN))
45+
.header(Header::new("qx-api-token", TEST_API_TOKEN))
4546
.json(&post_event)
4647
.dispatch();
4748
assert_eq!(resp.status(), Status::Ok);
4849

4950
let resp = client.get("/api/event/current")
50-
.header(Header::new("qx-api-token", API_TOKEN))
51+
.header(Header::new("qx-api-token", TEST_API_TOKEN))
5152
.dispatch();
5253
assert_eq!(resp.status(), Status::Ok);
5354
assert_eq!(resp.content_type(), Some(ContentType::JSON));
5455
let event = resp.into_json::<EventRecord>().unwrap();
5556
assert_eq!(event.name, post_event.name);
5657
assert_eq!(event.place, post_event.place);
5758
assert_eq!(event.start_time.0, post_event.start_time);
59+
assert_eq!(event.stage, post_event.stage);
60+
assert_eq!(event.stage_count, post_event.stage_count);
5861
}
5962

6063
#[test]
@@ -66,7 +69,7 @@ fn upload_file() {
6669
let data = b"foo-bar-baz";
6770
let compressed_data = util::test::zip_data(data).unwrap();
6871
let resp = client.post(format!("/api/event/current/file?name={file_name}"))
69-
.header(Header::new("qx-api-token", API_TOKEN))
72+
.header(Header::new("qx-api-token", TEST_API_TOKEN))
7073
.header(ContentType::ZIP)
7174
.body(compressed_data)
7275
.dispatch();
@@ -107,7 +110,7 @@ fn upload_test_file(client: &Client, file_name: &str) {
107110

108111
let compressed = util::test::zip_data(&data).unwrap();
109112
let resp = client.post(format!("/api/event/current/file?name={file_name}"))
110-
.header(Header::new("qx-api-token", API_TOKEN))
113+
.header(Header::new("qx-api-token", TEST_API_TOKEN))
111114
.header(ContentType::ZIP)
112115
.body(compressed)
113116
.dispatch();
@@ -132,7 +135,7 @@ fn post_qe_change() {
132135

133136
fn apply_change(client: &Client, run_id: DataId, change: &RunsRecord) -> RunsRecord {
134137
let resp = client.post(format!("/api/event/current/changes/run-updated?run_id={}", run_id.unwrap()))
135-
.header(Header::new("qx-api-token", API_TOKEN))
138+
.header(Header::new("qx-api-token", TEST_API_TOKEN))
136139
.header(ContentType::JSON)
137140
.json(&change)
138141
.dispatch();

templates/event-edit.html.hbs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@
2020
<b>Place</b>
2121
<input class="w3-input w3-border w3-margin-bottom" type="text" placeholder="Enter event place" name="place" value="{{place}}" >
2222
</label>
23+
<label>
24+
<b>Stage</b>
25+
<input class="w3-input w3-border w3-margin-bottom" type="number" name="stage" value="{{stage}}" >
26+
</label>
27+
<label>
28+
<b>Stage count</b>
29+
<input class="w3-input w3-border w3-margin-bottom" type="number" name="stage_count" value="{{stage_count}}" >
30+
</label>
2331
<label>
2432
<b>Start time</b>
2533
<input class="w3-input w3-border w3-margin-bottom" type="text" placeholder="Enter event start date-time with UTC offset" name="start_time" value="{{start_time}}" >

templates/event.html.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{{#*inline "page"}}
22

3-
<h2>{{ event.name }}</h2>
3+
<h2>{{ event.name }} {{#if (gt event.stage_count 1)}} E{{ event.stage }} {{/if}}</h2>
44
<div class="w3-bar">
55
{{#if is_event_owner}}
66
<a href="/event/{{event.id}}/edit" class="w3-button w3-theme w3-round-large w3-border"><i class="fa fa-cog"></i> edit</a>

templates/index.html.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<table class="w3-table-all w3-hoverable">
1414
{{#each events}}
1515
<tr>
16-
<td><a href="/event/{{ this.id }}">{{this.name}}</a></td>
16+
<td><a href="/event/{{ this.id }}">{{this.name}} {{#if (gt this.stage_count 1)}} E{{ this.stage }} {{/if}}</a></td>
1717
<td>{{this.place}}</td>
1818
<td>{{dtstr this.start_time}}</td>
1919
<td>{{dtstr this.owner}}</td>

0 commit comments

Comments
 (0)