Add sunrise/sunset document

This commit is contained in:
Joscha 2025-03-08 23:53:48 +01:00
parent 14b48c3c21
commit a24532d9cc
11 changed files with 277 additions and 2 deletions

19
Cargo.lock generated
View file

@ -397,6 +397,15 @@ version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7588475145507237ded760e52bf2f1085495245502033756d28ea72ade0e498b"
[[package]]
name = "chrono"
version = "0.4.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
dependencies = [
"num-traits",
]
[[package]]
name = "ciborium"
version = "0.2.2"
@ -2628,6 +2637,7 @@ dependencies = [
"serde",
"showbits-assets",
"showbits-typst",
"sunrise",
"tokio",
]
@ -2782,6 +2792,15 @@ dependencies = [
"syn",
]
[[package]]
name = "sunrise"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3217c5830416956b1f2dc731f526150a82c144ebe83d2f0e78853c8356a22ada"
dependencies = [
"chrono",
]
[[package]]
name = "svgtypes"
version = "0.15.3"

View file

@ -20,6 +20,7 @@ rust-embed = "8.6.0"
serde = { version = "1.0.218", features = ["derive"] }
showbits-assets.path = "./showbits-assets"
showbits-typst.path = "./showbits-typst"
sunrise = "1.0.1"
tokio = "1.43.0"
typst = "0.13.0"
typst-assets = { version = "0.13.0", features = ["fonts"] }

View file

@ -0,0 +1,100 @@
<script setup lang="ts">
import { useApiRequest } from "@/apiRequest";
import { ref } from "vue";
import CError from "./CError.vue";
const { disabled, error, makeRequest } = useApiRequest();
const today = new Date();
const latitude = ref<number>();
const longitude = ref<number>();
const year = ref(today.getFullYear());
const month = ref(today.getMonth() + 1);
const feed = ref(true);
function submit() {
if (typeof latitude.value !== "number") return;
if (typeof longitude.value !== "number") return;
const data = new URLSearchParams();
data.append("latitude", latitude.value.toFixed(5));
data.append("longitude", longitude.value.toFixed(5));
data.append("year", year.value.toFixed());
data.append("month", month.value.toFixed());
data.append("feed", String(feed.value));
void makeRequest("api/sunrise", data);
}
</script>
<template>
<form @submit.prevent="submit">
<h2>Sunrise and Sunset</h2>
<label class="wide">
Latitude:
<input
v-model="latitude"
type="number"
step="any"
min="-90"
max="90"
required
:disabled
/>
</label>
<label class="wide">
Longitude:
<input
v-model="longitude"
type="number"
step="any"
min="-180"
max="180"
required
:disabled
/>
</label>
<label class="wide">
Year:
<input
v-model="year"
type="number"
min="-9999"
max="9999"
required
:disabled
/>
</label>
<label class="wide">
Month:
<input
v-model="month"
type="number"
min="1"
max="12"
required
:disabled
/>
</label>
<label><input v-model="feed" type="checkbox" :disabled /> Feed</label>
<button :disabled>Print</button>
<CError :message="error" />
</form>
</template>
<style scoped>
form {
display: flex;
flex-direction: column;
gap: 16px;
}
.wide {
display: flex;
flex-direction: column;
}
</style>

View file

@ -5,11 +5,19 @@ import CDocumentCells from "./CDocumentCells.vue";
import CDocumentChat from "./CDocumentChat.vue";
import CDocumentEgg from "./CDocumentEgg.vue";
import CDocumentImage from "./CDocumentImage.vue";
import CDocumentSunrise from "./CDocumentSunrise.vue";
import CDocumentText from "./CDocumentText.vue";
import CDocumentTictactoe from "./CDocumentTictactoe.vue";
const mode = ref<
"calendar" | "chat" | "cells" | "egg" | "image" | "text" | "tictactoe"
| "calendar"
| "cells"
| "chat"
| "egg"
| "image"
| "sunrise"
| "text"
| "tictactoe"
>();
</script>
@ -17,10 +25,11 @@ const mode = ref<
<div class="outer">
<button v-if="mode" class="close" @click="mode = undefined">X</button>
<CDocumentCalendar v-if="mode === 'calendar'" />
<CDocumentChat v-if="mode === 'chat'" />
<CDocumentCells v-if="mode === 'cells'" />
<CDocumentChat v-if="mode === 'chat'" />
<CDocumentEgg v-if="mode === 'egg'" />
<CDocumentImage v-if="mode === 'image'" />
<CDocumentSunrise v-if="mode === 'sunrise'" />
<CDocumentText v-if="mode === 'text'" />
<CDocumentTictactoe v-if="mode === 'tictactoe'" />
<hr v-if="mode !== undefined" />
@ -32,6 +41,7 @@ const mode = ref<
<button @click="mode = 'chat'">Chat Message</button>
<button @click="mode = 'egg'">Easter Egg</button>
<button @click="mode = 'image'">Image</button>
<button @click="mode = 'sunrise'">Sunrise and Sunset</button>
<button @click="mode = 'text'">Text</button>
<button @click="mode = 'tictactoe'">Tic Tac Toe</button>
<a href="photo.html">Take a photo</a>

View file

@ -18,6 +18,7 @@ rust-embed = { workspace = true }
serde = { workspace = true }
showbits-assets = { workspace = true }
showbits-typst = { workspace = true }
sunrise = { workspace = true }
tokio = { workspace = true, features = ["full"] }
[lints]

View file

@ -5,6 +5,7 @@ pub mod cells;
pub mod chat;
pub mod egg;
pub mod image;
pub mod sunrise;
pub mod text;
pub mod tictactoe;

View file

@ -0,0 +1,38 @@
{
"year": 2025,
"month": 3,
"times": [
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"],
["01:23", "45:67"]
],
"feed": false
}

View file

@ -0,0 +1 @@
../lib

View file

@ -0,0 +1,46 @@
#import "@preview/oxifmt:0.2.1": strfmt
#import "lib/main.typ" as lib;
#show: it => lib.init(it)
#let data = json("data.json")
#let date = datetime(year: data.year, month: data.month, day: 1)
#let month_length = 32 - (date + duration(days: 31)).day()
#let head(name) = text(size: 32pt, name)
#let empty = box()
#let day(content) = box(
width: 100%,
height: 100%,
stroke: 2pt + black,
content,
)
#align(center + horizon)[
#set par(spacing: 8pt)
Sonnenauf- und Untergang
#strfmt("{:04}-{:02}", date.year(), date.month())
#set par(leading: 5pt)
#grid(
columns: (50pt,) * 7,
rows: 50pt,
gutter: 4pt,
head[Mo], head[Di], head[Mi], head[Do], head[Fr], head[Sa], head[So],
..for _ in range(date.weekday() - 1) { (empty,) },
..for (i, (sunrise, sunset)) in data.times.enumerate() {
(
day[
#strfmt("{:02}", i + 1) \
#sunrise \
#sunset
],
)
},
)
]
#if data.feed {
lib.feed
}

View file

@ -0,0 +1,57 @@
use axum::{Form, extract::State};
use jiff::{Timestamp, ToSpan, Zoned, civil};
use serde::{Deserialize, Serialize};
use crate::server::{Server, somehow};
#[derive(Serialize)]
struct Data {
year: i16,
month: i8,
times: Vec<(String, String)>,
feed: bool,
}
#[derive(Deserialize)]
pub struct FormData {
pub latitude: f64,
pub longitude: f64,
pub year: Option<i16>,
pub month: Option<i8>,
pub feed: Option<bool>,
}
pub async fn post(server: State<Server>, Form(form): Form<FormData>) -> somehow::Result<()> {
let now = Zoned::now().date();
let year = form.year.unwrap_or(now.year());
let month = form.month.unwrap_or(now.month());
let first = civil::Date::new(year, month, 1)?;
let mut times = vec![];
for day in 1..=first.days_in_month() {
let date = first + day.days();
let (rise, set) = sunrise::sunrise_sunset(
form.latitude,
form.longitude,
date.year() as i32,
date.month() as u32,
date.day() as u32,
);
let rise = Timestamp::new(rise, 0)?.strftime("%H:%M").to_string();
let set = Timestamp::new(set, 0)?.strftime("%H:%M").to_string();
times.push((rise, set));
}
let data = Data {
year,
month,
times,
feed: form.feed.unwrap_or(true),
};
let typst = super::typst_with_lib()
.with_json("/data.json", &data)
.with_main_file(include_str!("main.typ"));
server.print_typst(typst).await
}

View file

@ -41,6 +41,7 @@ pub async fn run(tx: mpsc::Sender<Command>, addr: String) -> anyhow::Result<()>
.route("/api/chat", post(documents::chat::post))
.route("/api/egg", post(documents::egg::post))
.route("/api/image", post(documents::image::post))
.route("/api/sunrise", post(documents::sunrise::post))
.route("/api/text", post(documents::text::post))
.route("/api/tictactoe", post(documents::tictactoe::post))
// Rest