Initial commit.
This commit is contained in:
0
src/components/Data.vue
Normal file
0
src/components/Data.vue
Normal file
85
src/components/charts/CasesPerDay.vue
Normal file
85
src/components/charts/CasesPerDay.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<label>
|
||||
Counties
|
||||
<select v-model="selectedCounty">
|
||||
<option v-for="county of counties" :key="county" :value="county">{{county}}</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Statistic
|
||||
<select v-model="selectedStat">
|
||||
<option v-for="(func, stat) of statistics" :key="stat" :value="stat">{{stat}}</option>
|
||||
</select>
|
||||
</label>
|
||||
<br>
|
||||
<label>
|
||||
Start
|
||||
<input type="date" v-model="selectedStartDate"/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
End
|
||||
<input type="date" v-model="selectedEndDate"/>
|
||||
</label>
|
||||
<br>
|
||||
<JSCharting v-if="data.length" :options="chartOptions"></JSCharting>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import Papa from 'papaparse'
|
||||
|
||||
let data = ref([])
|
||||
let selectedCounty = ref('Georgia')
|
||||
let selectedStat = ref('Positive PCR Tests')
|
||||
|
||||
let selectedStartDate = ref('2021-01-01')
|
||||
let selectedEndDate = ref('2021-12-31')
|
||||
|
||||
const counties = computed(() => {
|
||||
return Array.from(new Set(data.value.map(r => r.county)))
|
||||
})
|
||||
|
||||
const statistics = {
|
||||
'PCR Tests Performed': r => r['ALL PCR tests performed'],
|
||||
'Positive PCR Tests': r => r['All PCR positive tests'],
|
||||
'PCR+Antigen Tests Performed': r => parseInt(r['ALL PCR tests performed']) + parseInt(r['Antigen Tests Performed']),
|
||||
'Antigen Tests Performed': r => r['Antigen Tests Performed'],
|
||||
'Positive Antigen Tests': r => r['Antigen Positive Tests'],
|
||||
'Positive PCR & Antigen Tests': r => parseInt(r['All PCR positive tests']) + parseInt(r['Antigen Positive Tests']),
|
||||
'7-day Percent Positive': r => r['7 day percent positive'],
|
||||
'14-day Percent Positive': r => r['14 day percent positive'],
|
||||
}
|
||||
|
||||
|
||||
const colors = ['#f5f3ff','#ede9fe','#ddd6fe','#c4b5fd','#a78bfa','#8b5cf6','#7c3aed','#6d28d9','#5b21b6','#4c1d95']
|
||||
|
||||
const chartOptions = computed(() => {
|
||||
return {
|
||||
type: 'calendar solid',
|
||||
palette: { colors },
|
||||
data: data.value
|
||||
.filter(r => {
|
||||
return r.county == selectedCounty.value
|
||||
&& r.report_date >= selectedStartDate.value
|
||||
&& r.report_date <= selectedEndDate.value
|
||||
})
|
||||
.map(r => ([
|
||||
`${r.report_date} 12:00:00`,
|
||||
+statistics[selectedStat.value](r)
|
||||
]))
|
||||
}
|
||||
})
|
||||
|
||||
async function getData() {
|
||||
const csv = await fetch('/data/2021-12-28/pcr_antigen.csv').then(res => res.text())
|
||||
return Papa.parse(csv, {
|
||||
header: true
|
||||
}).data
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
data.value = await getData()
|
||||
})
|
||||
</script>
|
||||
76
src/components/jscharting/JSCharting.vue
Normal file
76
src/components/jscharting/JSCharting.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Chart } from 'jscharting';
|
||||
|
||||
export default {
|
||||
name: 'JSCharting',
|
||||
props: {
|
||||
options: { type: Object, required: true },
|
||||
mutable: { type: Boolean, default: true },
|
||||
ignoreStateUpdate: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
instance: undefined
|
||||
};
|
||||
},
|
||||
mounted: function() {
|
||||
this.createChart();
|
||||
},
|
||||
beforeUnmount: function() {
|
||||
this.destroyChart();
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
deep: true,
|
||||
handler(oldValue, newValue) {
|
||||
// Skip update if chart exists and ignoreStateUpdate is true
|
||||
if (this.instance && this.ignoreStateUpdate) return;
|
||||
|
||||
if (oldValue !== newValue) {
|
||||
this.destroyChart()
|
||||
this.createChart()
|
||||
} else {
|
||||
this.createChart();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createChart: function() {
|
||||
const options = this.options || {};
|
||||
|
||||
// If the instance does not exist yet, create one
|
||||
if (!this.instance) {
|
||||
this.renderChart(options);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mutable && !this.instance.dirty) {
|
||||
// If the mutable is true and the instance is not dirty, update the existing instance
|
||||
this.instance.options(options);
|
||||
} else {
|
||||
// Create a new instance with the new values
|
||||
this.renderChart(options);
|
||||
}
|
||||
},
|
||||
renderChart(options) {
|
||||
this.destroyChart();
|
||||
this.instance = new Chart(
|
||||
options.targetElement || this.$el,
|
||||
options,
|
||||
chart => this.$emit('rendered', chart)
|
||||
);
|
||||
},
|
||||
destroyChart: function() {
|
||||
if (this.instance) {
|
||||
this.instance.destroy();
|
||||
this.instance = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
47
src/components/jscharting/JSGrid.vue
Normal file
47
src/components/jscharting/JSGrid.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as JSC from 'jscharting';
|
||||
export default {
|
||||
name: 'JSCGrid',
|
||||
props: {
|
||||
options: { type: Object, required: true },
|
||||
mutable: { type: Boolean, default: true }
|
||||
},
|
||||
mounted: function() {
|
||||
this.renderGrid();
|
||||
},
|
||||
beforeUnmount: function() {
|
||||
this.destroyGrid();
|
||||
},
|
||||
watch: {
|
||||
options: function() {
|
||||
this.renderGrid();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
destroyGrid: function() {
|
||||
if (this.instance) {
|
||||
this.instance.destroy();
|
||||
this.instance = undefined;
|
||||
}
|
||||
},
|
||||
renderGrid: function() {
|
||||
const options = this.options;
|
||||
const updateExisting = this.instance && this.mutable !== false;
|
||||
if (updateExisting) {
|
||||
this.instance.options(this.options);
|
||||
} else {
|
||||
this.destroyGrid();
|
||||
const containerElement = this.$el;
|
||||
JSC.Grid(containerElement, options).then(grid => {
|
||||
this.instance = grid;
|
||||
this.$emit('rendered', grid);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
35
src/components/jscharting/JSLabel.vue
Normal file
35
src/components/jscharting/JSLabel.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as JSC from 'jscharting';
|
||||
export default {
|
||||
name: 'JSCLabel',
|
||||
props: {
|
||||
options: { type: String, default: '' }
|
||||
},
|
||||
mounted: function() {
|
||||
this.renderLabel();
|
||||
},
|
||||
beforeUnmount: function() {
|
||||
this.destroyLabel();
|
||||
},
|
||||
watch: {
|
||||
options: function() {
|
||||
this.renderLabel();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
destroyLabel: function() {
|
||||
const containerElement = this.$el;
|
||||
containerElement.innerHTML = '';
|
||||
},
|
||||
renderLabel: function() {
|
||||
this.destroyLabel();
|
||||
const containerElement = this.$el;
|
||||
JSC.label(containerElement, this.options);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user