From b3289258e339a79af7ce77adc18a95b677816a47 Mon Sep 17 00:00:00 2001 From: Joshua Bemenderfer Date: Sat, 1 Jan 2022 21:50:26 -0500 Subject: [PATCH] Add summary section to index. --- LICENSE.md | 7 + README.md | 96 +++++++- components.d.ts | 13 +- data/parser.js | 4 + data/parser/summary.js | 41 ++++ package.json | 1 + public/data/summary.json | 1 + src/components/Card.vue | 2 +- .../charts/overall/cases/ChipsCases.vue | 28 +-- src/components/charts/overall/cases/store.js | 4 +- .../charts/overall/cases/store_combined.js | 4 +- src/components/charts/overall/deaths/store.js | 4 +- .../charts/overall/deaths/store_combined.js | 4 +- .../charts/overall/hospitalizations/store.js | 4 +- .../hospitalizations/store_combined.js | 6 +- .../charts/overall/testing/ChipsTesting.vue | 4 +- .../charts/overall/testing/store.js | 4 +- src/components/charts/risk/age/store.js | 4 +- .../charts/risk/health-conditions/store.js | 4 +- .../charts/summary/ChipsSummary.vue | 216 ++++++++++++++++++ src/components/charts/summary/store.js | 13 ++ src/components/charts/util.js | 2 +- src/layouts/components/Navigation.vue | 63 +++-- src/layouts/default.vue | 6 +- src/pages/index.mdx | 139 ++++++++++- src/pages/overall/testing.mdx | 3 +- src/pages/risk/health-conditions.mdx | 4 +- 27 files changed, 593 insertions(+), 88 deletions(-) create mode 100644 LICENSE.md create mode 100644 data/parser/summary.js create mode 100644 public/data/summary.json create mode 100644 src/components/charts/summary/ChipsSummary.vue create mode 100644 src/components/charts/summary/store.js diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..4786f7ea9 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2022 Joshua Bemenderfer + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index c2b6c08e5..cd34012ac 100644 --- a/README.md +++ b/README.md @@ -1 +1,95 @@ -# TODO: Write +# About + +This site exists to provide a simplified, targeted view into information provided by the [Georgia Department of Health (DPH)](https://dph.georgia.gov/covid-19-daily-status-report) on the COVID-19 pandemic. + +While the DPH provides an excellent range of datasets and visualizations, how to understand their utility and interpret them is left as an exercise to the reader.
+Here I've attempted to provide explanations of the utility of certain statistics as well as provide some new visualizations not currently provided by the Department of Health dashboard. + +## Disclaimer + +The data provided on this site is based on information made available by the Georgia Department of Health at the time of reporting. +It may at times lack accuracy due to unreported, delayed, or improperly processed information. +Reporting time and quality for certain statistics may vary depending on the processes of individual organizations and jurisdictions. + +This site is manually built from daily DPH reports. I will attempt to have new data incorporated by 5:00 PM on weekdays. Updates may be delayed due to personal circumstances. + +**For case reports**, at-home rapid tests are not accounted for. Case numbers may be much higher than reported if [test positivity](https://ga-covid.thederf.com/overall/testing) is unusually high. + +## The following additional reports are planned + +- **What is the overall trend in vaccinations?** - [Data available here](https://experience.arcgis.com/experience/3d8eea39f5c1443db1743a4cb8948a9c) as XLSX, pending processing. +- **Who is most at risk by ethnicity and sex?** - Data available in daily reports, pending processing. +- **Who is most at risk by vaccination status?** - Weekly reports on breakthrough cases and deaths [are available here](https://dph.georgia.gov/covid-19-breakthrough-reports), but challenging to process. +- **How many hospital resources are available?** - [Data available here](https://www.arcgis.com/apps/opsdashboard/index.html#/47c1cee4d02542bea35bc3324d6cf5e3) but requires querying ArcGIS directly. +- **How many hospital patients have COVID?** - [Data available here](https://www.arcgis.com/apps/opsdashboard/index.html#/e40c39564f724af7bfe8fd5d88deadb6) but requires querying ArcGIS directly. + +**Please note:** *This is a citizen science project and is not affiliated with or endorsed in any way by the State of Georgia.* + +## Data Sources + +- Georgia Department of Health, COVID-19 Daily Status Report - [https://ga-covid19.ondemand.sas.com/docs/ga_covid_data.zip](https://ga-covid19.ondemand.sas.com/docs/ga_covid_data.zip) + - **Update Frequency** - The dataset above is updated each working day around 2:50 PM. I load it onto this site and rebuild shortly thereafter. + - **Additional Data** - Some reports on this site may derive additional time-based information by comparing totals from multiple daily reports. +- David Eldersveld, TopoJSON Collection, Georgia Counties TopoJSON - [https://github.com/deldersveld/topojson/blob/master/countries/us-states/GA-13-georgia-counties.json](https://github.com/deldersveld/topojson/blob/master/countries/us-states/GA-13-georgia-counties.json) + - Tweaked and used for rendering county maps. + +## Developing + +Feel free to build it yourself, take a look around, validate the reports, and [suggest improvements](#contact).
+*Note, the code is still very much in the "make it work" stage, and has much room for being simplified, abstracted, and cleaned up. Ignore the fact that I'm using TypeScript as if it were un-typed JavaScript.* + +### Stack + +- [îles](https://iles-docs.netlify.app/) - Generates static pages from MDX files and Vue components, with optional component-level client-side hydration. + - Excellent for getting a site built with a modern tech stack and minimal extraneous config. Still in beta with some rough edges, but I greatly enjoyed working with it. +- [Vue 3](https://v3.vuejs.org/) - Used for layout and client-side components such as charts. +- [JSCharting](https://jscharting.com/) - Used for charts. Straightforward to get up and running with, but provides a vast array of data visualization options. +- [TailwindCSS](https://tailwindcss.com/) - Because people keep telling me to try it. I get the appeal. It made style tweaking incredibly fast and fluid, but it definitely results in messy markup. + +### Building & Running + +1. Clone repository using [git](https://git-scm.com/) + ```bash + $ git clone https://git.thederf.com/thederf/ga-covid.thederf.com.git + ``` +2. Install dependencies using [npm](https://www.npmjs.com/) or your preferred [node.js](https://nodejs.org) package manager of choice. + ```bash + $ cd ga-covid.thederf.com/ + $ npm install + ``` +3. (Optional) Rebuild the data json files. + ```bash + npm run process:data + ``` +4. Run a local server for development + ```bash + npm run dev + ``` +5. Build static site + ```bash + npm run build + ``` + +### File Tree + +```txt +data/ - Scripts for comverting the raw CSV source files into browser-friendly JSON, removing extraneous data points, and segmenting data into separate files. + - parser.js - Data parser entrypoint. + parser/... - Scripts for producing json files for specific reports. + raw/ + - YYYY-MM-DD.zip - Raw Georgia DPH datasets from the link above, by date downloaded. + Including historical datasets allows for building time-series reports for data provided exclusively in daily counts. + ... +public/ - Static assets. + maps/ - GeoJSON map files for rendering maps. + data/ - Generated JSON files prepared for browser loading by the parser above. + ... +src/ + assets/ - CSS & Image assets processed by Vite + components/ - Vue components for rendering client-interactive parts of the website. + charts/ - Components that handle chart & chip rendering. + layouts/ - Vue components for laying out the site. Primarily server-rendered. + pages/ - MDX files for site pages. + ... +... +``` diff --git a/components.d.ts b/components.d.ts index 4a2f75458..34cf6bb43 100644 --- a/components.d.ts +++ b/components.d.ts @@ -10,16 +10,27 @@ declare module 'vue' { ChipsDeaths: typeof import('./src/components/charts/overall/deaths/ChipsDeaths.vue')['default'] ChipsHospitalizations: typeof import('./src/components/charts/overall/hospitalizations/ChipsHospitalizations.vue')['default'] ChipsRiskAge: typeof import('./src/components/charts/risk/age/ChipsRiskAge.vue')['default'] + ChipsRiskHealthConditions: typeof import('./src/components/charts/risk/health-conditions/ChipsRiskHealthConditions.vue')['default'] + ChipsSummary: typeof import('./src/components/charts/summary/ChipsSummary.vue')['default'] ChipsTesting: typeof import('./src/components/charts/overall/testing/ChipsTesting.vue')['default'] County_time_series: typeof import('./src/components/charts/state/cases/county_time_series.js')['default'] County_time_series_store: typeof import('./src/components/charts/state/cases/county_time_series_store.js')['default'] IconHealthiconsClinicalFOutline: typeof import('~icons/healthicons/clinical-f-outline')['default'] + IconHealthiconsHospitalized: typeof import('~icons/healthicons/hospitalized')['default'] + IconHealthiconsHospitalizedOutline: typeof import('~icons/healthicons/hospitalized-outline')['default'] + IconHealthiconsVentilator: typeof import('~icons/healthicons/ventilator')['default'] + IconHealthiconsVentilatorAl: typeof import('~icons/healthicons/ventilator-al')['default'] + IconHealthiconsVentilatorAlt: typeof import('~icons/healthicons/ventilator-alt')['default'] + IconHealthiconsVentilatorOutline: typeof import('~icons/healthicons/ventilator-outline')['default'] IconMdiGenderMaleFemale: typeof import('~icons/mdi/gender-male-female')['default'] IconMdiGraveStone: typeof import('~icons/mdi/grave-stone')['default'] IconMdiHospitalBoxOutline: typeof import('~icons/mdi/hospital-box-outline')['default'] IconMdiHumanCane: typeof import('~icons/mdi/human-cane')['default'] IconMdiPercentOutline: typeof import('~icons/mdi/percent-outline')['default'] + IconMdiPerson: typeof import('~icons/mdi/person')['default'] + IconMdiSyringe: typeof import('~icons/mdi/syringe')['default'] IconMdiTestTube: typeof import('~icons/mdi/test-tube')['default'] + IconMdiUser: typeof import('~icons/mdi/user')['default'] IconMdiVirusOutline: typeof import('~icons/mdi/virus-outline')['default'] IconMdiWeb: typeof import('~icons/mdi/web')['default'] Island: typeof import('./node_modules/iles/dist/client/app/components/Island.vue')['default'] @@ -37,7 +48,7 @@ declare module 'vue' { RiskAgeParameters: typeof import('./src/components/charts/risk/age/RiskAgeParameters.vue')['default'] SliceSelector: typeof import('./src/components/SliceSelector.vue')['default'] StatCard: typeof import('./src/components/cards/StatCard.vue')['default'] - Store: typeof import('./src/components/charts/overall/cases/store.js')['default'] + Store: typeof import('./src/components/charts/summary/store.js')['default'] Store_combined: typeof import('./src/components/charts/overall/cases/store_combined.js')['default'] TestingDataSetup: typeof import('./src/components/pages/state/TestingDataSetup.vue')['default'] TestingParameters: typeof import('./src/components/charts/state/testing/TestingParameters.vue')['default'] diff --git a/data/parser.js b/data/parser.js index c21ba59ad..149ba1a2d 100644 --- a/data/parser.js +++ b/data/parser.js @@ -11,6 +11,8 @@ import OverallDeaths from './parser/overall/deaths.js' import RiskAge from './parser/risk/age.js' import RiskHealthConditions from './parser/risk/health-conditions.js' +import Summary from './parser/summary.js' + async function main() { const sources = await fg(['./data/raw/*.zip']) sources.sort() @@ -29,6 +31,8 @@ async function main() { await RiskAge(zips) await RiskHealthConditions(zips) + + await Summary() } main() diff --git a/data/parser/summary.js b/data/parser/summary.js new file mode 100644 index 000000000..670436160 --- /dev/null +++ b/data/parser/summary.js @@ -0,0 +1,41 @@ + +import mkdirp from 'mkdirp' +import path from 'path' +import fsp from 'fs/promises' + +async function readJSON(file) { + return JSON.parse(await fsp.readFile(file, 'utf-8')) +} + +export default async function process () { + const testing = await readJSON('./public/data/overall/testing/by-county/-- All --.json') + const cases = await readJSON('./public/data/overall/cases/by-county/-- All --.json') + const hospitalizations = await readJSON('./public/data/overall/hospitalizations/by-county/-- All --.json') + const deaths = await readJSON('./public/data/overall/deaths/by-county/-- All --.json') + + const summary = { + last_report_date: testing.rows.at(-1)[0], + testing: { + headers: testing.headers, + current: testing.rows.at(-1), + prev: testing.rows.at(-2), + }, + cases: { + headers: cases.headers, + current: cases.rows.at(-1), + prev: cases.rows.at(-2), + }, + hospitalizations: { + headers: hospitalizations.headers, + current: hospitalizations.rows.at(-1), + prev: hospitalizations.rows.at(-2), + }, + deaths: { + headers: deaths.headers, + current: deaths.rows.at(-1), + prev: deaths.rows.at(-2), + } + } + + await fsp.writeFile('./public/data/summary.json', JSON.stringify(summary)) +} diff --git a/package.json b/package.json index 97cab5ddc..f4d5eeb0d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "ga-covid-charts", "private": true, + "license": "MIT", "version": "0.0.0", "scripts": { "dev": "iles dev", diff --git a/public/data/summary.json b/public/data/summary.json new file mode 100644 index 000000000..4e4242f90 --- /dev/null +++ b/public/data/summary.json @@ -0,0 +1 @@ +{"last_report_date":"2021-12-31","testing":{"headers":["report_date","pcr_performed","pcr_positive","antigen_performed","antigen_positive","combined_performed","combined_positive","seven_day_percent_positive","combined_performed_running_total"],"current":["2021-12-31",54958,21204,27064,6629,82022,27833,32.1,18485118],"prev":["2021-12-30",57566,21851,31395,7521,88961,29372,30,18403096]},"cases":{"headers":["report_date","population","cases","cases_per_capita","cases_14_days","case_rate_14_days","total_cases","total_cases_per_capita"],"current":["2021-12-31",10833472,17641,0.0016283791567467936,null,null,1839082,0.16975924246631183],"prev":["2021-12-30",10833472,17923,0.0016544095927879816,null,null,1814762,0.16751434812403632]},"hospitalizations":{"headers":["report_date","population","hospitalizations","hospitalizations_per_capita","hospitalizations_last_14_days","hospitalizations_last_14_days_per_capita"],"current":["2021-12-31",10833472,248,0.00002289201467451986,2747,0.0002535659851246212],"prev":["2021-12-30",10833472,283,0.000026122742551972257,2590,0.00023907386293147754]},"deaths":{"headers":["report_date","population","deaths","deaths_per_capita","deaths_14_days","death_rate_14_days","total_deaths","total_deaths_per_capita"],"current":["2021-12-31",10833472,18,0.0000016615171941183769,340,0.00003138421366668045,26425,0.0024391995474765614],"prev":["2021-12-30",10833472,49,0.000004523019028433359,407,0.00003756874988923219,26407,0.002437538030282443]}} \ No newline at end of file diff --git a/src/components/Card.vue b/src/components/Card.vue index aa886268b..58423240e 100644 --- a/src/components/Card.vue +++ b/src/components/Card.vue @@ -1,5 +1,5 @@ diff --git a/src/components/charts/overall/cases/ChipsCases.vue b/src/components/charts/overall/cases/ChipsCases.vue index 08bbf411d..13f3dcf70 100644 --- a/src/components/charts/overall/cases/ChipsCases.vue +++ b/src/components/charts/overall/cases/ChipsCases.vue @@ -1,26 +1,26 @@