changeset 44:e7e91f288868

Add origin country chart
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 19 Jul 2022 20:18:25 -0700
parents 2ecb25f3adfe
children 90a4e4589d5e
files assets/index.html.hbs src/logsdb.rs src/main.rs
diffstat 3 files changed, 74 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/assets/index.html.hbs	Tue Jul 19 19:56:14 2022 -0700
+++ b/assets/index.html.hbs	Tue Jul 19 20:18:25 2022 -0700
@@ -86,11 +86,16 @@
         <div class="plotframe halfwidth">Top paths<canvas id="topPaths"></canvas> </div>
         <div class="plotframe halfwidth">Requests per Session<canvas id="requestsBySession"></canvas> </div>
     </div>
+    <div class="plotrow row3">
+        <div class="plotframe halfwidth">Sessions by country<canvas id="sessionsByCountry" height="30"></canvas> </div>
+        <div class="plotframe halfwidth">Requests per Session<canvas id="BLAH"></canvas> </div>
+    </div>
 
     <script>
         let plots = {"visitsAndSessions": {{{ chartconfig.visitsAndSessions }}},
                      "topPaths": {{{ chartconfig.topPaths }}},
-                     "requestsBySession": {{{ chartconfig.requestsBySession }}} };
+                     "requestsBySession": {{{ chartconfig.requestsBySession }}},
+                     "sessionsByCountry": {{{ chartconfig.sessionsByCountry }}} };
 
         Object.keys(plots).forEach((cv) => {
                     if (plots[cv] == undefined) {
--- a/src/logsdb.rs	Tue Jul 19 19:56:14 2022 -0700
+++ b/src/logsdb.rs	Tue Jul 19 20:18:25 2022 -0700
@@ -157,6 +157,32 @@
         Ok((dates, visits, sessions))
     }
 
+    pub async fn query_top_countries(
+        &mut self,
+        from: OffsetDateTime,
+        to: OffsetDateTime,
+        tz_offset: Option<i64>,
+        domain: Option<&str>,
+    ) -> Result<Vec<(String, i64)>, Error> {
+        let tz_offset = tz_offset.unwrap_or(0);
+        let result = sqlx::query(
+            r#"
+SELECT origin_country, COUNT(*) AS count
+FROM sessions
+WHERE start+? > ? AND start+? < ? AND domain LIKE ?
+GROUP BY origin_country
+ORDER BY count DESC;"#,
+        )
+        .bind(tz_offset)
+        .bind(from.unix_timestamp())
+        .bind(tz_offset)
+        .bind(to.unix_timestamp())
+        .bind(domain.unwrap_or("%"))
+        .fetch_all(&mut *self.0).await?;
+
+        Ok(result.into_iter().map(|row| (row.get(0), row.get(1))).collect())
+    }
+
     pub async fn query_top_paths(
         &mut self,
         from: OffsetDateTime,
--- a/src/main.rs	Tue Jul 19 19:56:14 2022 -0700
+++ b/src/main.rs	Tue Jul 19 20:18:25 2022 -0700
@@ -144,13 +144,12 @@
     }
     let datasets: Vec<Value> = values
         .iter()
-        .zip(colors)
-        .map(|((name, val), col)| {
+        .map(|((name, val))| {
             json!({
                 "label": name,
                 "data": val,
-                "borderColor": col,
-                "backgroundColor": col,
+                "borderColor": colors,
+                "backgroundColor": colors,
             })
         })
         .collect();
@@ -237,10 +236,6 @@
         .await
     {
         Ok((dates, visits, sessions)) => {
-            info!(
-                "Successfully queried visits/sessions: {:?}, {:?}",
-                visits, sessions
-            );
             create_chart(
                 dates,
                 vec![("Visits".into(), visits), ("Sessions".into(), sessions)],
@@ -251,7 +246,10 @@
             )
             .to_string()
         }
-        Err(e) => "undefined".to_string(), //return Template::render("index", context![loggedin: true, domain: domain, username: lig.0, flash: f, chartconfig: context![], error: format!("{:?}", e)],),
+        Err(e) => {
+            error!("Couldn't build chart: {}", e);
+            "undefined".to_string()
+        }
     };
     let toppaths = match LogsDBSession(&mut conn)
         .query_top_paths(begin, end, Some(tz_offset), domain, 10)
@@ -267,7 +265,10 @@
             },
         )
         .to_string(),
-        Err(e) => "undefined".to_string(),
+        Err(e) => {
+            error!("Couldn't build chart: {}", e);
+            "undefined".to_string()
+        }
     };
     let reqbyses = match LogsDBSession(&mut conn)
         .query_requests_per_session(begin, end, Some(tz_offset), domain)
@@ -285,7 +286,31 @@
             },
         )
         .to_string(),
-        Err(e) => "undefined".to_string(),
+        Err(e) => {
+            error!("Couldn't build chart: {}", e);
+            "undefined".to_string()
+        }
+    };
+    let sesbycountry = match LogsDBSession(&mut conn)
+        .query_top_countries(begin, end, Some(tz_offset), domain)
+        .await
+    {
+        Ok(rs) => create_chart(
+            rs.iter().map(|(c, n)| c).collect(),
+            vec![(
+                "Sessions by Country".into(),
+                rs.iter().map(|(c, n)| n).collect(),
+            )],
+            &ChartOptions {
+                typ: "pie".into(),
+                ..ChartOptions::default()
+            },
+        )
+        .to_string(),
+        Err(e) => {
+            error!("Couldn't build chart: {}", e);
+            "undefined".to_string()
+        }
     };
 
     let ymd_format = time::format_description::parse("[year]-[month]-[day]").unwrap();
@@ -303,7 +328,7 @@
             ("today", tmpl_today.as_str()),
             ("duration", tmpl_duration.as_str())].into_iter()),
         beginnav: vec![HashMap::<&str, &str>::from_iter([("link", "/abc"), ("title", "3d")].into_iter())],
-        chartconfig: context![ visitsAndSessions: vissess, topPaths: toppaths, requestsBySession: reqbyses ]],
+        chartconfig: context![ visitsAndSessions: vissess, topPaths: toppaths, requestsBySession: reqbyses, sessionsByCountry: sesbycountry ]],
     )
 }
 
@@ -359,7 +384,11 @@
             let orig = geoipdb.as_ref().and_then(|g| g.lookup(numip));
             let is_bot = ua.to_lowercase().contains("bot");
             match conn
-                .start_session(host.as_ref().map(String::as_str).unwrap_or(""), orig, is_bot)
+                .start_session(
+                    host.as_ref().map(String::as_str).unwrap_or(""),
+                    orig,
+                    is_bot,
+                )
                 .await
             {
                 Ok(id) => {