Skip to content

Commit

Permalink
Documentation fixes (#27)
Browse files Browse the repository at this point in the history
* Add instructions to change hostname

* Extend what comes next section

* Add unittest for public transport widget

* Add test section to README

* Truncate error text of widgets

* Bump version of app crate to 1.0.0

* Add more badges to README

* Rename frontend sections
  • Loading branch information
eliabieri authored Jan 6, 2023
1 parent aa30e1a commit 13ac6b7
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 30 deletions.
26 changes: 21 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
<br/>
</div>

[![Cargo test](https://github.com/eliabieri/wg_display/actions/workflows/cargo_test.yml/badge.svg)](https://github.com/eliabieri/wg_display/actions/workflows/cargo_test.yml)
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/eliabieri/wg_display/cargo_test.yml?label=test&logo=github)
![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/eliabieri/wg_display/build.yml?logo=github)

![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/eliabieri/wg_display?logo=github)
![GitHub commits since latest release (by SemVer)](https://img.shields.io/github/commits-since/eliabieri/wg_display/latest/main?logo=github)

## ⭐️ What WG Display can show you

Expand Down Expand Up @@ -42,7 +46,8 @@
- [Building the project](#building-the-project)
- [👏 Writing your own widget](#-writing-your-own-widget)
- [📖 Documentation (rustdocs)](#-documentation-rustdocs)
- [🔮 Upcoming features](#-upcoming-features)
- [🧪 Testing](#-testing)
- [🔮 What comes next](#-what-comes-next)
- [🔒 Safety](#-safety)
- [♻️ Updating the dependencies](#️-updating-the-dependencies)
- [🦾 Developing on target](#-developing-on-target)
Expand All @@ -57,11 +62,14 @@ The web interface allows the users to configure system aspects like the backgrou
## 🚀 Getting started

1. Download the latest [release](https://github.com/eliabieri/wg_display/releases)
- Raspberry Pi Zero 1 / Zero W / Zero WH -> wg-display-arm-unknown-linux-gnueabihf
- Raspberry Pi 2 / 3 / 4 / Zero 2 W -> wg-display-armv7-unknown-linux-gnueabihf
- Raspberry Pi Zero 1 / Zero W / Zero WH -> `wg-display-arm-unknown-linux-gnueabihf`
- Raspberry Pi 2 / 3 / 4 / Zero 2 W -> `wg-display-armv7-unknown-linux-gnueabihf`
2. Copy the binary over to the target
3. Add the full path of the binary to the end of ~/.bashrc
This way, the binary is run at reboot.
4. Change the hostname of the target to wgdisplay
This way, the configuration interface can be accessed via [wgdisplay.local](http://wgdisplay.local)
`sudo raspi-config` -> `Network Options` -> `Hostname`

## 🛠️ Assembling the hardware

Expand Down Expand Up @@ -158,10 +166,18 @@ This generates three separate documentations, one for each crate
[common](common/target/doc/common/index.html): ```common/target/doc/common/index.html```
[frontend](frontend/target/doc/frontend/index.html): ```frontend/target/doc/frontend/index.html```

## 🔮 Upcoming features
## 🧪 Testing

Widgets should provide unit tests for their functionality where adequate.
Asynchronous functions can be tested using the [tokio_test::block_on](https://docs.rs/tokio-test/latest/tokio_test/fn.block_on.html) function.

## 🔮 What comes next

- [ ] Allow user to configure WiFi credentials via web interface
- [ ] Starting the binary through systemd
- [ ] Implement an update mechanism
- [ ] Implement authencation for the web interface
- [ ] Dynamically loading widgets (currently, the widgets are part of the app crate)

## 🔒 Safety

Expand Down
16 changes: 15 additions & 1 deletion app/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions app/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "app"
version = "0.1.0"
version = "1.0.0"
edition = "2021"
readme = "README.md"
repository = "https://github.com/eliabieri/wg_display"
Expand All @@ -23,12 +23,17 @@ time-humanize = "0.1.3"
cursive = { version = "0.20.0", features = [
"termion-backend",
], default-features = false }
# Dashboard

# Server
time = { version = "0.3.17", features = ["serde-well-known"] }
urlencoding = "2.1.2"
rocket = { version = "0.5.0-rc.2", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
rust-embed = "6.4.2"

# Persistence
sled = "0.34.7"
lazy_static = "1.4.0"

# Testing
tokio-test = "0.4.2"
8 changes: 4 additions & 4 deletions app/src/renderer/widgets/aare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ impl Widget for Aare {
);
self.last_updated = Instant::now();
}
Err(e) => {
self.content = format!("Could not deserialize data: {}", e);
Err(_) => {
self.content = "Could not deserialize data".to_string();
self.last_updated = Instant::now();
}
},
Err(error) => {
self.content = format!("Could not update data: {}", error);
Err(_) => {
self.content = "Could not update data".to_string();
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions app/src/renderer/widgets/bernaqua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ impl Widget for Bernaqua {
self.content = format!("{:.1}% occupied", data.get_capacity());
self.last_updated = Instant::now();
}
Err(e) => {
self.content = format!("Could not deserialize data: {}", e);
Err(_) => {
self.content = "Could not deserialize data".to_string();
self.last_updated = Instant::now();
}
},
Err(error) => {
self.content = format!("Could not update data: {}", error);
Err(_) => {
self.content = "Could not update data".to_string();
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions app/src/renderer/widgets/cafete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ impl Widget for Cafete {
self.content = data.get_lineup();
self.last_updated = Instant::now();
}
Err(e) => {
self.content = format!("Could not deserialize data: {}", e);
Err(_) => {
self.content = "Could not deserialize data".to_string();
self.last_updated = Instant::now();
}
},
Err(error) => {
self.content = format!("Could not update data: {}", error);
Err(_) => {
self.content = "Could not update data".to_string();
}
}
}
Expand Down
126 changes: 118 additions & 8 deletions app/src/renderer/widgets/public_transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl Widget for PublicTransport {
async fn update(&mut self, config: &WidgetConfiguration) {
let config = &config.public_transport_config;
if config.from.is_empty() || config.to.is_empty() {
self.content = "From and to need to be specified!".to_string();
self.content = "`from` and `to` need to be configured!".to_string();
return;
}

Expand All @@ -106,13 +106,13 @@ impl Widget for PublicTransport {
.dedup_by(|a, b| a.from.departure == b.from.departure);
self.last_updated = Some(Instant::now());
}
Err(e) => {
self.content = format!("Could not deserialize data: {}", e);
Err(_) => {
self.content = "Could not deserialize data".to_string();
self.last_updated = Some(Instant::now());
}
},
Err(error) => {
self.content = format!("Could not update data: {}", error);
Err(_) => {
self.content = "Could not update data".to_string();
}
}
}
Expand All @@ -122,6 +122,11 @@ impl PublicTransport {
fn update_departure_string(&mut self, num_departures: usize) {
self.content = format!("{} -> {}", self.data.from.name, self.data.to.name);

if self.data.connections.is_empty() {
self.content += "\nNo departures";
return;
}

let connections = self
.data
.connections
Expand All @@ -146,9 +151,7 @@ impl PublicTransport {
let format = format_description::parse("[hour]:[minute]").unwrap();
match departure.format(&format) {
Ok(departure) => departure,
Err(e) => {
format!("Could not format departure: {}", e)
}
Err(_) => "Could not format departure".to_string(),
}
}

Expand All @@ -157,3 +160,110 @@ impl PublicTransport {
HumanTime::from(departure_offset.unsigned_abs()).to_text_en(Accuracy::Rough, Tense::Future)
}
}

#[cfg(test)]
mod tests {
use common::models::{BaseWidgetConfig, PublicTransportConfig, WidgetConfiguration};

use super::*;

#[test]
fn test_formatting() {
let mut public_transport = PublicTransport::new();
public_transport.last_updated = Some(Instant::now());
public_transport.data = PublicTransportData {
connections: vec![
ConnectionData {
from: FromData {
departure: OffsetDateTime::now_utc() + Duration::from_secs(23 * 60 + 1),
},
},
ConnectionData {
from: FromData {
departure: OffsetDateTime::now_utc() + Duration::from_secs(120 * 60 + 1),
},
},
],
from: FromMetaData {
name: "Bern".to_string(),
},
to: ToMetaData {
name: "Basel".to_string(),
},
};

let config = WidgetConfiguration {
public_transport_config: PublicTransportConfig {
base_config: BaseWidgetConfig { enabled: true },
from: "Bern".to_string(),
to: "Basel".to_string(),
num_connections_to_show: 2,
},
..Default::default()
};
tokio_test::block_on(public_transport.update(&config));

assert!(public_transport.content.contains("Bern -> Basel"));
assert!(public_transport.content.contains("\nin 23 minutes"));
assert!(public_transport.content.contains("\nin 2 hours"));
}

#[test]
fn test_formatting_with_no_departures() {
let mut public_transport = PublicTransport::new();
public_transport.last_updated = Some(Instant::now());
public_transport.data = PublicTransportData {
connections: vec![],
from: FromMetaData {
name: "Bern".to_string(),
},
to: ToMetaData {
name: "Basel".to_string(),
},
};

let config = WidgetConfiguration {
public_transport_config: PublicTransportConfig {
base_config: BaseWidgetConfig { enabled: true },
from: "Bern".to_string(),
to: "Basel".to_string(),
num_connections_to_show: 2,
},
..Default::default()
};
tokio_test::block_on(public_transport.update(&config));

assert_eq!(public_transport.content, "Bern -> Basel\nNo departures");
}

#[test]
fn test_from_to_not_configured() {
let mut public_transport = PublicTransport::new();
public_transport.last_updated = Some(Instant::now());
public_transport.data = PublicTransportData {
connections: vec![],
from: FromMetaData {
name: "Bern".to_string(),
},
to: ToMetaData {
name: "Basel".to_string(),
},
};

let config = WidgetConfiguration {
public_transport_config: PublicTransportConfig {
base_config: BaseWidgetConfig { enabled: true },
from: "".to_string(),
to: "".to_string(),
num_connections_to_show: 2,
},
..Default::default()
};
tokio_test::block_on(public_transport.update(&config));

assert_eq!(
public_transport.content,
"`from` and `to` need to be configured!"
);
}
}
4 changes: 2 additions & 2 deletions frontend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ fn main_component() -> Html {
<img src="assets/logo.png" alt="" class="h-24 object-contain py-4"/>
// Content
<div>
<DividerComponent text="Configuration"/>
<DividerComponent text="General"/>

<BackgroundColorConfigComponent
config={system_config.clone()}
/>


<DividerComponent text="Widget configuration"/>
<DividerComponent text="Widgets"/>

<div>
<ConfigCardComponent>
Expand Down

0 comments on commit 13ac6b7

Please sign in to comment.