Skip to content

Commit

Permalink
Merge pull request #95 from offspot/total_usage_details
Browse files Browse the repository at this point in the history
  • Loading branch information
benoit74 authored Jan 23, 2024
2 parents 9733aed + cbf5041 commit 97f927b
Show file tree
Hide file tree
Showing 15 changed files with 497 additions and 143 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]


### Added

- Implement the total usage details page (#55)
- Implement the package popularity details page (#53)
- Implement a simplified version of top toolbar mobile version (#89)

### Changed

- Remove the footer (#63)
- Revisit backend logic to fix concurrency issue and cope with high log volume (#41 and #42)
- Make backend log level customizable (#40)
- Use colors from an accessible color scheme (#86)
- Upgrade Python (3.12) + Python and Node.JS dependencies (#73)
- Migrate Docker image to alpine instead of slim-bookworm (#72)

### Fixed

- Include a better system description in README.md (#12)
- Fallback logo image should be a PNG (#93)
- Fix responsivness of top toolbar on medium screens (#77)
- Do not consider there are only Caddy files in the log folder (#81)

## [0.1.0] - 2023-12-21

### Added
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Metrics

The `metrics` subsystem of Kiwix `offspot` is responsible to transform raw data (e.g. web server logs) available on the `offspot` into business-oriented KPIs displayed on web dashboards.

![Home dashboard](home_dashboard.png)

The whole system is runing locally on the `offspot`. Dashboards are hence only presenting local `offspot` data.

At some point in the future, the system is meant to centralize data in a Cloud for aggregating multiple offspots. An intermediate data format is hence present, between raw data and KPIs.
Expand Down
11 changes: 7 additions & 4 deletions backend/src/offspot_metrics_backend/business/log_watcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class NewLineEvent:

ENCODING = "utf-8"

OBSERVER_STOP_MAX_SECONDS = 10
OBSERVER_PAUSE_SECONDS = 0.001


class LogWatcherHandler(FileSystemEventHandler):
"""Handler of watchdog events"""
Expand Down Expand Up @@ -185,9 +188,9 @@ async def run_async(self): # pragma: no cover
logger.info("Log watcher has started succesfully")

while self.observer.is_alive():
self.observer.join(0.001)
self.observer.join(OBSERVER_PAUSE_SECONDS)
# perform a very small sleep, just to let the coroutine pause
await sleep(0.001)
await sleep(OBSERVER_PAUSE_SECONDS)

logger.info("Log watcher run is terminating")

Expand All @@ -208,7 +211,7 @@ def run_sync(self):
logger.info("Log watcher has started succesfully")

while self.observer.is_alive():
self.observer.join(0.001)
self.observer.join(OBSERVER_PAUSE_SECONDS)

logger.info("Log watcher run is terminating")

Expand All @@ -221,7 +224,7 @@ def stop(self):
if self.observer.is_alive():
logger.info("Log watcher is stopping")
self.observer.stop()
self.observer.join()
self.observer.join(OBSERVER_STOP_MAX_SECONDS) # do not wait forever
else:
logger.info("Log watcher is already dead")

Expand Down
22 changes: 11 additions & 11 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,34 @@
"@fortawesome/free-regular-svg-icons": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/vue-fontawesome": "^3.0.5",
"apexcharts": "^3.44.2",
"apexcharts": "^3.45.2",
"axios": "^1.6.0",
"pinia": "^2.1.4",
"vite-plugin-vuetify": "^2.0.1",
"vue": "^3.2.47",
"vue": "^3.4.15",
"vue-router": "4",
"vue3-apexcharts": "^1.4.4",
"vuetify": "^3.4.4"
"vuetify": "^3.5.1"
},
"devDependencies": {
"@types/glob": "^8.1.0",
"@typescript-eslint/eslint-plugin": "^6.18.1",
"@typescript-eslint/parser": "^6.18.1",
"@vitejs/plugin-vue": "^5.0.2",
"@typescript-eslint/eslint-plugin": "^6.19.1",
"@typescript-eslint/parser": "^6.19.1",
"@vitejs/plugin-vue": "^5.0.3",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-standard": "^8.0.1",
"@vue/eslint-config-typescript": "^12.0.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-html": "^7.1.0",
"eslint-plugin-prettier": "^5.1.2",
"eslint-plugin-vue": "^9.10.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.20.1",
"marked": "^11.1.1",
"prettier": "^3.1.1",
"prettier": "^3.2.4",
"ts-node": "^10.9.1",
"typescript": "^5.3.3",
"vite": "^5.0.11",
"vue-eslint-parser": "^9.1.1",
"vite": "^5.0.12",
"vue-eslint-parser": "^9.4.2",
"vue-tsc": "^1.2.0"
}
}
Binary file added frontend/public/Kiwix-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 0 additions & 20 deletions frontend/public/Kiwix_logo_v3.svg

This file was deleted.

2 changes: 1 addition & 1 deletion frontend/src/components/AppBarSmallScreen.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const store = useMainStore()
<template>
<v-col cols="12" class="d-flex py-2 align-center">
<div class="d-flex ma-2 mb-0 align-center">
<v-img :height="30" width="30" src="./Kiwix_logo_v3.svg"></v-img>
<v-img height="30" width="30" src="./Kiwix-logo.png"></v-img>
<div class="pl-2 flex-1-1-100">
<span class="float-left font-weight-black text-h6">Metrics</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
<script setup lang="ts">
import { usePackagePopularityStore } from '../stores/packagePopularity'
const packagePopularityStore = usePackagePopularityStore()
import { useMainStore } from '../stores/main'
const mainStore = useMainStore()
import { computed } from 'vue'
import VueApexCharts from 'vue3-apexcharts'
const invertedGraphicFillColor = '#000000'
const props = withDefaults(
defineProps<{
package: string
visits: number
value: number
total: number
inverted: boolean
label: string
}>(),
{ inverted: false },
)
const percentage = computed(() => {
const value = (100.0 * props.visits) / props.total
const value = (100.0 * props.value) / props.total
if (value < 10) {
return value.toFixed(1)
} else {
Expand Down Expand Up @@ -51,8 +54,8 @@ const chartOptions = computed(() => {
opacity: 1,
colors: [
props.inverted
? '#000000'
: packagePopularityStore.packageColor(props.package),
? invertedGraphicFillColor
: mainStore.getPackageColor(props.package),
],
},
stroke: {
Expand All @@ -64,7 +67,7 @@ const chartOptions = computed(() => {
const cardStyle = computed(() =>
props.inverted
? {
'background-color': packagePopularityStore.packageColor(props.package),
'background-color': mainStore.getPackageColor(props.package),
}
: {},
)
Expand All @@ -83,7 +86,7 @@ const cardStyle = computed(() =>
<div class="ml-4 mt-4">
<div class="package-name">{{ props.package }}</div>
<div class="nb-visits">
<span>{{ props.visits }}</span> sessions
<span>{{ props.value }}</span> {{ props.label }}
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/stores/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,9 @@ export const useMainStore = defineStore('main', {
case 'D': {
let month = monthText(this.aggregationValue.split('-')[1])
if (month.length > 3) {
month = month.substring(0, 3) + '.'
month = `${month.substring(0, 3)}.`
}
return month + ' ' + this.aggregationValue.split('-')[0]
return `${month} ${this.aggregationValue.split('-')[0]}`
}
case 'W':
return ''
Expand Down
19 changes: 14 additions & 5 deletions frontend/src/stores/totalUsage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,28 @@ export const useTotalUsageStore = defineStore('totalUsage', {
return (item: TotalUsageKpiItem) =>
(item.minutesActivity / this.kpiValue.totalMinutesActivity) * 100
},
itemLabel(): (item: TotalUsageKpiItem) => string {
return (item: TotalUsageKpiItem) => {
const value = item.minutesActivity / 60
valueInHours(): (minutes: number) => number {
return (minutes: number) => {
const value = minutes / 60
if (value < 10) {
return `${value.toFixed(1)}h`
return parseFloat(value.toFixed(1))
} else {
return `${value.toFixed(0)}h`
return parseFloat(value.toFixed(0))
}
}
},
itemLabel(): (item: TotalUsageKpiItem) => string {
return (item: TotalUsageKpiItem) => {
return `${this.valueInHours(item.minutesActivity)}h`
}
},
itemColor(): (item: TotalUsageKpiItem) => string {
return (item: TotalUsageKpiItem) =>
useMainStore().getPackageColor(item.package)
},
packageColor(): (packageName: string) => string {
return (packageName: string) =>
useMainStore().getPackageColor(packageName)
},
},
})
7 changes: 6 additions & 1 deletion frontend/src/views/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ const views: ViewInfo[] = [
value: Page.PackagePopularity,
icon: 'fas fa-chart-column',
},
{
title: 'Total usage',
value: Page.TotalUsage,
icon: 'far fa-clock',
},
]
</script>

Expand All @@ -45,7 +50,7 @@ const views: ViewInfo[] = [
location="left"
>
<div class="d-flex ma-4 mb-0 align-center">
<v-img :width="60" src="./Kiwix_logo_v3.svg"></v-img>
<v-img width="60" src="./Kiwix-logo.png"></v-img>
<div class="pl-2 flex-1-1-100">
<span class="float-left font-weight-black text-h4">Metrics</span>
</div>
Expand Down
19 changes: 11 additions & 8 deletions frontend/src/views/PackagePopularity.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import PackagePopularityElement from '../components/PackagePopularityElement.vue'
import PackageElement from '../components/PackageElement.vue'
import { usePackagePopularityStore } from '../stores/packagePopularity'
const packagePopularityStore = usePackagePopularityStore()
Expand Down Expand Up @@ -31,7 +31,7 @@ const chartOptions = computed(() => {
labels: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
formatter: function (val: any, _: any) {
return val + ' %'
return `${val} %`
},
},
},
Expand Down Expand Up @@ -75,12 +75,13 @@ const series = computed(() => {
Total of {{ packagePopularityStore.kpiValue.totalVisits }} sessions
</div>
<div id="first-package">
<PackagePopularityElement
<PackageElement
v-if="packagePopularityStore.kpiValue.items.length > 0 && lgAndUp"
:visits="packagePopularityStore.kpiValue.items[0].visits"
:value="packagePopularityStore.kpiValue.items[0].visits"
:package="packagePopularityStore.kpiValue.items[0].package"
:total="packagePopularityStore.kpiValue.totalVisits"
:inverted="true"
label="sessions"
/>
</div>
</v-col>
Expand Down Expand Up @@ -113,21 +114,23 @@ const series = computed(() => {
md="6"
lg="4"
>
<PackagePopularityElement
<PackageElement
v-if="packagePopularityStore.kpiValue.items.length > 0"
:visits="packagePopularityStore.kpiValue.items[0].visits"
:value="packagePopularityStore.kpiValue.items[0].visits"
:package="packagePopularityStore.kpiValue.items[0].package"
:total="packagePopularityStore.kpiValue.totalVisits"
:inverted="true"
label="sessions"
/>
</v-col>
<v-col v-for="item_no in 10" :key="item_no" cols="12" md="6" lg="4">
<PackagePopularityElement
<PackageElement
v-if="packagePopularityStore.kpiValue.items.length > item_no"
:visits="packagePopularityStore.kpiValue.items[item_no].visits"
:value="packagePopularityStore.kpiValue.items[item_no].visits"
:package="packagePopularityStore.kpiValue.items[item_no].package"
:total="packagePopularityStore.kpiValue.totalVisits"
:inverted="false"
label="sessions"
/>
</v-col>
</v-row>
Expand Down
Loading

0 comments on commit 97f927b

Please sign in to comment.