Daily statistics per region in Ukraine
Last active
November 12, 2020 10:33
-
-
Save aNNiMON/bb8782d10ab199bb5a76fb936fb066b8 to your computer and use it in GitHub Desktop.
Daily statistics per region in Ukraine
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
covid.json | |
config.json | |
own-modules |
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
{ | |
"dataFile":"covid.json", | |
"sourceChannel":"-1001280273449", | |
"botToken":"1234567890:AABBCCDDEE" | |
} | |
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
use ["std", "http", "json", "files", "functional", "types", "regex", "collections"] | |
// https://www.convertcsv.com/json-to-csv.htm | |
include "own-modules/telegram-bot/TelegramBot.own" | |
include "FileOperator.own" | |
files = new FileOperator() | |
config = files.readJson("config.json") | |
covidData = files.readJson(config.dataFile) | |
covidDataObj = linkedHashMap() | |
for row : covidData { | |
covidDataObj[row.date] = row | |
} | |
bot = new TelegramBot(config.botToken) | |
rows = stream(bot.getUpdatesSync()) | |
.filter(def(u) = (u.message.text ?? false)) | |
.filter(def(u) = (u.message.forward_from_chat.id ?? 0) == config.sourceChannel) | |
.map(def(u) = u.message.text) | |
.filter(def(text) = text.contains("Дані з тимчасово")) | |
.map(::extractData) | |
.toArray() | |
// Add new results | |
for row : rows { | |
covidDataObj[row.date] = row | |
} | |
covidData = arrayValues(covidDataObj) | |
files.writeJson(config.dataFile, covidData) | |
def matcher(regexStr, text) = regex(regexStr, Pattern.U).matcher(text) | |
def extractData(text) { | |
row = linkedHashMap() | |
onelinetext = text.replace(toChar(10), " ").replace(toChar(13), "").lower | |
m = matcher("([\d ]+) нов.*?станом на (\d+) ([а-яіїє]+) 202", onelinetext) | |
if m.find() { | |
row.date = match (m.group(3)) { | |
case "січня": "01." | |
case "лютого": "02." | |
case "березня": "03." | |
case "квітня": "04." | |
case "травня": "05." | |
case "червня": "06." | |
case "липня": "07." | |
case "серпня": "08." | |
case "вересня": "09." | |
case "жовтня": "10." | |
case "листопада": "11." | |
case "грудня": "12." | |
} + sprintf("%02d", parseInt(m.group(2))) | |
row.daily = parseInt(m.group(1).replace(" ", "")) | |
} | |
m = matcher("захворіло – ([\d ]+) ос", text) | |
if m.find() { | |
row.all = parseInt(m.group(1).replace(" ", "")) | |
} | |
places = ["Вінницька", "Волинська", "Дніпропетровська", | |
"Донецька", "Житомирська", "Закарпатська", | |
"Запорізька", "Івано-Франківська", "Кіровоградська", | |
"м. Київ", "Київська", "Львівська", "Луганська", | |
"Миколаївська", "Одеська", "Полтавська", | |
"Рівненська", "Сумська", "Тернопільська", | |
"Харківська", "Херсонська", "Хмельницька", | |
"Чернівецька", "Черкаська", "Чернігівська"] | |
for place : places { | |
m = matcher(place + "(.*?)(область)?\s*[-–—]\s*(\d+ ?\d*)\s*вип", text) | |
row[place] = m.find() ? parseInt(replace(m.group(3), " ", "")) : 0 | |
} | |
return row | |
} |
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
class FileOperator { | |
def readJson(file) { | |
f = fopen(file, "r") | |
data = jsondecode(readText(f)) | |
fclose(f) | |
return data | |
} | |
def writeJson(file, data) { | |
f = fopen(file, "w") | |
writeText(f, jsonencode(data)) | |
flush(f) | |
fclose(f) | |
} | |
} |
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
<!DOCTYPE html> | |
<html lang="ru"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge" /> | |
<title>COVID-19 Украина</title> | |
<link href="styles.css" rel="stylesheet" /> | |
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script> | |
<script src="https://cdn.jsdelivr.net/npm/vue-apexcharts"></script> | |
</head> | |
<body> | |
<div id="app"> | |
<div id="chart" class="container"> | |
<apexchart type="line" :options="chartOptions" :series="series"></apexchart> | |
</div> | |
<div class="container"> | |
<div class="flex-row"> | |
<h3>Данные по дням</h3> | |
<label> | |
<input type="checkbox" v-model="showDiffs"> | |
Показать изменения | |
</label> | |
</div> | |
<table class="table"> | |
<tr> | |
<th v-for="column in dailyTableColumns" v-key="column">{{ column }}</th> | |
</tr> | |
<tr v-for="row in dailyRows" v-key="row[0]"> | |
<td v-for="(column, index) in row" v-key="index" v-bind:class="column.class">{{ column.value }}</td> | |
</tr> | |
</table> | |
</div> | |
</div> | |
<script> | |
// Find top maximal values and mark them for highlight | |
function highlightTop(arr) { | |
for (let i = 0; i < arr.length; i++) { | |
// Skip 2 values (`date`, `daily`) and find max values, then take top 3 | |
let top = arr[i].slice(2).map(it => it.value).sort((a, b) => b - a).splice(0, 3); | |
arr[i].forEach(row => { | |
for (let topIndex = 0; topIndex < top.length; topIndex++) { | |
if (row.value == top[topIndex]) { | |
row.class = 'highlight-' + (topIndex + 1); | |
} | |
} | |
}); | |
} | |
} | |
const input = <?= file_get_contents("covid.json") ?>; | |
// All columns except `date` and `all` | |
let columns = ['daily'].concat(Object.keys(input[0]) | |
.filter(it => !['all', 'daily', 'date'].includes(it)) | |
.sort((a, b) => a.localeCompare(b))); | |
// X-Axis: dates + mapping for columns | |
let dates = []; | |
let mapping = {}; | |
input.forEach(item => { | |
dates.push(item.date); | |
columns.forEach(column => { | |
mapping[column] = mapping[column] || []; | |
mapping[column].push(item[column]); | |
}); | |
}); | |
// Populate series for chart | |
let series = []; | |
for (let [key, value] of Object.entries(mapping)) { | |
series.push({name: key, data: value}); | |
} | |
// Populate data for daily table | |
let dailyTableData = {rows: [], diffs: []}; | |
let yesterdayData = (new Array(columns.length + 1)).fill(({value: 0})); | |
for (let i = 0; i < dates.length; i++) { | |
// `date`, column1, column2, ..., columnN | |
let row1 = []; | |
row1.push({value: dates[i], class: ''}); | |
columns.forEach( column => row1.push({value: mapping[column][i], class: ''}) ); | |
dailyTableData.rows.push(row1); | |
// Calculate diffs | |
let row2 = row1.map(it => ({value: it.value, class: ''})); | |
for (let j = 1; j < row1.length; j++) { | |
row2[j].value -= yesterdayData[j].value; | |
yesterdayData[j] = {value: row1[j].value}; | |
if (row2[j].value > 0) { | |
row2[j].value = '+' + row2[j].value; | |
} | |
if (i == 0) { | |
row2[j].class = 'ignored'; | |
} | |
} | |
row2[1].class = 'ignored'; | |
dailyTableData.diffs.push(row2); | |
} | |
highlightTop(dailyTableData.rows); | |
highlightTop(dailyTableData.diffs); | |
new Vue({ | |
el: '#app', | |
components: { | |
apexchart: VueApexCharts, | |
}, | |
data: { | |
showDiffs: false, | |
dailyTableColumns: ['Дата'].concat(columns), | |
dailyTableData, | |
series, | |
chartOptions: { | |
chart: { | |
width: '100%', | |
type: 'line', | |
zoom: { | |
enabled: true | |
}, | |
animations: { | |
enabled: false, | |
}, | |
}, | |
responsive: [{ | |
breakpoint: 6000, | |
options: { | |
chart: { height: 640 } | |
} | |
}], | |
legend: { | |
position: 'left' | |
}, | |
colors: [ | |
"#F3B415", | |
"#F27036", | |
"#663F59", | |
"#6A6E94", | |
"#4E88B4", | |
"#00A7C6", | |
"#18D8D8", | |
"#A9D794", | |
"#46AF78", | |
"#A93F55", | |
"#8C5E58", | |
"#2176FF", | |
"#33A1FD", | |
"#7A918D", | |
"#BAFF29" | |
], | |
stroke: { | |
curve: 'straight', | |
width: 2, | |
}, | |
title: { | |
text: 'COVID-19 по областям Украины', | |
align: 'left' | |
}, | |
xaxis: { | |
categories: dates, | |
}, | |
noData: { | |
text: 'Загрузка...' | |
} | |
}, | |
}, | |
computed: { | |
dailyRows() { | |
return this.dailyTableData[this.showDiffs ? 'diffs' : 'rows'] | |
}, | |
} | |
}); | |
</script> | |
</body> | |
</html> |
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
[ | |
{ | |
"id": "18f1894447dfa72000a83011511a817c", | |
"name": "telegram-bot" | |
} | |
] |
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
use ["std"] | |
if (ARGS.length >= 2 && ARGS[0] == "owm") { | |
use ["files", "json", "java"] | |
File = newClass("java.io.File") | |
runtime = newClass("java.lang.Runtime").getRuntime() | |
def loadModulesJson(path = "modules.json") { | |
f = fopen(path, "r") | |
modules = jsondecode(readText(f)) | |
fclose(f) | |
return modules | |
} | |
def exec(cmd, dir = ".") = runtime.exec(cmd, null, new File(dir)) | |
match (ARGS[1]) { | |
case "install": { | |
modulesDir = "own-modules" | |
if (!exists(modulesDir)) { | |
mkdir(modulesDir) | |
} | |
for module : loadModulesJson() { | |
print module.name | |
moduleDir = modulesDir + "/" + module.name | |
if (!exists(moduleDir)) { | |
mkdir(moduleDir) | |
cmd = "git clone https://gist.github.com/" + module.id + ".git " + module.name | |
exec(cmd, modulesDir) | |
println " installed" | |
} else { | |
exec("git pull origin master", moduleDir) | |
println " updated" | |
} | |
} | |
} | |
} | |
} | |
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
body{height:100vh;background:#f9f9f9}#chart,.chart-box{padding-top:20px;padding-left:10px;background:#fff;border:1px solid #ddd;box-shadow:0 22px 35px -16px rgba(0,0,0,.1)}.container{max-width:1020px;margin:5px auto;margin-bottom:2rem}.table{display:block;border-collapse:collapse;border-spacing:0;text-align:left;width:100%;overflow-x:auto;max-height:600px;padding-bottom:.75rem}.table th{font-size:.9em}.table tbody tr:nth-of-type(2n+1){background:#f5f7f8}.table tbody td:first-child{font-weight:700;min-width:50px;border-right:.05rem solid #e1e5eb}.table tbody tr:nth-of-type(2n),.table thead tr{background:#fff}.table td,.table th{border-bottom:.05rem solid #e1e5eb;padding:.3rem .3rem;min-width:45px}.table th{text-align: center;word-wrap:anywhere}.table td{text-align:right}select.flat-select{-moz-appearance:none;-webkit-appearance:none;appearance:none;background:#008ffb url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' width=\'60px\' height=\'60px\'><polyline fill=\'white\' points=\'46.139,15.518 25.166,36.49 4.193,15.519\'/></svg>") no-repeat scroll right 2px top 9px/16px 16px;border:0 none;border-radius:3px;color:#fff;font-family:arial,tahoma;font-size:16px;font-weight:700;outline:0 none;height:33px;padding:5px 20px 5px 10px;text-align:center;text-indent:.01px;text-overflow:"";text-shadow:0 -1px 0 rgba(0,0,0,.25);transition:all .3s ease 0s;width:auto;-webkit-transition:.3s ease all;-moz-transition:.3s ease all;-ms-transition:.3s ease all;-o-transition:.3s ease all;transition:.3s ease all}select.flat-select:focus,select.flat-select:hover{border:0;outline:0}.apexcharts-canvas{margin:0 auto}.ignored{color:#949494;font-style:italic}.highlight-1{background:#ff7979;font-weight:700}.highlight-2{background:#f9b2b2}.highlight-3{background:#f7e5e5}.flex-row{display:flex;align-items:center;justify-content:space-between} | |
th:not([scope=row]){background:#f5f7f8;position:-webkit-sticky;position:sticky;top:0;z-index:2;} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment