Last active
May 17, 2024 09:30
-
-
Save alekrutkowski/e2ddbb99b6bc375ca51e9fa886830204 to your computer and use it in GitHub Desktop.
JAF2R_SpecGen – JAF specification generator for indicators based on Eurostat data for JAF2R (https://github.com/alekrutkowski/JAF2R)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
options(shiny.sanitize.errors = FALSE) | |
library(shiny) | |
library(data.table) | |
library(eurodata) | |
library(magrittr) | |
MetaBase <- | |
importMetabase() %>% | |
as.data.table() | |
DataList <- | |
importDataList() %>% | |
as.data.table() | |
DATASETS <- | |
data.table(Code=unique(MetaBase$Code)) %>% | |
merge(unique(DataList[,.(Code,`Dataset name`)]), | |
by='Code') %>% | |
{set_names(.$Code, | |
paste0('[',.$Code,'] ',.$`Dataset name`))} | |
# App definition ---------------------------------------------------------- | |
ui <- fluidPage( | |
titlePanel(HTML("<center>JAF specification generator for indicators based on Eurostat data</center>"), | |
'JAF spec gen'), | |
tags$head(HTML(r'{ | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link href="https://fonts.googleapis.com/css2?family=Reddit+Mono&display=swap" rel="stylesheet"> | |
<style> | |
* { | |
font-family: 'Reddit Mono', monospace; | |
} | |
pre { | |
font-family: 'Reddit Mono', monospace; | |
font-size: 16px; | |
} | |
/* Hide minor ticks */ | |
.irs-grid-pol { display: none; } | |
#TheCode { | |
position: fixed; | |
top: 120px; # Distance from the top of the viewport | |
right: 10px; # Distance from the right of the viewport | |
z-index: 99; # Ensures it floats above other content | |
background-color: white; # Optional: changes background color | |
border: 1px solid #ddd; # Optional: adds a border | |
padding: 5px; # Optional: adds space inside the box | |
box-shadow: 0px 0px 5px rgba(0,0,0,0.5); # Optional: adds shadow for better visibility | |
} | |
.button-copy { | |
z-index: 100; | |
cursor: pointer; | |
padding: 5px 10px; | |
margin: 5px; | |
background-color: #428bca; | |
color: white; | |
border: none; | |
border-radius: 5px; | |
position: fixed; // Position the button absolutely | |
top: 10px; // 5px from the top of the container | |
right: 10px; // 5px from the right of the container | |
} | |
</style> | |
<script> | |
document.addEventListener("DOMContentLoaded", function() { | |
var pres = document.querySelectorAll('pre'); | |
pres.forEach(function(pre) { | |
// Check if the pre element contains the string "Scroll up and correct" | |
if (!pre.textContent.includes("Scroll up and correct")) { | |
// Wrap pre in a container if not already wrapped | |
if (!pre.parentNode.classList.contains('pre-container')) { | |
var wrapper = document.createElement('div'); | |
wrapper.className = 'pre-container'; | |
pre.parentNode.insertBefore(wrapper, pre); | |
wrapper.appendChild(pre); | |
} else { | |
var wrapper = pre.parentNode; | |
} | |
// Create and add the button | |
var btn = document.createElement('button'); | |
btn.innerText = 'Copy to Clipboard'; | |
btn.className = 'button-copy'; | |
wrapper.appendChild(btn); | |
btn.onclick = function() { | |
var textArea = document.createElement('textarea'); | |
textArea.value = pre.textContent; | |
document.body.appendChild(textArea); | |
textArea.select(); | |
document.execCommand('copy'); | |
document.body.removeChild(textArea); | |
alert('Copied to clipboard!'); | |
}; | |
} | |
}); | |
}); | |
</script> | |
}')), | |
column(6, | |
textInput('JAF_KEY', | |
'Type the structurally correct JAF_KEY code of the new indicator e.g. PA1c.C1.55-74',width = "100%"), | |
textInput('IndicName', | |
'Type the descriptive full name of the indicator',width = "100%"), | |
textInput('IndicUnit', | |
'Type the name of the unit of indicator levels, e.g. %',width = "100%"), | |
textInput('IndicChangeUnit', | |
'Type the name of the unit of indicator changes, e.g. p.p.',width = "100%"), | |
sliderInput( | |
inputId = "NumOfDatasets", | |
label = "Number of datatsets: 1 for a simple definition, 2 or more for a multi-dataset formula-based definition", | |
min = 1L, max = 10L, value = 1L, ticks=TRUE, round=TRUE, step=1L, width = "100%" | |
), | |
conditionalPanel( | |
condition = "input.NumOfDatasets>1", | |
textInput('Formula', | |
HTML('Type the math formula using the appropriate letters (a, b, c, d, etc.) as variables,<br>e.g. (a + b)/(c*d)'),width = "100%")), | |
HTML('\u24D8 In the selection fields below, you can type to search, use <kbd>Delete</kbd> or <kbd>\u232B Backspace</kbd> keyboard keys to delete, and use <kbd>Esc</kbd> to hide the drop-down menu.'), | |
br(),br(), | |
uiOutput('DatasetSelections'), | |
br(), | |
uiOutput('DimSelections'), | |
sliderInput( | |
inputId = "CompendiumNum", | |
label = "Compendium Excel file number", | |
min = 1L, max = 10L, value = 1L, ticks=TRUE, round=TRUE, step=1L, width = "100%" | |
), | |
checkboxInput( | |
inputId = "HighIsGood", | |
label = strong("Tick if high values of the indicator are economically or socially good (untick if bad)"), | |
value = TRUE, width = "100%" | |
), | |
checkboxInput( | |
inputId = "IsCountryIndic", | |
label = strong("Tick if the indicator should be included in the Country Compendia"), | |
value = TRUE, width = "100%" | |
)), | |
column(6,br(),verbatimTextOutput('TheCode')) | |
) | |
verify_JAF_KEY <- function(txt) { | |
if (!grepl("^PA\\d+(\\.\\d)?[a-z]?(\\d+)?\\.[OSC]\\d+\\..*",txt)) | |
stop('Empty or structurally wrong JAF_KEY! Scroll up and correct.') else txt | |
} | |
sanitizeTxt <- function(txt) | |
gsub('"',r'{\"}',txt,fixed=TRUE) | |
verifyFormula <- function(txt) { | |
if (txt=="") stop('Empty formula! Scroll up and type the formula.') | |
tryCatch(parse(text=txt), | |
error=function(e) stop('Wrong formula!\n',e)) | |
txt | |
} | |
server <- function(input, output) { | |
Dims <- function(input,letter) | |
MetaBase[Code==input[[paste0("DatasetCode",letter)]], | |
Dim_name] %>% unique %>% | |
setdiff(c('geo','time')) | |
DimsDimValsPair <- function(input,letter,DimName) | |
paste0(DimName,'="',input[[paste0(letter,DimName)]],'"') | |
DimsDimValsPairs <- function(input,letter) | |
Dims(input,letter) %>% | |
sapply(\(DimName) DimsDimValsPair(input,letter,DimName)) %>% | |
paste(collapse=', ') | |
output$DatasetSelections <- renderUI( | |
lapply(letters[seq_len(input$NumOfDatasets)], | |
function(letter) | |
selectInput( | |
inputId = paste0("DatasetCode",letter), | |
label = paste0("Select Dataset '",letter,"'"), | |
selected='nama_10_gdp', | |
choices = DATASETS, | |
multiple=FALSE, | |
width='100%'))) | |
output$DimSelections <- renderUI( | |
lapply(letters[seq_len(input$NumOfDatasets)], | |
function(letter) list( | |
lapply(Dims(input,letter), | |
function(DimName) | |
selectInput( | |
inputId = paste0(letter,DimName), | |
label = paste0('\u25B6 ',letter,"\u2019s ",DimName,' ='), | |
choices = MetaBase[Code==input[[paste0("DatasetCode",letter)]] & | |
Dim_name==DimName, | |
.(Dim_val)] %>% | |
setnames('Dim_val',DimName) %>% | |
merge(importLabels(DimName), by=DimName) %>% | |
{set_names(.[[DimName]], | |
paste0('[',.[[DimName]],'] ',.[[paste0(DimName,'_labels')]]))}, | |
multiple=FALSE, | |
width='100%') | |
),br()))) | |
output$TheCode <- renderText( | |
if (input$NumOfDatasets==1) | |
paste0(' | |
inside(JAF_INDICATORS, indicator_named = "',verify_JAF_KEY(input$JAF_KEY), | |
'") = | |
specification( | |
name = "',sanitizeTxt(input$IndicName),'", | |
unit_of_level = "',sanitizeTxt(input$IndicUnit),'", | |
unit_of_change = "',sanitizeTxt(input$IndicChangeUnit),'", | |
indicator_groups = "COMPENDIUM ',input$CompendiumNum, | |
ifelse(as.logical(input$IsCountryIndic),' COUNTRY',""),'", | |
source = "Eurostat", | |
high_is_good = ',input$HighIsGood,', | |
value = fromEurostatDataset("',input$DatasetCodea,'", | |
with_filters(',DimsDimValsPairs(input,'a'),')) | |
) | |
') else | |
paste0(' | |
inside(JAF_INDICATORS, indicator_named = "',verify_JAF_KEY(input$JAF_KEY), | |
'") = | |
specification( | |
name = "',sanitizeTxt(input$IndicName),'", | |
unit_of_level = "',sanitizeTxt(input$IndicUnit),'", | |
unit_of_change = "',sanitizeTxt(input$IndicChangeUnit),'", | |
indicator_groups = "COMPENDIUM ',input$CompendiumNum, | |
ifelse(as.logical(input$IsCountryIndic),' COUNTRY',""),'", | |
source = "Eurostat", | |
high_is_good = ',input$HighIsGood,', | |
value = fromFormula(',verifyFormula(input$Formula),', | |
where = variables( | |
',letters[seq_len(input$NumOfDatasets)] %>% | |
sapply(function(letter) | |
paste0(' ',letter,' = fromEurostatDataset("',input[[paste0('DatasetCode',letter)]],'", | |
with_filters(',DimsDimValsPairs(input,letter),'))')) %>% | |
paste(collapse=',\n'),' | |
)) | |
) | |
') | |
) | |
} | |
# Run the application | |
shinyApp(ui = ui, server = server) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Running at https://shiny-r.dev/JAF2R_SpecGen/