Feedback should be send to goran.milovanovic_ext@wikimedia.de.

The campaign is run from 1. January 2018 to N January 2018.

knitr::opts_knit$set(root.dir = '/home/goransm/Work/___DataKolektiv/Projects/WikimediaDEU/_WMDE_Projects/_misc/NewEditors_Team/Thank_You_Campaign_2018/_dailyUpdate/')

0. Data Acquisiton

NOTE: the Data Acquisition code chunk is not fully reproducible from this Report. The data are collected by running the script ThankYou_2018_Production_SQL.R on stat1005.eqiad.wmnet, collecting the data as .tsv and .csv files, copying manually, and processing locally. Run from stat1005 stat box by executing Rscript /home/goransm/RScripts/WMDE_Campaigns/ThankYou2018/ThankYou_2018_Production_SQL.R.

### --- from stat1005: Thank You 2018 Banner Campaign
### --- production script: fetch the campaign data sets

### --- Campaign Details: 
# - estimated start: 1st January 2018 (+/- 2 days)
# - estimated duration: 6 to 10 days
# - Reporting should start on 2nd January 2018. 
# - The report must include any activity from the beginning of the campaign. 
# - The estimated start will be 1st January 2018.

# - Guided Tour names
# - (The training modules include 2 new guided tours):
# - ?tour=diskutieren
# - ?tour=seimutig

### --- Training Modules Schema: 
### --- https://meta.wikimedia.org/wiki/User:Stefan_Schneider_(WMDE)/dashboard_libraries/wikipedia-kurse.json
### --- the slug field is relevant for tracking

### --- Setup
library(dplyr)
library(tidyr)
library(stringr)
library(data.table)

### --- Directories
bannerImpressionsDir <- '/home/goransm/RScripts/WMDE_Campaigns/ThankYou2018/BannerImpressions'
bannerClicksDir <- '/home/goransm/RScripts/WMDE_Campaigns/ThankYou2018/BannerClicks'
dailyUpdateDir <- '/home/goransm/RScripts/WMDE_Campaigns/ThankYou2018/ThankYou2018_DailyUpdate' 

### --- Campaign time range
startDate <- '2018-01-02'
endDate <- '2018-01-08'

### ------------------------------------------------------------
### --- S1. Banner Impression Data
### ------------------------------------------------------------

# - campaign tag
# - Name: bt1, ?campaign=wmde_etc2017_bt1

### --- loop over date range, create query, fetch, and store
dateRange <- seq.POSIXt(from = as.POSIXlt(startDate, tz = "CET"),
                        to = as.POSIXlt(endDate, tz = "CET"),
                        by = 'hour')
dateRange <- dateRange[-length(dateRange)]
cetDateRange <- as.character(dateRange)
cetDateRange <- sapply(cetDateRange, function(x) {
  strsplit(x, split = " ", fixed = T)[[1]][1]
})
names(dateRange) <- cetDateRange
dateRange <- as.POSIXlt(dateRange, tz = "UTC")
# - up to the campaign end:
endCampaign <- as.POSIXlt(endDate, tz = "UTC")
w <- which(dateRange > endCampaign)
if (length(w) > 0) {
  dateRange <- dateRange[-w]
}
dR <- list()
for (i in 1:length(dateRange)) {
  dR[[i]] <- data.frame(
    cetName = names(dateRange[i]),
    utcYear = year(dateRange[i]),
    utcMonth = month(dateRange[i]),
    utcDay = mday(dateRange[i]),
    utcHour = hour(dateRange[i])
  )
}
dR <- rbindlist(dR)
dR <- dR %>%
  group_by(cetName, utcYear, utcMonth, utcDay) %>%
  summarise(utcHour = paste("hour=", utcHour, collapse = " OR ", sep = ""))

### ------------------------------------------------------------
### --- S2. Banner Landing Page Data
### ------------------------------------------------------------

# - landing page link including the appropriate campaign tag
# - Link:https://de.wikipedia.org/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia?campaign=wmde_etc2017_bt1

# - set bannerClicksDir
setwd(bannerClicksDir)

for (i in 1:length(unique(dR$cetName))) {
  
  wCetName <- which(dR$cetName %in% unique(dR$cetName)[i])
  
  for (j in 1:length(wCetName)) {
    
    # - construct HiveQL query:
    y <- dR$utcYear[wCetName[j]]
    m <- dR$utcMonth[wCetName[j]]
    d <- dR$utcDay[wCetName[j]]
    hour <- dR$utcHour[wCetName[j]]
    q <- paste(
      "USE wmf;
      SELECT uri_path, uri_query, referer FROM webrequest
      WHERE uri_host = 'de.wikipedia.org'
      AND uri_path = '/wiki/Wikipedia:Wikimedia_Deutschland/LerneWikipedia' 
      AND year = ", y,
      " AND month = ", m,
      " AND day = ", d,
      " AND (", hour, ");",
      sep = "")
    # - write hql
    write(q, 'thankyou2018_BannerClicks.hql')
    # - prepare output file:
    fileName <- "thankyou2018_BannerClicks_"
    fileName <- paste0(fileName,
                       as.character(unique(dR$cetName)[i]),
                       "_", j,
                       ".tsv")
    fileName <- paste0(bannerClicksDir, "/", fileName)
    # - execute hql script:
    hiveArgs <-
      'beeline -f'
    hiveInput <- paste0('thankyou2018_BannerClicks.hql > ',
                        fileName)
    # - command:
    hiveCommand <- paste(hiveArgs, hiveInput)
    system(command = hiveCommand, wait = TRUE)
    
  }
  
}

### --- Wrangle this dataset:

### --- Banner tags:
campaignBanner <- 'wmde_etc2017_bt1'

### --- Dataset:
# - count non-empty files:
c <- 0
lF <- list.files()
lF <- lF[grepl('.tsv', lF, fixed = T)]
lF <- lF[grepl('Clicks', lF, fixed = T)]
dataSet <- list()
for (i in 1:length(lF)) {
  dS <- readLines(lF[i], n = -1)
  timeStamp <- strsplit(lF[i], split = "_")[[1]][3]
  bannerClicks <- sum(grepl(campaignBanner, dS, fixed = T))
  dataSet[[i]] <- data.frame(timestamp = timeStamp,
                             bannerClicks = bannerClicks,
                             stringsAsFactors = F)
}
dataSet <- rbindlist(dataSet)

### --- store BannerClicksPageViews_Update.csv
setwd(dailyUpdateDir)
write.csv(dataSet, file = "thankyou2018_BannerClicksPageViews_Update.csv")

### --- SQL
startDate <- '2018-01-01'

### ------------------------------------------------------------
### --- S3. User Registration Data
### ------------------------------------------------------------

# - NOTE: UTC timestamps - adjustment for CE(S)T introduced. 
# - ServerSideAccountCreation_5487345
qCommand <- paste("mysql --defaults-file=/etc/mysql/conf.d/analytics-research-client.cnf -h analytics-slave.eqiad.wmnet -A -e \"select * from log.ServerSideAccountCreation_5487345 where ((webHost = 'de.wikipedia.org') and (timestamp >= ", gsub("-", "", startDate, fixed = T), "220000));\" > ",
            dailyUpdateDir, "/thankyou_2018_userRegistrations.tsv", sep = "")
system(command = qCommand, wait = TRUE)


### ------------------------------------------------------------
### --- S4. Guided Tours Data
### ------------------------------------------------------------

# - NOTE: UTC timestamps - adjustment for CE(S)T introduced. 
# - ServerSideAccountCreation_5487345
qCommand <- paste("mysql --defaults-file=/etc/mysql/conf.d/analytics-research-client.cnf -h analytics-slave.eqiad.wmnet -A -e \"select * from log.GuidedTourExited_8690566 where ((webHost = 'de.wikipedia.org') and (timestamp >= ", 
                  gsub("-", "", startDate, fixed = T), "220000));\" > ", 
                  dailyUpdateDir, "/thankyou_2018_guidedTours.tsv", sep = "")
system(command = qCommand, wait = TRUE)

### ------------------------------------------------------------
### --- S5. User Edit Data
### ------------------------------------------------------------

# - get user IDs from registered:
lF <- list.files()
lF <- lF[grepl('userRegistrations', lF, fixed = T)]
userReg <- read.table(lF, 
                      quote = "",
                      sep = "\t",
                      header = T,
                      check.names = F,
                      stringsAsFactors = F)
userReg <- userReg %>% 
  dplyr::select(event_userId, event_isSelfMade, event_campaign) %>% 
  filter(event_isSelfMade == 1) %>% 
  filter(event_campaign %in% "wmde_etc2017_bt1")
# - uids:
uid <- userReg$event_userId
# - sql query
sqlQuery <- paste('SELECT COUNT(*) as edits, rev_user FROM revision WHERE rev_user IN (',
                  paste(uid, collapse = ", "),
                  ') GROUP BY rev_user;',
                  sep = "")
mySqlCommand <- paste('mysql -h analytics-store.eqiad.wmnet dewiki -e ',
                      paste('"', sqlQuery, '" > ', sep = ""), 
                      dailyUpdateDir, '/thankyou_2018_userEdits.tsv', sep = "")
system(command = mySqlCommand, 
       wait = TRUE)

### ------------------------------------------------------------
### --- S6. Training Module Data
### ------------------------------------------------------------

1A. Campaign Banner Impressions

1A. 1 The data set

# - report: current update
print(paste0("Current update: ", as.character(Sys.time())))
[1] "Current update: 2018-01-17 15:02:42"
bannerImpressions <- read.csv('thankyouBannerImpressions.csv',
                              row.names = 1,
                              header = T,
                              stringsAsFactors = F) %>% 
  gather(key =  Banner,
         value = Views,
         B17WMDE_thankyou_authors:B17WMDE_thankyou_authors_mob_B) %>% 
  group_by(timeStamp, Banner) %>% 
  summarise(Views = sum(Views))
knitr::kable(bannerImpressions, format = "html") %>% 
  kable_styling(full_width = F, position = "left")
timeStamp Banner Views
2018-01-01 B17WMDE_thankyou_authors 257533
2018-01-01 B17WMDE_thankyou_authors_B 128423
2018-01-01 B17WMDE_thankyou_authors_mob_A 0
2018-01-01 B17WMDE_thankyou_authors_mob_B 0
2018-01-01 B17WMDE_thankyou_authors_pad_A 0
2018-01-01 B17WMDE_thankyou_authors_pad_B 0
2018-01-02 B17WMDE_thankyou_authors 741533
2018-01-02 B17WMDE_thankyou_authors_B 370737
2018-01-02 B17WMDE_thankyou_authors_mob_A 0
2018-01-02 B17WMDE_thankyou_authors_mob_B 3
2018-01-02 B17WMDE_thankyou_authors_pad_A 0
2018-01-02 B17WMDE_thankyou_authors_pad_B 0
2018-01-03 B17WMDE_thankyou_authors 824290
2018-01-03 B17WMDE_thankyou_authors_B 410722
2018-01-03 B17WMDE_thankyou_authors_mob_A 0
2018-01-03 B17WMDE_thankyou_authors_mob_B 0
2018-01-03 B17WMDE_thankyou_authors_pad_A 0
2018-01-03 B17WMDE_thankyou_authors_pad_B 0
2018-01-04 B17WMDE_thankyou_authors 818331
2018-01-04 B17WMDE_thankyou_authors_B 409420
2018-01-04 B17WMDE_thankyou_authors_mob_A 0
2018-01-04 B17WMDE_thankyou_authors_mob_B 0
2018-01-04 B17WMDE_thankyou_authors_pad_A 0
2018-01-04 B17WMDE_thankyou_authors_pad_B 0
2018-01-05 B17WMDE_thankyou_authors 750962
2018-01-05 B17WMDE_thankyou_authors_B 375077
2018-01-05 B17WMDE_thankyou_authors_mob_A 0
2018-01-05 B17WMDE_thankyou_authors_mob_B 2
2018-01-05 B17WMDE_thankyou_authors_pad_A 0
2018-01-05 B17WMDE_thankyou_authors_pad_B 0
2018-01-06 B17WMDE_thankyou_authors 586811
2018-01-06 B17WMDE_thankyou_authors_B 293237
2018-01-06 B17WMDE_thankyou_authors_mob_A 0
2018-01-06 B17WMDE_thankyou_authors_mob_B 0
2018-01-06 B17WMDE_thankyou_authors_pad_A 0
2018-01-06 B17WMDE_thankyou_authors_pad_B 0
2018-01-07 B17WMDE_thankyou_authors 706186
2018-01-07 B17WMDE_thankyou_authors_B 352536
2018-01-07 B17WMDE_thankyou_authors_mob_A 0
2018-01-07 B17WMDE_thankyou_authors_mob_B 0
2018-01-07 B17WMDE_thankyou_authors_pad_A 0
2018-01-07 B17WMDE_thankyou_authors_pad_B 0
2018-01-08 B17WMDE_thankyou_authors 920162
2018-01-08 B17WMDE_thankyou_authors_B 460469
2018-01-08 B17WMDE_thankyou_authors_mob_A 0
2018-01-08 B17WMDE_thankyou_authors_mob_B 0
2018-01-08 B17WMDE_thankyou_authors_pad_A 0
2018-01-08 B17WMDE_thankyou_authors_pad_B 0
2018-01-09 B17WMDE_thankyou_authors 988107
2018-01-09 B17WMDE_thankyou_authors_B 495139
2018-01-09 B17WMDE_thankyou_authors_mob_A 0
2018-01-09 B17WMDE_thankyou_authors_mob_B 0
2018-01-09 B17WMDE_thankyou_authors_pad_A 0
2018-01-09 B17WMDE_thankyou_authors_pad_B 0
2018-01-10 B17WMDE_thankyou_authors 990820
2018-01-10 B17WMDE_thankyou_authors_B 495235
2018-01-10 B17WMDE_thankyou_authors_mob_A 0
2018-01-10 B17WMDE_thankyou_authors_mob_B 0
2018-01-10 B17WMDE_thankyou_authors_pad_A 0
2018-01-10 B17WMDE_thankyou_authors_pad_B 0
2018-01-11 B17WMDE_thankyou_authors 986539
2018-01-11 B17WMDE_thankyou_authors_B 493215
2018-01-11 B17WMDE_thankyou_authors_mob_A 0
2018-01-11 B17WMDE_thankyou_authors_mob_B 1
2018-01-11 B17WMDE_thankyou_authors_pad_A 0
2018-01-11 B17WMDE_thankyou_authors_pad_B 0
2018-01-12 B17WMDE_thankyou_authors 852340
2018-01-12 B17WMDE_thankyou_authors_B 426957
2018-01-12 B17WMDE_thankyou_authors_mob_A 0
2018-01-12 B17WMDE_thankyou_authors_mob_B 0
2018-01-12 B17WMDE_thankyou_authors_pad_A 0
2018-01-12 B17WMDE_thankyou_authors_pad_B 0
2018-01-13 B17WMDE_thankyou_authors 628561
2018-01-13 B17WMDE_thankyou_authors_B 313426
2018-01-13 B17WMDE_thankyou_authors_mob_A 0
2018-01-13 B17WMDE_thankyou_authors_mob_B 0
2018-01-13 B17WMDE_thankyou_authors_pad_A 0
2018-01-13 B17WMDE_thankyou_authors_pad_B 0
2018-01-14 B17WMDE_thankyou_authors 779314
2018-01-14 B17WMDE_thankyou_authors_B 389017
2018-01-14 B17WMDE_thankyou_authors_mob_A 0
2018-01-14 B17WMDE_thankyou_authors_mob_B 0
2018-01-14 B17WMDE_thankyou_authors_pad_A 0
2018-01-14 B17WMDE_thankyou_authors_pad_B 0

1A. 2 Chart

ggplot(bannerImpressions, aes(x = timeStamp,
                    y = Views,
                    color = Banner,
                    label = Views)) +
  geom_line(aes(group = Banner), size = .25) +
  geom_point(size = 1.5) +
  geom_point(size = 1, color = "white") + 
  scale_y_continuous(labels = scales::comma) +
  ggtitle('Thank You 2018:\nOverview of BannerImpressions') +
  ylab("Impressions") +
  geom_text_repel(size = 3) +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90, size = 8, hjust = 1)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.minor.x = element_blank()) +
  theme(panel.background = element_blank())

1B. Campaign Pageviews (Banner Clicks)

1B. 1 The data set

pageviews <- read.csv('thankyou2018_BannerClicksPageViews_Update.csv',
                      row.names = 1,
                      header = T,
                      stringsAsFactors = F)
pageviews <- pageviews %>% 
  group_by(timestamp) %>% 
  summarise(Pageviews = sum(bannerClicks))
knitr::kable(pageviews, format = "html") %>% 
  kable_styling(full_width = F, position = "left")
timestamp Pageviews
2018-01-01 533
2018-01-02 1188
2018-01-03 1269
2018-01-04 1148
2018-01-05 1122
2018-01-06 1148
2018-01-07 1242
2018-01-08 1150
2018-01-09 1254
2018-01-10 1347
2018-01-11 1773
2018-01-12 1214
2018-01-13 1201
2018-01-14 1492

1B. 2 Chart

ggplot(pageviews, aes(x = timestamp,
                    y = Pageviews,
                    label = Pageviews)) +
  geom_bar(stat = "identity", 
           position = "dodge", 
           width = .15, 
           fill = "white", 
           color = "darkblue") +
  ggtitle('Thank You 2018:\nOverview of Landing Pageviews') +
  geom_label(size = 3) +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90, size = 8, hjust = 1)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.minor.x = element_blank()) +
  theme(panel.background = element_blank())

2. Campaign User Registrations

2. 1 The data set

userReg <- read.delim('thankyou_2018_userRegistrations.tsv',
                      sep = "\t",
                      quote = "",
                      header = T,
                      # row.names = 1,
                      stringsAsFactors = F)
userReg$timestamp <- as.character(userReg$timestamp)
userReg$timestamp <- sapply(userReg$timestamp, function(x) {
  y <- substr(x, 1, 4)
  m <- substr(x, 5, 6)
  d <- substr(x, 7, 8)
  part1Date <- paste(y, m, d, sep = "-")
  hr <- substr(x, 9, 10)
  mi <- substr(x, 11, 12)
  se <- substr(x, 13, 14)
  part2Date <- paste(hr, mi, se, sep = ":")
  paste(part1Date, part2Date, sep = " ")
})
userReg$timestamp <- as.POSIXct(userReg$timestamp, tz = "UTC")
timeDiff <- 
  as.POSIXct(as.character(Sys.time()), tz = "UTC") - as.POSIXct(as.character(Sys.time()), tz = "Europe/Berlin")
userReg$timestamp <- as.character(userReg$timestamp + timeDiff)
userReg$timestamp <- sapply(userReg$timestamp, function(x) {
  y <- substr(x, 1, 4)
  m <- substr(x, 6, 7)
  d <- substr(x, 9, 10) 
  paste(y, m, d, sep = "-")
})
userReg <- userReg %>%
  filter(event_campaign %in% 'wmde_etc2017_bt1') %>% 
  group_by(timestamp) %>% 
  summarise(Registrations = n())
knitr::kable(userReg, format = "html") %>% 
  kable_styling(full_width = F, position = "left")
timestamp Registrations
2018-01-01 1
2018-01-02 8
2018-01-03 15
2018-01-04 13
2018-01-05 5
2018-01-06 6
2018-01-07 12
2018-01-08 6
2018-01-09 9
2018-01-10 7
2018-01-11 15
2018-01-12 9
2018-01-13 3
2018-01-14 11
2018-01-15 1

2. 2 Chart

ggplot(userReg, aes(x = timestamp,
                    y = Registrations,
                    label = Registrations)) +
  geom_bar(stat = "identity", 
           position = "dodge", 
           width = .15, 
           fill = "white", 
           color = "darkblue") +
  ggtitle('Thank You 2018:\nOverview of User Registrations') +
  geom_label(size = 3) +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90, size = 8, hjust = 1)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.minor.x = element_blank()) +
  theme(panel.background = element_blank())

3. Campaign User Edits

3. 1 The data set

userEdits <- readLines('thankyou_2018_userEdits.tsv', n = -1)
if (length(userEdits) >= 1) {
  userEdits <- read.delim('thankyou_2018_userEdits.tsv',
                          sep = "\t",
                          header = T,
                          stringsAsFactors = F)
  # - report
  print(paste0(sum(userEdits$edits), " edits were made by the campaign registered users thus far."))
} else {
  print("There are currently no user edits from this campaign.")
}
[1] "135 edits were made by the campaign registered users thus far."

3. 2 Chart

4. Campaign Guided Tours

4. 1 The data set: Guided Tour Exit Steps

Campaign guided tours: diskutieren and seimutig.

tours <- c('diskutieren', 'seimutig')
# setwd('/home/goransm/Work/___DataKolektiv/Projects/WikimediaDEU/_WMDE_Projects/_misc/NewEditors_Team/Thank_You_Campaign_2018/_dailyUpdate/')
gTours <- read.delim('thankyou_2018_guidedTours.tsv',
                     sep = "\t",
                     header = T,
                     row.names = 1,
                     stringsAsFactors = F)
gTours$timestamp <- as.character(gTours$timestamp)
gTours$timestamp <- sapply(gTours$timestamp, function(x) {
  y <- substr(x, 1, 4)
  m <- substr(x, 5, 6)
  d <- substr(x, 7, 8)
  part1Date <- paste(y, m, d, sep = "-")
  hr <- substr(x, 9, 10)
  mi <- substr(x, 11, 12)
  se <- substr(x, 13, 14)
  part2Date <- paste(hr, mi, se, sep = ":")
  paste(part1Date, part2Date, sep = " ")
})
gTours$timestamp <- as.POSIXct(gTours$timestamp, tz = "UTC")
timeDiff <- 
  as.POSIXct(as.character(Sys.time()), tz = "UTC") - as.POSIXct(as.character(Sys.time()), tz = "Europe/Berlin")
gTours$timestamp <- as.character(gTours$timestamp + timeDiff)
gTours$timestamp <- sapply(gTours$timestamp, function(x) {
  y <- substr(x, 1, 4)
  m <- substr(x, 6, 7)
  d <- substr(x, 9, 10) 
  paste(y, m, d, sep = "-")
})
# - anonymize event_userId
eventUserId <- setdiff(unique(gTours$event_userId), 0)
an_userId <- character(length(eventUserId))
for (i in 1:length(an_userId)) {
  id <- round(runif(1, 1, 10e6))
  while (id %in% an_userId) {
    id <- round(runif(1, 1, 10e6))
  }
  an_userId[i] <- id
}
an_userId <- paste0("u_", an_userId)
gTours$an_userId <- sapply(gTours$event_userId, function(x) {
  w <- which(eventUserId %in% x)
  if (length(w) > 0) {
    an_userId[w]
  } else {
    x
  }
})
# - look up for the campaign guided tours
w <- which(gTours$event_tour %in% tours)
if (length(w) > 0) {
  gTours <- gTours[w, ]
  gTours <- gTours %>% 
    filter(event_userId != 0) %>% 
    select(timestamp, event_tour, event_step, an_userId)
  knitr::kable(gTours, format = "html") %>% 
  kable_styling(full_width = F, position = "left")
} else {
  print("There are currently no data on guided tours from this campaign.")
}
timestamp event_tour event_step an_userId
2018-01-03 diskutieren returnToTraining u_5167969
2018-01-03 seimutig returnToTraining u_5167969
2018-01-04 seimutig boldness u_6495795
2018-01-04 seimutig positionCursor u_6495795
2018-01-04 diskutieren returnToTraining u_6495795
2018-01-05 diskutieren saveReply u_6896279
2018-01-04 seimutig returnToTraining u_6495795
2018-01-04 seimutig editButtonCitation u_6495795
2018-01-06 seimutig editButton u_4706786
2018-01-06 seimutig boldness u_4706786
2018-01-06 diskutieren firstMessage u_4706786
2018-01-08 seimutig editBoldness u_2986526
2018-01-08 seimutig editSummary u_2986526
2018-01-08 diskutieren returnToTraining u_2986526
2018-01-10 seimutig editButton u_2672122
2018-01-10 seimutig editButton u_2672122
2018-01-10 seimutig editBoldness u_2672122
2018-01-10 seimutig editSummary u_2672122
2018-01-10 seimutig editButtonCitation u_2672122
2018-01-10 seimutig saveCitation u_2672122
2018-01-10 seimutig editButton u_2672122
2018-01-10 diskutieren returnToTraining u_2672122
2018-01-11 seimutig editBoldness u_771518
2018-01-11 seimutig boldness u_771518
2018-01-11 seimutig insertCitation u_771518
2018-01-11 seimutig anyEdit u_771518
2018-01-11 diskutieren returnToTraining u_771518
2018-01-11 seimutig anyEdit u_4425925

4. 2 Unique users in Guided Tours

Campaign guided tours: diskutieren and seimutig.

gTours %>% 
  select(event_tour) %>% 
  group_by(event_tour) %>% 
  summarise(`Unique users` = n()) %>% 
  knitr::kable(format = "html") %>% 
  kable_styling(full_width = F, position = "left")
event_tour Unique users
diskutieren 7
seimutig 21

5 Training Module

trainData <- read.csv('wmde_training_data.csv')
# - remove first two rows (test data)
trainData <- trainData[-c(1,2), ]
# - get user IDs from registered:
lF <- list.files()
lF <- lF[grepl('userRegistrations', lF, fixed = T)]
userData <- read.table(lF, 
                      quote = "",
                      sep = "\t",
                      header = T,
                      check.names = F,
                      stringsAsFactors = F)
gtData <- read.delim('thankyou_2018_guidedTours.tsv',
                     sep = "\t",
                     header = T,
                     row.names = 1,
                     stringsAsFactors = F)
edData <- read.delim('thankyou_2018_userEdits.tsv',
                     sep = "\t",
                     header = T,
                     stringsAsFactors = F)

5.1 Training Module Overview

How many registered users take the Training Module?

print(paste0("Number of uses who took the Training Module is ", 
             length(trainData$username),
             " , which is ",
             round(length(trainData$username)/sum(userReg$Registrations)*100, 2),
             "% of registered users."
               ))
[1] "Number of uses who took the Training Module is 67 , which is 55.37% of registered users."

What are the last completed slides per user Training Module?

slidesData <- trainData %>% 
  select(training_module, last_slide_completed) %>% 
  group_by(training_module, last_slide_completed) %>% 
  summarise(Count = n()) %>% 
  arrange(training_module, desc(Count))
knitr::kable(slidesData, format = "html") %>%
  kable_styling(full_width = F, position = "left")
training_module last_slide_completed Count
artikel-bewerten fertig 6
artikel-bewerten artikel-qualitat-bewerten 1
artikel-bewerten artikel-qualitat-quiz 1
editieren-basiswissen das-wars 7
editieren-basiswissen video-wiki-code 2
editieren-basiswissen visual-editor-buttons 2
editieren-basiswissen beobachtungsliste-video 1
editieren-basiswissen edit-vs-edit-quelle 1
editieren-basiswissen versuche-es 1
wikipedia-basiswissen relevanz-quiz-fortsetzung 35
wikipedia-basiswissen richtlinien-der-wikipedia 2
wikipedia-basiswissen urheberrecht-und-plagiate 2
wikipedia-basiswissen wikipedia-ist-frei 2
wikipedia-basiswissen nachweisbarkeit-quiz 1
wikipedia-basiswissen prinzipien-ruckblick 1
wikipedia-basiswissen relevanz 1
wikipedia-basiswissen sei-respektvoll 1
ggplot(slidesData, aes(x = last_slide_completed,
                       y = Count,
                       color = training_module,
                       label = Count)) +
  geom_line(aes(group = 1), size = .25) +
  geom_point(size = 1.5) +
  geom_point(size = 1, color = "white") + 
  scale_y_continuous(labels = scales::comma) +
  ggtitle('Thank You 2018:\nOverview of Training Modules') +
  ylab("No. Users") +
  geom_text_repel(size = 3) +
  facet_wrap(~training_module) +
  theme_minimal() + 
  theme(axis.text.x = element_text(angle = 90, size = 6.5, hjust = 1)) +
  theme(plot.title = element_text(size = 10)) +
  theme(legend.title = element_blank()) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.minor.x = element_blank()) +
  theme(panel.background = element_blank())

Do users start editing after training modules? Is there a difference in users with completed and not-completed or not at all having taken the modules?

userData <- userData %>% 
  filter(event_campaign %in% 'wmde_etc2017_bt1' & event_isSelfMade) %>% 
  select(event_userId, event_userName)
userData <- left_join(userData, trainData, by = c("event_userName" = "username"))
Column `event_userName`/`username` joining character vector and factor, coercing into character vector
userData$module_completion_date <- NA
userData <- left_join(userData, 
                      gtData[, c('event_userId', 'event_tour', 'event_step')],
                      by = c('event_userId'))
userData <- left_join(userData, edData,
                      by = c('event_userId' = 'rev_user'))

How many edits were made on behalf of those users who have started the Training Module vs. those who did not?

tAn <- userData %>% 
  select(training_module, edits) %>% 
  group_by(training_module) %>% 
  summarise(Users = n(), Edits = sum(edits, na.rm = T)) %>% 
  arrange(training_module, desc(Edits))
knitr::kable(tAn, format = "html") %>%
  kable_styling(full_width = F, position = "left")
training_module Users Edits
artikel-bewerten 7 36
editieren-basiswissen 6 36
wikipedia-basiswissen 20 38
NA 111 187
print(paste0("In total, the users who took the Training Module made ", 
             sum(tAn$Edits[1:3]),
             " edits, which is ",
             round(sum(tAn$Edits[1:3])/sum(tAn$Edits)*100, 2),
             "% of all edits."
               ))
[1] "In total, the users who took the Training Module made 110 edits, which is 37.04% of all edits."
print(paste0("On the other hand, the users who did not take the Training Module made ", 
             tAn$Edits[4],
             " edits, which is ",
             round(tAn$Edits[4]/sum(tAn$Edits)*100, 2),
             "% of all edits."
               ))
[1] "On the other hand, the users who did not take the Training Module made 187 edits, which is 62.96% of all edits."

Completion of the Training Module vs. number of edits

# - final slide for: wikipedia-basiswissen = relevanz-quiz-fortsetzung
# - final slide for: editieren-basiswissen = das-wars
# - final slide for: artikel-bewerten = fertig
finalSlide <- c('relevanz-quiz-fortsetzung', 'das', 'fertig')
names(finalSlide) <- c('wikipedia-basiswissen', 'editieren-basiswissen', 'relevanz-quiz-fortsetzung')
userData$completed_Training <- ifelse(userData$last_slide_completed %in% finalSlide, T, F)
completedTrainingEdits <- userData %>% 
  select(completed_Training, edits) %>% 
  group_by(completed_Training) %>% 
  summarise(Users = n(), Edits = sum(edits, na.rm = T))
knitr::kable(completedTrainingEdits, format = "html") %>%
  kable_styling(full_width = F, position = "left")
completed_Training Users Edits
FALSE 120 223
TRUE 24 74

We will express this result as the number of edits per use ratio for two groups: those who did complete the Training Module and those who did not. In other words, we divide the Edits column with the Users column, separately for TRUE and FALSE in the completed_Training column. This approach provides the following insight: in the group of users that did not complete the Training Module we have obtained 223/120 = 1.86 edits per user, while in the group of those who have completed the Training Module we find 74/24 = 3.08 edits per user. The ratio of these two ratios (i.e. 3.08 divided by 1.86) is 1.66 in favor of the group who has completed the Training Module, and we conclude that they have provided about 66% more edits per user compared to the group that has not completed the module. Let’s now take a look at the number of users who (a) made any edits at all, or(b) have reached their 10th edit, in these two groups:

userData$Edits <- (userData$edits > 0)
userData$Edits10 <- (userData$edits >= 10)
completedTrainingEdits <- userData %>% 
  select(completed_Training, Edits, Edits10) %>%
  group_by(completed_Training) %>% 
  summarise(Users = n(), Edits = sum(Edits, na.rm = T), Edits10 = sum(Edits10, na.rm = T))
knitr::kable(completedTrainingEdits, format = "html") %>%
  kable_styling(full_width = F, position = "left")
completed_Training Users Edits Edits10
FALSE 120 36 6
TRUE 24 13 0

We can see that 13 of users who have completed the Training Module have made at least one edit, 13/24 = .54, or about 54%. None of them, however, have reached their 10th edit. On the other hand, 36/120 = .3 or 30% of those who did not complete the Training Module have made at least one edit, while 6 of them have reached their 10th edit. This probably tells us that an intrinsic (aka “internal”) locus of motivation to contribute still works better than the (external) locus of motivation that we can help develop through our training modules - which does not present an unexpected result.

Detailed data for the editieren-basiswissen Training Module:

editModData <- userData %>% 
  select(training_module, last_slide_completed, edits) %>% 
  filter(training_module %in% 'editieren-basiswissen') %>% 
  group_by(last_slide_completed) %>% 
  summarise(edits = sum(edits, na.rm = T)) %>% 
  arrange(desc(edits))
knitr::kable(editModData, format = "html") %>%
  kable_styling(full_width = F, position = "left")
last_slide_completed edits
das-wars 36

There are six users only who took the editieren-basiswissen Training Module, and because we know that das-wars is the last slide in the editieren-basiswissen Training Module, we also know that only those who have completed this Training Module have made any edits!

LS0tCnRpdGxlOiAnVGhhbmsgWW91IENhbXBhaWduIDIwMTg6IFJlcG9ydCcKYXV0aG9yOiAiR29yYW4gUy4gTWlsb3Zhbm92aWMsIERhdGEgQW5hbHlzdCwgV01ERSIKZGF0ZTogIkphbnVhcnksIDIwMTgiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogc2ltcGxleAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDUKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQotLS0KCgoqKkZlZWRiYWNrKiogc2hvdWxkIGJlIHNlbmQgdG8gYGdvcmFuLm1pbG92YW5vdmljX2V4dEB3aWtpbWVkaWEuZGVgLiAKClRoZSBjYW1wYWlnbiBpcyBydW4gZnJvbSAxLiBKYW51YXJ5IDIwMTggdG8gTiBKYW51YXJ5IDIwMTguCgpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSAnL2hvbWUvZ29yYW5zbS9Xb3JrL19fX0RhdGFLb2xla3Rpdi9Qcm9qZWN0cy9XaWtpbWVkaWFERVUvX1dNREVfUHJvamVjdHMvX21pc2MvTmV3RWRpdG9yc19UZWFtL1RoYW5rX1lvdV9DYW1wYWlnbl8yMDE4L19kYWlseVVwZGF0ZS8nKQpgYGAKCmBgYHtyLCBlY2hvID0gRiwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGLCByZXN1bHRzID0gJ2hpZGUnfQojICFkaWFnbm9zdGljcyBvZmYKIyMjIC0tLSBTZXR1cAprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTUsIGZpZy5oZWlnaHQgPSA4KSAKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoRFQpCmxpYnJhcnkocmVzaGFwZTIpCmBgYAoKIyMgMC4gRGF0YSBBY3F1aXNpdG9uCgoqKk5PVEU6KiogdGhlIERhdGEgQWNxdWlzaXRpb24gY29kZSBjaHVuayBpcyBub3QgZnVsbHkgcmVwcm9kdWNpYmxlIGZyb20gdGhpcyBSZXBvcnQuIFRoZSBkYXRhIGFyZSBjb2xsZWN0ZWQgYnkgcnVubmluZyB0aGUgc2NyaXB0IGBUaGFua1lvdV8yMDE4X1Byb2R1Y3Rpb25fU1FMLlJgIG9uIHN0YXQxMDA1LmVxaWFkLndtbmV0LCBjb2xsZWN0aW5nIHRoZSBkYXRhIGFzIGAudHN2YCBhbmQgYC5jc3ZgIGZpbGVzLCBjb3B5aW5nIG1hbnVhbGx5LCBhbmQgcHJvY2Vzc2luZyBsb2NhbGx5LiBSdW4gZnJvbSBzdGF0MTAwNSBzdGF0IGJveCBieSBleGVjdXRpbmcgYFJzY3JpcHQgL2hvbWUvZ29yYW5zbS9SU2NyaXB0cy9XTURFX0NhbXBhaWducy9UaGFua1lvdTIwMTgvVGhhbmtZb3VfMjAxOF9Qcm9kdWN0aW9uX1NRTC5SYC4KCmBgYHtyLCBlY2hvID0gVCwgZXZhbCA9IEZ9CgojIyMgLS0tIGZyb20gc3RhdDEwMDU6IFRoYW5rIFlvdSAyMDE4IEJhbm5lciBDYW1wYWlnbgojIyMgLS0tIHByb2R1Y3Rpb24gc2NyaXB0OiBmZXRjaCB0aGUgY2FtcGFpZ24gZGF0YSBzZXRzCgojIyMgLS0tIENhbXBhaWduIERldGFpbHM6IAojIC0gZXN0aW1hdGVkIHN0YXJ0OiAxc3QgSmFudWFyeSAyMDE4ICgrLy0gMiBkYXlzKQojIC0gZXN0aW1hdGVkIGR1cmF0aW9uOiA2IHRvIDEwIGRheXMKIyAtIFJlcG9ydGluZyBzaG91bGQgc3RhcnQgb24gMm5kIEphbnVhcnkgMjAxOC4gCiMgLSBUaGUgcmVwb3J0IG11c3QgaW5jbHVkZSBhbnkgYWN0aXZpdHkgZnJvbSB0aGUgYmVnaW5uaW5nIG9mIHRoZSBjYW1wYWlnbi4gCiMgLSBUaGUgZXN0aW1hdGVkIHN0YXJ0IHdpbGwgYmUgMXN0IEphbnVhcnkgMjAxOC4KCiMgLSBHdWlkZWQgVG91ciBuYW1lcwojIC0gKFRoZSB0cmFpbmluZyBtb2R1bGVzIGluY2x1ZGUgMiBuZXcgZ3VpZGVkIHRvdXJzKToKIyAtID90b3VyPWRpc2t1dGllcmVuCiMgLSA/dG91cj1zZWltdXRpZwoKIyMjIC0tLSBUcmFpbmluZyBNb2R1bGVzIFNjaGVtYTogCiMjIyAtLS0gaHR0cHM6Ly9tZXRhLndpa2ltZWRpYS5vcmcvd2lraS9Vc2VyOlN0ZWZhbl9TY2huZWlkZXJfKFdNREUpL2Rhc2hib2FyZF9saWJyYXJpZXMvd2lraXBlZGlhLWt1cnNlLmpzb24KIyMjIC0tLSB0aGUgc2x1ZyBmaWVsZCBpcyByZWxldmFudCBmb3IgdHJhY2tpbmcKCiMjIyAtLS0gU2V0dXAKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRhdGEudGFibGUpCgojIyMgLS0tIERpcmVjdG9yaWVzCmJhbm5lckltcHJlc3Npb25zRGlyIDwtICcvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL1dNREVfQ2FtcGFpZ25zL1RoYW5rWW91MjAxOC9CYW5uZXJJbXByZXNzaW9ucycKYmFubmVyQ2xpY2tzRGlyIDwtICcvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL1dNREVfQ2FtcGFpZ25zL1RoYW5rWW91MjAxOC9CYW5uZXJDbGlja3MnCmRhaWx5VXBkYXRlRGlyIDwtICcvaG9tZS9nb3JhbnNtL1JTY3JpcHRzL1dNREVfQ2FtcGFpZ25zL1RoYW5rWW91MjAxOC9UaGFua1lvdTIwMThfRGFpbHlVcGRhdGUnIAoKIyMjIC0tLSBDYW1wYWlnbiB0aW1lIHJhbmdlCnN0YXJ0RGF0ZSA8LSAnMjAxOC0wMS0wMicKZW5kRGF0ZSA8LSAnMjAxOC0wMS0wOCcKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTMS4gQmFubmVyIEltcHJlc3Npb24gRGF0YQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gY2FtcGFpZ24gdGFnCiMgLSBOYW1lOiBidDEsID9jYW1wYWlnbj13bWRlX2V0YzIwMTdfYnQxCgojIyMgLS0tIGxvb3Agb3ZlciBkYXRlIHJhbmdlLCBjcmVhdGUgcXVlcnksIGZldGNoLCBhbmQgc3RvcmUKZGF0ZVJhbmdlIDwtIHNlcS5QT1NJWHQoZnJvbSA9IGFzLlBPU0lYbHQoc3RhcnREYXRlLCB0eiA9ICJDRVQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBhcy5QT1NJWGx0KGVuZERhdGUsIHR6ID0gIkNFVCIpLAogICAgICAgICAgICAgICAgICAgICAgICBieSA9ICdob3VyJykKZGF0ZVJhbmdlIDwtIGRhdGVSYW5nZVstbGVuZ3RoKGRhdGVSYW5nZSldCmNldERhdGVSYW5nZSA8LSBhcy5jaGFyYWN0ZXIoZGF0ZVJhbmdlKQpjZXREYXRlUmFuZ2UgPC0gc2FwcGx5KGNldERhdGVSYW5nZSwgZnVuY3Rpb24oeCkgewogIHN0cnNwbGl0KHgsIHNwbGl0ID0gIiAiLCBmaXhlZCA9IFQpW1sxXV1bMV0KfSkKbmFtZXMoZGF0ZVJhbmdlKSA8LSBjZXREYXRlUmFuZ2UKZGF0ZVJhbmdlIDwtIGFzLlBPU0lYbHQoZGF0ZVJhbmdlLCB0eiA9ICJVVEMiKQojIC0gdXAgdG8gdGhlIGNhbXBhaWduIGVuZDoKZW5kQ2FtcGFpZ24gPC0gYXMuUE9TSVhsdChlbmREYXRlLCB0eiA9ICJVVEMiKQp3IDwtIHdoaWNoKGRhdGVSYW5nZSA+IGVuZENhbXBhaWduKQppZiAobGVuZ3RoKHcpID4gMCkgewogIGRhdGVSYW5nZSA8LSBkYXRlUmFuZ2VbLXddCn0KZFIgPC0gbGlzdCgpCmZvciAoaSBpbiAxOmxlbmd0aChkYXRlUmFuZ2UpKSB7CiAgZFJbW2ldXSA8LSBkYXRhLmZyYW1lKAogICAgY2V0TmFtZSA9IG5hbWVzKGRhdGVSYW5nZVtpXSksCiAgICB1dGNZZWFyID0geWVhcihkYXRlUmFuZ2VbaV0pLAogICAgdXRjTW9udGggPSBtb250aChkYXRlUmFuZ2VbaV0pLAogICAgdXRjRGF5ID0gbWRheShkYXRlUmFuZ2VbaV0pLAogICAgdXRjSG91ciA9IGhvdXIoZGF0ZVJhbmdlW2ldKQogICkKfQpkUiA8LSByYmluZGxpc3QoZFIpCmRSIDwtIGRSICU+JQogIGdyb3VwX2J5KGNldE5hbWUsIHV0Y1llYXIsIHV0Y01vbnRoLCB1dGNEYXkpICU+JQogIHN1bW1hcmlzZSh1dGNIb3VyID0gcGFzdGUoImhvdXI9IiwgdXRjSG91ciwgY29sbGFwc2UgPSAiIE9SICIsIHNlcCA9ICIiKSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTMi4gQmFubmVyIExhbmRpbmcgUGFnZSBEYXRhCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgLSBsYW5kaW5nIHBhZ2UgbGluayBpbmNsdWRpbmcgdGhlIGFwcHJvcHJpYXRlIGNhbXBhaWduIHRhZwojIC0gTGluazpodHRwczovL2RlLndpa2lwZWRpYS5vcmcvd2lraS9XaWtpcGVkaWE6V2lraW1lZGlhX0RldXRzY2hsYW5kL0xlcm5lV2lraXBlZGlhP2NhbXBhaWduPXdtZGVfZXRjMjAxN19idDEKCiMgLSBzZXQgYmFubmVyQ2xpY2tzRGlyCnNldHdkKGJhbm5lckNsaWNrc0RpcikKCmZvciAoaSBpbiAxOmxlbmd0aCh1bmlxdWUoZFIkY2V0TmFtZSkpKSB7CiAgCiAgd0NldE5hbWUgPC0gd2hpY2goZFIkY2V0TmFtZSAlaW4lIHVuaXF1ZShkUiRjZXROYW1lKVtpXSkKICAKICBmb3IgKGogaW4gMTpsZW5ndGgod0NldE5hbWUpKSB7CiAgICAKICAgICMgLSBjb25zdHJ1Y3QgSGl2ZVFMIHF1ZXJ5OgogICAgeSA8LSBkUiR1dGNZZWFyW3dDZXROYW1lW2pdXQogICAgbSA8LSBkUiR1dGNNb250aFt3Q2V0TmFtZVtqXV0KICAgIGQgPC0gZFIkdXRjRGF5W3dDZXROYW1lW2pdXQogICAgaG91ciA8LSBkUiR1dGNIb3VyW3dDZXROYW1lW2pdXQogICAgcSA8LSBwYXN0ZSgKICAgICAgIlVTRSB3bWY7CiAgICAgIFNFTEVDVCB1cmlfcGF0aCwgdXJpX3F1ZXJ5LCByZWZlcmVyIEZST00gd2VicmVxdWVzdAogICAgICBXSEVSRSB1cmlfaG9zdCA9ICdkZS53aWtpcGVkaWEub3JnJwogICAgICBBTkQgdXJpX3BhdGggPSAnL3dpa2kvV2lraXBlZGlhOldpa2ltZWRpYV9EZXV0c2NobGFuZC9MZXJuZVdpa2lwZWRpYScgCiAgICAgIEFORCB5ZWFyID0gIiwgeSwKICAgICAgIiBBTkQgbW9udGggPSAiLCBtLAogICAgICAiIEFORCBkYXkgPSAiLCBkLAogICAgICAiIEFORCAoIiwgaG91ciwgIik7IiwKICAgICAgc2VwID0gIiIpCiAgICAjIC0gd3JpdGUgaHFsCiAgICB3cml0ZShxLCAndGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrcy5ocWwnKQogICAgIyAtIHByZXBhcmUgb3V0cHV0IGZpbGU6CiAgICBmaWxlTmFtZSA8LSAidGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrc18iCiAgICBmaWxlTmFtZSA8LSBwYXN0ZTAoZmlsZU5hbWUsCiAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKHVuaXF1ZShkUiRjZXROYW1lKVtpXSksCiAgICAgICAgICAgICAgICAgICAgICAgIl8iLCBqLAogICAgICAgICAgICAgICAgICAgICAgICIudHN2IikKICAgIGZpbGVOYW1lIDwtIHBhc3RlMChiYW5uZXJDbGlja3NEaXIsICIvIiwgZmlsZU5hbWUpCiAgICAjIC0gZXhlY3V0ZSBocWwgc2NyaXB0OgogICAgaGl2ZUFyZ3MgPC0KICAgICAgJ2JlZWxpbmUgLWYnCiAgICBoaXZlSW5wdXQgPC0gcGFzdGUwKCd0aGFua3lvdTIwMThfQmFubmVyQ2xpY2tzLmhxbCA+ICcsCiAgICAgICAgICAgICAgICAgICAgICAgIGZpbGVOYW1lKQogICAgIyAtIGNvbW1hbmQ6CiAgICBoaXZlQ29tbWFuZCA8LSBwYXN0ZShoaXZlQXJncywgaGl2ZUlucHV0KQogICAgc3lzdGVtKGNvbW1hbmQgPSBoaXZlQ29tbWFuZCwgd2FpdCA9IFRSVUUpCiAgICAKICB9CiAgCn0KCiMjIyAtLS0gV3JhbmdsZSB0aGlzIGRhdGFzZXQ6CgojIyMgLS0tIEJhbm5lciB0YWdzOgpjYW1wYWlnbkJhbm5lciA8LSAnd21kZV9ldGMyMDE3X2J0MScKCiMjIyAtLS0gRGF0YXNldDoKIyAtIGNvdW50IG5vbi1lbXB0eSBmaWxlczoKYyA8LSAwCmxGIDwtIGxpc3QuZmlsZXMoKQpsRiA8LSBsRltncmVwbCgnLnRzdicsIGxGLCBmaXhlZCA9IFQpXQpsRiA8LSBsRltncmVwbCgnQ2xpY2tzJywgbEYsIGZpeGVkID0gVCldCmRhdGFTZXQgPC0gbGlzdCgpCmZvciAoaSBpbiAxOmxlbmd0aChsRikpIHsKICBkUyA8LSByZWFkTGluZXMobEZbaV0sIG4gPSAtMSkKICB0aW1lU3RhbXAgPC0gc3Ryc3BsaXQobEZbaV0sIHNwbGl0ID0gIl8iKVtbMV1dWzNdCiAgYmFubmVyQ2xpY2tzIDwtIHN1bShncmVwbChjYW1wYWlnbkJhbm5lciwgZFMsIGZpeGVkID0gVCkpCiAgZGF0YVNldFtbaV1dIDwtIGRhdGEuZnJhbWUodGltZXN0YW1wID0gdGltZVN0YW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhbm5lckNsaWNrcyA9IGJhbm5lckNsaWNrcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKfQpkYXRhU2V0IDwtIHJiaW5kbGlzdChkYXRhU2V0KQoKIyMjIC0tLSBzdG9yZSBCYW5uZXJDbGlja3NQYWdlVmlld3NfVXBkYXRlLmNzdgpzZXR3ZChkYWlseVVwZGF0ZURpcikKd3JpdGUuY3N2KGRhdGFTZXQsIGZpbGUgPSAidGhhbmt5b3UyMDE4X0Jhbm5lckNsaWNrc1BhZ2VWaWV3c19VcGRhdGUuY3N2IikKCiMjIyAtLS0gU1FMCnN0YXJ0RGF0ZSA8LSAnMjAxOC0wMS0wMScKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTMy4gVXNlciBSZWdpc3RyYXRpb24gRGF0YQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gTk9URTogVVRDIHRpbWVzdGFtcHMgLSBhZGp1c3RtZW50IGZvciBDRShTKVQgaW50cm9kdWNlZC4gCiMgLSBTZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uXzU0ODczNDUKcUNvbW1hbmQgPC0gcGFzdGUoIm15c3FsIC0tZGVmYXVsdHMtZmlsZT0vZXRjL215c3FsL2NvbmYuZC9hbmFseXRpY3MtcmVzZWFyY2gtY2xpZW50LmNuZiAtaCBhbmFseXRpY3Mtc2xhdmUuZXFpYWQud21uZXQgLUEgLWUgXCJzZWxlY3QgKiBmcm9tIGxvZy5TZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uXzU0ODczNDUgd2hlcmUgKCh3ZWJIb3N0ID0gJ2RlLndpa2lwZWRpYS5vcmcnKSBhbmQgKHRpbWVzdGFtcCA+PSAiLCBnc3ViKCItIiwgIiIsIHN0YXJ0RGF0ZSwgZml4ZWQgPSBUKSwgIjIyMDAwMCkpO1wiID4gIiwKICAgICAgICAgICAgZGFpbHlVcGRhdGVEaXIsICIvdGhhbmt5b3VfMjAxOF91c2VyUmVnaXN0cmF0aW9ucy50c3YiLCBzZXAgPSAiIikKc3lzdGVtKGNvbW1hbmQgPSBxQ29tbWFuZCwgd2FpdCA9IFRSVUUpCgoKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIyMgLS0tIFM0LiBHdWlkZWQgVG91cnMgRGF0YQojIyMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIC0gTk9URTogVVRDIHRpbWVzdGFtcHMgLSBhZGp1c3RtZW50IGZvciBDRShTKVQgaW50cm9kdWNlZC4gCiMgLSBTZXJ2ZXJTaWRlQWNjb3VudENyZWF0aW9uXzU0ODczNDUKcUNvbW1hbmQgPC0gcGFzdGUoIm15c3FsIC0tZGVmYXVsdHMtZmlsZT0vZXRjL215c3FsL2NvbmYuZC9hbmFseXRpY3MtcmVzZWFyY2gtY2xpZW50LmNuZiAtaCBhbmFseXRpY3Mtc2xhdmUuZXFpYWQud21uZXQgLUEgLWUgXCJzZWxlY3QgKiBmcm9tIGxvZy5HdWlkZWRUb3VyRXhpdGVkXzg2OTA1NjYgd2hlcmUgKCh3ZWJIb3N0ID0gJ2RlLndpa2lwZWRpYS5vcmcnKSBhbmQgKHRpbWVzdGFtcCA+PSAiLCAKICAgICAgICAgICAgICAgICAgZ3N1YigiLSIsICIiLCBzdGFydERhdGUsIGZpeGVkID0gVCksICIyMjAwMDApKTtcIiA+ICIsIAogICAgICAgICAgICAgICAgICBkYWlseVVwZGF0ZURpciwgIi90aGFua3lvdV8yMDE4X2d1aWRlZFRvdXJzLnRzdiIsIHNlcCA9ICIiKQpzeXN0ZW0oY29tbWFuZCA9IHFDb21tYW5kLCB3YWl0ID0gVFJVRSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTNS4gVXNlciBFZGl0IERhdGEKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyAtIGdldCB1c2VyIElEcyBmcm9tIHJlZ2lzdGVyZWQ6CmxGIDwtIGxpc3QuZmlsZXMoKQpsRiA8LSBsRltncmVwbCgndXNlclJlZ2lzdHJhdGlvbnMnLCBsRiwgZml4ZWQgPSBUKV0KdXNlclJlZyA8LSByZWFkLnRhYmxlKGxGLCAKICAgICAgICAgICAgICAgICAgICAgIHF1b3RlID0gIiIsCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgIGNoZWNrLm5hbWVzID0gRiwKICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQp1c2VyUmVnIDwtIHVzZXJSZWcgJT4lIAogIGRwbHlyOjpzZWxlY3QoZXZlbnRfdXNlcklkLCBldmVudF9pc1NlbGZNYWRlLCBldmVudF9jYW1wYWlnbikgJT4lIAogIGZpbHRlcihldmVudF9pc1NlbGZNYWRlID09IDEpICU+JSAKICBmaWx0ZXIoZXZlbnRfY2FtcGFpZ24gJWluJSAid21kZV9ldGMyMDE3X2J0MSIpCiMgLSB1aWRzOgp1aWQgPC0gdXNlclJlZyRldmVudF91c2VySWQKIyAtIHNxbCBxdWVyeQpzcWxRdWVyeSA8LSBwYXN0ZSgnU0VMRUNUIENPVU5UKCopIGFzIGVkaXRzLCByZXZfdXNlciBGUk9NIHJldmlzaW9uIFdIRVJFIHJldl91c2VyIElOICgnLAogICAgICAgICAgICAgICAgICBwYXN0ZSh1aWQsIGNvbGxhcHNlID0gIiwgIiksCiAgICAgICAgICAgICAgICAgICcpIEdST1VQIEJZIHJldl91c2VyOycsCiAgICAgICAgICAgICAgICAgIHNlcCA9ICIiKQpteVNxbENvbW1hbmQgPC0gcGFzdGUoJ215c3FsIC1oIGFuYWx5dGljcy1zdG9yZS5lcWlhZC53bW5ldCBkZXdpa2kgLWUgJywKICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKCciJywgc3FsUXVlcnksICciID4gJywgc2VwID0gIiIpLCAKICAgICAgICAgICAgICAgICAgICAgIGRhaWx5VXBkYXRlRGlyLCAnL3RoYW5reW91XzIwMThfdXNlckVkaXRzLnRzdicsIHNlcCA9ICIiKQpzeXN0ZW0oY29tbWFuZCA9IG15U3FsQ29tbWFuZCwgCiAgICAgICB3YWl0ID0gVFJVRSkKCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMjIC0tLSBTNi4gVHJhaW5pbmcgTW9kdWxlIERhdGEKIyMjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKYGBgCgojIyAxQS4gQ2FtcGFpZ24gQmFubmVyIEltcHJlc3Npb25zCgojIyMgMUEuIDEgVGhlIGRhdGEgc2V0CmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KIyAtIHJlcG9ydDogY3VycmVudCB1cGRhdGUKcHJpbnQocGFzdGUwKCJDdXJyZW50IHVwZGF0ZTogIiwgYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpKSkKYmFubmVySW1wcmVzc2lvbnMgPC0gcmVhZC5jc3YoJ3RoYW5reW91QmFubmVySW1wcmVzc2lvbnMuY3N2JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpICU+JSAKICBnYXRoZXIoa2V5ID0gIEJhbm5lciwKICAgICAgICAgdmFsdWUgPSBWaWV3cywKICAgICAgICAgQjE3V01ERV90aGFua3lvdV9hdXRob3JzOkIxN1dNREVfdGhhbmt5b3VfYXV0aG9yc19tb2JfQikgJT4lIAogIGdyb3VwX2J5KHRpbWVTdGFtcCwgQmFubmVyKSAlPiUgCiAgc3VtbWFyaXNlKFZpZXdzID0gc3VtKFZpZXdzKSkKa25pdHI6OmthYmxlKGJhbm5lckltcHJlc3Npb25zLCBmb3JtYXQgPSAiaHRtbCIpICU+JSAKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikKYGBgCiMjIyAxQS4gMiBDaGFydAoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQpnZ3Bsb3QoYmFubmVySW1wcmVzc2lvbnMsIGFlcyh4ID0gdGltZVN0YW1wLAogICAgICAgICAgICAgICAgICAgIHkgPSBWaWV3cywKICAgICAgICAgICAgICAgICAgICBjb2xvciA9IEJhbm5lciwKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFZpZXdzKSkgKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBCYW5uZXIpLCBzaXplID0gLjI1KSArCiAgZ2VvbV9wb2ludChzaXplID0gMS41KSArCiAgZ2VvbV9wb2ludChzaXplID0gMSwgY29sb3IgPSAid2hpdGUiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArCiAgZ2d0aXRsZSgnVGhhbmsgWW91IDIwMTg6XG5PdmVydmlldyBvZiBCYW5uZXJJbXByZXNzaW9ucycpICsKICB5bGFiKCJJbXByZXNzaW9ucyIpICsKICBnZW9tX3RleHRfcmVwZWwoc2l6ZSA9IDMpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgMUIuIENhbXBhaWduIFBhZ2V2aWV3cyAoQmFubmVyIENsaWNrcykKCiMjIyAxQi4gMSBUaGUgZGF0YSBzZXQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KcGFnZXZpZXdzIDwtIHJlYWQuY3N2KCd0aGFua3lvdTIwMThfQmFubmVyQ2xpY2tzUGFnZVZpZXdzX1VwZGF0ZS5jc3YnLAogICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gMSwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKcGFnZXZpZXdzIDwtIHBhZ2V2aWV3cyAlPiUgCiAgZ3JvdXBfYnkodGltZXN0YW1wKSAlPiUgCiAgc3VtbWFyaXNlKFBhZ2V2aWV3cyA9IHN1bShiYW5uZXJDbGlja3MpKQprbml0cjo6a2FibGUocGFnZXZpZXdzLCBmb3JtYXQgPSAiaHRtbCIpICU+JSAKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikKYGBgCgojIyMgMUIuIDIgQ2hhcnQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZ2dwbG90KHBhZ2V2aWV3cywgYWVzKHggPSB0aW1lc3RhbXAsCiAgICAgICAgICAgICAgICAgICAgeSA9IFBhZ2V2aWV3cywKICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFBhZ2V2aWV3cykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiLCAKICAgICAgICAgICB3aWR0aCA9IC4xNSwgCiAgICAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsIAogICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdndGl0bGUoJ1RoYW5rIFlvdSAyMDE4OlxuT3ZlcnZpZXcgb2YgTGFuZGluZyBQYWdldmlld3MnKSArCiAgZ2VvbV9sYWJlbChzaXplID0gMykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA4LCBoanVzdCA9IDEpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKIyMgMi4gQ2FtcGFpZ24gVXNlciBSZWdpc3RyYXRpb25zCgojIyMgMi4gMSBUaGUgZGF0YSBzZXQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KdXNlclJlZyA8LSByZWFkLmRlbGltKCd0aGFua3lvdV8yMDE4X3VzZXJSZWdpc3RyYXRpb25zLnRzdicsCiAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgcXVvdGUgPSAiIiwKICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAjIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKdXNlclJlZyR0aW1lc3RhbXAgPC0gYXMuY2hhcmFjdGVyKHVzZXJSZWckdGltZXN0YW1wKQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBzYXBwbHkodXNlclJlZyR0aW1lc3RhbXAsIGZ1bmN0aW9uKHgpIHsKICB5IDwtIHN1YnN0cih4LCAxLCA0KQogIG0gPC0gc3Vic3RyKHgsIDUsIDYpCiAgZCA8LSBzdWJzdHIoeCwgNywgOCkKICBwYXJ0MURhdGUgPC0gcGFzdGUoeSwgbSwgZCwgc2VwID0gIi0iKQogIGhyIDwtIHN1YnN0cih4LCA5LCAxMCkKICBtaSA8LSBzdWJzdHIoeCwgMTEsIDEyKQogIHNlIDwtIHN1YnN0cih4LCAxMywgMTQpCiAgcGFydDJEYXRlIDwtIHBhc3RlKGhyLCBtaSwgc2UsIHNlcCA9ICI6IikKICBwYXN0ZShwYXJ0MURhdGUsIHBhcnQyRGF0ZSwgc2VwID0gIiAiKQp9KQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBhcy5QT1NJWGN0KHVzZXJSZWckdGltZXN0YW1wLCB0eiA9ICJVVEMiKQp0aW1lRGlmZiA8LSAKICBhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihTeXMudGltZSgpKSwgdHogPSAiVVRDIikgLSBhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihTeXMudGltZSgpKSwgdHogPSAiRXVyb3BlL0JlcmxpbiIpCnVzZXJSZWckdGltZXN0YW1wIDwtIGFzLmNoYXJhY3Rlcih1c2VyUmVnJHRpbWVzdGFtcCArIHRpbWVEaWZmKQp1c2VyUmVnJHRpbWVzdGFtcCA8LSBzYXBwbHkodXNlclJlZyR0aW1lc3RhbXAsIGZ1bmN0aW9uKHgpIHsKICB5IDwtIHN1YnN0cih4LCAxLCA0KQogIG0gPC0gc3Vic3RyKHgsIDYsIDcpCiAgZCA8LSBzdWJzdHIoeCwgOSwgMTApIAogIHBhc3RlKHksIG0sIGQsIHNlcCA9ICItIikKfSkKdXNlclJlZyA8LSB1c2VyUmVnICU+JQogIGZpbHRlcihldmVudF9jYW1wYWlnbiAlaW4lICd3bWRlX2V0YzIwMTdfYnQxJykgJT4lIAogIGdyb3VwX2J5KHRpbWVzdGFtcCkgJT4lIAogIHN1bW1hcmlzZShSZWdpc3RyYXRpb25zID0gbigpKQprbml0cjo6a2FibGUodXNlclJlZywgZm9ybWF0ID0gImh0bWwiKSAlPiUgCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKIyMjIDIuIDIgQ2hhcnQKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KZ2dwbG90KHVzZXJSZWcsIGFlcyh4ID0gdGltZXN0YW1wLAogICAgICAgICAgICAgICAgICAgIHkgPSBSZWdpc3RyYXRpb25zLAogICAgICAgICAgICAgICAgICAgIGxhYmVsID0gUmVnaXN0cmF0aW9ucykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgCiAgICAgICAgICAgcG9zaXRpb24gPSAiZG9kZ2UiLCAKICAgICAgICAgICB3aWR0aCA9IC4xNSwgCiAgICAgICAgICAgZmlsbCA9ICJ3aGl0ZSIsIAogICAgICAgICAgIGNvbG9yID0gImRhcmtibHVlIikgKwogIGdndGl0bGUoJ1RoYW5rIFlvdSAyMDE4OlxuT3ZlcnZpZXcgb2YgVXNlciBSZWdpc3RyYXRpb25zJykgKwogIGdlb21fbGFiZWwoc2l6ZSA9IDMpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBzaXplID0gOCwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgMy4gQ2FtcGFpZ24gVXNlciBFZGl0cwoKIyMjIDMuIDEgVGhlIGRhdGEgc2V0CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnVzZXJFZGl0cyA8LSByZWFkTGluZXMoJ3RoYW5reW91XzIwMThfdXNlckVkaXRzLnRzdicsIG4gPSAtMSkKaWYgKGxlbmd0aCh1c2VyRWRpdHMpID49IDEpIHsKICB1c2VyRWRpdHMgPC0gcmVhZC5kZWxpbSgndGhhbmt5b3VfMjAxOF91c2VyRWRpdHMudHN2JywKICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAiXHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgIyAtIHJlcG9ydAogIHByaW50KHBhc3RlMChzdW0odXNlckVkaXRzJGVkaXRzKSwgIiBlZGl0cyB3ZXJlIG1hZGUgYnkgdGhlIGNhbXBhaWduIHJlZ2lzdGVyZWQgdXNlcnMgdGh1cyBmYXIuIikpCn0gZWxzZSB7CiAgcHJpbnQoIlRoZXJlIGFyZSBjdXJyZW50bHkgbm8gdXNlciBlZGl0cyBmcm9tIHRoaXMgY2FtcGFpZ24uIikKfQpgYGAKCiMjIyAzLiAyIENoYXJ0CgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CmBgYAoKIyMgNC4gQ2FtcGFpZ24gR3VpZGVkIFRvdXJzCgojIyMgNC4gMSBUaGUgZGF0YSBzZXQ6IEd1aWRlZCBUb3VyIEV4aXQgU3RlcHMKCkNhbXBhaWduIGd1aWRlZCB0b3VyczogYGRpc2t1dGllcmVuYCBhbmQgYHNlaW11dGlnYC4KCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KdG91cnMgPC0gYygnZGlza3V0aWVyZW4nLCAnc2VpbXV0aWcnKQojIHNldHdkKCcvaG9tZS9nb3JhbnNtL1dvcmsvX19fRGF0YUtvbGVrdGl2L1Byb2plY3RzL1dpa2ltZWRpYURFVS9fV01ERV9Qcm9qZWN0cy9fbWlzYy9OZXdFZGl0b3JzX1RlYW0vVGhhbmtfWW91X0NhbXBhaWduXzIwMTgvX2RhaWx5VXBkYXRlLycpCmdUb3VycyA8LSByZWFkLmRlbGltKCd0aGFua3lvdV8yMDE4X2d1aWRlZFRvdXJzLnRzdicsCiAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpnVG91cnMkdGltZXN0YW1wIDwtIGFzLmNoYXJhY3RlcihnVG91cnMkdGltZXN0YW1wKQpnVG91cnMkdGltZXN0YW1wIDwtIHNhcHBseShnVG91cnMkdGltZXN0YW1wLCBmdW5jdGlvbih4KSB7CiAgeSA8LSBzdWJzdHIoeCwgMSwgNCkKICBtIDwtIHN1YnN0cih4LCA1LCA2KQogIGQgPC0gc3Vic3RyKHgsIDcsIDgpCiAgcGFydDFEYXRlIDwtIHBhc3RlKHksIG0sIGQsIHNlcCA9ICItIikKICBociA8LSBzdWJzdHIoeCwgOSwgMTApCiAgbWkgPC0gc3Vic3RyKHgsIDExLCAxMikKICBzZSA8LSBzdWJzdHIoeCwgMTMsIDE0KQogIHBhcnQyRGF0ZSA8LSBwYXN0ZShociwgbWksIHNlLCBzZXAgPSAiOiIpCiAgcGFzdGUocGFydDFEYXRlLCBwYXJ0MkRhdGUsIHNlcCA9ICIgIikKfSkKZ1RvdXJzJHRpbWVzdGFtcCA8LSBhcy5QT1NJWGN0KGdUb3VycyR0aW1lc3RhbXAsIHR6ID0gIlVUQyIpCnRpbWVEaWZmIDwtIAogIGFzLlBPU0lYY3QoYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpLCB0eiA9ICJVVEMiKSAtIGFzLlBPU0lYY3QoYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpLCB0eiA9ICJFdXJvcGUvQmVybGluIikKZ1RvdXJzJHRpbWVzdGFtcCA8LSBhcy5jaGFyYWN0ZXIoZ1RvdXJzJHRpbWVzdGFtcCArIHRpbWVEaWZmKQpnVG91cnMkdGltZXN0YW1wIDwtIHNhcHBseShnVG91cnMkdGltZXN0YW1wLCBmdW5jdGlvbih4KSB7CiAgeSA8LSBzdWJzdHIoeCwgMSwgNCkKICBtIDwtIHN1YnN0cih4LCA2LCA3KQogIGQgPC0gc3Vic3RyKHgsIDksIDEwKSAKICBwYXN0ZSh5LCBtLCBkLCBzZXAgPSAiLSIpCn0pCiMgLSBhbm9ueW1pemUgZXZlbnRfdXNlcklkCmV2ZW50VXNlcklkIDwtIHNldGRpZmYodW5pcXVlKGdUb3VycyRldmVudF91c2VySWQpLCAwKQphbl91c2VySWQgPC0gY2hhcmFjdGVyKGxlbmd0aChldmVudFVzZXJJZCkpCmZvciAoaSBpbiAxOmxlbmd0aChhbl91c2VySWQpKSB7CiAgaWQgPC0gcm91bmQocnVuaWYoMSwgMSwgMTBlNikpCiAgd2hpbGUgKGlkICVpbiUgYW5fdXNlcklkKSB7CiAgICBpZCA8LSByb3VuZChydW5pZigxLCAxLCAxMGU2KSkKICB9CiAgYW5fdXNlcklkW2ldIDwtIGlkCn0KYW5fdXNlcklkIDwtIHBhc3RlMCgidV8iLCBhbl91c2VySWQpCmdUb3VycyRhbl91c2VySWQgPC0gc2FwcGx5KGdUb3VycyRldmVudF91c2VySWQsIGZ1bmN0aW9uKHgpIHsKICB3IDwtIHdoaWNoKGV2ZW50VXNlcklkICVpbiUgeCkKICBpZiAobGVuZ3RoKHcpID4gMCkgewogICAgYW5fdXNlcklkW3ddCiAgfSBlbHNlIHsKICAgIHgKICB9Cn0pCiMgLSBsb29rIHVwIGZvciB0aGUgY2FtcGFpZ24gZ3VpZGVkIHRvdXJzCncgPC0gd2hpY2goZ1RvdXJzJGV2ZW50X3RvdXIgJWluJSB0b3VycykKaWYgKGxlbmd0aCh3KSA+IDApIHsKICBnVG91cnMgPC0gZ1RvdXJzW3csIF0KICBnVG91cnMgPC0gZ1RvdXJzICU+JSAKICAgIGZpbHRlcihldmVudF91c2VySWQgIT0gMCkgJT4lIAogICAgc2VsZWN0KHRpbWVzdGFtcCwgZXZlbnRfdG91ciwgZXZlbnRfc3RlcCwgYW5fdXNlcklkKQogIGtuaXRyOjprYWJsZShnVG91cnMsIGZvcm1hdCA9ICJodG1sIikgJT4lIAogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImxlZnQiKQp9IGVsc2UgewogIHByaW50KCJUaGVyZSBhcmUgY3VycmVudGx5IG5vIGRhdGEgb24gZ3VpZGVkIHRvdXJzIGZyb20gdGhpcyBjYW1wYWlnbi4iKQp9CmBgYAojIyMgNC4gMiBVbmlxdWUgdXNlcnMgaW4gR3VpZGVkIFRvdXJzCgpDYW1wYWlnbiBndWlkZWQgdG91cnM6IGBkaXNrdXRpZXJlbmAgYW5kIGBzZWltdXRpZ2AuCgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CmdUb3VycyAlPiUgCiAgc2VsZWN0KGV2ZW50X3RvdXIpICU+JSAKICBncm91cF9ieShldmVudF90b3VyKSAlPiUgCiAgc3VtbWFyaXNlKGBVbmlxdWUgdXNlcnNgID0gbigpKSAlPiUgCiAga25pdHI6OmthYmxlKGZvcm1hdCA9ICJodG1sIikgJT4lIAogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEYsIHBvc2l0aW9uID0gImxlZnQiKQpgYGAKCiMjIDUgVHJhaW5pbmcgTW9kdWxlCgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnRyYWluRGF0YSA8LSByZWFkLmNzdignd21kZV90cmFpbmluZ19kYXRhLmNzdicpCiMgLSByZW1vdmUgZmlyc3QgdHdvIHJvd3MgKHRlc3QgZGF0YSkKdHJhaW5EYXRhIDwtIHRyYWluRGF0YVstYygxLDIpLCBdCiMgLSBnZXQgdXNlciBJRHMgZnJvbSByZWdpc3RlcmVkOgpsRiA8LSBsaXN0LmZpbGVzKCkKbEYgPC0gbEZbZ3JlcGwoJ3VzZXJSZWdpc3RyYXRpb25zJywgbEYsIGZpeGVkID0gVCldCnVzZXJEYXRhIDwtIHJlYWQudGFibGUobEYsIAogICAgICAgICAgICAgICAgICAgICAgcXVvdGUgPSAiIiwKICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICAgICAgICAgY2hlY2submFtZXMgPSBGLAogICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmd0RGF0YSA8LSByZWFkLmRlbGltKCd0aGFua3lvdV8yMDE4X2d1aWRlZFRvdXJzLnRzdicsCiAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFQsCiAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEsCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQplZERhdGEgPC0gcmVhZC5kZWxpbSgndGhhbmt5b3VfMjAxOF91c2VyRWRpdHMudHN2JywKICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVCwKICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmBgYAoKCiMjIyA1LjEgVHJhaW5pbmcgTW9kdWxlIE92ZXJ2aWV3CgpIb3cgbWFueSByZWdpc3RlcmVkIHVzZXJzIHRha2UgdGhlIFRyYWluaW5nIE1vZHVsZT8KCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KcHJpbnQocGFzdGUwKCJOdW1iZXIgb2YgdXNlcyB3aG8gdG9vayB0aGUgVHJhaW5pbmcgTW9kdWxlIGlzICIsIAogICAgICAgICAgICAgbGVuZ3RoKHRyYWluRGF0YSR1c2VybmFtZSksCiAgICAgICAgICAgICAiICwgd2hpY2ggaXMgIiwKICAgICAgICAgICAgIHJvdW5kKGxlbmd0aCh0cmFpbkRhdGEkdXNlcm5hbWUpL3N1bSh1c2VyUmVnJFJlZ2lzdHJhdGlvbnMpKjEwMCwgMiksCiAgICAgICAgICAgICAiJSBvZiByZWdpc3RlcmVkIHVzZXJzLiIKICAgICAgICAgICAgICAgKSkKYGBgCgpXaGF0IGFyZSB0aGUgbGFzdCBjb21wbGV0ZWQgc2xpZGVzIHBlciB1c2VyIFRyYWluaW5nIE1vZHVsZT8KCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0Kc2xpZGVzRGF0YSA8LSB0cmFpbkRhdGEgJT4lIAogIHNlbGVjdCh0cmFpbmluZ19tb2R1bGUsIGxhc3Rfc2xpZGVfY29tcGxldGVkKSAlPiUgCiAgZ3JvdXBfYnkodHJhaW5pbmdfbW9kdWxlLCBsYXN0X3NsaWRlX2NvbXBsZXRlZCkgJT4lIAogIHN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lIAogIGFycmFuZ2UodHJhaW5pbmdfbW9kdWxlLCBkZXNjKENvdW50KSkKa25pdHI6OmthYmxlKHNsaWRlc0RhdGEsIGZvcm1hdCA9ICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQpnZ3Bsb3Qoc2xpZGVzRGF0YSwgYWVzKHggPSBsYXN0X3NsaWRlX2NvbXBsZXRlZCwKICAgICAgICAgICAgICAgICAgICAgICB5ID0gQ291bnQsCiAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB0cmFpbmluZ19tb2R1bGUsCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBDb3VudCkpICsKICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gMSksIHNpemUgPSAuMjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsKICBnZW9tX3BvaW50KHNpemUgPSAxLCBjb2xvciA9ICJ3aGl0ZSIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpICsKICBnZ3RpdGxlKCdUaGFuayBZb3UgMjAxODpcbk92ZXJ2aWV3IG9mIFRyYWluaW5nIE1vZHVsZXMnKSArCiAgeWxhYigiTm8uIFVzZXJzIikgKwogIGdlb21fdGV4dF9yZXBlbChzaXplID0gMykgKwogIGZhY2V0X3dyYXAofnRyYWluaW5nX21vZHVsZSkgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHNpemUgPSA2LjUsIGhqdXN0ID0gMSkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCkRvIHVzZXJzIHN0YXJ0IGVkaXRpbmcgYWZ0ZXIgdHJhaW5pbmcgbW9kdWxlcz8gSXMgdGhlcmUgYSBkaWZmZXJlbmNlIGluIHVzZXJzIHdpdGggY29tcGxldGVkIGFuZCBub3QtY29tcGxldGVkIG9yIG5vdCBhdCBhbGwgaGF2aW5nIHRha2VuIHRoZSBtb2R1bGVzPwoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQp1c2VyRGF0YSA8LSB1c2VyRGF0YSAlPiUgCiAgZmlsdGVyKGV2ZW50X2NhbXBhaWduICVpbiUgJ3dtZGVfZXRjMjAxN19idDEnICYgZXZlbnRfaXNTZWxmTWFkZSkgJT4lIAogIHNlbGVjdChldmVudF91c2VySWQsIGV2ZW50X3VzZXJOYW1lKQp1c2VyRGF0YSA8LSBsZWZ0X2pvaW4odXNlckRhdGEsIHRyYWluRGF0YSwgYnkgPSBjKCJldmVudF91c2VyTmFtZSIgPSAidXNlcm5hbWUiKSkKdXNlckRhdGEkbW9kdWxlX2NvbXBsZXRpb25fZGF0ZSA8LSBOQQp1c2VyRGF0YSA8LSBsZWZ0X2pvaW4odXNlckRhdGEsIAogICAgICAgICAgICAgICAgICAgICAgZ3REYXRhWywgYygnZXZlbnRfdXNlcklkJywgJ2V2ZW50X3RvdXInLCAnZXZlbnRfc3RlcCcpXSwKICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygnZXZlbnRfdXNlcklkJykpCnVzZXJEYXRhIDwtIGxlZnRfam9pbih1c2VyRGF0YSwgZWREYXRhLAogICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCdldmVudF91c2VySWQnID0gJ3Jldl91c2VyJykpCmBgYAoKSG93IG1hbnkgZWRpdHMgd2VyZSBtYWRlIG9uIGJlaGFsZiBvZiB0aG9zZSB1c2VycyB3aG8gaGF2ZSBzdGFydGVkIHRoZSBUcmFpbmluZyBNb2R1bGUgdnMuIHRob3NlIHdobyBkaWQgbm90PwoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQp0QW4gPC0gdXNlckRhdGEgJT4lIAogIHNlbGVjdCh0cmFpbmluZ19tb2R1bGUsIGVkaXRzKSAlPiUgCiAgZ3JvdXBfYnkodHJhaW5pbmdfbW9kdWxlKSAlPiUgCiAgc3VtbWFyaXNlKFVzZXJzID0gbigpLCBFZGl0cyA9IHN1bShlZGl0cywgbmEucm0gPSBUKSkgJT4lIAogIGFycmFuZ2UodHJhaW5pbmdfbW9kdWxlLCBkZXNjKEVkaXRzKSkKa25pdHI6OmthYmxlKHRBbiwgZm9ybWF0ID0gImh0bWwiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGLCBwb3NpdGlvbiA9ICJsZWZ0IikKYGBgCgpgYGB7ciBlY2hvID0gVCwgd2FybmluZyA9ICdoaWRlJywgbWVzc2FnZSA9IEZ9CnByaW50KHBhc3RlMCgiSW4gdG90YWwsIHRoZSB1c2VycyB3aG8gdG9vayB0aGUgVHJhaW5pbmcgTW9kdWxlIG1hZGUgIiwgCiAgICAgICAgICAgICBzdW0odEFuJEVkaXRzWzE6M10pLAogICAgICAgICAgICAgIiBlZGl0cywgd2hpY2ggaXMgIiwKICAgICAgICAgICAgIHJvdW5kKHN1bSh0QW4kRWRpdHNbMTozXSkvc3VtKHRBbiRFZGl0cykqMTAwLCAyKSwKICAgICAgICAgICAgICIlIG9mIGFsbCBlZGl0cy4iCiAgICAgICAgICAgICAgICkpCnByaW50KHBhc3RlMCgiT24gdGhlIG90aGVyIGhhbmQsIHRoZSB1c2VycyB3aG8gZGlkIG5vdCB0YWtlIHRoZSBUcmFpbmluZyBNb2R1bGUgbWFkZSAiLCAKICAgICAgICAgICAgIHRBbiRFZGl0c1s0XSwKICAgICAgICAgICAgICIgZWRpdHMsIHdoaWNoIGlzICIsCiAgICAgICAgICAgICByb3VuZCh0QW4kRWRpdHNbNF0vc3VtKHRBbiRFZGl0cykqMTAwLCAyKSwKICAgICAgICAgICAgICIlIG9mIGFsbCBlZGl0cy4iCiAgICAgICAgICAgICAgICkpCmBgYAoKQ29tcGxldGlvbiBvZiB0aGUgVHJhaW5pbmcgTW9kdWxlIHZzLiBudW1iZXIgb2YgZWRpdHMKCmBgYHtyIGVjaG8gPSBULCB3YXJuaW5nID0gJ2hpZGUnLCBtZXNzYWdlID0gRn0KIyAtIGZpbmFsIHNsaWRlIGZvcjogd2lraXBlZGlhLWJhc2lzd2lzc2VuID0gcmVsZXZhbnotcXVpei1mb3J0c2V0enVuZwojIC0gZmluYWwgc2xpZGUgZm9yOiBlZGl0aWVyZW4tYmFzaXN3aXNzZW4gPSBkYXMtd2FycwojIC0gZmluYWwgc2xpZGUgZm9yOiBhcnRpa2VsLWJld2VydGVuID0gZmVydGlnCmZpbmFsU2xpZGUgPC0gYygncmVsZXZhbnotcXVpei1mb3J0c2V0enVuZycsICdkYXMnLCAnZmVydGlnJykKbmFtZXMoZmluYWxTbGlkZSkgPC0gYygnd2lraXBlZGlhLWJhc2lzd2lzc2VuJywgJ2VkaXRpZXJlbi1iYXNpc3dpc3NlbicsICdyZWxldmFuei1xdWl6LWZvcnRzZXR6dW5nJykKdXNlckRhdGEkY29tcGxldGVkX1RyYWluaW5nIDwtIGlmZWxzZSh1c2VyRGF0YSRsYXN0X3NsaWRlX2NvbXBsZXRlZCAlaW4lIGZpbmFsU2xpZGUsIFQsIEYpCmNvbXBsZXRlZFRyYWluaW5nRWRpdHMgPC0gdXNlckRhdGEgJT4lIAogIHNlbGVjdChjb21wbGV0ZWRfVHJhaW5pbmcsIGVkaXRzKSAlPiUgCiAgZ3JvdXBfYnkoY29tcGxldGVkX1RyYWluaW5nKSAlPiUgCiAgc3VtbWFyaXNlKFVzZXJzID0gbigpLCBFZGl0cyA9IHN1bShlZGl0cywgbmEucm0gPSBUKSkKa25pdHI6OmthYmxlKGNvbXBsZXRlZFRyYWluaW5nRWRpdHMsIGZvcm1hdCA9ICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKV2Ugd2lsbCBleHByZXNzIHRoaXMgcmVzdWx0IGFzIHRoZSBudW1iZXIgb2YgZWRpdHMgcGVyIHVzZSByYXRpbyBmb3IgdHdvIGdyb3VwczogdGhvc2Ugd2hvIGRpZCBjb21wbGV0ZSB0aGUgVHJhaW5pbmcgTW9kdWxlIGFuZCB0aG9zZSB3aG8gZGlkIG5vdC4gSW4gb3RoZXIgd29yZHMsIHdlIGRpdmlkZSB0aGUgRWRpdHMgY29sdW1uIHdpdGggdGhlIFVzZXJzIGNvbHVtbiwgc2VwYXJhdGVseSBmb3IgYFRSVUVgIGFuZCBgRkFMU0VgIGluIHRoZSBgY29tcGxldGVkX1RyYWluaW5nYCBjb2x1bW4uIFRoaXMgYXBwcm9hY2ggcHJvdmlkZXMgdGhlIGZvbGxvd2luZyBpbnNpZ2h0OiBpbiB0aGUgZ3JvdXAgb2YgdXNlcnMgdGhhdCBkaWQgbm90IGNvbXBsZXRlIHRoZSBUcmFpbmluZyBNb2R1bGUgd2UgaGF2ZSBvYnRhaW5lZCBgMjIzLzEyMCA9IDEuODZgIGVkaXRzIHBlciB1c2VyLCB3aGlsZSBpbiB0aGUgZ3JvdXAgb2YgdGhvc2Ugd2hvIGhhdmUgY29tcGxldGVkIHRoZSBUcmFpbmluZyBNb2R1bGUgd2UgZmluZCBgNzQvMjRgID0gMy4wOCBlZGl0cyBwZXIgdXNlci4gVGhlIHJhdGlvIG9mIHRoZXNlIHR3byByYXRpb3MgKGkuZS4gYDMuMDggZGl2aWRlZCBieSAxLjg2YCkgaXMgMS42NiBpbiBmYXZvciBvZiB0aGUgZ3JvdXAgd2hvIGhhcyBjb21wbGV0ZWQgdGhlIFRyYWluaW5nIE1vZHVsZSwgYW5kIHdlIGNvbmNsdWRlIHRoYXQgdGhleSBoYXZlIHByb3ZpZGVkIGFib3V0IDY2JSBtb3JlIGVkaXRzIHBlciB1c2VyIGNvbXBhcmVkIHRvIHRoZSBncm91cCB0aGF0IGhhcyBub3QgY29tcGxldGVkIHRoZSBtb2R1bGUuIExldCdzIG5vdyB0YWtlIGEgbG9vayBhdCB0aGUgbnVtYmVyIG9mIHVzZXJzIHdobyAoYSkgbWFkZSBhbnkgZWRpdHMgYXQgYWxsLCBvcihiKSBoYXZlIHJlYWNoZWQgdGhlaXIgMTB0aCBlZGl0LCBpbiB0aGVzZSB0d28gZ3JvdXBzOgoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQp1c2VyRGF0YSRFZGl0cyA8LSAodXNlckRhdGEkZWRpdHMgPiAwKQp1c2VyRGF0YSRFZGl0czEwIDwtICh1c2VyRGF0YSRlZGl0cyA+PSAxMCkKY29tcGxldGVkVHJhaW5pbmdFZGl0cyA8LSB1c2VyRGF0YSAlPiUgCiAgc2VsZWN0KGNvbXBsZXRlZF9UcmFpbmluZywgRWRpdHMsIEVkaXRzMTApICU+JQogIGdyb3VwX2J5KGNvbXBsZXRlZF9UcmFpbmluZykgJT4lIAogIHN1bW1hcmlzZShVc2VycyA9IG4oKSwgRWRpdHMgPSBzdW0oRWRpdHMsIG5hLnJtID0gVCksIEVkaXRzMTAgPSBzdW0oRWRpdHMxMCwgbmEucm0gPSBUKSkKa25pdHI6OmthYmxlKGNvbXBsZXRlZFRyYWluaW5nRWRpdHMsIGZvcm1hdCA9ICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IDEzIG9mIHVzZXJzIHdobyBoYXZlIGNvbXBsZXRlZCB0aGUgVHJhaW5pbmcgTW9kdWxlIGhhdmUgbWFkZSBhdCBsZWFzdCBvbmUgZWRpdCwgYDEzLzI0ID0gLjU0YCwgb3IgYWJvdXQgNTQlLiBOb25lIG9mIHRoZW0sIGhvd2V2ZXIsIGhhdmUgcmVhY2hlZCB0aGVpciAxMHRoIGVkaXQuIE9uIHRoZSBvdGhlciBoYW5kLCBgMzYvMTIwID0gLjNgIG9yIDMwJSBvZiB0aG9zZSB3aG8gZGlkIG5vdCBjb21wbGV0ZSB0aGUgVHJhaW5pbmcgTW9kdWxlIGhhdmUgbWFkZSBhdCBsZWFzdCBvbmUgZWRpdCwgd2hpbGUgNiBvZiB0aGVtIGhhdmUgcmVhY2hlZCB0aGVpciAxMHRoIGVkaXQuIFRoaXMgcHJvYmFibHkgdGVsbHMgdXMgdGhhdCBhbiBpbnRyaW5zaWMgKGFrYSAiaW50ZXJuYWwiKSBsb2N1cyBvZiBtb3RpdmF0aW9uIHRvIGNvbnRyaWJ1dGUgc3RpbGwgd29ya3MgYmV0dGVyIHRoYW4gdGhlIChleHRlcm5hbCkgbG9jdXMgb2YgbW90aXZhdGlvbiB0aGF0IHdlIGNhbiBoZWxwIGRldmVsb3AgdGhyb3VnaCBvdXIgdHJhaW5pbmcgbW9kdWxlcyAtIHdoaWNoIGRvZXMgbm90IHByZXNlbnQgYW4gdW5leHBlY3RlZCByZXN1bHQuCgpEZXRhaWxlZCBkYXRhIGZvciB0aGUgYGVkaXRpZXJlbi1iYXNpc3dpc3NlbmAgVHJhaW5pbmcgTW9kdWxlOgoKYGBge3IgZWNobyA9IFQsIHdhcm5pbmcgPSAnaGlkZScsIG1lc3NhZ2UgPSBGfQplZGl0TW9kRGF0YSA8LSB1c2VyRGF0YSAlPiUgCiAgc2VsZWN0KHRyYWluaW5nX21vZHVsZSwgbGFzdF9zbGlkZV9jb21wbGV0ZWQsIGVkaXRzKSAlPiUgCiAgZmlsdGVyKHRyYWluaW5nX21vZHVsZSAlaW4lICdlZGl0aWVyZW4tYmFzaXN3aXNzZW4nKSAlPiUgCiAgZ3JvdXBfYnkobGFzdF9zbGlkZV9jb21wbGV0ZWQpICU+JSAKICBzdW1tYXJpc2UoZWRpdHMgPSBzdW0oZWRpdHMsIG5hLnJtID0gVCkpICU+JSAKICBhcnJhbmdlKGRlc2MoZWRpdHMpKQprbml0cjo6a2FibGUoZWRpdE1vZERhdGEsIGZvcm1hdCA9ICJodG1sIikgJT4lCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAibGVmdCIpCmBgYAoKVGhlcmUgYXJlIHNpeCB1c2VycyBvbmx5IHdobyB0b29rIHRoZSBgZWRpdGllcmVuLWJhc2lzd2lzc2VuYCBUcmFpbmluZyBNb2R1bGUsIGFuZCBiZWNhdXNlIHdlIGtub3cgdGhhdCBgZGFzLXdhcnNgIGlzIHRoZSBsYXN0IHNsaWRlIGluIHRoZSBgZWRpdGllcmVuLWJhc2lzd2lzc2VuYCBUcmFpbmluZyBNb2R1bGUsIHdlIGFsc28ga25vdyB0aGF0IG9ubHkgdGhvc2Ugd2hvIGhhdmUgY29tcGxldGVkIHRoaXMgVHJhaW5pbmcgTW9kdWxlIGhhdmUgbWFkZSBhbnkgZWRpdHMhCg==