changeset 48:6ee4923958f0

Allow for attaching notes to points.
author Lewin Bormann <lbo@spheniscida.de>
date Thu, 03 Dec 2020 21:56:22 +0100
parents c3812802146c
children 27b4391e7df8
files pgsql_schema.sql src/db.rs src/main.rs src/types.rs
diffstat 4 files changed, 68 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/pgsql_schema.sql	Thu Dec 03 17:44:36 2020 +0100
+++ b/pgsql_schema.sql	Thu Dec 03 21:56:22 2020 +0100
@@ -35,6 +35,7 @@
     t timestamp with time zone not null,
     ele double precision
     secret bytea,
+    note text
 );
 
 
--- a/src/db.rs	Thu Dec 03 17:44:36 2020 +0100
+++ b/src/db.rs	Thu Dec 03 21:56:22 2020 +0100
@@ -21,16 +21,31 @@
     ) -> Result<types::GeoJSON, postgres::Error> {
         let mut returnable = types::GeoJSON::new();
         let stmt = self.0.prepare_cached(
-            r"SELECT id, t, lat, long, spd, ele FROM geohub.geodata
+            r"SELECT id, t, lat, long, spd, ele, note FROM geohub.geodata
         WHERE (client = $1) and (t between $2 and $3) AND (secret = public.digest($4, 'sha256') or secret is null) AND (id > $5)
         ORDER BY t ASC
         LIMIT $6").unwrap(); // Must succeed.
         let rows = stmt.query(&[&name, &from_ts, &to_ts, &secret, &last.unwrap_or(0), &limit])?;
         returnable.reserve_features(rows.len());
         for row in rows.iter() {
-            let (id, ts, lat, long, spd, ele) =
-                (row.get(0), row.get(1), row.get(2), row.get(3), row.get(4), row.get(5));
-            returnable.push_feature(types::geofeature_from_row(id, ts, lat, long, spd, ele));
+            let (id, ts, lat, long, spd, ele, note) = (
+                row.get(0),
+                row.get(1),
+                row.get(2),
+                row.get(3),
+                row.get(4),
+                row.get(5),
+                row.get(6),
+            );
+            let point = types::GeoPoint {
+                lat: lat,
+                long: long,
+                spd: spd,
+                ele: ele,
+                note: note,
+                time: ts,
+            };
+            returnable.push_feature(types::geofeature_from_point(id, point));
         }
         Ok(returnable)
     }
@@ -41,8 +56,12 @@
         secret: &Option<String>,
         point: &types::GeoPoint,
     ) -> Result<(), postgres::Error> {
-        let stmt = self.0.prepare_cached("INSERT INTO geohub.geodata (client, lat, long, spd, t, ele, secret) VALUES ($1, $2, $3, $4, $5, $6, public.digest($7, 'sha256'))").unwrap();
-        let channel = format!("NOTIFY {}, '{}'", ids::channel_name(name, secret.as_ref().unwrap_or(&"".into()).as_str()), name);
+        let stmt = self.0.prepare_cached("INSERT INTO geohub.geodata (client, lat, long, spd, t, ele, secret, note) VALUES ($1, $2, $3, $4, $5, $6, public.digest($7, 'sha256'), $8)").unwrap();
+        let channel = format!(
+            "NOTIFY {}, '{}'",
+            ids::channel_name(name, secret.as_ref().unwrap_or(&"".into()).as_str()),
+            name
+        );
         let notify = self.0.prepare_cached(channel.as_str()).unwrap();
         stmt.execute(&[
             &name,
@@ -52,6 +71,7 @@
             &point.time,
             &point.ele,
             &secret,
+            &point.note,
         ])
         .unwrap();
         notify.execute(&[]).unwrap();
@@ -68,7 +88,7 @@
     ) -> Option<(types::GeoJSON, i32)> {
         let mut returnable = types::GeoJSON::new();
         let check_for_new = self.0.prepare_cached(
-            r"SELECT id, t, lat, long, spd, ele FROM geohub.geodata
+            r"SELECT id, t, lat, long, spd, ele, note FROM geohub.geodata
             WHERE (client = $1) and (id > $2) AND (secret = public.digest($3, 'sha256') or secret is null)
             ORDER BY id DESC
             LIMIT $4").unwrap(); // Must succeed.
@@ -84,15 +104,24 @@
                 let mut last = 0;
 
                 for row in rows.iter() {
-                    let (id, ts, lat, long, spd, ele) = (
+                    let (id, ts, lat, long, spd, ele, note) = (
                         row.get(0),
                         row.get(1),
                         row.get(2),
                         row.get(3),
                         row.get(4),
                         row.get(5),
+                        row.get(6),
                     );
-                    returnable.push_feature(types::geofeature_from_row(Some(id), ts, lat, long, spd, ele));
+                    let point = types::GeoPoint {
+                        time: ts,
+                        lat: lat,
+                        long: long,
+                        spd: spd,
+                        ele: ele,
+                        note: note,
+                    };
+                    returnable.push_feature(types::geofeature_from_point(Some(id), point));
                     if id > last {
                         last = id;
                     }
--- a/src/main.rs	Thu Dec 03 17:44:36 2020 +0100
+++ b/src/main.rs	Thu Dec 03 21:56:22 2020 +0100
@@ -94,7 +94,10 @@
 
 /// time is like 2020-11-30T20:12:36.444Z (ISO 8601). By default, server time is set.
 /// secret can be used to protect points.
-#[rocket::post("/geo/<name>/log?<lat>&<longitude>&<time>&<s>&<ele>&<secret>")]
+#[rocket::post(
+    "/geo/<name>/log?<lat>&<longitude>&<time>&<s>&<ele>&<secret>",
+    data = "<note>"
+)]
 fn log(
     db: db::DBConn,
     name: String,
@@ -104,6 +107,7 @@
     time: Option<String>,
     s: Option<f64>,
     ele: Option<f64>,
+    note: Option<String>,
 ) -> http::GeoHubResponse {
     let db = db::DBQuery(&db.0);
     // Check that secret and client name are legal.
@@ -117,12 +121,25 @@
     if let Some(time) = time {
         ts = util::flexible_timestamp_parse(time).unwrap_or(ts);
     }
+
+    // Only store a note if one was attached.
+    let note = if let Some(note) = note {
+        if note.is_empty() {
+            None
+        } else {
+            Some(note)
+        }
+    } else {
+        note
+    };
+
     let point = types::GeoPoint {
         lat: lat,
         long: longitude,
         time: ts,
         spd: s,
         ele: ele,
+        note: note,
     };
     if let Err(e) = db.log_geopoint(name.as_str(), &secret, &point) {
         return http::server_error(e.to_string());
--- a/src/types.rs	Thu Dec 03 17:44:36 2020 +0100
+++ b/src/types.rs	Thu Dec 03 21:56:22 2020 +0100
@@ -6,6 +6,7 @@
     pub spd: Option<f64>,
     pub ele: Option<f64>,
     pub time: chrono::DateTime<chrono::Utc>,
+    pub note: Option<String>,
 }
 
 #[derive(serde::Serialize, Debug)]
@@ -35,14 +36,17 @@
     time: chrono::DateTime<chrono::Utc>,
     altitude: Option<f64>,
     speed: Option<f64>,
+    /// The unique ID of the point.
     id: Option<i32>,
+    /// An arbitrary note attached by the logging client.
+    note: Option<String>,
 }
 
 #[derive(serde::Serialize, Debug, Clone)]
 pub struct GeoGeometry {
     #[serde(rename = "type")]
     typ: String, // always "Point"
-    coordinates: Vec<f64>, // always [long, lat]
+    coordinates: (f64, f64), // always [long, lat]
 }
 
 #[derive(serde::Serialize, Debug, Clone)]
@@ -75,25 +79,19 @@
     }
 }
 
-pub fn geofeature_from_row(
-    id: Option<i32>,
-    ts: chrono::DateTime<chrono::Utc>,
-    lat: Option<f64>,
-    long: Option<f64>,
-    spd: Option<f64>,
-    ele: Option<f64>,
-) -> GeoFeature {
+pub fn geofeature_from_point(id: Option<i32>, point: GeoPoint) -> GeoFeature {
     GeoFeature {
         typ: "Feature".into(),
         properties: GeoProperties {
             id: id,
-            time: ts,
-            altitude: ele,
-            speed: spd,
+            time: point.time,
+            altitude: point.ele,
+            speed: point.spd,
+            note: point.note,
         },
         geometry: GeoGeometry {
             typ: "Point".into(),
-            coordinates: vec![long.unwrap_or(0.), lat.unwrap_or(0.)],
+            coordinates: (point.long, point.lat),
         },
     }
 }