Skip to content

Commit d91a7c3

Browse files
committed
Add prices, format dates nicely
1 parent 8b55aa9 commit d91a7c3

8 files changed

+66
-12
lines changed

rust/Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55
authors = ["Penelope Yong <penelopeysm@gmail.com>"]
66
description = "Fetch upcoming classical music concerts in London"
77
repository = "https://github.com/penelopeysm/london_classical"
8+
default-run = "london_classical"
89

910
[lib]
1011
path = "src/lib.rs"
@@ -14,6 +15,7 @@ chrono = {version = "0.4.37", features = ["serde"]}
1415
chrono-tz = "0.9.0"
1516
futures = "0.3.30"
1617
html-escape = "0.2.13"
18+
regex = "1.10.4"
1719
reqwest = {version = "0.12.3", features = ["json"]}
1820
scraper = "0.19.0"
1921
serde = {version = "1.0.197", features = ["derive"]}

rust/src/bin/export-types.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use ts_rs::TS;
2+
use london_classical::core::Concert;
3+
4+
fn main() {
5+
let output_dir = env!("CARGO_MANIFEST_DIR").to_string() + "/../src/lib/bindings";
6+
match Concert::export_all_to(&output_dir) {
7+
Ok(_) => println!("Exported types to {}", output_dir),
8+
Err(e) => eprintln!("Error: {}", e),
9+
}
10+
}

rust/src/core.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,18 @@ use serde::{Deserialize, Serialize};
33
use ts_rs::TS;
44

55
#[derive(Debug, Serialize, Deserialize, TS)]
6-
#[ts(export)]
76
pub struct Piece {
87
pub composer: String,
98
pub title: String,
109
}
1110

1211
#[derive(Debug, Serialize, Deserialize, TS)]
13-
#[ts(export)]
1412
pub struct Performer {
1513
pub name: String,
1614
pub instrument: Option<String>,
1715
}
1816

1917
#[derive(Debug, Serialize, Deserialize, TS)]
20-
#[ts(export)]
2118
pub struct Concert {
2219
pub datetime: DateTime<Utc>,
2320
pub url: String,
@@ -29,4 +26,6 @@ pub struct Concert {
2926
pub programme_pdf_url: Option<String>,
3027
pub pieces: Vec<Piece>,
3128
pub venue: String,
29+
pub min_price: Option<u32>, // pennies
30+
pub max_price: Option<u32>, // pennies
3231
}

rust/src/wigmore.rs

+20
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::core;
22
use chrono::{DateTime, Utc};
33
use futures::future::join_all;
44
use html_escape::decode_html_entities;
5+
use regex::Regex;
56
use scraper::{Html, Selector};
67
use serde::{Deserialize, Serialize};
78

@@ -170,6 +171,23 @@ fn parse_concert_json(
170171
}
171172
}
172173

174+
// Figure out prices
175+
let prices_text = json["data"]["page"]["pricesText"].as_str().unwrap();
176+
let (min_price, max_price) = match prices_text {
177+
_ if prices_text.to_lowercase() == "free" => (Some(0), Some(0)),
178+
_ => {
179+
let re = Regex::new(r"£(\d+)").unwrap();
180+
let all_prices_in_pennies: Vec<u32> = re
181+
.captures_iter(prices_text)
182+
.map(|cap| cap[1].parse::<u32>().unwrap())
183+
.collect::<Vec<u32>>();
184+
(
185+
all_prices_in_pennies.iter().min().map(|&x| x * 100),
186+
all_prices_in_pennies.iter().max().map(|&x| x * 100),
187+
)
188+
}
189+
};
190+
173191
fn clean_up_description(s: &str) -> String {
174192
// Split paragraphs
175193
let s = decode_html_entities(s)
@@ -204,6 +222,8 @@ fn parse_concert_json(
204222
is_wigmore_u35,
205223
performers,
206224
pieces,
225+
min_price,
226+
max_price,
207227
}
208228
}
209229

src/lib/Details.svelte

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script lang="ts">
22
import { type Concert } from "src/lib/bindings/Concert";
3+
import { formatDate } from "src/utils";
34
45
export let selectedConcert: Concert | null;
56
</script>
@@ -14,7 +15,22 @@
1415
{/if}
1516
</h2>
1617
<p>
17-
{selectedConcert.datetime.toLocaleString()} at {selectedConcert.venue}
18+
{formatDate(new Date(selectedConcert.datetime))}
19+
20+
{selectedConcert.venue}
21+
22+
{#if selectedConcert.min_price !== null && selectedConcert.max_price !== null}
23+
{#if selectedConcert.min_price === selectedConcert.max_price}
24+
{#if selectedConcert.min_price === 0}
25+
Free entry
26+
{:else}
27+
£{selectedConcert.min_price / 100}
28+
{/if}
29+
{:else}
30+
£{selectedConcert.min_price /
31+
100}–£{selectedConcert.max_price / 100}
32+
{/if}
33+
{/if}
1834
</p>
1935

2036
<p>
@@ -33,7 +49,11 @@
3349
<div class="two-col-grid">
3450
{#each selectedConcert.performers as performer}
3551
<span>{performer.name}</span>
36-
<span>{performer.instrument ? performer.instrument : ""}</span>
52+
<span
53+
>{performer.instrument
54+
? performer.instrument
55+
: ""}</span
56+
>
3757
{/each}
3858
</div>
3959
{/if}

src/lib/Overview.svelte

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
<script lang="ts">
22
import { type Concert } from "src/lib/bindings/Concert";
33
import { type FiltersType } from "src/lib/filters";
4+
import { formatDate } from "src/utils";
45
56
export let selectedConcert: Concert | null;
67
export let filters: FiltersType;
78
89
// Initialise list of concerts
910
import rawConcerts from "src/assets/concerts.json";
10-
const concerts = rawConcerts.map((c) => {
11-
return {
12-
...c,
13-
datetime: new Date(c.datetime),
14-
} as unknown as Concert;
15-
});
11+
const concerts = rawConcerts as Concert[];
1612
let concertsToShow: Concert[] = [];
1713
1814
function satisfiesFilters(concert: Concert, filters: FiltersType): boolean {
@@ -53,7 +49,7 @@
5349
{#if concert.subtitle !== null}
5450
<h4>{concert.subtitle}</h4>
5551
{/if}
56-
<p>{concert.datetime.toLocaleString()}</p>
52+
<p>{formatDate(new Date(concert.datetime))}</p>
5753
<p>{concert.venue}</p>
5854
</button>
5955
{/each}

src/utils.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export function formatDate(date: Date): string {
2+
let day_of_week = date.toLocaleString(undefined, { weekday: 'long' });
3+
let date_long = date.toLocaleString(undefined, { day: '2-digit', month: 'long', year: 'numeric' });
4+
let time = date.toLocaleString(undefined, { hour: 'numeric', minute: '2-digit', hour12: true });
5+
return `${date_long} (${day_of_week}), ${time}`;
6+
}

0 commit comments

Comments
 (0)