Skip to content

Instantly share code, notes, and snippets.

@pkese
Last active November 13, 2021 09:33
Show Gist options
  • Save pkese/cddf758bfd3c6be8a1d9effb4bbb6b2f to your computer and use it in GitHub Desktop.
Save pkese/cddf758bfd3c6be8a1d9effb4bbb6b2f to your computer and use it in GitHub Desktop.
#!/usr/bin/env -S dotnet fsi /langversion:preview
// - Fetch EPISARI hospitalizations data from podatki.gov.si
// - Fetch vaccination data from api.sledilnik.org/api/vaccinations
// - Mix in static population demography data
// - Render hospitalizatized per 100k population json
#r "nuget: FSharp.Data, 4.2.5"
#r "nuget: FSharp.Json, 0.4.1"
open System
open FSharp.Data
open System.Net.Http
open FSharp.Json
module Pop = // population demography (from Sledilnik)
// SURS 2021-H1: see https://docs.google.com/spreadsheets/d/1aBOp6GW7RpUg3GcrHThWb-6Czj8mlgATlHF2dUw9rTU/edit#gid=2092138705
let ageGroupPopulation =
[| {| minAge= 0; maxAge=17; count=374210 |}
{| minAge=18; maxAge=24; count=141126 |}
{| minAge=25; maxAge=29; count=113005 |}
{| minAge=30; maxAge=34; count=133642 |}
{| minAge=35; maxAge=39; count=150074 |}
{| minAge=40; maxAge=44; count=161814 |}
{| minAge=45; maxAge=49; count=152766 |}
{| minAge=50; maxAge=54; count=149801 |}
{| minAge=55; maxAge=59; count=152352 |}
{| minAge=60; maxAge=64; count=144472 |}
{| minAge=65; maxAge=69; count=136527 |}
{| minAge=70; maxAge=74; count=107823 |}
{| minAge=75; maxAge=79; count=75738 |}
{| minAge=80; maxAge=84; count=61052 |}
{| minAge=85; maxAge=89; count=36642 |}
{| minAge=90; maxAge=110; count=17933 |}
|]
let below65, above65 =
let y, o = ageGroupPopulation |> Array.partition (fun d -> d.minAge < 65)
y |> Array.sumBy (fun d -> d.count), o |> Array.sumBy (fun d -> d.count)
module Sari =
// https://podatki.gov.si/dataset/spremljanje-covid-19-v-bolnisnicah-episari
[<Literal>]
let sariUrl = "https://podatki.gov.si/dataset/7d73b91a-b551-4783-bb2c-8e4c8953a974/resource/7716561d-3c4e-4522-be4c-493cce35510a/download/episaricsvdo7.11.2021.popravljen.csv"
// WARNING: the ^^^^^ avbove url is changing with time (figure out how to get current)
type SariData = CsvProvider<sariUrl, Separators=";">
let fetch () =
let csvData = SariData.GetSample()
csvData.Rows
|> Seq.map (fun row ->
let week = row.Leto_teden
let day =
match week.Split '_' with
| [|y;w|] -> DateTime(int y, 1, 1) + TimeSpan.FromDays((float w - 1.0) * 7.0)
| _ -> failwithf "invalid week"
let vaccOver65 = row.``SARI_potrjeni_covid_popolnomacepljeni_>65_let``
let vaccAll = row.SARI_potrjeni_covid_popolnomacepljeni
let hospOver65 =
row.``SARI_potrjeni_covid_65-74_let`` +
row.``SARI_potrjeni_covid_75-84_let`` +
row.``SARI_potrjeni_covid_>85_let``
let hospUnder65 =
row.``SARI_potrjeni_covid_<5_let`` +
row.``SARI_potrjeni_covid_5-14_let`` +
row.``SARI_potrjeni_covid_15-24_let`` +
row.``SARI_potrjeni_covid_25-34_let``.GetValueOrDefault(0) +
row.``SARI_potrjeni_covid_35-44_let`` +
row.``SARI_potrjeni_covid_45-54_let`` +
row.``SARI_potrjeni_covid_55-64_let``
let all = row.SARI_potrjeni_covid
//printfn $"""day={day.ToString "yyyy-MM-dd"} all={all} vaccAll={vaccAll} vaccOver65={vaccOver65} 15-65={hospUnder65} >65={hospOver65} sum={hospUnder65+hospOver65}"""
{| day=day; vaccUnder65=vaccAll-vaccOver65; vaccOver65=vaccOver65; hospUnder65=hospUnder65; hospOver65=hospOver65 |}
)
|> Seq.last
module Vaccination =
[<Literal>]
let vaccUrl = "https://api.sledilnik.org/api/vaccinations"
type VaccData = JsonProvider<vaccUrl>
let getForDay (targetDay: DateTime) =
//printfn "querying date: %A" targetDay
let json = VaccData.Load(vaccUrl)
json.[^21..] // look max 3 weeks back
|> Seq.choose (fun d ->
let day = DateTime(d.Year, d.Month, d.Day)
//printfn "day: %A" day
if day = targetDay then
let y, o = d.AdministeredPerAge |> Array.partition(fun d -> d.AgeFrom < 65)
Some {| day=day; under65=y |> Array.sumBy (fun a -> a.Administered2nd |> Option.defaultValue 0); over65=o |> Array.sumBy (fun a -> a.Administered2nd |> Option.defaultValue 0) |}
else
None
)
|> Seq.head
let renderHospRates () =
let sari = Sari.fetch()
let vacc = Vaccination.getForDay sari.day
let getRatios totalHospitalized hospitalizedVaccinated totalVaccinated population =
let totalHospitalized, hospitalizedVaccinated, totalVaccinated, population = totalHospitalized, hospitalizedVaccinated, totalVaccinated, population
let hospitalizedUnvaccinated = totalHospitalized - hospitalizedVaccinated
let totalUnvaccinated = population - totalVaccinated
{|
hospVacc = hospitalizedVaccinated
hospUnvacc = hospitalizedUnvaccinated
hospTotal = totalHospitalized
vaccTotal = totalVaccinated
population = population
hospUnvacPer100k = float hospitalizedUnvaccinated / float totalUnvaccinated * 100_000.0
hospVaccPer100k = float hospitalizedVaccinated / float totalVaccinated * 100_000.0
|}
let data = {|
date = sari.day.ToString "yyyy-MM-dd"
under65 = getRatios sari.hospUnder65 sari.vaccUnder65 vacc.under65 Pop.below65
over65 = getRatios sari.hospOver65 sari.vaccOver65 vacc.over65 Pop.above65
totals = getRatios (sari.hospUnder65+sari.hospOver65) (sari.vaccUnder65+sari.vaccOver65) (vacc.under65+vacc.over65) (Pop.below65+Pop.above65)
|}
Json.serializeEx (JsonConfig.create(unformatted=false)) data
|> printfn "%s"
renderHospRates ()
@pkese
Copy link
Author

pkese commented Nov 13, 2021

And it renders:

{
  "date": "2021-10-29",
  "under65": {
    "hospTotal": 219,
    "hospUnvacPer100k": 20.151066567166165,
    "hospUnvacc": 182,
    "hospVacc": 37,
    "hospVaccPer100k": 4.8046706593046995,
    "population": 1673262,
    "vaccTotal": 770084
  },
  "over65": {
    "hospTotal": 311,
    "hospUnvacPer100k": 195.9907849946704,
    "hospUnvacc": 171,
    "hospVacc": 140,
    "hospVaccPer100k": 40.176086045697424,
    "population": 435715,
    "vaccTotal": 348466
  },
  "totals": {
    "hospTotal": 530,
    "hospUnvacPer100k": 35.641193141947866,
    "hospUnvacc": 353,
    "hospVacc": 177,
    "hospVaccPer100k": 15.824057932144296,
    "population": 2108977,
    "vaccTotal": 1118550
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment