Compare commits

...

19 Commits

Author SHA1 Message Date
Paul van Tilburg a289bd9ef0
Bump the version to 0.2.5
Check Details
Lints Details
Test Suite Details
2023-03-24 13:10:22 +01:00
Paul van Tilburg 122f98a92d
Update the changelog 2023-03-24 13:09:53 +01:00
Paul van Tilburg 1426405943
Bump dependencies on cached and chrono-tz 2023-03-24 13:05:42 +01:00
Paul van Tilburg 34be96d187
Update to Rocket 0.5.0-rc.3 2023-03-24 13:04:56 +01:00
Paul van Tilburg bc140a9d1e
Remove unnecessary debug statement
Check Details
Lints Details
Test Suite Details
2023-03-23 16:57:30 +01:00
Paul van Tilburg 39c224eb90
Cargo update; fixes RUSTSEC-2023-0018 2023-03-23 16:56:48 +01:00
Paul van Tilburg b517448fd7
Speed up workflow by using sparce Cargo index for crates.io
Check Details
Lints Details
Test Suite Details
2023-03-21 11:50:00 +01:00
Paul van Tilburg 3de66dbd41
Add Gitea Actions (CI) workflow for cargo
Check Details
Lints Details
Test Suite Details
2023-03-21 11:16:48 +01:00
Paul van Tilburg 6a04fc958f
Fix float comparison in tests 2023-03-21 11:15:32 +01:00
Paul van Tilburg c8b951ab7e
Fix clippy issue 2023-03-21 11:05:03 +01:00
Paul van Tilburg 32ec6b516c
Cargo update
This fixes build issues with the `geo-types` crate versio 0.7.7.
Also replace use of now deprecated functions/methods.
2023-01-31 14:02:46 +01:00
Paul van Tilburg a6301fa678 Fix markdownlint issues 2022-10-23 10:50:05 +02:00
Paul van Tilburg f00537d5f3
Add more lints; fix issues 2022-10-17 20:02:54 +02:00
Paul van Tilburg c8970fa3bb
Cargo update 2022-10-17 19:53:01 +02:00
Paul van Tilburg aee3409f4a Bump dependency on cached to 0.38.0
This fixes the unused `*_prime_cache` compile warnings.
2022-08-12 09:46:17 +02:00
Paul van Tilburg dbdd7bef0f Cargo update 2022-08-12 09:44:15 +02:00
Paul van Tilburg d749233b24 Merge uses 2022-08-12 09:44:08 +02:00
Paul van Tilburg abb6657212
Cargo update 2022-07-17 13:25:22 +02:00
Paul van Tilburg 8b03f2162b
Bump dependency on geocoding to 0.4.0
This finally removes the duplicate dependency tree on older versions of
crates we're already using (chrono, request, etc.).
2022-07-17 13:25:22 +02:00
9 changed files with 922 additions and 1129 deletions

View File

@ -0,0 +1,82 @@
name: "Check, Test and Lint Using Cargo"
on:
- push
- pull_request
- workflow_dispatch
jobs:
check:
name: Check
runs-on: debian-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Install stable toolchain
uses: https://github.com/actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- name: Use sparse Cargo index for crates.io
run: echo -e '[registries.crates-io]\nprotocol = "sparse"' >> /root/.cargo/config.toml
- name: Run cargo check
uses: https://github.com/actions-rs/cargo@v1
with:
command: check
test:
name: Test Suite
runs-on: debian-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Install stable toolchain
uses: https://github.com/actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- name: Use sparse Cargo index for crates.io
run: echo -e '[registries.crates-io]\nprotocol = "sparse"' >> /root/.cargo/config.toml
- name: Run cargo test
uses: https://github.com/actions-rs/cargo@v1
with:
command: test
args: --all-features
lints:
name: Lints
runs-on: debian-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Install stable toolchain
uses: https://github.com/actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy
- name: Use sparse Cargo index for crates.io
run: echo -e '[registries.crates-io]\nprotocol = "sparse"' >> /root/.cargo/config.toml
- name: Run cargo fmt
uses: https://github.com/actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Run cargo clippy
uses: https://github.com/actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings

View File

@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [0.2.5]
### Added
* Add Gitea Actions workflow for cargo
### Changed
* Updated dependencies on `cached`, `chrono-tz` and `geocoding`
### Fixed
* Fix float comparison in tests
* Fix clippy issues
### Security
* Update dependencies ([RUSTSEC-2023-0018](https://rustsec.org/advisories/RUSTSEC-2023-0018.html))
## [0.2.4] - 2022-07-05 ## [0.2.4] - 2022-07-05
### Added ### Added
@ -69,7 +88,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Initial release. Initial release.
[Unreleased]: https://git.luon.net/paul/sinoptik/compare/v0.2.4...HEAD [Unreleased]: https://git.luon.net/paul/sinoptik/compare/v0.2.5...HEAD
[0.2.5]: https://git.luon.net/paul/sinoptik/compare/v0.2.4...v0.2.5
[0.2.4]: https://git.luon.net/paul/sinoptik/compare/v0.2.3...v0.2.4 [0.2.4]: https://git.luon.net/paul/sinoptik/compare/v0.2.3...v0.2.4
[0.2.3]: https://git.luon.net/paul/sinoptik/compare/v0.2.2...v0.2.3 [0.2.3]: https://git.luon.net/paul/sinoptik/compare/v0.2.2...v0.2.3
[0.2.2]: https://git.luon.net/paul/sinoptik/compare/v0.2.1...v0.2.2 [0.2.2]: https://git.luon.net/paul/sinoptik/compare/v0.2.1...v0.2.2

1866
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "sinoptik" name = "sinoptik"
version = "0.2.4" version = "0.2.5"
authors = [ authors = [
"Admar Schoonen <admar@luon.net", "Admar Schoonen <admar@luon.net",
"Paul van Tilburg <paul@luon.net>" "Paul van Tilburg <paul@luon.net>"
@ -12,14 +12,14 @@ repository = "https://git.luon.net/paul/sinoptik"
license = "MIT" license = "MIT"
[dependencies] [dependencies]
cached = { version = "0.34.0", features = ["async"] } cached = { version = "0.42.0", features = ["async"] }
chrono = "0.4.19" chrono = "0.4.19"
chrono-tz = "0.6.1" chrono-tz = "0.8.1"
csv = "1.1.6" csv = "1.1.6"
geocoding = "0.3.1" geocoding = "0.4.0"
image = "0.24.1" image = "0.24.1"
reqwest = { version = "0.11.9", features = ["json"] } reqwest = { version = "0.11.9", features = ["json"] }
rocket = { version = "0.5.0-rc.2", features = ["json"] } rocket = { version = "0.5.0-rc.3", features = ["json"] }
thiserror = "1.0.31" thiserror = "1.0.31"
[dev-dependencies] [dev-dependencies]

View File

@ -67,7 +67,6 @@ GET /forecast?address=Stationsplein,Utrecht&metrics[]=all
or directly by using its geocoded position: or directly by using its geocoded position:
```http ```http
GET /forecast?lat=52.0902&lon=5.1114&metrics[]=all GET /forecast?lat=52.0902&lon=5.1114&metrics[]=all
``` ```
@ -75,8 +74,8 @@ GET /forecast?lat=52.0902&lon=5.1114&metrics[]=all
### Metrics ### Metrics
When querying, the metrics need to be selected. It can be one of: `AQI`, `NO2`, When querying, the metrics need to be selected. It can be one of: `AQI`, `NO2`,
`O3`, `PAQI`, `PM10`, `pollen`, `precipitation` or `UVI`. If you use metric `all`, or `O3`, `PAQI`, `PM10`, `pollen`, `precipitation` or `UVI`. If you use metric
`all` is part of the selected metrics, all metrics will be retrieved. `all`, or `all` is part of the selected metrics, all metrics will be retrieved.
Note that the parameter "array" notation as well as the repeated parameter Note that the parameter "array" notation as well as the repeated parameter
notation are supported. For example: notation are supported. For example:
@ -86,7 +85,7 @@ GET /forecast?address=Stationsplein,Utrecht&metrics=AQI&metrics=pollen
GET /forecast?address=Stationsplein,Utrecht&metrics=all GET /forecast?address=Stationsplein,Utrecht&metrics=all
``` ```
### Response ### Forecast responses
The response of the API is a JSON object that contains three fixed fields: The response of the API is a JSON object that contains three fixed fields:
@ -160,8 +159,8 @@ selecting the maximum value for each hour:
#### Errors #### Errors
If geocoding of an address is requested but fails, a not found error is returned (HTTP 404). If geocoding of an address is requested but fails, a not found error is
with the following body (this will change in the future): returned (HTTP 404). with the following body (this will change in the future):
```json ```json
{ {
@ -173,10 +172,10 @@ with the following body (this will change in the future):
} }
``` ```
If for any specific metric an error occurs, the list with forecast items will be absent. If for any specific metric an error occurs, the list with forecast items will
However, the `errors` field will contain the error message for each failed metric. be absent. However, the `errors` field will contain the error message for each
For example, say Buienradar is down and precipitation forecast items can not be failed metric. For example, say Buienradar is down and precipitation forecast
retrieved: items can not be retrieved:
```json ```json
{ {
@ -210,7 +209,7 @@ or directly by using its geocoded position:
GET /map?lat=52.0902&lon=5.1114&metric=pollen GET /map?lat=52.0902&lon=5.1114&metric=pollen
``` ```
### Response ### Map responses
The response is a PNG image with a crosshair drawn on the map. If geocoding of The response is a PNG image with a crosshair drawn on the map. If geocoding of
an address fails or if the position is out of bounds of the map, nothing is an address fails or if the position is out of bounds of the map, nothing is

View File

@ -1,9 +1,17 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![warn( #![warn(
clippy::all, clippy::all,
missing_copy_implementations,
missing_debug_implementations, missing_debug_implementations,
rust_2018_idioms, rust_2018_idioms,
rustdoc::broken_intra_doc_links rustdoc::broken_intra_doc_links,
trivial_casts,
trivial_numeric_casts,
renamed_and_removed_lints,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)] )]
#![deny(missing_docs)] #![deny(missing_docs)]
@ -60,7 +68,7 @@ pub(crate) enum Error {
UnsupportedMetric(Metric), UnsupportedMetric(Metric),
} }
impl<'r, 'o: 'r> rocket::response::Responder<'r, 'o> for Error { impl<'r, 'o: 'r> Responder<'r, 'o> for Error {
fn respond_to(self, _request: &'r Request<'_>) -> rocket::response::Result<'o> { fn respond_to(self, _request: &'r Request<'_>) -> rocket::response::Result<'o> {
eprintln!("💥 Encountered error during request: {}", self); eprintln!("💥 Encountered error during request: {}", self);
@ -155,7 +163,7 @@ fn rocket(maps_handle: MapsHandle) -> Rocket<Build> {
.attach(AdHoc::on_liftoff("Maps refresher", |_| { .attach(AdHoc::on_liftoff("Maps refresher", |_| {
Box::pin(async move { Box::pin(async move {
// We don't care about the join handle nor error results? // We don't care about the join handle nor error results?
let _ = rocket::tokio::spawn(maps_refresher); let _refresher = rocket::tokio::spawn(maps_refresher);
}) })
})) }))
} }
@ -205,8 +213,8 @@ mod tests {
let response = client.get("/forecast?address=eindhoven").dispatch(); let response = client.get("/forecast?address=eindhoven").dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
let json = response.into_json::<JsonValue>().expect("Not valid JSON"); let json = response.into_json::<JsonValue>().expect("Not valid JSON");
assert_f64_near!(json["lat"].as_f64().unwrap(), 51.4392648); assert_float_absolute_eq!(json["lat"].as_f64().unwrap(), 51.44855695, 1e-8);
assert_f64_near!(json["lon"].as_f64().unwrap(), 5.478633); assert_float_absolute_eq!(json["lon"].as_f64().unwrap(), 5.45012252, 1e-8);
assert_matches!(json["time"], JsonValue::Number(_)); assert_matches!(json["time"], JsonValue::Number(_));
assert_matches!(json.get("AQI"), None); assert_matches!(json.get("AQI"), None);
assert_matches!(json.get("NO2"), None); assert_matches!(json.get("NO2"), None);
@ -223,8 +231,8 @@ mod tests {
.dispatch(); .dispatch();
assert_eq!(response.status(), Status::Ok); assert_eq!(response.status(), Status::Ok);
let json = response.into_json::<JsonValue>().expect("Not valid JSON"); let json = response.into_json::<JsonValue>().expect("Not valid JSON");
assert_f64_near!(json["lat"].as_f64().unwrap(), 51.4392648); assert_float_absolute_eq!(json["lat"].as_f64().unwrap(), 51.44855695, 1e-8);
assert_f64_near!(json["lon"].as_f64().unwrap(), 5.478633); assert_float_absolute_eq!(json["lon"].as_f64().unwrap(), 5.45012252, 1e-8);
assert_matches!(json["time"], JsonValue::Number(_)); assert_matches!(json["time"], JsonValue::Number(_));
assert_matches!(json.get("AQI"), Some(JsonValue::Array(_))); assert_matches!(json.get("AQI"), Some(JsonValue::Array(_)));
assert_matches!(json.get("NO2"), Some(JsonValue::Array(_))); assert_matches!(json.get("NO2"), Some(JsonValue::Array(_)));

View File

@ -1,9 +1,17 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
#![warn( #![warn(
clippy::all, clippy::all,
missing_copy_implementations,
missing_debug_implementations, missing_debug_implementations,
rust_2018_idioms, rust_2018_idioms,
rustdoc::broken_intra_doc_links rustdoc::broken_intra_doc_links,
trivial_casts,
trivial_numeric_casts,
renamed_and_removed_lints,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)] )]
#![deny(missing_docs)] #![deny(missing_docs)]

View File

@ -393,7 +393,7 @@ fn sample<I: GenericImageView<Pixel = Rgba<u8>>>(
.expect("Maximum color is always a map key color") as u8; .expect("Maximum color is always a map key color") as u8;
samples.push(Sample { time, score }); samples.push(Sample { time, score });
time = time + chrono::Duration::seconds(interval as i64); time += chrono::Duration::seconds(interval);
offset += width; offset += width;
} }

View File

@ -4,9 +4,8 @@
//! and <https://www.buienradar.nl/overbuienradar/gratis-weerdata>. //! and <https://www.buienradar.nl/overbuienradar/gratis-weerdata>.
use cached::proc_macro::cached; use cached::proc_macro::cached;
use chrono::offset::TimeZone;
use chrono::serde::ts_seconds; use chrono::serde::ts_seconds;
use chrono::{DateTime, Datelike, Duration, NaiveTime, ParseError, Utc}; use chrono::{DateTime, Datelike, Duration, NaiveTime, ParseError, TimeZone, Utc};
use chrono_tz::Europe; use chrono_tz::Europe;
use csv::ReaderBuilder; use csv::ReaderBuilder;
use reqwest::Url; use reqwest::Url;
@ -66,10 +65,10 @@ impl TryFrom<Row> for Item {
/// time zone. /// time zone.
fn parse_time(t: &str) -> Result<DateTime<Utc>, ParseError> { fn parse_time(t: &str) -> Result<DateTime<Utc>, ParseError> {
// First, get the current date in the Europe/Amsterdam time zone. // First, get the current date in the Europe/Amsterdam time zone.
let today = Utc::now().with_timezone(&Europe::Amsterdam).date(); let today = Utc::now().with_timezone(&Europe::Amsterdam).date_naive();
// Then, parse the time and interpret it relative to "today". // Then, parse the time and interpret it relative to "today".
let ntime = NaiveTime::parse_from_str(t, "%H:%M")?; let ntime = NaiveTime::parse_from_str(t, "%H:%M")?;
let ndtime = today.naive_local().and_time(ntime); let ndtime = today.and_time(ntime);
// Finally, interpret the naive date/time in the Europe/Amsterdam time zone and convert it to // Finally, interpret the naive date/time in the Europe/Amsterdam time zone and convert it to
// the UTC time zone. // the UTC time zone.
let ldtime = Europe::Amsterdam.from_local_datetime(&ndtime).unwrap(); let ldtime = Europe::Amsterdam.from_local_datetime(&ndtime).unwrap();
@ -98,8 +97,9 @@ fn fix_items_day_boundary(items: Vec<Item>) -> Vec<Item> {
let now = Utc::now().with_timezone(&Europe::Amsterdam); let now = Utc::now().with_timezone(&Europe::Amsterdam);
// Use noon on the same day as "now" as a comparison moment. // Use noon on the same day as "now" as a comparison moment.
let noon = Europe::Amsterdam let noon = Europe::Amsterdam
.ymd(now.year(), now.month(), now.day()) .with_ymd_and_hms(now.year(), now.month(), now.day(), 12, 0, 0)
.and_hms(12, 0, 0); .single()
.expect("Invalid date: input date is invalid or not unambiguous");
if now < noon { if now < noon {
// It is still before noon, so bump timestamps after noon a day back. // It is still before noon, so bump timestamps after noon a day back.
@ -107,7 +107,7 @@ fn fix_items_day_boundary(items: Vec<Item>) -> Vec<Item> {
.into_iter() .into_iter()
.map(|mut item| { .map(|mut item| {
if item.time > noon { if item.time > noon {
item.time = item.time - Duration::days(1) item.time -= Duration::days(1)
} }
item item
}) })
@ -118,7 +118,7 @@ fn fix_items_day_boundary(items: Vec<Item>) -> Vec<Item> {
.into_iter() .into_iter()
.map(|mut item| { .map(|mut item| {
if item.time < noon { if item.time < noon {
item.time = item.time + Duration::days(1) item.time += Duration::days(1)
} }
item item
}) })