mirror of
https://github.com/cupcakearmy/ora.git
synced 2024-12-22 16:16:23 +00:00
lots of stuff
This commit is contained in:
parent
36c5ff9a73
commit
f469d78ce3
@ -1,10 +1,8 @@
|
|||||||
{
|
{
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"name": "Borderify",
|
"name": "Ora",
|
||||||
"version": "1.0",
|
"version": "0.1",
|
||||||
|
"description": "See how much time you spend on each website",
|
||||||
"description": "Adds a red border to all webpages matching mozilla.org.",
|
|
||||||
|
|
||||||
"icons": {
|
"icons": {
|
||||||
"512": "icons/timer.png"
|
"512": "icons/timer.png"
|
||||||
},
|
},
|
||||||
@ -12,7 +10,7 @@
|
|||||||
"default_icon": {
|
"default_icon": {
|
||||||
"512": "icons/stopwatch-inv.svg"
|
"512": "icons/stopwatch-inv.svg"
|
||||||
},
|
},
|
||||||
"default_title": "Ora Dash",
|
"default_title": "Ora Dashboard",
|
||||||
"theme_icons": [
|
"theme_icons": [
|
||||||
{
|
{
|
||||||
"light": "./icons/stopwatch-inv.svg",
|
"light": "./icons/stopwatch-inv.svg",
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
"name": "ora",
|
"name": "ora",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "parcel --no-hmr manifest.json src/dashboard/index.html",
|
"dev": "parcel watch --no-hmr manifest.json src/dashboard/index.html",
|
||||||
|
"build": "parcel build manifest.json src/dashboard/index.html",
|
||||||
"launch": "web-ext run -s dist"
|
"launch": "web-ext run -s dist"
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
@ -10,17 +11,13 @@
|
|||||||
"last 2 firefox versions"
|
"last 2 firefox versions"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@amcharts/amcharts4": "^4.10.2",
|
|
||||||
"chart.js": "^2.9.3",
|
|
||||||
"dayjs": "^1.8.36",
|
"dayjs": "^1.8.36",
|
||||||
"faker": "^5.1.0",
|
"faker": "^5.1.0",
|
||||||
"google-palette": "^1.1.0",
|
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"nedb": "^1.8.0",
|
"nedb": "^1.8.0",
|
||||||
"nedb-promises": "^4.0.4",
|
"nedb-promises": "^4.0.4",
|
||||||
"spectre.css": "^0.5.9",
|
"spectre.css": "^0.5.9",
|
||||||
"tailwindcss": "^1.8.10",
|
"tailwindcss": "^1.8.10",
|
||||||
"uhrwerk": "^1.0.2",
|
|
||||||
"webextension-polyfill": "^0.6.0"
|
"webextension-polyfill": "^0.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import browser from 'webextension-polyfill'
|
import browser from 'webextension-polyfill'
|
||||||
|
|
||||||
import { dashboard } from '../shared/utils'
|
import { dashboard } from '../shared/utils'
|
||||||
import { Logs } from '../shared/db'
|
import { insertLog, normalizeTimestamp } from '../shared/db'
|
||||||
|
|
||||||
browser.browserAction.onClicked.addListener(() => browser.tabs.create({ url: dashboard, active: true }))
|
browser.browserAction.onClicked.addListener(() => browser.tabs.create({ url: dashboard, active: true }))
|
||||||
|
|
||||||
const frequency = 3000
|
const frequency = 3000
|
||||||
|
|
||||||
async function getAllTabs() {
|
async function getAllTabs() {
|
||||||
|
console.log('Checking...')
|
||||||
const tabs = await browser.tabs.query({})
|
const tabs = await browser.tabs.query({})
|
||||||
const windows = await browser.windows.getAll()
|
const windows = await browser.windows.getAll()
|
||||||
const active = tabs
|
const active = tabs
|
||||||
@ -23,10 +24,10 @@ async function getAllTabs() {
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
active.map(({ host }) => {
|
active.map(({ host }) => {
|
||||||
if (host)
|
if (host)
|
||||||
return Logs.insert({
|
return insertLog({
|
||||||
timestamp: new Date(),
|
timestamp: normalizeTimestamp(new Date()),
|
||||||
host,
|
host,
|
||||||
frequency,
|
seconds: (frequency / 1000) | 0,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
<script>
|
<script>
|
||||||
import DateInput from './components/Date.svelte'
|
import DateInput from './components/DateInput.svelte'
|
||||||
import Chart from './components/Chart.svelte'
|
// import Chart from './components/Chart.svelte'
|
||||||
import Dev from './components/Dev.svelte'
|
import Dev from './components/Dev.svelte'
|
||||||
|
import RangeChooser from './components/RangeChooser.svelte'
|
||||||
|
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { Duration } from 'uhrwerk'
|
|
||||||
|
|
||||||
import { data, countInGroup } from './lib'
|
import { data, countInGroup } from './lib'
|
||||||
import { env } from 'process'
|
import { env } from 'process'
|
||||||
|
|
||||||
|
let loading = true
|
||||||
|
let init = false
|
||||||
let counted
|
let counted
|
||||||
let top = 20
|
let top = 20
|
||||||
let start = dayjs().subtract(3, 'days').toDate()
|
let start = dayjs().subtract(3, 'days').toDate()
|
||||||
@ -20,12 +22,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function calculate() {
|
async function calculate() {
|
||||||
console.log('Calculating')
|
try {
|
||||||
|
loading = true
|
||||||
const logs = await data({
|
const logs = await data({
|
||||||
start,
|
start,
|
||||||
end: dayjs(end).endOf('day'),
|
end: dayjs(end).endOf('day'),
|
||||||
})
|
})
|
||||||
counted = countInGroup(logs)
|
counted = countInGroup(logs)
|
||||||
|
} finally {
|
||||||
|
loading = false
|
||||||
|
}
|
||||||
// const onlyTop = counted.slice(0, top)
|
// const onlyTop = counted.slice(0, top)
|
||||||
// console.log(onlyTop)
|
// console.log(onlyTop)
|
||||||
// topData = {
|
// topData = {
|
||||||
@ -34,13 +40,16 @@
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
$: if (init) {
|
||||||
start, end
|
start, end
|
||||||
calculate()
|
calculate()
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
calculate()
|
setTimeout(() => {
|
||||||
|
init = true
|
||||||
|
// calculate()
|
||||||
|
}, 250)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -52,9 +61,9 @@
|
|||||||
max-width: 50em;
|
max-width: 50em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.date > .spacer {
|
/* .date > .spacer {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
}
|
} */
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
margin-left: 2em;
|
margin-left: 2em;
|
||||||
@ -70,22 +79,19 @@
|
|||||||
<div class="top rounded">
|
<div class="top rounded">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<h2 class="text-4xl">Top {top}</h2>
|
<h2 class="text-4xl">Top {top}</h2>
|
||||||
<div class="date flex px-1 rounded">
|
<RangeChooser bind:start bind:end />
|
||||||
<DateInput bind:date={start} />
|
|
||||||
<div class="spacer" />
|
|
||||||
<DateInput bind:date={end} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{#if loading}
|
||||||
{#if counted}
|
<div class="loading loading-lg" />
|
||||||
<Chart data={counted.slice(0, top)} />
|
{:else if counted}
|
||||||
<table>
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Time Spent</th>
|
<th>Time Spent</th>
|
||||||
<th>Host</th>
|
<th>Host</th>
|
||||||
</tr>
|
</tr>
|
||||||
{#each counted as { host, total, human }}
|
{#each counted as { host, total, human }}
|
||||||
<tr>
|
<tr>
|
||||||
<td><b>{human}</b></td>
|
<td>{human}</td>
|
||||||
<td class="link"><a href={'https://' + host}>{host}</a></td>
|
<td class="link"><a href={'https://' + host}>{host}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
<script>
|
|
||||||
import * as am4core from '@amcharts/amcharts4/core'
|
|
||||||
import * as am4charts from '@amcharts/amcharts4/charts'
|
|
||||||
import am4themes_frozen from '@amcharts/amcharts4/themes/frozen'
|
|
||||||
import am4themes_animated from '@amcharts/amcharts4/themes/animated'
|
|
||||||
import { onMount } from 'svelte'
|
|
||||||
|
|
||||||
/* Chart code */
|
|
||||||
// Themes begin
|
|
||||||
am4core.useTheme(am4themes_frozen)
|
|
||||||
am4core.useTheme(am4themes_animated)
|
|
||||||
|
|
||||||
let el
|
|
||||||
let chart
|
|
||||||
|
|
||||||
export let data = [
|
|
||||||
{
|
|
||||||
network: 'Facebook',
|
|
||||||
MAU: 2255250000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'Google+',
|
|
||||||
MAU: 430000000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'Instagram',
|
|
||||||
MAU: 1000000000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'Pinterest',
|
|
||||||
MAU: 246500000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'Reddit',
|
|
||||||
MAU: 355000000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'TikTok',
|
|
||||||
MAU: 500000000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'Tumblr',
|
|
||||||
MAU: 624000000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'Twitter',
|
|
||||||
MAU: 329500000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'WeChat',
|
|
||||||
MAU: 1000000000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'Weibo',
|
|
||||||
MAU: 431000000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'Whatsapp',
|
|
||||||
MAU: 1433333333,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
network: 'YouTube',
|
|
||||||
MAU: 1900000000,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
$: if (chart) {
|
|
||||||
chart.data = data
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
chart = am4core.create(el, am4charts.XYChart)
|
|
||||||
|
|
||||||
let categoryAxis = chart.yAxes.push(new am4charts.CategoryAxis())
|
|
||||||
categoryAxis.renderer.grid.template.location = 0
|
|
||||||
categoryAxis.dataFields.category = 'host'
|
|
||||||
categoryAxis.renderer.minGridDistance = 1
|
|
||||||
categoryAxis.renderer.inversed = true
|
|
||||||
categoryAxis.renderer.grid.template.disabled = true
|
|
||||||
|
|
||||||
let valueAxis = chart.xAxes.push(new am4charts.ValueAxis())
|
|
||||||
valueAxis.min = 0
|
|
||||||
|
|
||||||
let series = chart.series.push(new am4charts.ColumnSeries())
|
|
||||||
series.dataFields.categoryY = 'host'
|
|
||||||
series.dataFields.valueX = 'total'
|
|
||||||
series.columns.template.strokeOpacity = 0
|
|
||||||
series.columns.template.column.cornerRadiusBottomRight = 5
|
|
||||||
series.columns.template.column.cornerRadiusTopRight = 5
|
|
||||||
|
|
||||||
let labelBullet = series.bullets.push(new am4charts.LabelBullet())
|
|
||||||
labelBullet.label.horizontalCenter = 'left'
|
|
||||||
labelBullet.label.dx = 10
|
|
||||||
labelBullet.label.text = '{values.valueX.workingValue}'
|
|
||||||
// labelBullet.label.text = "{values.valueX.workingValue.formatNumber('#.0as')}"
|
|
||||||
labelBullet.locationX = 1
|
|
||||||
|
|
||||||
// as by default columns of the same series are of the same color, we add adapter which takes colors from chart.colors color set
|
|
||||||
series.columns.template.adapter.add('fill', function (fill, target) {
|
|
||||||
return chart.colors.getIndex(target.dataItem.index)
|
|
||||||
})
|
|
||||||
|
|
||||||
categoryAxis.sortBySeries = series
|
|
||||||
chart.data = data
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
div {
|
|
||||||
width: 100%;
|
|
||||||
height: 32em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div bind:this={el}>chart</div>
|
|
@ -1,68 +1,115 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import * as am4core from '@amcharts/amcharts4/core'
|
||||||
|
import * as am4charts from '@amcharts/amcharts4/charts'
|
||||||
|
import am4themes_frozen from '@amcharts/amcharts4/themes/frozen'
|
||||||
|
import am4themes_animated from '@amcharts/amcharts4/themes/animated'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
import Chart from 'chart.js'
|
/* Chart code */
|
||||||
import palette from 'google-palette'
|
// Themes begin
|
||||||
|
am4core.useTheme(am4themes_frozen)
|
||||||
|
am4core.useTheme(am4themes_animated)
|
||||||
|
|
||||||
Chart.defaults.global.legend.display = false
|
let el
|
||||||
|
let chart
|
||||||
|
|
||||||
export let type = 'horizontalBar'
|
export let data = [
|
||||||
export let data = {
|
|
||||||
labels: [],
|
|
||||||
data: [],
|
|
||||||
}
|
|
||||||
export let options = {
|
|
||||||
scales: {
|
|
||||||
xAxes: [
|
|
||||||
{
|
{
|
||||||
// type: 'logarithmic',
|
network: 'Facebook',
|
||||||
ticks: {
|
MAU: 2255250000,
|
||||||
beginAtZero: true,
|
|
||||||
},
|
},
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctx
|
|
||||||
let mounted
|
|
||||||
|
|
||||||
function draw() {
|
|
||||||
const backgroundColor = palette('rainbow', data.data.length).map((color) => '#' + color + '88')
|
|
||||||
|
|
||||||
new Chart(ctx, {
|
|
||||||
type,
|
|
||||||
options,
|
|
||||||
data: {
|
|
||||||
labels: data.labels,
|
|
||||||
datasets: [
|
|
||||||
{
|
{
|
||||||
data: data.data,
|
network: 'Google+',
|
||||||
// backgroundColor: backgroundColor,
|
MAU: 430000000,
|
||||||
backgroundColor: '#dddddd',
|
|
||||||
borderColor: '#000000',
|
|
||||||
borderWidth: 1,
|
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
|
network: 'Instagram',
|
||||||
|
MAU: 1000000000,
|
||||||
},
|
},
|
||||||
})
|
{
|
||||||
}
|
network: 'Pinterest',
|
||||||
|
MAU: 246500000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: 'Reddit',
|
||||||
|
MAU: 355000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: 'TikTok',
|
||||||
|
MAU: 500000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: 'Tumblr',
|
||||||
|
MAU: 624000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: 'Twitter',
|
||||||
|
MAU: 329500000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: 'WeChat',
|
||||||
|
MAU: 1000000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: 'Weibo',
|
||||||
|
MAU: 431000000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: 'Whatsapp',
|
||||||
|
MAU: 1433333333,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
network: 'YouTube',
|
||||||
|
MAU: 1900000000,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
$: if (mounted) {
|
$: if (chart) {
|
||||||
data
|
chart.data = data
|
||||||
draw()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
mounted = true
|
chart = am4core.create(el, am4charts.XYChart)
|
||||||
|
|
||||||
|
let categoryAxis = chart.yAxes.push(new am4charts.CategoryAxis())
|
||||||
|
categoryAxis.renderer.grid.template.location = 0
|
||||||
|
categoryAxis.dataFields.category = 'host'
|
||||||
|
categoryAxis.renderer.minGridDistance = 1
|
||||||
|
categoryAxis.renderer.inversed = true
|
||||||
|
categoryAxis.renderer.grid.template.disabled = true
|
||||||
|
|
||||||
|
let valueAxis = chart.xAxes.push(new am4charts.ValueAxis())
|
||||||
|
valueAxis.min = 0
|
||||||
|
|
||||||
|
let series = chart.series.push(new am4charts.ColumnSeries())
|
||||||
|
series.dataFields.categoryY = 'host'
|
||||||
|
series.dataFields.valueX = 'total'
|
||||||
|
series.columns.template.strokeOpacity = 0
|
||||||
|
series.columns.template.column.cornerRadiusBottomRight = 5
|
||||||
|
series.columns.template.column.cornerRadiusTopRight = 5
|
||||||
|
|
||||||
|
let labelBullet = series.bullets.push(new am4charts.LabelBullet())
|
||||||
|
labelBullet.label.horizontalCenter = 'left'
|
||||||
|
labelBullet.label.dx = 10
|
||||||
|
labelBullet.label.text = '{values.valueX.workingValue}'
|
||||||
|
// labelBullet.label.text = "{values.valueX.workingValue.formatNumber('#.0as')}"
|
||||||
|
labelBullet.locationX = 1
|
||||||
|
|
||||||
|
// as by default columns of the same series are of the same color, we add adapter which takes colors from chart.colors color set
|
||||||
|
series.columns.template.adapter.add('fill', function (fill, target) {
|
||||||
|
return chart.colors.getIndex(target.dataItem.index)
|
||||||
|
})
|
||||||
|
|
||||||
|
categoryAxis.sortBySeries = series
|
||||||
|
chart.data = data
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
canvas {
|
div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 20em;
|
height: 32em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<canvas bind:this={ctx} />
|
<div bind:this={el}>chart</div>
|
||||||
|
68
src/dashboard/components/ChartOlder.svelte
Normal file
68
src/dashboard/components/ChartOlder.svelte
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
|
import Chart from 'chart.js'
|
||||||
|
import palette from 'google-palette'
|
||||||
|
|
||||||
|
Chart.defaults.global.legend.display = false
|
||||||
|
|
||||||
|
export let type = 'horizontalBar'
|
||||||
|
export let data = {
|
||||||
|
labels: [],
|
||||||
|
data: [],
|
||||||
|
}
|
||||||
|
export let options = {
|
||||||
|
scales: {
|
||||||
|
xAxes: [
|
||||||
|
{
|
||||||
|
// type: 'logarithmic',
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctx
|
||||||
|
let mounted
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
const backgroundColor = palette('rainbow', data.data.length).map((color) => '#' + color + '88')
|
||||||
|
|
||||||
|
new Chart(ctx, {
|
||||||
|
type,
|
||||||
|
options,
|
||||||
|
data: {
|
||||||
|
labels: data.labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
data: data.data,
|
||||||
|
// backgroundColor: backgroundColor,
|
||||||
|
backgroundColor: '#dddddd',
|
||||||
|
borderColor: '#000000',
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (mounted) {
|
||||||
|
data
|
||||||
|
draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
mounted = true
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 20em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<canvas bind:this={ctx} />
|
@ -1,12 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { onMount } from 'svelte'
|
|
||||||
import dayjs from 'dayjs'
|
|
||||||
|
|
||||||
export let name = ''
|
|
||||||
export let date = new Date()
|
|
||||||
let internal = dayjs(date).format('YYYY-MM-DD')
|
|
||||||
|
|
||||||
$: date = dayjs(internal, 'YYYY-MM-DD')
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<label class="form-label">{name}<input class="form-input" type="date" bind:value={internal} /> </label>
|
|
19
src/dashboard/components/DateInput.svelte
Normal file
19
src/dashboard/components/DateInput.svelte
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
const format = 'YYYY-MM-DD'
|
||||||
|
|
||||||
|
export let name = ''
|
||||||
|
|
||||||
|
export let date = new Date()
|
||||||
|
let internal
|
||||||
|
|
||||||
|
const input = (x) => (internal = dayjs(x).format(format))
|
||||||
|
const output = (x) => (date = dayjs(x, format).toDate())
|
||||||
|
|
||||||
|
$: input(date)
|
||||||
|
$: output(internal)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input class="form-input input-sm" type="date" bind:value={internal} {name} />
|
@ -3,29 +3,25 @@
|
|||||||
import day from 'dayjs'
|
import day from 'dayjs'
|
||||||
import { range, random } from 'lodash'
|
import { range, random } from 'lodash'
|
||||||
|
|
||||||
import { Logs } from '../../shared/db'
|
import { insertLog, normalizeTimestamp } from '../../shared/db'
|
||||||
|
|
||||||
let loading = false
|
let loading = false
|
||||||
|
|
||||||
async function fill() {
|
async function fill() {
|
||||||
try {
|
try {
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
const start = day().subtract('7', 'days').valueOf()
|
const start = day().subtract('7', 'days').valueOf()
|
||||||
const end = Date.now()
|
const end = Date.now()
|
||||||
|
for (const n of range(20)) {
|
||||||
const all = []
|
|
||||||
for (const n of range(50)) {
|
|
||||||
const host = faker.internet.domainName()
|
const host = faker.internet.domainName()
|
||||||
for (const m of range(random(500))) {
|
for (const m of range(random(20))) {
|
||||||
const frequency = random(1, 10) * 1000
|
const date = new Date(random(start, end))
|
||||||
const timestamp = new Date(random(start, end))
|
const timestamp = normalizeTimestamp(date)
|
||||||
all.push({ host, timestamp, frequency })
|
const seconds = random(15 * 60)
|
||||||
|
// console.log(host, date, seconds)
|
||||||
|
await insertLog({ host, timestamp, seconds })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(`Generated ${all.length} data points`)
|
|
||||||
console.debug(all)
|
|
||||||
await Logs.insert(all)
|
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
|
41
src/dashboard/components/RangeChooser.svelte
Normal file
41
src/dashboard/components/RangeChooser.svelte
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<script>
|
||||||
|
import dj from 'dayjs'
|
||||||
|
|
||||||
|
import DateInput from './DateInput.svelte'
|
||||||
|
|
||||||
|
export let start
|
||||||
|
export let end
|
||||||
|
|
||||||
|
function set(interval, amount = 1) {
|
||||||
|
return () => {
|
||||||
|
start = dj().subtract(amount, interval).toDate()
|
||||||
|
end = new Date()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function all() {
|
||||||
|
start = new Date(0)
|
||||||
|
end = new Date()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.spacer {
|
||||||
|
width: 0.5em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- <div class="flex flex-col"> -->
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn btn-sm" on:click={all}>All</button>
|
||||||
|
<button class="btn btn-sm" on:click={set('month')}>Month</button>
|
||||||
|
<button class="btn btn-sm" on:click={set('week')}>Week</button>
|
||||||
|
<button class="btn btn-sm" on:click={set('day')}>Day</button>
|
||||||
|
</div>
|
||||||
|
<div class="spacer" />
|
||||||
|
<div class="input-group">
|
||||||
|
<DateInput bind:date={start} />
|
||||||
|
<DateInput bind:date={end} />
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,5 +1,10 @@
|
|||||||
import { groupBy, orderBy } from 'lodash'
|
import { each, groupBy, orderBy } from 'lodash'
|
||||||
import { Duration } from 'uhrwerk'
|
import dj from 'dayjs'
|
||||||
|
import RelativeTime from 'dayjs/plugin/relativeTime'
|
||||||
|
import Duration from 'dayjs/plugin/duration'
|
||||||
|
|
||||||
|
dj.extend(Duration)
|
||||||
|
dj.extend(RelativeTime)
|
||||||
|
|
||||||
import { Logs } from '../shared/db'
|
import { Logs } from '../shared/db'
|
||||||
|
|
||||||
@ -17,9 +22,8 @@ export async function getLogsBetweenDates({ start, end }) {
|
|||||||
|
|
||||||
export function countInGroup(grouped) {
|
export function countInGroup(grouped) {
|
||||||
const counted = Object.entries(grouped).map(([key, data]) => {
|
const counted = Object.entries(grouped).map(([key, data]) => {
|
||||||
const total = data.reduce((acc, cur) => acc + cur.frequency, 0)
|
const total = data.reduce((acc, cur) => acc + cur.seconds, 0)
|
||||||
const human = new Duration(total, 'ms').humanize()
|
const human = dj.duration(total, 'seconds').humanize()
|
||||||
// const human = 'some'
|
|
||||||
return {
|
return {
|
||||||
host: key,
|
host: key,
|
||||||
total,
|
total,
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
// import browser from 'webextension-polyfill'
|
|
||||||
// import '@babel/polyfill'
|
|
||||||
|
|
||||||
import App from './App.svelte'
|
import App from './App.svelte'
|
||||||
|
|
||||||
new App({ target: window.document.getElementById('root') })
|
new App({ target: window.document.getElementById('root') })
|
||||||
|
@ -1,6 +1,29 @@
|
|||||||
import NeDB from 'nedb-promises'
|
import NeDB from 'nedb-promises'
|
||||||
|
import day from 'dayjs'
|
||||||
|
|
||||||
export const Logs = NeDB.create({
|
export const Logs = NeDB.create({
|
||||||
filename: 'logs.db',
|
filename: 'logs.db',
|
||||||
autoload: true,
|
autoload: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export function normalizeTimestamp(timestamp) {
|
||||||
|
// Normalize every dato to 15 minutes
|
||||||
|
const t = day(timestamp)
|
||||||
|
const min = t.minute()
|
||||||
|
return t
|
||||||
|
.millisecond(0)
|
||||||
|
.second(0)
|
||||||
|
.minute(min - (min % 15))
|
||||||
|
.toDate()
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function insertLog({ timestamp, host, seconds }) {
|
||||||
|
Logs.update(
|
||||||
|
{
|
||||||
|
host,
|
||||||
|
timestamp,
|
||||||
|
},
|
||||||
|
{ $inc: { seconds } },
|
||||||
|
{ upsert: true }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user