Skip to content

Commit 3140a67

Browse files
feat: float metrics
Signed-off-by: Henry Gressmann <[email protected]>
1 parent 6e21abd commit 3140a67

File tree

10 files changed

+35
-31
lines changed

10 files changed

+35
-31
lines changed

src/app/core/reports.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::fmt::{Debug, Display};
33

44
use crate::app::DuckDBConn;
55
use crate::utils::validate;
6+
use crate::web::routes::dashboard::GraphValue;
67
use duckdb::params;
78
use eyre::Result;
89
use itertools::Itertools;
@@ -24,11 +25,11 @@ pub struct DateRange {
2425

2526
impl DateRange {
2627
pub fn start(&self) -> OffsetDateTime {
27-
OffsetDateTime::from_unix_timestamp(self.start as i64).unwrap_or(OffsetDateTime::UNIX_EPOCH)
28+
OffsetDateTime::from_unix_timestamp((self.start / 1000) as i64).unwrap_or(OffsetDateTime::UNIX_EPOCH)
2829
}
2930

3031
pub fn end(&self) -> OffsetDateTime {
31-
OffsetDateTime::from_unix_timestamp(self.end as i64).unwrap_or(OffsetDateTime::UNIX_EPOCH)
32+
OffsetDateTime::from_unix_timestamp((self.end / 1000) as i64).unwrap_or(OffsetDateTime::UNIX_EPOCH)
3233
}
3334

3435
pub fn prev(&self) -> DateRange {
@@ -77,7 +78,7 @@ pub enum FilterType {
7778
IsNull,
7879
}
7980

80-
pub type ReportGraph = Vec<u64>;
81+
pub type ReportGraph = Vec<GraphValue>;
8182
pub type ReportTable = BTreeMap<String, u64>;
8283

8384
#[derive(Object, Clone, Debug, Default)]
@@ -86,7 +87,7 @@ pub struct ReportStats {
8687
pub total_views: u64,
8788
pub total_sessions: u64,
8889
pub unique_visitors: u64,
89-
pub avg_views_per_session: u64, // 3 decimal places
90+
pub avg_views_per_session: f64,
9091
}
9192

9293
#[derive(Object, Debug)]
@@ -152,7 +153,7 @@ pub fn overall_report(
152153
metric: &Metric,
153154
) -> Result<ReportGraph> {
154155
if entities.is_empty() {
155-
return Ok(vec![0; data_points as usize]);
156+
return Ok(vec![GraphValue::U64(0); data_points as usize]);
156157
}
157158

158159
// recheck the validity of the entity IDs to be super sure there's no SQL injection
@@ -217,14 +218,14 @@ pub fn overall_report(
217218

218219
match metric {
219220
Metric::Views | Metric::UniqueVisitors | Metric::Sessions => {
220-
let rows = stmt.query_map(duckdb::params_from_iter(params), |row| row.get(1))?;
221-
let report_graph = rows.collect::<Result<Vec<u64>, duckdb::Error>>()?;
221+
let rows = stmt.query_map(duckdb::params_from_iter(params), |row| Ok(GraphValue::U64(row.get(1)?)))?;
222+
let report_graph = rows.collect::<Result<Vec<GraphValue>, duckdb::Error>>()?;
222223
Ok(report_graph)
223224
}
224225
Metric::AvgViewsPerSession => {
225-
let rows = stmt.query_map(duckdb::params_from_iter(params), |row| row.get(1))?;
226-
let report_graph = rows.collect::<Result<Vec<f64>, duckdb::Error>>()?;
227-
Ok(report_graph.iter().map(|v| (v * 1000.0).round() as u64).collect())
226+
let rows = stmt.query_map(duckdb::params_from_iter(params), |row| Ok(GraphValue::F64(row.get(1)?)))?;
227+
let report_graph = rows.collect::<Result<Vec<GraphValue>, duckdb::Error>>()?;
228+
Ok(report_graph)
228229
}
229230
}
230231
}
@@ -295,7 +296,7 @@ pub fn overall_stats(
295296
total_views: row.get(0)?,
296297
total_sessions: row.get(1)?,
297298
unique_visitors: row.get(2)?,
298-
avg_views_per_session: (row.get::<_, Option<f64>>(3)?.unwrap_or(0.0) * 1000.0).round() as u64,
299+
avg_views_per_session: row.get::<_, Option<f64>>(3)?.unwrap_or(0.0),
299300
})
300301
})?;
301302

src/web/routes/dashboard.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@ use poem::http::StatusCode;
88
use poem::web::Data;
99
use poem_openapi::param::Path;
1010
use poem_openapi::payload::Json;
11-
use poem_openapi::{Object, OpenApi};
11+
use poem_openapi::{Object, OpenApi, Union};
12+
13+
#[derive(Union, Debug, PartialEq, Clone, Copy)]
14+
pub enum GraphValue {
15+
U64(u64),
16+
F64(f64),
17+
}
1218

1319
#[derive(Object)]
1420
struct GraphResponse {
15-
data: Vec<u64>,
21+
data: Vec<GraphValue>,
1622
}
1723

1824
#[derive(Object)]

web/src/api/dashboard.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

web/src/components/dimensions/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export const DimensionTable = ({
102102
<DimensionValueBar value={d.value} biggest={biggest}>
103103
<DimensionLabel dimension={dimension} value={d} />
104104
</DimensionValueBar>
105-
<div>{formatMetricVal(metric, d.value)}</div>
105+
<div>{formatMetricVal(d.value)}</div>
106106
</div>
107107
);
108108
})}

web/src/components/dimensions/modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export const DetailsModal = ({
7070
<DimensionValueBar value={d.value} biggest={biggest}>
7171
<DimensionLabel dimension={dimension} value={d} />
7272
</DimensionValueBar>
73-
<div>{formatMetricVal(metric, d.value)}</div>
73+
<div>{formatMetricVal(d.value)}</div>
7474
</div>
7575
);
7676
})}

web/src/components/graph/graph.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import styles from "./graph.module.css";
44
import { ResponsiveLine, type SliceTooltipProps } from "@nivo/line";
55
import { addMonths } from "date-fns";
66
import type { DataPoint } from ".";
7+
import { formatMetricVal } from "../../utils";
78

89
export type GraphRange = "year" | "month" | "day" | "hour";
910

@@ -22,12 +23,13 @@ const formatDate = (date: Date, range: GraphRange = "day") => {
2223

2324
const Tooltip = (props: SliceTooltipProps & { title: string; range: GraphRange }) => {
2425
const point = props.slice.points[0].data;
26+
const value = point.y.valueOf() as number;
2527

2628
return (
2729
<div data-theme="dark" className={styles.tooltip}>
2830
<h2>{props.title}</h2>
2931
<h3>
30-
<span>{formatDate(new Date(point.x), props.range)}</span> {point.y.toString()}
32+
<span>{formatDate(new Date(point.x), props.range)}</span> {formatMetricVal(value)}
3133
</h3>
3234
</div>
3335
);

web/src/components/graph/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ export const toDataPoints = (data: number[], range: { start: number; end: number
1515
const step = differenceInSeconds(range.end, range.start) / data.length;
1616
return data.map((value, i) => ({
1717
x: new Date(range.start + i * step * 1000 + 1000),
18-
y: metric === "avg_views_per_session" ? value / 1000 : value,
18+
y: value,
1919
}));
2020
};

web/src/components/project/metric.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export const SelectMetric = ({
7979
<CardButton onClick={onSelect} active={selected} className={styles.metric}>
8080
<h2>{title}</h2>
8181
<h3>
82-
{formatMetricVal(metric, value)}
82+
{formatMetricVal(value)}
8383
<span style={{ color }} className={styles.change}>
8484
{icon} {formatPercent(changePercent)}
8585
</span>

web/src/components/project/project.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const LiveVisitorCount = ({ count }: { count: number }) => {
3131
<span className={styles.online}>
3232
<CircleIcon fill="#22c55e" color="#22c55e" size={10} />
3333
<CircleIcon fill="#22c55e" color="#22c55e" size={10} className={styles.pulse} />
34-
{formatMetricVal("unique_visitors", count)} {count === 1 ? "Current Visitor" : "Current Visitors"}
34+
{formatMetricVal(count)} {count === 1 ? "Current Visitor" : "Current Visitors"}
3535
</span>
3636
);
3737
};

web/src/utils.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,16 @@ export const cls = (class1: ClassName | ClassName[], ...classes: (ClassName | Cl
1111
// get the username cookie or undefined if not set
1212
export const getUsername = () => document.cookie.match(/username=(.*?)(;|$)/)?.[1];
1313

14-
export const formatMetricVal = (metric: Metric, value: number) => {
15-
let res = value;
16-
if (metric === "avg_views_per_session") {
17-
res = value / 1000;
14+
export const formatMetricVal = (value: number) => {
15+
if (value >= 1000) {
16+
return `${(value / 1000).toFixed(1).replace(/\.0$/, "")}k`;
1817
}
1918

20-
if (res >= 1000) {
21-
return `${(res / 1000).toFixed(1).replace(/\.0$/, "")}K`;
19+
if (value >= 1000000) {
20+
return `${(value / 1000000).toFixed(1).replace(/\.0$/, "")}M`;
2221
}
2322

24-
if (res >= 1000000) {
25-
return `${(res / 1000000).toFixed(1).replace(/\.0$/, "")}M`;
26-
}
27-
28-
return res.toFixed(1).replace(/\.0$/, "") || "0";
23+
return value.toFixed(1).replace(/\.0$/, "") || "0";
2924
};
3025

3126
export const formatPercent = (value: number) => {

0 commit comments

Comments
 (0)