Rework data loading, get first dashboard roughly in order.

This commit is contained in:
Joshua Bemenderfer
2021-12-30 13:25:31 -05:00
parent b8cca082ed
commit 38976cf29d
180 changed files with 632 additions and 263 deletions

View File

@@ -1,5 +1,11 @@
<template>
<div class="bg-white shadow rounded-lg p-4 lg:p-8 max-w-none prose lg:prose-base prose-headings:font-light prose-headings:text-indigo-900 prose-h1:mb-0">
<div :class="['bg-white shadow rounded-lg p-4 lg:p-8 max-w-none', prose ? 'prose lg:prose-base prose-headings:font-light prose-headings:text-indigo-900 prose-h1:mb-0' : '']">
<slot></slot>
</div>
</template>
<script setup>
defineProps({
prose: Boolean
})
</script>

View File

@@ -1,21 +0,0 @@
<template></template>
<script setup>
import { onMounted, watch } from 'vue'
const { parameters, data, watcher, refresher } = defineProps({
parameters: Object,
data: Object,
watcher: Function,
refresher: Function
})
console.log(parameters, data, watcher, refresher)
async function refreshData(parameters) {
data.value = await refresher(parameters)
}
watch(watcher, () => refreshData(parameters))
onMounted(() => refreshData(parameters))
</script>

View File

@@ -1,5 +1,5 @@
<template>
<div class='flex flex-wrap bg-white shadow rounded-lg p-4 space-x-6 lg:px-6 lg:py-0 lg:fixed lg:z-50 lg:top-0 lg:right-0 lg:left-96 lg:h-16 lg:shadow-none lg:flex-nowrap lg:!mt-0'>
<div v-if="parameters" class='flex flex-wrap bg-white shadow rounded-lg p-4 space-x-6 mt-2 lg:px-6 lg:py-0 lg:fixed lg:z-50 lg:top-0 lg:right-0 lg:left-96 lg:h-16 lg:shadow-none lg:flex-nowrap lg:!mt-0'>
<label class='w-full flex items-center lg:inline-flex lg:w-auto'>
County
<select v-model="parameters.county" class='ml-4 flex-1'>
@@ -20,9 +20,9 @@
</template>
<script setup lang='ts'>
import { ref, inject } from 'vue'
import cache from '@/data/cache.js'
const parameters = inject('parameters')
const parameters = cache.parameters
const counties = [
'All',

View File

@@ -0,0 +1,7 @@
<template>
<Card>
<h2 class="text-xl font-light text-indigo-900 mb-2"><slot name="heading"></slot></h2>
<p class="text-3xl text-indigo-600 font-bold mb-3"><slot name="value"></slot></p>
<p class="text-base font-light"><slot name="meta"></slot></p>
</Card>
</template>

View File

@@ -1,28 +0,0 @@
<template>
<JSCharting v-if="data" :options="chartOptions"></JSCharting>
</template>
<script setup>
import { computed, inject } from 'vue'
import { colors, col } from './util.js'
const parameters = inject('parameters')
const data = inject('data')
const chartOptions = computed(() => {
if (!data.value) return
return {
type: 'calendar solid',
palette: { colors },
data: data.value.rows
.filter(r => {
return col(data, r, 'report_date') >= parameters.start && col(data, r, 'report_date') <= parameters.end
})
.map(r => ([
`${col(data, r, 'report_date')} 12:00:00`,
col(data, r, parameters.column)
]))
}
})
</script>

View File

@@ -0,0 +1,109 @@
<template>
<StatCard>
<template v-slot:heading>Total Tests Performed</template>
<template v-slot:value v-if="chips.total_tests">
{{chips.total_tests.toLocaleString()}}
</template>
<template v-slot:meta v-if="chips.total_tests">
PCR + Antigen
</template>
</StatCard>
<StatCard>
<template v-slot:heading>Highest Testing Day</template>
<template v-slot:value v-if="chips.tests">
{{chips.tests.date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}}
</template>
<template v-slot:meta v-if="chips.tests">
<span class="text-indigo-500 font-bold">{{chips.tests.value.toLocaleString()}}</span> tests performed
</template>
</StatCard>
<StatCard>
<template v-slot:heading>Highest Positive Day</template>
<template v-slot:value v-if="chips.positive">
<span class="text-red-800">{{chips.positive.date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}}</span>
</template>
<template v-slot:meta v-if="chips.positive">
<span class="text-red-800 font-bold">{{chips.positive.value.toLocaleString()}}</span> new cases
</template>
</StatCard>
<StatCard>
<template v-slot:heading>7-Day Percent Positive</template>
<template v-slot:value v-if="chips.percent_positive">
<span :class="{
'text-green-600': chips.percent_positive < 2,
'text-yellow-500': chips.percent_positive >= 2 && chips.percent_positive < 5,
'text-orange-500': chips.percent_positive >= 5 && chips.percent_positive < 20,
'text-red-800': chips.percent_positive > 20
}">
{{chips.percent_positive}}<span class="font-light">%</span>
</span>
</template>
<template v-slot:meta v-if="chips.positive">
<div class="inline-flex flex-wrap text-xs -mx-1">
<div class="flex-1">
<div class="px-1 text-green-600 font-medium whitespace-nowrap">Low: &lt; 2%</div>
<div class="px-1 text-yellow-600 font-medium whitespace-nowrap">Moderate &lt; 5%</div>
</div>
<div class="flex-1">
<div class="px-1 text-orange-600 font-medium whitespace-nowrap">High &lt; 20%</div>
<div class="px-1 text-red-700 font-medium whitespace-nowrap">Very high &gt; 20%</div>
</div>
</div>
<a class="link block mt-2 text-xs" href="https://www.who.int/publications/i/item/considerations-in-adjusting-public-health-and-social-measures-in-the-context-of-covid-19-interim-guidanceLow">
Source, see p. 18
</a>
</template>
</StatCard>
</template>
<script setup>
import { reactive, computed } from 'vue'
import { col } from '@/components/charts/util'
import cache from '@/data/cache.js'
const data = cache.data
const chips = reactive({
total_tests: computed(() => {
if (!data.value) return null
const r = data.value.rows.at(-1)
return col(data, r, 'combined_performed_running_total')
}),
tests: computed(() => {
if (!data.value) return null
return data.value.rows.reduce((prev, r) => {
const date = new Date(col(data, r, 'report_date'))
const value = +col(data, r, 'combined_performed')
if (prev && prev.value > value) return prev
else return { date, value }
})
}),
positive: computed(() => {
if (!data.value) return null
return data.value.rows.reduce((prev, r) => {
const date = new Date(col(data, r, 'report_date'))
const value = +col(data, r, 'combined_positive')
if (prev && prev.value > value) return prev
else return { date, value }
})
}),
percent_positive: computed(() => {
if (!data.value) return null
const r = data.value.rows.at(-1)
return col(data, r, 'seven_day_percent_positive')
})
})
</script>

View File

@@ -0,0 +1,68 @@
<template>
<Card>
<h2 class="mb-6 text-xl text-indigo-900 flex justify-between items-center">
Daily Positive Antigen Results
<select v-model="chart" class="text-base m-0">
<option value="calendar">Calendar</option>
<option value="line">Line Chart</option>
</select>
</h2>
<JSCharting v-if="chart === 'calendar'" :options="calendarOptions"></JSCharting>
<JSCharting v-if="chart === 'line'" :options="areaOptions"></JSCharting>
</Card>
</template>
<script setup>
import { ref, computed } from 'vue'
import { col, colors } from '@/components/charts/util'
import cache from '@/data/cache.js'
const chart = ref('calendar')
const column = 'antigen_positive'
const parameters = cache.parameters
const data = cache.data
const rows = computed(() => {
if (!data.value) return []
return data.value.rows
.filter(r => {
return col(data, r, 'report_date') >= parameters.value.start && col(data, r, 'report_date') <= parameters.value.end
})
})
const calendarOptions = computed(() => {
return {
type: 'calendar month solid',
palette: { colors },
calendar_initial: parameters.value.end,
data: rows.value.map(r => ([
`${col(data, r, 'report_date')} 12:00:00`,
col(data, r, column)
]))
}
})
const areaOptions = computed(() => {
return {
type: 'lineSpline',
legend_visible: false,
xAxis: { crosshair_enabled: true, scale_type: 'time' },
palette: { colors },
defaultSeries: {
shape_opacity: 0.55,
color: colors[Math.round(colors.length / 2)],
defaultPoint_marker_visible: false
},
series: [
{
points: rows.value.map(r => ([
`${col(data, r, 'report_date')} 12:00:00`,
col(data, r, column)
]))
}
]
}
})
</script>

View File

@@ -0,0 +1,67 @@
<template>
<Card>
<h2 class="mb-6 text-xl text-indigo-900 flex justify-between items-center">
Daily Antigen Tests Performed
<select v-model="chart" class="text-base m-0">
<option value="calendar">Calendar</option>
<option value="line">Line Chart</option>
</select>
</h2>
<JSCharting v-if="chart === 'calendar'" :options="calendarOptions"></JSCharting>
<JSCharting v-if="chart === 'line'" :options="areaOptions"></JSCharting>
</Card>
</template>
<script setup>
import { ref, computed } from 'vue'
import { col, colors } from '@/components/charts/util'
import cache from '@/data/cache.js'
const chart = ref('calendar')
const column = 'antigen_performed'
const parameters = cache.parameters
const data = cache.data
const rows = computed(() => {
if (!data.value) return []
return data.value.rows
.filter(r => {
return col(data, r, 'report_date') >= parameters.value.start && col(data, r, 'report_date') <= parameters.value.end
})
})
const calendarOptions = computed(() => {
return {
type: 'calendar month solid',
palette: { colors },
calendar_initial: parameters.value.end,
data: rows.value.map(r => ([
`${col(data, r, 'report_date')} 12:00:00`,
col(data, r, column)
]))
}
})
const areaOptions = computed(() => {
return {
type: 'lineSpline',
legend_visible: false,
xAxis: { crosshair_enabled: true, scale_type: 'time' },
palette: { colors },
defaultSeries: {
shape_opacity: 0.55,
color: colors[Math.round(colors.length / 2)],
defaultPoint_marker_visible: false
},
series: [
{
points: rows.value.map(r => ([
`${col(data, r, 'report_date')} 12:00:00`,
col(data, r, column)
]))
}
]
}
})
</script>

View File

@@ -0,0 +1,68 @@
<template>
<Card>
<h2 class="mb-6 text-xl text-indigo-900 flex justify-between items-center">
Daily Positive PCR Results
<select v-model="chart" class="text-base m-0">
<option value="calendar">Calendar</option>
<option value="line">Line Chart</option>
</select>
</h2>
<JSCharting v-if="chart === 'calendar'" :options="calendarOptions"></JSCharting>
<JSCharting v-if="chart === 'line'" :options="areaOptions"></JSCharting>
</Card>
</template>
<script setup>
import { ref, computed } from 'vue'
import { col, colors } from '@/components/charts/util'
import cache from '@/data/cache.js'
const chart = ref('calendar')
const column = 'pcr_positive'
const parameters = cache.parameters
const data = cache.data
const rows = computed(() => {
if (!data.value) return []
return data.value.rows
.filter(r => {
return col(data, r, 'report_date') >= parameters.value.start && col(data, r, 'report_date') <= parameters.value.end
})
})
const calendarOptions = computed(() => {
return {
type: 'calendar month solid',
palette: { colors },
calendar_initial: parameters.value.end,
data: rows.value.map(r => ([
`${col(data, r, 'report_date')} 12:00:00`,
col(data, r, column)
]))
}
})
const areaOptions = computed(() => {
return {
type: 'lineSpline',
legend_visible: false,
xAxis: { crosshair_enabled: true, scale_type: 'time' },
palette: { colors },
defaultSeries: {
shape_opacity: 0.55,
color: colors[Math.round(colors.length / 2)],
defaultPoint_marker_visible: false
},
series: [
{
points: rows.value.map(r => ([
`${col(data, r, 'report_date')} 12:00:00`,
col(data, r, column)
]))
}
]
}
})
</script>

View File

@@ -0,0 +1,68 @@
<template>
<Card>
<h2 class="mb-6 text-xl text-indigo-900 flex justify-between items-center">
Daily PCR Tests Performed
<select v-model="chart" class="text-base m-0">
<option value="calendar">Calendar</option>
<option value="line">Line Chart</option>
</select>
</h2>
<JSCharting v-if="chart === 'calendar'" :options="calendarOptions"></JSCharting>
<JSCharting v-if="chart === 'line'" :options="areaOptions"></JSCharting>
</Card>
</template>
<script setup>
import { ref, computed } from 'vue'
import { col, colors } from '@/components/charts/util'
import cache from '@/data/cache.js'
const chart = ref('calendar')
const column = 'pcr_performed'
const parameters = cache.parameters
const data = cache.data
const rows = computed(() => {
if (!data.value) return []
return data.value.rows
.filter(r => {
return col(data, r, 'report_date') >= parameters.value.start && col(data, r, 'report_date') <= parameters.value.end
})
})
const calendarOptions = computed(() => {
return {
type: 'calendar month solid',
palette: { colors },
calendar_initial: parameters.value.end,
data: rows.value.map(r => ([
`${col(data, r, 'report_date')} 12:00:00`,
col(data, r, column)
]))
}
})
const areaOptions = computed(() => {
return {
type: 'lineSpline',
legend_visible: false,
xAxis: { crosshair_enabled: true, scale_type: 'time' },
palette: { colors },
defaultSeries: {
shape_opacity: 0.55,
color: colors[Math.round(colors.length / 2)],
defaultPoint_marker_visible: false
},
series: [
{
points: rows.value.map(r => ([
`${col(data, r, 'report_date')} 12:00:00`,
col(data, r, column)
]))
}
]
}
})
</script>

View File

@@ -0,0 +1,39 @@
<template></template>
<script setup>
import { watch, onMounted } from 'vue'
import cache from '@/data/cache.js'
const parameters = cache.parameters
const data = cache.data
function formatDate(d) {
return [
d.getFullYear(),
('0' + (d.getMonth() + 1)).slice(-2),
('0' + d.getDate()).slice(-2)
].join('-');
}
const today = new Date()
const end = new Date()
const start = new Date(today.valueOf() - 2592000000)
console.log(start, end)
parameters.value = {
county: 'All',
start: formatDate(start),
end: formatDate(end),
}
async function refreshData() {
data.value = await fetch(`/data/testing-trend/${parameters.value.county}.json`).then(res => res.json())
}
watch(() => parameters.value.county, () => {
refreshData()
})
onMounted(() => refreshData())
</script>