Recreate the dashboard as a command line app

This commit is contained in:
Savanni D'Gerinel 2023-08-03 00:31:04 -04:00
parent 1ac7ce43f2
commit a1cae2ed2b
5 changed files with 99 additions and 204 deletions

124
dashboard/Cargo.lock generated
View File

@ -17,15 +17,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "android-tzdata" name = "android-tzdata"
version = "0.1.1" version = "0.1.1"
@ -141,28 +132,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "chrono-tz"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c39203181991a7dd4343b8005bd804e7a9a37afb8ac070e43771e8c820bbde"
dependencies = [
"chrono",
"chrono-tz-build",
"phf",
]
[[package]]
name = "chrono-tz-build"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f509c3a87b33437b05e2458750a0700e5bdd6956176773e6c7d6dd15a283a0c"
dependencies = [
"parse-zoneinfo",
"phf",
"phf_codegen",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.3" version = "0.9.3"
@ -203,7 +172,6 @@ name = "dashboard"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"chrono-tz",
"fluent", "fluent",
"fluent-ergonomics", "fluent-ergonomics",
"futures", "futures",
@ -931,60 +899,12 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "parse-zoneinfo"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41"
dependencies = [
"regex",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [
"phf_generator",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
"uncased",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.2" version = "1.1.2"
@ -1086,35 +1006,6 @@ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
] ]
[[package]]
name = "regex"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.18" version = "0.11.18"
@ -1305,12 +1196,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.8" version = "0.4.8"
@ -1566,15 +1451,6 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "uncased"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b9bc53168a4be7402ab86c3aad243a84dd7381d09be0eddc81280c1da95ca68"
dependencies = [
"version_check",
]
[[package]] [[package]]
name = "unic-langid" name = "unic-langid"
version = "0.9.1" version = "0.9.1"

View File

@ -7,7 +7,6 @@ edition = "2018"
[dependencies] [dependencies]
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
chrono-tz = { version = "0.6" }
fluent = { version = "0.16" } fluent = { version = "0.16" }
fluent-ergonomics = { path = "../fluent-ergonomics/" } fluent-ergonomics = { path = "../fluent-ergonomics/" }
futures = { version = "0.3" } futures = { version = "0.3" }

View File

@ -5,8 +5,7 @@ extern crate horrorshow;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
use chrono::{Datelike, Utc}; use chrono::{Datelike, Local, TimeZone, Utc};
use chrono_tz::America::New_York;
use fluent::{FluentArgs, FluentValue}; use fluent::{FluentArgs, FluentValue};
use geo_types::{Latitude, Longitude}; use geo_types::{Latitude, Longitude};
use horrorshow::helper::doctype; use horrorshow::helper::doctype;
@ -23,8 +22,9 @@ mod soluna_client;
use soluna_client::{SolunaClient, SunMoon}; use soluna_client::{SolunaClient, SunMoon};
mod solstices; mod solstices;
use solstices::{Event, Solstices, EVENTS}; use solstices::{Event, EVENTS};
/*
const EO_TEXT: &'static str = " const EO_TEXT: &'static str = "
day = {$day -> day = {$day ->
*[Sunday] Dimanĉo *[Sunday] Dimanĉo
@ -75,8 +75,10 @@ fn date(fluent: Arc<fluent_ergonomics::FluentErgo>, today: IFC::IFC) -> impl Ren
owned_html! {p : format!("{}, {} {}, {}", tago, monato, today.day(), today.year());} owned_html! {p : format!("{}, {} {}, {}", tago, monato, today.day(), today.year());}
} }
*/
fn soluna_desegno(sun_moon: SunMoon) -> impl Render { /*
fn soluna_desegno<Tz: TimeZone>(sun_moon: SunMoon<Tz>) -> impl Render {
owned_html! { owned_html! {
table { table {
tr { tr {
@ -86,16 +88,18 @@ fn soluna_desegno(sun_moon: SunMoon) -> impl Render {
th : "Lunfalo"; th : "Lunfalo";
} }
tr { tr {
td : format!("{}", sun_moon.sunleviĝo.format("%H:%M")); td : format!("{}", sun_moon.sunrise.format("%H:%M"));
td : format!("{}", sun_moon.sunfalo.format("%H:%M")); td : format!("{}", sun_moon.sunset.format("%H:%M"));
td : sun_moon.lunleviĝo.map(|v| format!("{}", v.format("%H:%M"))).unwrap_or("".to_string()); td : sun_moon.moonrise.map(|v| format!("{}", v.format("%H:%M"))).unwrap_or("".to_string());
td : sun_moon.lunfalo.map(|v| format!("{}", v.format("%H:%M"))).unwrap_or("".to_string()); td : sun_moon.moonset.map(|v| format!("{}", v.format("%H:%M"))).unwrap_or("".to_string());
td : format!("{:?}", sun_moon.lunfazo); td : format!("{:?}", sun_moon.moon_phase);
} }
} }
} }
} }
*/
/*
fn astronomia_eventa_desegno( fn astronomia_eventa_desegno(
fluent: Arc<fluent_ergonomics::FluentErgo>, fluent: Arc<fluent_ergonomics::FluentErgo>,
event: Option<Event>, event: Option<Event>,
@ -104,10 +108,10 @@ fn astronomia_eventa_desegno(
None => "".to_owned(), None => "".to_owned(),
Some(event) => { Some(event) => {
let eventa_str = match event { let eventa_str = match event {
Event::SpringEquinox(_) => fluent.tr("printempa_ekvinokso", None), Event::SpringEquinox(_) => fluent.tr("spring_equinox", None),
Event::SummerSolstice(_) => fluent.tr("somera_solstico", None), Event::SummerSolstice(_) => fluent.tr("summer_solstice", None),
Event::AutumnEquinox(_) => fluent.tr("auxtuna_ekvinokso", None), Event::AutumnEquinox(_) => fluent.tr("autumn_equinox", None),
Event::WinterSolstice(_) => fluent.tr("vintra_solstico", None), Event::WinterSolstice(_) => fluent.tr("winter_solstice", None),
} }
.unwrap(); .unwrap();
format!("{} {}", eventa_str, event.date().format("%Y-%m-%d")) format!("{} {}", eventa_str, event.date().format("%Y-%m-%d"))
@ -117,6 +121,7 @@ fn astronomia_eventa_desegno(
div : s.clone(); div : s.clone();
} }
} }
*/
/* /*
fn page_template( fn page_template(
@ -164,33 +169,42 @@ async fn page(
#[tokio::main] #[tokio::main]
pub async fn main() { pub async fn main() {
/* let now = Local::now();
let latitudo = Latitude::from(41.78); let ifc = IFC::IFC::from(now.date_naive().with_year(12023).unwrap());
let longitudo = Longitude::from(-71.41); let next_event = EVENTS.next_event(now.with_timezone(&Utc)).unwrap();
let eo_id = "eo".parse::<unic_langid::LanguageIdentifier>().unwrap();
let mut fluent = fluent_ergonomics::FluentErgo::new(&[eo_id.clone()]);
let solar_client = Arc::new(SolunaClient::new());
fluent.add_from_text(eo_id, EO_TEXT.to_owned()).unwrap(); println!(
let fluent = Arc::new(fluent); "{:?}, {:?} {}, {}",
ifc.weekday(),
ifc.month(),
ifc.day(),
ifc.year()
);
let pageview = { println!("{:?}", next_event);
warp::path::end().and_then(move || {
page(
fluent.clone(),
solar_client.clone(),
latitudo.clone(),
longitudo.clone(),
)
})
};
let server = warp::serve(paĝvojo); let latitude = Latitude::from(41.78);
server let longitude = Longitude::from(-71.41);
.run(SocketAddr::new( let soluna_client = SolunaClient::new();
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), let sun_moon = soluna_client
8000, .request(latitude, longitude, Local::now())
))
.await; .await;
*/
println!(
"Solar Transit: {} -> {}",
sun_moon.sunrise.format("%H:%M").to_string(),
sun_moon.sunset.format("%H:%M").to_string()
);
println!(
"Lunar Transit: {} -> {} [{:?}]",
sun_moon
.moonrise
.map(|time| time.format("%H:%M").to_string())
.unwrap_or(" -- ".to_owned()),
sun_moon
.moonset
.map(|time| time.format("%H:%M").to_string())
.unwrap_or(" -- ".to_owned()),
sun_moon.moon_phase
);
} }

View File

@ -1,8 +1,9 @@
// 41.78, -71.41 // 41.78, -71.41
// https://api.solunar.org/solunar/41.78,-71.41,20211029,-4 // https://api.solunar.org/solunar/41.78,-71.41,20211029,-4
use chrono::{Date, DateTime, Duration, NaiveTime, Offset, Timelike, Utc}; use chrono::{
use chrono_tz::Tz; DateTime, Duration, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike, Utc,
};
use geo_types::{Latitude, Longitude}; use geo_types::{Latitude, Longitude};
use memorycache::MemoryCache; use memorycache::MemoryCache;
use reqwest; use reqwest;
@ -11,32 +12,30 @@ const ENDPOINT: &str = "https://api.solunar.org/solunar";
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct SunMoon { pub struct SunMoon {
pub sunleviĝo: DateTime<Tz>, pub sunrise: NaiveTime,
pub sunfalo: DateTime<Tz>, pub sunset: NaiveTime,
pub lunleviĝo: Option<DateTime<Tz>>, pub moonrise: Option<NaiveTime>,
pub lunfalo: Option<DateTime<Tz>>, pub moonset: Option<NaiveTime>,
pub lunfazo: LunarPhase, pub moon_phase: LunarPhase,
} }
impl SunMoon { impl SunMoon {
fn from_js(day: Date<Tz>, val: SunMoonJs) -> Self { fn from_js(val: SunMoonJs) -> Self {
fn new_time(day: Date<Tz>, val: String) -> Option<DateTime<Tz>> { fn parse_time(val: String) -> Option<NaiveTime> {
NaiveTime::parse_from_str(&val, "%H:%M") NaiveTime::parse_from_str(&val, "%H:%M").ok()
.map(|time| day.and_hms(time.hour(), time.minute(), 0))
.ok()
} }
let sunleviĝo = new_time(day.clone(), val.sunleviĝo).unwrap(); let sunrise = parse_time(val.sunrise).unwrap();
let sunfalo = new_time(day.clone(), val.sunfalo).unwrap(); let sunset = parse_time(val.sunset).unwrap();
let lunleviĝo = val.lunleviĝo.and_then(|v| new_time(day.clone(), v)); let moonrise = val.moonrise.and_then(|v| parse_time(v));
let lunfalo = val.lunfalo.and_then(|v| new_time(day.clone(), v)); let moonset = val.moonset.and_then(|v| parse_time(v));
Self { Self {
sunleviĝo, sunrise,
sunfalo, sunset,
lunleviĝo, moonrise,
lunfalo, moonset,
lunfazo: val.lunfazo, moon_phase: val.moon_phase,
} }
} }
} }
@ -44,15 +43,15 @@ impl SunMoon {
#[derive(Clone, Debug, Deserialize)] #[derive(Clone, Debug, Deserialize)]
pub(crate) struct SunMoonJs { pub(crate) struct SunMoonJs {
#[serde(alias = "sunRise")] #[serde(alias = "sunRise")]
sunleviĝo: String, sunrise: String,
#[serde(alias = "sunSet")] #[serde(alias = "sunSet")]
sunfalo: String, sunset: String,
#[serde(alias = "moonRise")] #[serde(alias = "moonRise")]
lunleviĝo: Option<String>, moonrise: Option<String>,
#[serde(alias = "moonSet")] #[serde(alias = "moonSet")]
lunfalo: Option<String>, moonset: Option<String>,
#[serde(alias = "moonPhase")] #[serde(alias = "moonPhase")]
lunfazo: LunarPhase, moon_phase: LunarPhase,
} }
#[derive(Clone, Debug, Deserialize, PartialEq)] #[derive(Clone, Debug, Deserialize, PartialEq)]
@ -88,13 +87,13 @@ impl SolunaClient {
} }
} }
pub async fn request( pub async fn request<Tz: TimeZone>(
&self, &self,
latitude: Latitude, latitude: Latitude,
longitude: Longitude, longitude: Longitude,
day: Date<Tz>, day: DateTime<Tz>,
) -> SunMoon { ) -> SunMoon {
let date = day.format("%Y%m%d"); let date = day.date_naive().format("%Y%m%d");
let url = format!( let url = format!(
"{}/{},{},{},{}", "{}/{},{},{},{}",
ENDPOINT, ENDPOINT,
@ -119,32 +118,29 @@ impl SolunaClient {
}) })
.await; .await;
SunMoon::from_js(day, js) SunMoon::from_js(js)
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use chrono::TimeZone;
use chrono_tz::America::New_York;
use serde_json; use serde_json;
const EXAMPLE: &str = "{\"sunRise\":\"7:15\",\"sunTransit\":\"12:30\",\"sunSet\":\"17:45\",\"moonRise\":null,\"moonTransit\":\"7:30\",\"moonUnder\":\"19:54\",\"moonSet\":\"15:02\",\"moonPhase\":\"Waning Crescent\",\"moonIllumination\":0.35889454647387764,\"sunRiseDec\":7.25,\"sunTransitDec\":12.5,\"sunSetDec\":17.75,\"moonRiseDec\":null,\"moonSetDec\":15.033333333333333,\"moonTransitDec\":7.5,\"moonUnderDec\":19.9,\"minor1Start\":null,\"minor1Stop\":null,\"minor2StartDec\":14.533333333333333,\"minor2Start\":\"14:32\",\"minor2StopDec\":15.533333333333333,\"minor2Stop\":\"15:32\",\"major1StartDec\":6.5,\"major1Start\":\"06:30\",\"major1StopDec\":8.5,\"major1Stop\":\"08:30\",\"major2StartDec\":18.9,\"major2Start\":\"18:54\",\"major2StopDec\":20.9,\"major2Stop\":\"20:54\",\"dayRating\":1,\"hourlyRating\":{\"0\":20,\"1\":20,\"2\":0,\"3\":0,\"4\":0,\"5\":0,\"6\":20,\"7\":40,\"8\":40,\"9\":20,\"10\":0,\"11\":0,\"12\":0,\"13\":0,\"14\":0,\"15\":20,\"16\":20,\"17\":20,\"18\":40,\"19\":20,\"20\":20,\"21\":20,\"22\":0,\"23\":0}}"; const EXAMPLE: &str = "{\"sunRise\":\"7:15\",\"sunTransit\":\"12:30\",\"sunSet\":\"17:45\",\"moonRise\":null,\"moonTransit\":\"7:30\",\"moonUnder\":\"19:54\",\"moonSet\":\"15:02\",\"moonPhase\":\"Waning Crescent\",\"moonIllumination\":0.35889454647387764,\"sunRiseDec\":7.25,\"sunTransitDec\":12.5,\"sunSetDec\":17.75,\"moonRiseDec\":null,\"moonSetDec\":15.033333333333333,\"moonTransitDec\":7.5,\"moonUnderDec\":19.9,\"minor1Start\":null,\"minor1Stop\":null,\"minor2StartDec\":14.533333333333333,\"minor2Start\":\"14:32\",\"minor2StopDec\":15.533333333333333,\"minor2Stop\":\"15:32\",\"major1StartDec\":6.5,\"major1Start\":\"06:30\",\"major1StopDec\":8.5,\"major1Stop\":\"08:30\",\"major2StartDec\":18.9,\"major2Start\":\"18:54\",\"major2StopDec\":20.9,\"major2Stop\":\"20:54\",\"dayRating\":1,\"hourlyRating\":{\"0\":20,\"1\":20,\"2\":0,\"3\":0,\"4\":0,\"5\":0,\"6\":20,\"7\":40,\"8\":40,\"9\":20,\"10\":0,\"11\":0,\"12\":0,\"13\":0,\"14\":0,\"15\":20,\"16\":20,\"17\":20,\"18\":40,\"19\":20,\"20\":20,\"21\":20,\"22\":0,\"23\":0}}";
#[test] #[test]
fn it_parses_a_response() { fn it_parses_a_response() {
let day = New_York.ymd(2021, 10, 29);
let sun_moon_js: SunMoonJs = serde_json::from_str(EXAMPLE).unwrap(); let sun_moon_js: SunMoonJs = serde_json::from_str(EXAMPLE).unwrap();
let sun_moon = SunMoon::from_js(day.clone(), sun_moon_js); let sun_moon = SunMoon::from_js(sun_moon_js);
assert_eq!( assert_eq!(
sun_moon, sun_moon,
SunMoon { SunMoon {
sunleviĝo: day.and_hms(7, 15, 0), sunrise: NaiveTime::from_hms_opt(7, 15, 0).unwrap(),
sunfalo: day.and_hms(17, 45, 0), sunset: NaiveTime::from_hms_opt(17, 45, 0).unwrap(),
lunleviĝo: None, moonrise: None,
lunfalo: Some(day.and_hms(15, 02, 0)), moonset: Some(NaiveTime::from_hms_opt(15, 02, 0).unwrap()),
lunfazo: LunarPhase::WaningCrescent, moon_phase: LunarPhase::WaningCrescent,
} }
); );
} }

View File

@ -158,7 +158,7 @@ impl IFC {
} }
} }
pub fn month_ifc(&self) -> Month { pub fn month(&self) -> Month {
match *self { match *self {
IFC::Day(Day { month, .. }) => Month::from(month), IFC::Day(Day { month, .. }) => Month::from(month),
IFC::LeapDay(_) => Month::June, IFC::LeapDay(_) => Month::June,
@ -166,7 +166,15 @@ impl IFC {
} }
} }
pub fn weekday_ifc(&self) -> DayOfWeek { pub fn day(&self) -> u8 {
match *self {
IFC::Day(Day { day, .. }) => day,
IFC::LeapDay(_) => 29,
IFC::YearDay(_) => 29,
}
}
pub fn weekday(&self) -> DayOfWeek {
match *self { match *self {
IFC::LeapDay(_) => DayOfWeek::LeapDay, IFC::LeapDay(_) => DayOfWeek::LeapDay,
IFC::YearDay(_) => DayOfWeek::YearDay, IFC::YearDay(_) => DayOfWeek::YearDay,
@ -297,6 +305,7 @@ impl From<chrono::NaiveDate> for IFC {
} }
*/ */
/*
impl Datelike for IFC { impl Datelike for IFC {
fn year(&self) -> i32 { fn year(&self) -> i32 {
// self.year as i32 // self.year as i32
@ -418,6 +427,7 @@ impl Datelike for IFC {
unimplemented!() unimplemented!()
} }
} }
*/
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {