neiam
/fieri-forever
the flavortown lunch engine
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
Log in to mark this repository.
Opening hours, cuisine pages, area rename, I-went date
6dd7389 · Gabriel Morell · 2026-10-28 20:32
Message
{commit_body(@commit)}
Files changed
modified
Cargo.toml
+1
−0
| 50 | 50 | # OSM APIs (Photon geocoder + POI lookup) |
| 51 | 51 | reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } |
| 52 | 52 | rand = "0.9" |
| 53 | +opening-hours = { version = "2.1.4", default-features = false } | |
| 53 | 54 | |
| 54 | 55 | # Auth |
| 55 | 56 | argon2 = { version = "0.5", features = ["std"] } |
modified
README.md
+14
−0
| 98 | 98 | Neutral 1. Obsessed records when it started; picking it again keeps that |
| 99 | 99 | date, and it can be backdated from the rank widget. |
| 100 | 100 | Unranked counts as Neutral, so nobody has to rank everything. |
| 101 | +- **Open.** If OSM says a place is closed at the meal time (noon by | |
| 102 | + default; the planner and votes take a time), it's out. Unknown or | |
| 103 | + unreadable hours count as open. Parsed with the `opening-hours` crate at | |
| 104 | + naive local time, so trip areas in other time zones need no tz handling. | |
| 101 | 105 | - **Freshness.** `1 / (1 + 3 × load)`, where load sums every visit in the last |
| 102 | 106 | 70 days, each decayed by a 10-day half-life. Yesterday's spot keeps about a |
| 103 | 107 | quarter of its weight; one from a month ago is mostly forgiven. |
| 132 | 136 | anything the tile server's import predates. Picking a candidate copies them |
| 133 | 137 | into the form. Places OSM has no `cuisine` tag for stay blank to type in. |
| 134 | 138 | |
| 139 | +Restaurants added before hours were tracked can pick them up with | |
| 140 | +**Refresh from OSM** on the restaurant page, or for every OSM-linked place at | |
| 141 | +once under **Manage** on the area page. Refresh replaces hours (they change) | |
| 142 | +but only fills cuisine, website and address where blank. | |
| 143 | + | |
| 144 | +**Cuisines** (`/cuisines`, optionally `?area=`) lists every cuisine tag with | |
| 145 | +counts; each tag's page shows every matching restaurant grouped by area, with | |
| 146 | +your rank and whether it's open at noon today. Cuisine tags link there | |
| 147 | +everywhere they appear. | |
| 148 | + | |
| 135 | 149 | Anything OSM doesn't know about can be added by dropping a pin and typing the |
| 136 | 150 | name. Adding an OSM place that's already here just records your rank on the |
| 137 | 151 | existing one. |
modified
migration/src/lib.rs
+2
−0
| 4 | 4 | mod m20261028_000001_create_lunch_tables; |
| 5 | 5 | mod m20261028_000002_create_outings; |
| 6 | 6 | mod m20261028_000003_obsessed; |
| 7 | +mod m20261028_000004_opening_hours; | |
| 7 | 8 | |
| 8 | 9 | pub struct Migrator; |
| 9 | 10 |
| 15 | 16 | Box::new(m20261028_000001_create_lunch_tables::Migration), |
| 16 | 17 | Box::new(m20261028_000002_create_outings::Migration), |
| 17 | 18 | Box::new(m20261028_000003_obsessed::Migration), |
| 19 | + Box::new(m20261028_000004_opening_hours::Migration), | |
| 18 | 20 | ] |
| 19 | 21 | } |
| 20 | 22 | } |
added
migration/src/m20261028_000004_opening_hours.rs
+50
−0
| 1 | +//! Opening hours (the raw OSM `opening_hours` expression) on restaurants, and | |
| 2 | +//! the time an outing is for, so suggestions can skip places that are closed | |
| 3 | +//! when you'd arrive. | |
| 4 | +use sea_orm_migration::prelude::*; | |
| 5 | + | |
| 6 | +#[derive(DeriveMigrationName)] | |
| 7 | +pub struct Migration; | |
| 8 | + | |
| 9 | +#[async_trait::async_trait] | |
| 10 | +impl MigrationTrait for Migration { | |
| 11 | + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { | |
| 12 | + manager | |
| 13 | + .alter_table( | |
| 14 | + Table::alter() | |
| 15 | + .table(Restaurants::Table) | |
| 16 | + .add_column(ColumnDef::new(Restaurants::OpeningHours).string_len(512).null()) | |
| 17 | + .to_owned(), | |
| 18 | + ) | |
| 19 | + .await?; | |
| 20 | + manager | |
| 21 | + .alter_table( | |
| 22 | + Table::alter() | |
| 23 | + .table(Outings::Table) | |
| 24 | + .add_column(ColumnDef::new(Outings::MealTime).time().not_null().default("12:00:00")) | |
| 25 | + .to_owned(), | |
| 26 | + ) | |
| 27 | + .await | |
| 28 | + } | |
| 29 | + | |
| 30 | + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { | |
| 31 | + manager | |
| 32 | + .alter_table(Table::alter().table(Outings::Table).drop_column(Outings::MealTime).to_owned()) | |
| 33 | + .await?; | |
| 34 | + manager | |
| 35 | + .alter_table(Table::alter().table(Restaurants::Table).drop_column(Restaurants::OpeningHours).to_owned()) | |
| 36 | + .await | |
| 37 | + } | |
| 38 | +} | |
| 39 | + | |
| 40 | +#[derive(DeriveIden)] | |
| 41 | +enum Restaurants { | |
| 42 | + Table, | |
| 43 | + OpeningHours, | |
| 44 | +} | |
| 45 | + | |
| 46 | +#[derive(DeriveIden)] | |
| 47 | +enum Outings { | |
| 48 | + Table, | |
| 49 | + MealTime, | |
| 50 | +} |
modified
src/bin/webservice.rs
+5
−0
| 83 | 83 | get(handlers::lunch::show_planner).post(handlers::lunch::make_plan), |
| 84 | 84 | ) |
| 85 | 85 | .route("/areas/{id}/history", get(handlers::lunch::show_history)) |
| 86 | + .route("/areas/{id}/rename", post(handlers::areas::rename_area)) | |
| 87 | + .route("/areas/{id}/refresh", post(handlers::areas::refresh_area)) | |
| 88 | + .route("/cuisines", get(handlers::cuisines::list_cuisines)) | |
| 89 | + .route("/cuisines/{name}", get(handlers::cuisines::show_cuisine)) | |
| 86 | 90 | .route("/areas/{id}/outings", post(handlers::outings::create_outing)) |
| 87 | 91 | .route("/outings/{id}", get(handlers::outings::show_outing)) |
| 88 | 92 | .route("/outings/{id}/panel", get(handlers::outings::panel)) |
| 102 | 106 | ) |
| 103 | 107 | .route("/restaurants/{id}/rank", post(handlers::restaurants::set_rank)) |
| 104 | 108 | .route("/restaurants/{id}/archive", post(handlers::restaurants::toggle_archive)) |
| 109 | + .route("/restaurants/{id}/refresh", post(handlers::restaurants::refresh_restaurant)) | |
| 105 | 110 | .route("/lunches", post(handlers::lunch::log_lunch)) |
| 106 | 111 | .route("/lunches/{id}/delete", post(handlers::lunch::delete_lunch)) |
| 107 | 112 | .layer(axum::middleware::from_fn_with_state(db.clone(), web_auth_middleware)); |
modified
src/entities/outings.rs
+2
−0
Click to load diff…
modified
src/entities/restaurants.rs
+20
−0
Click to load diff…
modified
src/handlers/areas.rs
+69
−1
Click to load diff…
added
src/handlers/cuisines.rs
+155
−0
Click to load diff…
modified
src/handlers/lunch.rs
+12
−2
Click to load diff…
modified
src/handlers/mod.rs
+10
−0
Click to load diff…
modified
src/handlers/outings.rs
+3
−2
Click to load diff…
modified
src/handlers/restaurants.rs
+67
−0
Click to load diff…
added
src/hours.rs
+83
−0
Click to load diff…
modified
src/lib.rs
+1
−0
Click to load diff…
modified
src/osm.rs
+75
−43
Click to load diff…
modified
src/suggest.rs
+61
−21
Click to load diff…
modified
templates/_macros.html
+16
−0
Click to load diff…
modified
templates/areas/show.html
+32
−2
Click to load diff…
added
templates/cuisines/list.html
+27
−0
Click to load diff…
added
templates/cuisines/show.html
+44
−0
Click to load diff…
modified
templates/lunch/_plan.html
+9
−5
Click to load diff…
modified
templates/lunch/plan.html
+6
−1
Click to load diff…
modified
templates/outings/_panel.html
+1
−1
Click to load diff…
modified
templates/outings/show.html
+1
−1
Click to load diff…
modified
templates/places/_candidates.html
+1
−0
Click to load diff…
modified
templates/restaurants/new.html
+4
−0
Click to load diff…
modified
templates/restaurants/show.html
+19
−3
Click to load diff…
modified
tests/templates.rs
+60
−2
Click to load diff…
Parents: 2475dea