Some personal and computational notes loosely based on the analytical work I have done in response to COVID-19 pandemic in Scotland. In my GIS Analyst role I use wide range of proprietary and open source GIS software as well as tend to blend R and Python languages. Here is a flavor of my work in open and reproducible notebook, following a Donald Knuth’s “literate programming” paradigm.

Packages

First,all packages necessary for the analysis.

library(dplyr)
library(tidyr)
library(lubridate)
library(readr)
library(ggplot2)
library(sf)
library(stringr)
library(tmap)
library(hrbrthemes)
library(zoo)

Data Wrangling

Now, lets read the data from the Data Science Scotland repository

cases_scot = read_csv("https://raw.githubusercontent.com/DataScienceScotland/COVID-19-Management-Information/master/COVID19%20-%20Daily%20Management%20Information%20-%20Scotland%20-%20Testing.csv") %>% 
  select(Date,'Testing - Cumulative people tested for COVID-19 - Positive') %>% 
  rename(total = 'Testing - Cumulative people tested for COVID-19 - Positive') %>% 
  mutate(date = as.Date(Date, "%d-%b-%Y"))
  

# deaths 
deaths_scot = read_csv("https://raw.githubusercontent.com/DataScienceScotland/COVID-19-Management-Information/master/COVID19%20-%20Daily%20Management%20Information%20-%20Scotland%20-%20Deaths.csv") %>% 
    rename(total = 'Number of COVID-19 confirmed deaths registered to date') %>% 
    mutate(date = as.Date(Date, "%d-%b-%Y"))

Since we only have on variable with total cases and deaths, we are going to calculate additional attributes: daily new cases, percentage change and seven days rolling average.

#cases 
cases_scot  = cases_scot  %>% 
mutate(new_cases = (total - lag(total)), # new daily cases 
       pct_change = new_cases / lag(total) * 100, # percentage change
       cases_07da = rollmean(new_cases, k = 7, fill = NA)) # 7 days rolling average



# deaths
deaths_scot  = deaths_scot  %>% 
mutate(new_deaths = (total - lag(total)), # new daily cases 
      pct_change = new_deaths / lag(total) * 100, # percentage change
      deaths_07da = rollmean(new_deaths, k = 7, fill = NA)) # 7 days rolling average

Plotting

We are ready to plot our variables using ggplot package

ggplot()+
  geom_line(data = cases_scot, aes(x=Date, y=new_cases), color='gray') +
  geom_line(data = cases_scot, aes(x=Date, y=cases_07da), color='#ba0000') +
  labs( title = "Scotland's 7 days rolling average COVID-19 cases",
        subtitle = "Between March 2020 and March 2021", 
        caption="Source:https://github.com/DataScienceScotland/COVID-19-Management-Information ",
        y = "Cases", 
        x = "Month") +
  scale_x_date(date_breaks = '1 month', date_labels = '%m %y') +
  scale_y_log10() +
  hrbrthemes::theme_ipsum()

Mapping

We can also create a map showing latest cases at NHS Health Board level. But first we need to get the data for each geograph unit.

# load data
cases_by_hb = read_csv("https://raw.githubusercontent.com/DataScienceScotland/COVID-19-Management-Information/master/COVID19%20-%20Daily%20Management%20Information%20-%20Scottish%20Health%20Boards%20-%20Cumulative%20cases.csv")

# pivot data to long format nd select latest date
last_cases_by_hb_long = cases_by_hb %>%
pivot_longer(!Date,names_to = "hb_name", values_to = "cases")  %>%
mutate(cases = as.numeric(cases)) %>% 
filter(Date == max(Date))

# load mid 2019 population estimatie
pop_hb = read_csv("data/hb_pop_2019.csv")

# first join: cases with population, also calculate rate per 100k
cases_by_hb_pop = left_join(last_cases_by_hb_long, pop_hb) %>% 
  mutate(
    rate_100k = cases / hb_pop * 100000
  )

# load Health Board geographies
health_boards = st_read("data/SG_NHS_HealthBoards_2019.shp", quiet = TRUE)

# second join: health board boundaries 
cases_hb_rate100k = left_join(health_boards,cases_by_hb_pop, by = c("HBCode" = "hb_code"))

Finally, we are ready to produce the map using tmap package. Note that we are saving the output to png file.

map = tm_shape(cases_hb_rate100k) +
  tm_polygons(col = "rate_100k",
              title = "Data from 17th March 2021 \nNumber of confirmed cases per 100,000 people",
              lwd = 1,
              border.col = "white",
              style = "equal",
              n = 5,
              palette="cividis" # choose between reds or cividis
             # legend.format=list(fun=function(x) paste0(formatC(x, digits=0, format="f")))
              ) +
  tm_layout(
           # fontfamily = "arial",
            title = "Total COVID - 19 cases \nper 100,000 people, by NHS Health Board",
            title.size = 0.9,
            legend.title.size = 1,
            attr.outside= TRUE,
            attr.outside.position = "bottom",
            attr.outside.size = .1,
            attr.just = c("left", "top"),
            inner.margins = 0.02,
            outer.margins = c(0,0.01,0.01,0.01),
            asp = 4/6,
            frame = F,
            design.mode = F
           ) +
  tm_credits("Cases: https://www.gov.scot/coronavirus-covid-19\nPopulation data: NRS Population Estimates, mid-2018\nBoundaries:  SpatialData.gov.scot",
             col = "grey50",
             position=c("left","top"), 
             align = "left",
             size = 0.325)
# save graphic to png file
tmap_save(
  tm = map,
  filename = "hb_map_cases_rate.png",
  height = 6,
  width = 4,
  units = "in",
  asp = 4/6
)

Ok, map is ready !

I can’t recommend tmap package enough.

Session info

sessionInfo()
## R version 4.0.2 (2020-06-22)
## Platform: x86_64-apple-darwin17.0 (64-bit)
## Running under: macOS Mojave 10.14.6
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] zoo_1.8-9          hrbrthemes_0.8.0   tmap_3.0           stringr_1.4.0.9000
##  [5] sf_0.9-6           ggplot2_3.3.2      readr_1.4.0        lubridate_1.7.9.2 
##  [9] tidyr_1.1.2        dplyr_1.0.2       
## 
## loaded via a namespace (and not attached):
##  [1] Rcpp_1.0.6         lattice_0.20-41    png_0.1-7          class_7.3-17      
##  [5] assertthat_0.2.1   digest_0.6.27      R6_2.5.0           evaluate_0.14     
##  [9] e1071_1.7-4        highr_0.8          pillar_1.4.7       gdtools_0.2.2     
## [13] rlang_0.4.10       curl_4.3           rstudioapi_0.13    extrafontdb_1.0   
## [17] raster_3.1-5       rmarkdown_2.6.4    extrafont_0.17     htmlwidgets_1.5.1 
## [21] munsell_0.5.0      compiler_4.0.2     xfun_0.22          systemfonts_0.2.1 
## [25] pkgconfig_2.0.3    tmaptools_3.0      base64enc_0.1-3    htmltools_0.5.1.1 
## [29] tidyselect_1.1.0   tibble_3.0.4       codetools_0.2-16   XML_3.99-0.3      
## [33] viridisLite_0.3.0  crayon_1.4.1       withr_2.3.0        grid_4.0.2        
## [37] lwgeom_0.2-3       Rttf2pt1_1.3.8     gtable_0.3.0       lifecycle_0.2.0   
## [41] DBI_1.1.0          magrittr_2.0.1     units_0.6-7        scales_1.1.1      
## [45] KernSmooth_2.23-17 cli_2.3.1          stringi_1.5.3      farver_2.0.3      
## [49] leafsync_0.1.0     leaflet_2.0.3      sp_1.4-5           ellipsis_0.3.1    
## [53] generics_0.1.0     vctrs_0.3.6        RColorBrewer_1.1-2 tools_4.0.2       
## [57] dichromat_2.0-0    leafem_0.1.1       glue_1.4.2         purrr_0.3.4       
## [61] hms_0.5.3          crosstalk_1.1.0.1  abind_1.4-5        parallel_4.0.2    
## [65] yaml_2.2.1         colorspace_1.4-1   stars_0.4-1        classInt_0.4-3    
## [69] knitr_1.31
 

A work by Michal Michalski

 

LS0tCnRpdGxlOiAiQ09WSUQtMTkgYW5hbHlzaXMgYW5kIG1hcHBpbmciCmF1dGhvcjogIk1pY2hhbCBNaWNoYWxza2kiCmRhdGU6ICIwMS8wMy8yMDIxIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogZmxhdGx5CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogCiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQogIAotLS0KPHN0eWxlPgpkaXYuYmx1ZSB7IGJhY2tncm91bmQtY29sb3I6I0QzRDNEMzsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAyMHB4O30KPC9zdHlsZT4KPGRpdiBjbGFzcyA9ICJibHVlIj4KU29tZSBwZXJzb25hbCBhbmQgY29tcHV0YXRpb25hbCBub3RlcyBsb29zZWx5IGJhc2VkIG9uIHRoZSBhbmFseXRpY2FsIHdvcmsgSSBoYXZlIGRvbmUgaW4gcmVzcG9uc2UgdG8gQ09WSUQtMTkgcGFuZGVtaWMgaW4gU2NvdGxhbmQuCkluIG15IEdJUyBBbmFseXN0IHJvbGUgSSB1c2Ugd2lkZSByYW5nZSBvZiBwcm9wcmlldGFyeSBhbmQgb3BlbiBzb3VyY2UgR0lTIHNvZnR3YXJlIGFzIHdlbGwgYXMgdGVuZCB0byBibGVuZCBSIGFuZCBQeXRob24gbGFuZ3VhZ2VzLiAKSGVyZSBpcyBhIGZsYXZvciBvZiBteSB3b3JrIGluIG9wZW4gYW5kIHJlcHJvZHVjaWJsZSBub3RlYm9vaywgZm9sbG93aW5nIGEgRG9uYWxkIEtudXRoJ3MgImxpdGVyYXRlIHByb2dyYW1taW5nIiBwYXJhZGlnbS4KPC9kaXY+CgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgojIyBQYWNrYWdlcwoKRmlyc3QsYWxsIHBhY2thZ2VzIG5lY2Vzc2FyeSBmb3IgdGhlIGFuYWx5c2lzLgpgYGB7ciBwYWNrYWdlcywgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0KCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2YpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeSh0bWFwKQpsaWJyYXJ5KGhyYnJ0aGVtZXMpCmxpYnJhcnkoem9vKQpgYGAKCiMjIERhdGEgV3JhbmdsaW5nCgpOb3csIGxldHMgcmVhZCB0aGUgZGF0YSBmcm9tIHRoZSAqRGF0YSBTY2llbmNlIFNjb3RsYW5kKiByZXBvc2l0b3J5CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0KY2FzZXNfc2NvdCA9IHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vRGF0YVNjaWVuY2VTY290bGFuZC9DT1ZJRC0xOS1NYW5hZ2VtZW50LUluZm9ybWF0aW9uL21hc3Rlci9DT1ZJRDE5JTIwLSUyMERhaWx5JTIwTWFuYWdlbWVudCUyMEluZm9ybWF0aW9uJTIwLSUyMFNjb3RsYW5kJTIwLSUyMFRlc3RpbmcuY3N2IikgJT4lIAogIHNlbGVjdChEYXRlLCdUZXN0aW5nIC0gQ3VtdWxhdGl2ZSBwZW9wbGUgdGVzdGVkIGZvciBDT1ZJRC0xOSAtIFBvc2l0aXZlJykgJT4lIAogIHJlbmFtZSh0b3RhbCA9ICdUZXN0aW5nIC0gQ3VtdWxhdGl2ZSBwZW9wbGUgdGVzdGVkIGZvciBDT1ZJRC0xOSAtIFBvc2l0aXZlJykgJT4lIAogIG11dGF0ZShkYXRlID0gYXMuRGF0ZShEYXRlLCAiJWQtJWItJVkiKSkKICAKCiMgZGVhdGhzIApkZWF0aHNfc2NvdCA9IHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vRGF0YVNjaWVuY2VTY290bGFuZC9DT1ZJRC0xOS1NYW5hZ2VtZW50LUluZm9ybWF0aW9uL21hc3Rlci9DT1ZJRDE5JTIwLSUyMERhaWx5JTIwTWFuYWdlbWVudCUyMEluZm9ybWF0aW9uJTIwLSUyMFNjb3RsYW5kJTIwLSUyMERlYXRocy5jc3YiKSAlPiUgCiAgICByZW5hbWUodG90YWwgPSAnTnVtYmVyIG9mIENPVklELTE5IGNvbmZpcm1lZCBkZWF0aHMgcmVnaXN0ZXJlZCB0byBkYXRlJykgJT4lIAogICAgbXV0YXRlKGRhdGUgPSBhcy5EYXRlKERhdGUsICIlZC0lYi0lWSIpKQoKYGBgCgpTaW5jZSB3ZSBvbmx5IGhhdmUgb24gdmFyaWFibGUgd2l0aCB0b3RhbCBjYXNlcyBhbmQgZGVhdGhzLCB3ZSBhcmUgZ29pbmcgdG8gY2FsY3VsYXRlIGFkZGl0aW9uYWwgYXR0cmlidXRlczogCmRhaWx5IG5ldyBjYXNlcywgcGVyY2VudGFnZSBjaGFuZ2UgYW5kIHNldmVuIGRheXMgcm9sbGluZyBhdmVyYWdlLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9CgojY2FzZXMgCmNhc2VzX3Njb3QgID0gY2FzZXNfc2NvdCAgJT4lIAptdXRhdGUobmV3X2Nhc2VzID0gKHRvdGFsIC0gbGFnKHRvdGFsKSksICMgbmV3IGRhaWx5IGNhc2VzIAogICAgICAgcGN0X2NoYW5nZSA9IG5ld19jYXNlcyAvIGxhZyh0b3RhbCkgKiAxMDAsICMgcGVyY2VudGFnZSBjaGFuZ2UKICAgICAgIGNhc2VzXzA3ZGEgPSByb2xsbWVhbihuZXdfY2FzZXMsIGsgPSA3LCBmaWxsID0gTkEpKSAjIDcgZGF5cyByb2xsaW5nIGF2ZXJhZ2UKCgoKIyBkZWF0aHMKZGVhdGhzX3Njb3QgID0gZGVhdGhzX3Njb3QgICU+JSAKbXV0YXRlKG5ld19kZWF0aHMgPSAodG90YWwgLSBsYWcodG90YWwpKSwgIyBuZXcgZGFpbHkgY2FzZXMgCiAgICAgIHBjdF9jaGFuZ2UgPSBuZXdfZGVhdGhzIC8gbGFnKHRvdGFsKSAqIDEwMCwgIyBwZXJjZW50YWdlIGNoYW5nZQogICAgICBkZWF0aHNfMDdkYSA9IHJvbGxtZWFuKG5ld19kZWF0aHMsIGsgPSA3LCBmaWxsID0gTkEpKSAjIDcgZGF5cyByb2xsaW5nIGF2ZXJhZ2UKCgpgYGAKCiMjIFBsb3R0aW5nCgpXZSBhcmUgcmVhZHkgdG8gcGxvdCBvdXIgdmFyaWFibGVzIHVzaW5nICoqZ2dwbG90KiogcGFja2FnZQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9CgpnZ3Bsb3QoKSsKICBnZW9tX2xpbmUoZGF0YSA9IGNhc2VzX3Njb3QsIGFlcyh4PURhdGUsIHk9bmV3X2Nhc2VzKSwgY29sb3I9J2dyYXknKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBjYXNlc19zY290LCBhZXMoeD1EYXRlLCB5PWNhc2VzXzA3ZGEpLCBjb2xvcj0nI2JhMDAwMCcpICsKICBsYWJzKCB0aXRsZSA9ICJTY290bGFuZCdzIDcgZGF5cyByb2xsaW5nIGF2ZXJhZ2UgQ09WSUQtMTkgY2FzZXMiLAogICAgICAgIHN1YnRpdGxlID0gIkJldHdlZW4gTWFyY2ggMjAyMCBhbmQgTWFyY2ggMjAyMSIsIAogICAgICAgIGNhcHRpb249IlNvdXJjZTpodHRwczovL2dpdGh1Yi5jb20vRGF0YVNjaWVuY2VTY290bGFuZC9DT1ZJRC0xOS1NYW5hZ2VtZW50LUluZm9ybWF0aW9uICIsCiAgICAgICAgeSA9ICJDYXNlcyIsIAogICAgICAgIHggPSAiTW9udGgiKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gJzEgbW9udGgnLCBkYXRlX2xhYmVscyA9ICclbSAleScpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIGhyYnJ0aGVtZXM6OnRoZW1lX2lwc3VtKCkKYGBgCgojIyBNYXBwaW5nCgpXZSBjYW4gYWxzbyBjcmVhdGUgYSBtYXAgc2hvd2luZyBsYXRlc3QgY2FzZXMgYXQgTkhTIEhlYWx0aCBCb2FyZCBsZXZlbC4KQnV0IGZpcnN0IHdlIG5lZWQgdG8gZ2V0IHRoZSBkYXRhIGZvciBlYWNoIGdlb2dyYXBoIHVuaXQuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0KIyBsb2FkIGRhdGEKY2FzZXNfYnlfaGIgPSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0RhdGFTY2llbmNlU2NvdGxhbmQvQ09WSUQtMTktTWFuYWdlbWVudC1JbmZvcm1hdGlvbi9tYXN0ZXIvQ09WSUQxOSUyMC0lMjBEYWlseSUyME1hbmFnZW1lbnQlMjBJbmZvcm1hdGlvbiUyMC0lMjBTY290dGlzaCUyMEhlYWx0aCUyMEJvYXJkcyUyMC0lMjBDdW11bGF0aXZlJTIwY2FzZXMuY3N2IikKCiMgcGl2b3QgZGF0YSB0byBsb25nIGZvcm1hdCBuZCBzZWxlY3QgbGF0ZXN0IGRhdGUKbGFzdF9jYXNlc19ieV9oYl9sb25nID0gY2FzZXNfYnlfaGIgJT4lCnBpdm90X2xvbmdlcighRGF0ZSxuYW1lc190byA9ICJoYl9uYW1lIiwgdmFsdWVzX3RvID0gImNhc2VzIikgICU+JQptdXRhdGUoY2FzZXMgPSBhcy5udW1lcmljKGNhc2VzKSkgJT4lIApmaWx0ZXIoRGF0ZSA9PSBtYXgoRGF0ZSkpCgojIGxvYWQgbWlkIDIwMTkgcG9wdWxhdGlvbiBlc3RpbWF0aWUKcG9wX2hiID0gcmVhZF9jc3YoImRhdGEvaGJfcG9wXzIwMTkuY3N2IikKCiMgZmlyc3Qgam9pbjogY2FzZXMgd2l0aCBwb3B1bGF0aW9uLCBhbHNvIGNhbGN1bGF0ZSByYXRlIHBlciAxMDBrCmNhc2VzX2J5X2hiX3BvcCA9IGxlZnRfam9pbihsYXN0X2Nhc2VzX2J5X2hiX2xvbmcsIHBvcF9oYikgJT4lIAogIG11dGF0ZSgKICAgIHJhdGVfMTAwayA9IGNhc2VzIC8gaGJfcG9wICogMTAwMDAwCiAgKQoKIyBsb2FkIEhlYWx0aCBCb2FyZCBnZW9ncmFwaGllcwpoZWFsdGhfYm9hcmRzID0gc3RfcmVhZCgiZGF0YS9TR19OSFNfSGVhbHRoQm9hcmRzXzIwMTkuc2hwIiwgcXVpZXQgPSBUUlVFKQoKIyBzZWNvbmQgam9pbjogaGVhbHRoIGJvYXJkIGJvdW5kYXJpZXMgCmNhc2VzX2hiX3JhdGUxMDBrID0gbGVmdF9qb2luKGhlYWx0aF9ib2FyZHMsY2FzZXNfYnlfaGJfcG9wLCBieSA9IGMoIkhCQ29kZSIgPSAiaGJfY29kZSIpKQoKCmBgYAoKRmluYWxseSwgd2UgYXJlIHJlYWR5IHRvIHByb2R1Y2UgdGhlIG1hcCB1c2luZyAqKnRtYXAqKiBwYWNrYWdlLiAKTm90ZSB0aGF0IHdlIGFyZSBzYXZpbmcgdGhlIG91dHB1dCB0byBwbmcgZmlsZS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQoKbWFwID0gdG1fc2hhcGUoY2FzZXNfaGJfcmF0ZTEwMGspICsKICB0bV9wb2x5Z29ucyhjb2wgPSAicmF0ZV8xMDBrIiwKICAgICAgICAgICAgICB0aXRsZSA9ICJEYXRhIGZyb20gMTd0aCBNYXJjaCAyMDIxIFxuTnVtYmVyIG9mIGNvbmZpcm1lZCBjYXNlcyBwZXIgMTAwLDAwMCBwZW9wbGUiLAogICAgICAgICAgICAgIGx3ZCA9IDEsCiAgICAgICAgICAgICAgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIsCiAgICAgICAgICAgICAgc3R5bGUgPSAiZXF1YWwiLAogICAgICAgICAgICAgIG4gPSA1LAogICAgICAgICAgICAgIHBhbGV0dGU9ImNpdmlkaXMiICMgY2hvb3NlIGJldHdlZW4gcmVkcyBvciBjaXZpZGlzCiAgICAgICAgICAgICAjIGxlZ2VuZC5mb3JtYXQ9bGlzdChmdW49ZnVuY3Rpb24oeCkgcGFzdGUwKGZvcm1hdEMoeCwgZGlnaXRzPTAsIGZvcm1hdD0iZiIpKSkKICAgICAgICAgICAgICApICsKICB0bV9sYXlvdXQoCiAgICAgICAgICAgIyBmb250ZmFtaWx5ID0gImFyaWFsIiwKICAgICAgICAgICAgdGl0bGUgPSAiVG90YWwgQ09WSUQgLSAxOSBjYXNlcyBcbnBlciAxMDAsMDAwIHBlb3BsZSwgYnkgTkhTIEhlYWx0aCBCb2FyZCIsCiAgICAgICAgICAgIHRpdGxlLnNpemUgPSAwLjksCiAgICAgICAgICAgIGxlZ2VuZC50aXRsZS5zaXplID0gMSwKICAgICAgICAgICAgYXR0ci5vdXRzaWRlPSBUUlVFLAogICAgICAgICAgICBhdHRyLm91dHNpZGUucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgYXR0ci5vdXRzaWRlLnNpemUgPSAuMSwKICAgICAgICAgICAgYXR0ci5qdXN0ID0gYygibGVmdCIsICJ0b3AiKSwKICAgICAgICAgICAgaW5uZXIubWFyZ2lucyA9IDAuMDIsCiAgICAgICAgICAgIG91dGVyLm1hcmdpbnMgPSBjKDAsMC4wMSwwLjAxLDAuMDEpLAogICAgICAgICAgICBhc3AgPSA0LzYsCiAgICAgICAgICAgIGZyYW1lID0gRiwKICAgICAgICAgICAgZGVzaWduLm1vZGUgPSBGCiAgICAgICAgICAgKSArCiAgdG1fY3JlZGl0cygiQ2FzZXM6IGh0dHBzOi8vd3d3Lmdvdi5zY290L2Nvcm9uYXZpcnVzLWNvdmlkLTE5XG5Qb3B1bGF0aW9uIGRhdGE6IE5SUyBQb3B1bGF0aW9uIEVzdGltYXRlcywgbWlkLTIwMThcbkJvdW5kYXJpZXM6ICBTcGF0aWFsRGF0YS5nb3Yuc2NvdCIsCiAgICAgICAgICAgICBjb2wgPSAiZ3JleTUwIiwKICAgICAgICAgICAgIHBvc2l0aW9uPWMoImxlZnQiLCJ0b3AiKSwgCiAgICAgICAgICAgICBhbGlnbiA9ICJsZWZ0IiwKICAgICAgICAgICAgIHNpemUgPSAwLjMyNSkKIyBzYXZlIGdyYXBoaWMgdG8gcG5nIGZpbGUKdG1hcF9zYXZlKAogIHRtID0gbWFwLAogIGZpbGVuYW1lID0gImhiX21hcF9jYXNlc19yYXRlLnBuZyIsCiAgaGVpZ2h0ID0gNiwKICB3aWR0aCA9IDQsCiAgdW5pdHMgPSAiaW4iLAogIGFzcCA9IDQvNgopCmBgYAoKT2ssIG1hcCBpcyByZWFkeSAhCgpgYGB7ciwgZWNobz1GQUxTRSwgb3V0LndpZHRoPSA2MDAsIGZpZy5jYXA9IiIsIGZpZy5hbGlnbj0nbGVmdCd9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJvdXRwdXQvaGJfbWFwX2Nhc2VzX3JhdGUucG5nIikKYGBgCgpJIGNhbid0IHJlY29tbWVuZCAqKnRtYXAqKiBwYWNrYWdlIGVub3VnaC4KCmBgYHtyLCBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IDYwMCwgZmlnLmNhcD0iIiwgZmlnLmFsaWduPSdsZWZ0J30Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIm91dHB1dC90bWFwX2FuaW0uZ2lmIikKYGBgCgojIyBTZXNzaW9uIGluZm8KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoKCiZuYnNwOwo8aHIgLz4KPHAgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPkEgd29yayBieSA8YSBocmVmPSJodHRwczovL2dpdGh1Yi5jb20vdG9wb2dyYXBob3MvIj5NaWNoYWwgTWljaGFsc2tpPC9hPjwvcD4KJm5ic3A7Cg==