text-to-map
Version:
Text To Map usiluje o lepší, strojově zpracovatelné využití částí vyhlášek s výčtem ulic a dalších lokací. Jde o rozšiřitelnou sadu konceptů a nástrojů, které zajistí hladký převod výčtu ulic a jejich rozsahů v lidsky srozumitelném jazyce do strojově zpra
207 lines • 36.8 kB
JavaScript
import { createReadStream, createWriteStream, existsSync, rmSync } from "fs";
import fetch from "node-fetch";
import { join } from "path";
import parser from "stream-json";
import Pick from "stream-json/filters/Pick";
import StreamArray from "stream-json/streamers/StreamArray";
import { pipeline } from "stream/promises";
import { insertFounders } from "../db/founders";
import { insertSchools } from "../db/schools";
import { getKnexDb } from "../db/db";
import { MunicipalityType, SchoolType, SyncPart, } from "../db/types";
import { prepareOptions, } from "../utils/helpers";
import { runSyncPart } from "./common";
const downloadJsonld = async (options) => {
if (existsSync(getJsonldFilePath(options))) {
return;
}
console.log("Downloading a large JSON-LD file with school data...");
const response = await fetch(options.schoolsJsonldUrl);
if (response.status !== 200) {
throw new Error(`The JSON-LD file could not be downloaded. HTTP Code: ${response.status}`);
}
await pipeline(response.body, createWriteStream(getJsonldFilePath(options)));
console.log("Finished downloading.");
};
const SCHOOL_TYPE_KINDERGARTEN = "A00";
const SCHOOL_TYPE_ELEMENTARY = "B00";
const getCorrectFounderType = (founderType) => {
return founderType === "" ? "101" : founderType;
};
const getMunicipalityType = (founderType) => {
return founderType === "261"
? MunicipalityType.City
: founderType === "263"
? MunicipalityType.District
: MunicipalityType.Other;
};
const processSchoolRegisterJsonld = async (options) => {
const founders = new Map();
const schools = [];
const schoolsWithoutRuian = [];
const stream = createReadStream(getJsonldFilePath(options))
.pipe(parser())
.pipe(new Pick({ filter: "list" }))
.pipe(new StreamArray());
for await (const { value: entity } of stream) {
const redizo = entity.redIzo ?? "";
const ico = entity.ico ?? "";
const entityName = entity.uplnyNazev ?? "";
const currentSchools = [];
for (const school of entity.skolyAZarizeni ?? []) {
const type = school.druh === SCHOOL_TYPE_KINDERGARTEN
? SchoolType.Kindergarten
: school.druh === SCHOOL_TYPE_ELEMENTARY
? SchoolType.Elementary
: null;
if (type === null)
continue;
const capacityEntry = school.kapacity?.find((k) => k.mernaJednotka === "01") ??
school.kapacity?.[0];
const capacity = capacityEntry?.nejvyssiPovolenyPocet ?? 0;
const allPlaces = school.mistaVyuky ?? [];
const locationsWithRuian = allPlaces.filter((p) => p.adresa?.kodRUIAN != null);
// Prefer the main building (IDmista matches school IZO), fall back to first available
const mainPlace = locationsWithRuian.find((p) => p.IDmista === school.izo) ??
locationsWithRuian[0];
const locations = mainPlace
? [{ addressPointId: mainPlace.adresa.kodRUIAN }]
: [];
if (!mainPlace && allPlaces.length > 0) {
const addr = allPlaces[0].adresa ?? {};
const addressParts = [
addr.ulice,
addr.cisloDomovni?.toString(),
addr.obec,
].filter(Boolean);
schoolsWithoutRuian.push({ izo: school.izo, address: addressParts, type });
}
currentSchools.push({
izo: school.izo,
name: entityName || school.uplnyNazev || "",
redizo,
capacity,
type,
locations,
});
}
schools.push(...currentSchools);
for (const z of entity.zrizovatele ?? []) {
let founderIco;
let founderName;
let founderType;
if (z.druhOsoby === "PO") {
founderIco = z.Ico ?? "";
founderName = z.nazevOsoby ?? "";
founderType = getCorrectFounderType(z.pravniForma ?? "");
}
else {
founderIco = z.datumNarozeni ?? "";
founderName = z.nazevOsoby ?? "";
founderType = getCorrectFounderType("");
}
if (founderIco === "" || founderName === "") {
founderIco = ico;
founderName = entityName;
founderType = "224";
}
if (currentSchools.length > 0) {
const key = founderName + founderIco;
if (founders.has(key)) {
founders.get(key).schools.push(...currentSchools);
}
else {
founders.set(key, {
name: founderName,
ico: founderIco,
originalType: parseInt(founderType),
municipalityType: getMunicipalityType(founderType),
schools: [...currentSchools],
});
}
}
}
}
return { schools, founders, schoolsWithoutRuian };
};
const importDataToDb = async (options, saveFoundersToCsv = false, saveSchoolsWithoutRuianToCsv = false) => {
const { schools, founders, schoolsWithoutRuian } = await processSchoolRegisterJsonld(options);
if (saveFoundersToCsv) {
const csvFile = "founders.csv";
if (existsSync(csvFile)) {
rmSync(csvFile);
}
var csv = createWriteStream(csvFile, {
flags: "a",
});
csv.write("IČO;Zřizovatel;Právní forma;Počet škol;Školy\n");
founders.forEach((founder) => {
csv.write(`#${founder.ico};${founder.name};${founder.originalType};${founder.schools.length};${founder.schools.map((school) => school.name).join("---")}\n`);
});
csv.end();
}
if (saveSchoolsWithoutRuianToCsv) {
const csvFile = "schoolsWithoutRuian.csv";
if (existsSync(csvFile)) {
rmSync(csvFile);
}
var csv = createWriteStream(csvFile, {
flags: "a",
});
csv.write("IZO;Je mateřská;Je základní;adresa1;adresa2;adresa3\n");
schoolsWithoutRuian.forEach((schoolAddress) => {
csv.write(`#${schoolAddress.izo};${schoolAddress.type === SchoolType.Kindergarten ? "TRUE" : "FALSE"};${schoolAddress.type === SchoolType.Elementary ? "TRUE" : "FALSE"};${schoolAddress.address.join(";")}\n`);
});
csv.end();
}
const knex = getKnexDb();
const schoolsBefore = (await knex("school").count("izo as count").first()).count;
const foundersBefore = (await knex("founder").count("id as count").first()).count;
// Compute change stats before inserting (uses same logic as insertSchools)
const existingSnap = await knex("school")
.leftJoin("school_location", "school.izo", "school_location.school_izo")
.select("school.izo", "school.name", "school.capacity", "school_location.address_point_id");
const existingSnapMap = new Map(existingSnap.map((r) => [r.izo, r]));
const statsFieldUpdates = schools.filter((s) => {
const row = existingSnapMap.get(s.izo);
return row && (row.name !== s.name || row.capacity !== s.capacity);
}).length;
const statsLocationUpdates = schools.filter((s) => {
const row = existingSnapMap.get(s.izo);
const newRuians = s.locations.map((l) => l.addressPointId);
return row && row.address_point_id !== null && newRuians.length > 0 && !newRuians.includes(row.address_point_id);
}).length;
await insertSchools(schools);
await insertFounders(Array.from(founders.values()));
const schoolsAfter = (await knex("school").count("izo as count").first()).count;
const foundersAfter = (await knex("founder").count("id as count").first()).count;
const kindergartens = schools.filter((s) => s.type === SchoolType.Kindergarten).length;
const elementary = schools.filter((s) => s.type === SchoolType.Elementary).length;
console.log(`\n=== School sync statistics ===`);
console.log(`Schools parsed: ${schools.length} (${kindergartens} kindergartens, ${elementary} elementary)`);
console.log(`Founders parsed: ${founders.size}`);
console.log(`New schools inserted: ${schoolsAfter - schoolsBefore}`);
console.log(`New founders inserted: ${foundersAfter - foundersBefore}`);
console.log(`Field updates: ${statsFieldUpdates} (name/capacity changed)`);
console.log(`Location updates: ${statsLocationUpdates} (first location changed)`);
console.log(`Total schools in DB: ${schoolsAfter}`);
console.log(`Total founders in DB: ${foundersAfter}`);
};
const getJsonldFilePath = (options) => {
return join(options.tmpDir, options.schoolsJsonldFileName);
};
export const downloadAndImportSchools = async (options = {}, saveFoundersToCsv = false, saveSchoolsWithoutRuianToCsv = false) => {
await runSyncPart(SyncPart.Schools, [SyncPart.AddressPoints], async () => {
const runOptions = prepareOptions(options);
await downloadJsonld(runOptions);
await importDataToDb(runOptions, saveFoundersToCsv, saveSchoolsWithoutRuianToCsv);
deleteSchoolsJsonldFile(runOptions);
});
};
export const deleteSchoolsJsonldFile = (options = {}) => {
const runOptions = prepareOptions(options);
if (existsSync(getJsonldFilePath(runOptions))) {
rmSync(getJsonldFilePath(runOptions));
}
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Nob29scy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9vcGVuLWRhdGEtc3luYy9zY2hvb2xzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxpQkFBaUIsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQzdFLE9BQU8sS0FBSyxNQUFNLFlBQVksQ0FBQztBQUMvQixPQUFPLEVBQUUsSUFBSSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQzVCLE9BQU8sTUFBTSxNQUFNLGFBQWEsQ0FBQztBQUNqQyxPQUFPLElBQUksTUFBTSwwQkFBMEIsQ0FBQztBQUM1QyxPQUFPLFdBQVcsTUFBTSxtQ0FBbUMsQ0FBQztBQUU1RCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDM0MsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDOUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUNyQyxPQUFPLEVBRUwsZ0JBQWdCLEVBR2hCLFVBQVUsRUFDVixRQUFRLEdBQ1QsTUFBTSxhQUFhLENBQUM7QUFDckIsT0FBTyxFQUdMLGNBQWMsR0FDZixNQUFNLGtCQUFrQixDQUFDO0FBQzFCLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFFdkMsTUFBTSxjQUFjLEdBQUcsS0FBSyxFQUFFLE9BQTRCLEVBQWlCLEVBQUU7SUFDM0UsSUFBSSxVQUFVLENBQUMsaUJBQWlCLENBQUMsT0FBTyxDQUFDLENBQUMsRUFBRTtRQUMxQyxPQUFPO0tBQ1I7SUFFRCxPQUFPLENBQUMsR0FBRyxDQUFDLHNEQUFzRCxDQUFDLENBQUM7SUFDcEUsTUFBTSxRQUFRLEdBQUcsTUFBTSxLQUFLLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLENB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QixFQUNqQiw0QkFBNEIsQ0FDN0IsQ0FBQztRQUNGLHVCQUF1QixDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ3RDLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUFDO0FBRUYsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsQ0FDckMsVUFBc0MsRUFBRSxFQUN4QyxFQUFFO0lBQ0YsTUFBTSxVQUFVLEdBQUcsY0FBYyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBRTNDLElBQUksVUFBVSxDQUFDLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxDQUFDLEVBQUU7UUFDN0MsTUFBTSxDQUFDLGlCQUFpQixDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUM7S0FDdkM7QUFDSCxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjcmVhdGVSZWFkU3RyZWFtLCBjcmVhdGVXcml0ZVN0cmVhbSwgZXhpc3RzU3luYywgcm1TeW5jIH0gZnJvbSBcImZzXCI7XHJcbmltcG9ydCBmZXRjaCBmcm9tIFwibm9kZS1mZXRjaFwiO1xyXG5pbXBvcnQgeyBqb2luIH0gZnJvbSBcInBhdGhcIjtcclxuaW1wb3J0IHBhcnNlciBmcm9tIFwic3RyZWFtLWpzb25cIjtcclxuaW1wb3J0IFBpY2sgZnJvbSBcInN0cmVhbS1qc29uL2ZpbHRlcnMvUGlja1wiO1xyXG5pbXBvcnQgU3RyZWFtQXJyYXkgZnJvbSBcInN0cmVhbS1qc29uL3N0cmVhbWVycy9TdHJlYW1BcnJheVwiO1xyXG5cclxuaW1wb3J0IHsgcGlwZWxpbmUgfSBmcm9tIFwic3RyZWFtL3Byb21pc2VzXCI7XHJcbmltcG9ydCB7IGluc2VydEZvdW5kZXJzIH0gZnJvbSBcIi4uL2RiL2ZvdW5kZXJzXCI7XHJcbmltcG9ydCB7IGluc2VydFNjaG9vbHMgfSBmcm9tIFwiLi4vZGIvc2Nob29sc1wiO1xyXG5pbXBvcnQgeyBnZXRLbmV4RGIgfSBmcm9tIFwiLi4vZGIvZGJcIjtcclxuaW1wb3J0IHtcclxuICBGb3VuZGVyLFxyXG4gIE11bmljaXBhbGl0eVR5cGUsXHJcbiAgU2Nob29sLFxyXG4gIFNjaG9vbExvY2F0aW9uLFxyXG4gIFNjaG9vbFR5cGUsXHJcbiAgU3luY1BhcnQsXHJcbn0gZnJvbSBcIi4uL2RiL3R5cGVzXCI7XHJcbmltcG9ydCB7XHJcbiAgT3BlbkRhdGFTeW5jT3B0aW9ucyxcclxuICBPcGVuRGF0YVN5bmNPcHRpb25zUGFydGlhbCxcclxuICBwcmVwYXJlT3B0aW9ucyxcclxufSBmcm9tIFwiLi4vdXRpbHMvaGVscGVyc1wiO1xyXG5pbXBvcnQgeyBydW5TeW5jUGFydCB9IGZyb20gXCIuL2NvbW1vblwiO1xyXG5cclxuY29uc3QgZG93bmxvYWRKc29ubGQgPSBhc3luYyAob3B0aW9uczogT3BlbkRhdGFTeW5jT3B0aW9ucyk6IFByb21pc2U8dm9pZD4gPT4ge1xyXG4gIGlmIChleGlzdHNTeW5jKGdldEpzb25sZEZpbGVQYXRoKG9wdGlvbnMpKSkge1xyXG4gICAgcmV0dXJuO1xyXG4gIH1cclxuXHJcbiAgY29uc29sZS5sb2coXCJEb3dubG9hZGluZyBhIGxhcmdlIEpTT04tTEQgZmlsZSB3aXRoIHNjaG9vbCBkYXRhLi4uXCIpO1xyXG4gIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2gob3B0aW9ucy5zY2hvb2xzSnNvbmxkVXJsKTtcclxuICBpZiAocmVzcG9uc2Uuc3RhdHVzICE9PSAyMDApIHtcclxuICAgIHRocm93IG5ldyBFcnJvcihcclxuICAgICAgYFRoZSBKU09OLUxEIGZpbGUgY291bGQgbm90IGJlIGRvd25sb2FkZWQuIEhUVFAgQ29kZTogJHtyZXNwb25zZS5zdGF0dXN9YFxyXG4gICAgKTtcclxuICB9XHJcbiAgYXdhaXQgcGlwZWxpbmUocmVzcG9uc2UuYm9keSwgY3JlYXRlV3JpdGVTdHJlYW0oZ2V0SnNvbmxkRmlsZVBhdGgob3B0aW9ucykpKTtcclxuICBjb25zb2xlLmxvZyhcIkZpbmlzaGVkIGRvd25sb2FkaW5nLlwiKTtcclxufTtcclxuXHJcbmNvbnN0IFNDSE9PTF9UWVBFX0tJTkRFUkdBUlRFTiA9IFwiQTAwXCI7XHJcbmNvbnN0IFNDSE9PTF9UWVBFX0VMRU1FTlRBUlkgPSBcIkIwMFwiO1xyXG5cclxuY29uc3QgZ2V0Q29ycmVjdEZvdW5kZXJUeXBlID0gKGZvdW5kZXJUeXBlOiBzdHJpbmcpOiBzdHJpbmcgPT4ge1xyXG4gIHJldHVybiBmb3VuZGVyVHlwZSA9PT0gXCJcIiA/IFwiMTAxXCIgOiBmb3VuZGVyVHlwZTtcclxufTtcclxuXHJcbmNvbnN0IGdldE11bmljaXBhbGl0eVR5cGUgPSAoZm91bmRlclR5cGU6IHN0cmluZyk6IE11bmljaXBhbGl0eVR5cGUgPT4ge1xyXG4gIHJldHVybiBmb3VuZGVyVHlwZSA9PT0gXCIyNjFcIlxyXG4gICAgPyBNdW5pY2lwYWxpdHlUeXBlLkNpdHlcclxuICAgIDogZm91bmRlclR5cGUgPT09IFwiMjYzXCJcclxuICAgID8gTXVuaWNpcGFsaXR5VHlwZS5EaXN0cmljdFxyXG4gICAgOiBNdW5pY2lwYWxpdHlUeXBlLk90aGVyO1xyXG59O1xyXG5cclxudHlwZSBTY2hvb2xBZGRyZXNzID0ge1xyXG4gIGl6bzogc3RyaW5nO1xyXG4gIGFkZHJlc3M6IHN0cmluZ1tdO1xyXG4gIHR5cGU6IFNjaG9vbFR5cGU7XHJcbn07XHJcblxyXG5jb25zdCBwcm9jZXNzU2Nob29sUmVnaXN0ZXJKc29ubGQgPSBhc3luYyAoXHJcbiAgb3B0aW9uczogT3BlbkRhdGFTeW5jT3B0aW9uc1xyXG4pOiBQcm9taXNlPHtcclxuICBzY2hvb2xzOiBTY2hvb2xbXTtcclxuICBmb3VuZGVyczogTWFwPHN0cmluZywgRm91bmRlcj47XHJcbiAgc2Nob29sc1dpdGhvdXRSdWlhbjogU2Nob29sQWRkcmVzc1tdO1xyXG59PiA9PiB7XHJcbiAgY29uc3QgZm91bmRlcnMgPSBuZXcgTWFwPHN0cmluZywgRm91bmRlcj4oKTtcclxuICBjb25zdCBzY2hvb2xzOiBTY2hvb2xbXSA9IFtdO1xyXG4gIGNvbnN0IHNjaG9vbHNXaXRob3V0UnVpYW46IFNjaG9vbEFkZHJlc3NbXSA9IFtdO1xyXG5cclxuICBjb25zdCBzdHJlYW0gPSBjcmVhdGVSZWFkU3RyZWFtKGdldEpzb25sZEZpbGVQYXRoKG9wdGlvbnMpKVxyXG4gICAgLnBpcGUocGFyc2VyKCkpXHJcbiAgICAucGlwZShuZXcgUGljayh7IGZpbHRlcjogXCJsaXN0XCIgfSkpXHJcbiAgICAucGlwZShuZXcgU3RyZWFtQXJyYXkoKSk7XHJcblxyXG4gIGZvciBhd2FpdCAoY29uc3QgeyB2YWx1ZTogZW50aXR5IH0gb2Ygc3RyZWFtKSB7XHJcbiAgICBjb25zdCByZWRpem86IHN0cmluZyA9IGVudGl0eS5yZWRJem8gPz8gXCJcIjtcclxuICAgIGNvbnN0IGljbzogc3RyaW5nID0gZW50aXR5LmljbyA/PyBcIlwiO1xyXG4gICAgY29uc3QgZW50aXR5TmFtZTogc3RyaW5nID0gZW50aXR5LnVwbG55TmF6ZXYgPz8gXCJcIjtcclxuICAgIGNvbnN0IGN1cnJlbnRTY2hvb2xzOiBTY2hvb2xbXSA9IFtdO1xyXG5cclxuICAgIGZvciAoY29uc3Qgc2Nob29sIG9mIGVudGl0eS5za29seUFaYXJpemVuaSA/PyBbXSkge1xyXG4gICAgICBjb25zdCB0eXBlID1cclxuICAgICAgICBzY2hvb2wuZHJ1aCA9PT0gU0NIT09MX1RZUEVfS0lOREVSR0FSVEVOXHJcbiAgICAgICAgICA/IFNjaG9vbFR5cGUuS2luZGVyZ2FydGVuXHJcbiAgICAgICAgICA6IHNjaG9vbC5kcnVoID09PSBTQ0hPT0xfVFlQRV9FTEVNRU5UQVJZXHJcbiAgICAgICAgICA/IFNjaG9vbFR5cGUuRWxlbWVudGFyeVxyXG4gICAgICAgICAgOiBudWxsO1xyXG5cclxuICAgICAgaWYgKHR5cGUgPT09IG51bGwpIGNvbnRpbnVlO1xyXG5cclxuICAgICAgY29uc3QgY2FwYWNpdHlFbnRyeSA9XHJcbiAgICAgICAgc2Nob29sLmthcGFjaXR5Py5maW5kKChrKSA9PiBrLm1lcm5hSmVkbm90a2EgPT09IFwiMDFcIikgPz9cclxuICAgICAgICBzY2hvb2wua2FwYWNpdHk/LlswXTtcclxuICAgICAgY29uc3QgY2FwYWNpdHk6IG51bWJlciA9IGNhcGFjaXR5RW50cnk/Lm5lanZ5c3NpUG92b2xlbnlQb2NldCA/PyAwO1xyXG4gICAgICBjb25zdCBhbGxQbGFjZXMgPSBzY2hvb2wubWlzdGFWeXVreSA/PyBbXTtcclxuICAgICAgY29uc3QgbG9jYXRpb25zV2l0aFJ1aWFuID0gYWxsUGxhY2VzLmZpbHRlcihcclxuICAgICAgICAocCkgPT4gcC5hZHJlc2E/LmtvZFJVSUFOICE9IG51bGxcclxuICAgICAgKTtcclxuICAgICAgLy8gUHJlZmVyIHRoZSBtYWluIGJ1aWxkaW5nIChJRG1pc3RhIG1hdGNoZXMgc2Nob29sIElaTyksIGZhbGwgYmFjayB0byBmaXJzdCBhdmFpbGFibGVcclxuICAgICAgY29uc3QgbWFpblBsYWNlID1cclxuICAgICAgICBsb2NhdGlvbnNXaXRoUnVpYW4uZmluZCgocCkgPT4gcC5JRG1pc3RhID09PSBzY2hvb2wuaXpvKSA/P1xyXG4gICAgICAgIGxvY2F0aW9uc1dpdGhSdWlhblswXTtcclxuXHJcbiAgICAgIGNvbnN0IGxvY2F0aW9uczogU2Nob29sTG9jYXRpb25bXSA9IG1haW5QbGFjZVxyXG4gICAgICAgID8gW3sgYWRkcmVzc1BvaW50SWQ6IG1haW5QbGFjZS5hZHJlc2Eua29kUlVJQU4gfV1cclxuICAgICAgICA6IFtdO1xyXG5cclxuICAgICAgaWYgKCFtYWluUGxhY2UgJiYgYWxsUGxhY2VzLmxlbmd0aCA+IDApIHtcclxuICAgICAgICBjb25zdCBhZGRyID0gYWxsUGxhY2VzWzBdLmFkcmVzYSA/PyB7fTtcclxuICAgICAgICBjb25zdCBhZGRyZXNzUGFydHMgPSBbXHJcbiAgICAgICAgICBhZGRyLnVsaWNlLFxyXG4gICAgICAgICAgYWRkci5jaXNsb0RvbW92bmk/LnRvU3RyaW5nKCksXHJcbiAgICAgICAgICBhZGRyLm9iZWMsXHJcbiAgICAgICAgXS5maWx0ZXIoQm9vbGVhbik7XHJcbiAgICAgICAgc2Nob29sc1dpdGhvdXRSdWlhbi5wdXNoKHsgaXpvOiBzY2hvb2wuaXpvLCBhZGRyZXNzOiBhZGRyZXNzUGFydHMsIHR5cGUgfSk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGN1cnJlbnRTY2hvb2xzLnB1c2goe1xyXG4gICAgICAgIGl6bzogc2Nob29sLml6byxcclxuICAgICAgICBuYW1lOiBlbnRpdHlOYW1lIHx8IHNjaG9vbC51cGxueU5hemV2IHx8IFwiXCIsXHJcbiAgICAgICAgcmVkaXpvLFxyXG4gICAgICAgIGNhcGFjaXR5LFxyXG4gICAgICAgIHR5cGUsXHJcbiAgICAgICAgbG9jYXRpb25zLFxyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBzY2hvb2xzLnB1c2goLi4uY3VycmVudFNjaG9vbHMpO1xyXG5cclxuICAgIGZvciAoY29uc3QgeiBvZiBlbnRpdHkuenJpem92YXRlbGUgPz8gW10pIHtcclxuICAgICAgbGV0IGZvdW5kZXJJY286IHN0cmluZztcclxuICAgICAgbGV0IGZvdW5kZXJOYW1lOiBzdHJpbmc7XHJcbiAgICAgIGxldCBmb3VuZGVyVHlwZTogc3RyaW5nO1xyXG5cclxuICAgICAgaWYgKHouZHJ1aE9zb2J5ID09PSBcIlBPXCIpIHtcclxuICAgICAgICBmb3VuZGVySWNvID0gei5JY28gPz8gXCJcIjtcclxuICAgICAgICBmb3VuZGVyTmFtZSA9IHoubmF6ZXZPc29ieSA/PyBcIlwiO1xyXG4gICAgICAgIGZvdW5kZXJUeXBlID0gZ2V0Q29ycmVjdEZvdW5kZXJUeXBlKHoucHJhdm5pRm9ybWEgPz8gXCJcIik7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgZm91bmRlckljbyA9IHouZGF0dW1OYXJvemVuaSA/PyBcIlwiO1xyXG4gICAgICAgIGZvdW5kZXJOYW1lID0gei5uYXpldk9zb2J5ID8/IFwiXCI7XHJcbiAgICAgICAgZm91bmRlclR5cGUgPSBnZXRDb3JyZWN0Rm91bmRlclR5cGUoXCJcIik7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIGlmIChmb3VuZGVySWNvID09PSBcIlwiIHx8IGZvdW5kZXJOYW1lID09PSBcIlwiKSB7XHJcbiAgICAgICAgZm91bmRlckljbyA9IGljbztcclxuICAgICAgICBmb3VuZGVyTmFtZSA9IGVudGl0eU5hbWU7XHJcbiAgICAgICAgZm91bmRlclR5cGUgPSBcIjIyNFwiO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAoY3VycmVudFNjaG9vbHMubGVuZ3RoID4gMCkge1xyXG4gICAgICAgIGNvbnN0IGtleSA9IGZvdW5kZXJOYW1lICsgZm91bmRlckljbztcclxuICAgICAgICBpZiAoZm91bmRlcnMuaGFzKGtleSkpIHtcclxuICAgICAgICAgIGZvdW5kZXJzLmdldChrZXkpLnNjaG9vbHMucHVzaCguLi5jdXJyZW50U2Nob29scyk7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgIGZvdW5kZXJzLnNldChrZXksIHtcclxuICAgICAgICAgICAgbmFtZTogZm91bmRlck5hbWUsXHJcbiAgICAgICAgICAgIGljbzogZm91bmRlckljbyxcclxuICAgICAgICAgICAgb3JpZ2luYWxUeXBlOiBwYXJzZUludChmb3VuZGVyVHlwZSksXHJcbiAgICAgICAgICAgIG11bmljaXBhbGl0eVR5cGU6IGdldE11bmljaXBhbGl0eVR5cGUoZm91bmRlclR5cGUpLFxyXG4gICAgICAgICAgICBzY2hvb2xzOiBbLi4uY3VycmVudFNjaG9vbHNdLFxyXG4gICAgICAgICAgfSk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICByZXR1cm4geyBzY2hvb2xzLCBmb3VuZGVycywgc2Nob29sc1dpdGhvdXRSdWlhbiB9O1xyXG59O1xyXG5cclxuY29uc3QgaW1wb3J0RGF0YVRvRGIgPSBhc3luYyAoXHJcbiAgb3B0aW9uczogT3BlbkRhdGFTeW5jT3B0aW9ucyxcclxuICBzYXZlRm91bmRlcnNUb0NzdjogYm9vbGVhbiA9IGZhbHNlLFxyXG4gIHNhdmVTY2hvb2xzV2l0aG91dFJ1aWFuVG9Dc3Y6IGJvb2xlYW4gPSBmYWxzZVxyXG4pID0+IHtcclxuICBjb25zdCB7IHNjaG9vbHMsIGZvdW5kZXJzLCBzY2hvb2xzV2l0aG91dFJ1aWFuIH0gPVxyXG4gICAgYXdhaXQgcHJvY2Vzc1NjaG9vbFJlZ2lzdGVySnNvbmxkKG9wdGlvbnMpO1xyXG5cclxuICBpZiAoc2F2ZUZvdW5kZXJzVG9Dc3YpIHtcclxuICAgIGNvbnN0IGNzdkZpbGUgPSBcImZvdW5kZXJzLmNzdlwiO1xyXG5cclxuICAgIGlmIChleGlzdHNTeW5jKGNzdkZpbGUpKSB7XHJcbiAgICAgIHJtU3luYyhjc3ZGaWxlKTtcclxuICAgIH1cclxuXHJcbiAgICB2YXIgY3N2ID0gY3JlYXRlV3JpdGVTdHJlYW0oY3N2RmlsZSwge1xyXG4gICAgICBmbGFnczogXCJhXCIsXHJcbiAgICB9KTtcclxuICAgIGNzdi53cml0ZShcIknEjE87WsWZaXpvdmF0ZWw7UHLDoXZuw60gZm9ybWE7UG/EjWV0IMWha29sO8Wga29seVxcblwiKTtcclxuICAgIGZvdW5kZXJzLmZvckVhY2goKGZvdW5kZXIpID0+IHtcclxuICAgICAgY3N2LndyaXRlKFxyXG4gICAgICAgIGAjJHtmb3VuZGVyLmljb307JHtmb3VuZGVyLm5hbWV9OyR7Zm91bmRlci5vcmlnaW5hbFR5cGV9OyR7XHJcbiAgICAgICAgICBmb3VuZGVyLnNjaG9vbHMubGVuZ3RoXHJcbiAgICAgICAgfTske2ZvdW5kZXIuc2Nob29scy5tYXAoKHNjaG9vbCkgPT4gc2Nob29sLm5hbWUpLmpvaW4oXCItLS1cIil9XFxuYFxyXG4gICAgICApO1xyXG4gICAgfSk7XHJcbiAgICBjc3YuZW5kKCk7XHJcbiAgfVxyXG5cclxuICBpZiAoc2F2ZVNjaG9vbHNXaXRob3V0UnVpYW5Ub0Nzdikge1xyXG4gICAgY29uc3QgY3N2RmlsZSA9IFwic2Nob29sc1dpdGhvdXRSdWlhbi5jc3ZcIjtcclxuXHJcbiAgICBpZiAoZXhpc3RzU3luYyhjc3ZGaWxlKSkge1xyXG4gICAgICBybVN5bmMoY3N2RmlsZSk7XHJcbiAgICB9XHJcblxyXG4gICAgdmFyIGNzdiA9IGNyZWF0ZVdyaXRlU3RyZWFtKGNzdkZpbGUsIHtcclxuICAgICAgZmxhZ3M6IFwiYVwiLFxyXG4gICAgfSk7XHJcbiAgICBjc3Yud3JpdGUoXCJJWk87SmUgbWF0ZcWZc2vDoTtKZSB6w6FrbGFkbsOtO2FkcmVzYTE7YWRyZXNhMjthZHJlc2EzXFxuXCIpO1xyXG4gICAgc2Nob29sc1dpdGhvdXRSdWlhbi5mb3JFYWNoKChzY2hvb2xBZGRyZXNzKSA9PiB7XHJcbiAgICAgIGNzdi53cml0ZShcclxuICAgICAgICBgIyR7c2Nob29sQWRkcmVzcy5pem99OyR7XHJcbiAgICAgICAgICBzY2hvb2xBZGRyZXNzLnR5cGUgPT09IFNjaG9vbFR5cGUuS2luZGVyZ2FydGVuID8gXCJUUlVFXCIgOiBcIkZBTFNFXCJcclxuICAgICAgICB9OyR7XHJcbiAgICAgICAgICBzY2hvb2xBZGRyZXNzLnR5cGUgPT09IFNjaG9vbFR5cGUuRWxlbWVudGFyeSA/IFwiVFJVRVwiIDogXCJGQUxTRVwiXHJcbiAgICAgICAgfTske3NjaG9vbEFkZHJlc3MuYWRkcmVzcy5qb2luKFwiO1wiKX1cXG5gXHJcbiAgICAgICk7XHJcbiAgICB9KTtcclxuICAgIGNzdi5lbmQoKTtcclxuICB9XHJcblxyXG4gIGNvbnN0IGtuZXggPSBnZXRLbmV4RGIoKTtcclxuICBjb25zdCBzY2hvb2xzQmVmb3JlID0gKGF3YWl0IGtuZXgoXCJzY2hvb2xcIikuY291bnQoXCJpem8gYXMgY291bnRcIikuZmlyc3QoKSkuY291bnQgYXMgbnVtYmVyO1xyXG4gIGNvbnN0IGZvdW5kZXJzQmVmb3JlID0gKGF3YWl0IGtuZXgoXCJmb3VuZGVyXCIpLmNvdW50KFwiaWQgYXMgY291bnRcIikuZmlyc3QoKSkuY291bnQgYXMgbnVtYmVyO1xyXG5cclxuICAvLyBDb21wdXRlIGNoYW5nZSBzdGF0cyBiZWZvcmUgaW5zZXJ0aW5nICh1c2VzIHNhbWUgbG9naWMgYXMgaW5zZXJ0U2Nob29scylcclxuICBjb25zdCBleGlzdGluZ1NuYXA6IHsgaXpvOiBzdHJpbmc7IG5hbWU6IHN0cmluZzsgY2FwYWNpdHk6IG51bWJlcjsgYWRkcmVzc19wb2ludF9pZDogbnVtYmVyIHwgbnVsbCB9W10gPVxyXG4gICAgYXdhaXQga25leChcInNjaG9vbFwiKVxyXG4gICAgICAubGVmdEpvaW4oXCJzY2hvb2xfbG9jYXRpb25cIiwgXCJzY2hvb2wuaXpvXCIsIFwic2Nob29sX2xvY2F0aW9uLnNjaG9vbF9pem9cIilcclxuICAgICAgLnNlbGVjdChcInNjaG9vbC5pem9cIiwgXCJzY2hvb2wubmFtZVwiLCBcInNjaG9vbC5jYXBhY2l0eVwiLCBcInNjaG9vbF9sb2NhdGlvbi5hZGRyZXNzX3BvaW50X2lkXCIpO1xyXG4gIGNvbnN0IGV4aXN0aW5nU25hcE1hcCA9IG5ldyBNYXAoZXhpc3RpbmdTbmFwLm1hcCgocikgPT4gW3IuaXpvLCByXSkpO1xyXG4gIGNvbnN0IHN0YXRzRmllbGRVcGRhdGVzID0gc2Nob29scy5maWx0ZXIoKHMpID0+IHtcclxuICAgIGNvbnN0IHJvdyA9IGV4aXN0aW5nU25hcE1hcC5nZXQocy5pem8pO1xyXG4gICAgcmV0dXJuIHJvdyAmJiAocm93Lm5hbWUgIT09IHMubmFtZSB8fCByb3cuY2FwYWNpdHkgIT09IHMuY2FwYWNpdHkpO1xyXG4gIH0pLmxlbmd0aDtcclxuICBjb25zdCBzdGF0c0xvY2F0aW9uVXBkYXRlcyA9IHNjaG9vbHMuZmlsdGVyKChzKSA9PiB7XHJcbiAgICBjb25zdCByb3cgPSBleGlzdGluZ1NuYXBNYXAuZ2V0KHMuaXpvKTtcclxuICAgIGNvbnN0IG5ld1J1aWFucyA9IHMubG9jYXRpb25zLm1hcCgobCkgPT4gbC5hZGRyZXNzUG9pbnRJZCk7XHJcbiAgICByZXR1cm4gcm93ICYmIHJvdy5hZGRyZXNzX3BvaW50X2lkICE9PSBudWxsICYmIG5ld1J1aWFucy5sZW5ndGggPiAwICYmICFuZXdSdWlhbnMuaW5jbHVkZXMocm93LmFkZHJlc3NfcG9pbnRfaWQpO1xyXG4gIH0pLmxlbmd0aDtcclxuXHJcbiAgYXdhaXQgaW5zZXJ0U2Nob29scyhzY2hvb2xzKTtcclxuICBhd2FpdCBpbnNlcnRGb3VuZGVycyhBcnJheS5mcm9tKGZvdW5kZXJzLnZhbHVlcygpKSk7XHJcblxyXG4gIGNvbnN0IHNjaG9vbHNBZnRlciA9IChhd2FpdCBrbmV4KFwic2Nob29sXCIpLmNvdW50KFwiaXpvIGFzIGNvdW50XCIpLmZpcnN0KCkpLmNvdW50IGFzIG51bWJlcjtcclxuICBjb25zdCBmb3VuZGVyc0FmdGVyID0gKGF3YWl0IGtuZXgoXCJmb3VuZGVyXCIpLmNvdW50KFwiaWQgYXMgY291bnRcIikuZmlyc3QoKSkuY291bnQgYXMgbnVtYmVyO1xyXG5cclxuICBjb25zdCBraW5kZXJnYXJ0ZW5zID0gc2Nob29scy5maWx0ZXIoKHMpID0+IHMudHlwZSA9PT0gU2Nob29sVHlwZS5LaW5kZXJnYXJ0ZW4pLmxlbmd0aDtcclxuICBjb25zdCBlbGVtZW50YXJ5ID0gc2Nob29scy5maWx0ZXIoKHMpID0+IHMudHlwZSA9PT0gU2Nob29sVHlwZS5FbGVtZW50YXJ5KS5sZW5ndGg7XHJcblxyXG4gIGNvbnNvbGUubG9nKGBcXG49PT0gU2Nob29sIHN5bmMgc3RhdGlzdGljcyA9PT1gKTtcclxuICBjb25zb2xlLmxvZyhgU2Nob29scyBwYXJzZWQ6ICAgICAgICAgJHtzY2hvb2xzLmxlbmd0aH0gKCR7a2luZGVyZ2FydGVuc30ga2luZGVyZ2FydGVucywgJHtlbGVtZW50YXJ5fSBlbGVtZW50YXJ5KWApO1xyXG4gIGNvbnNvbGUubG9nKGBGb3VuZGVycyBwYXJzZWQ6ICAgICAgICAke2ZvdW5kZXJzLnNpemV9YCk7XHJcbiAgY29uc29sZS5sb2coYE5ldyBzY2hvb2xzIGluc2VydGVkOiAgICR7c2Nob29sc0FmdGVyIC0gc2Nob29sc0JlZm9yZX1gKTtcclxuICBjb25zb2xlLmxvZyhgTmV3IGZvdW5kZXJzIGluc2VydGVkOiAgJHtmb3VuZGVyc0FmdGVyIC0gZm91bmRlcnNCZWZvcmV9YCk7XHJcbiAgY29uc29sZS5sb2coYEZpZWxkIHVwZGF0ZXM6ICAgICAgICAgICR7c3RhdHNGaWVsZFVwZGF0ZXN9IChuYW1lL2NhcGFjaXR5IGNoYW5nZWQpYCk7XHJcbiAgY29uc29sZS5sb2coYExvY2F0aW9uIHVwZGF0ZXM6ICAgICAgICR7c3RhdHNMb2NhdGlvblVwZGF0ZXN9IChmaXJzdCBsb2NhdGlvbiBjaGFuZ2VkKWApO1xyXG4gIGNvbnNvbGUubG9nKGBUb3RhbCBzY2hvb2xzIGluIERCOiAgICAke3NjaG9vbHNBZnRlcn1gKTtcclxuICBjb25zb2xlLmxvZyhgVG90YWwgZm91bmRlcnMgaW4gREI6ICAgJHtmb3VuZGVyc0FmdGVyfWApO1xyXG59O1xyXG5cclxuY29uc3QgZ2V0SnNvbmxkRmlsZVBhdGggPSAob3B0aW9uczogT3BlbkRhdGFTeW5jT3B0aW9uc1BhcnRpYWwpOiBzdHJpbmcgPT4ge1xyXG4gIHJldHVybiBqb2luKG9wdGlvbnMudG1wRGlyLCBvcHRpb25zLnNjaG9vbHNKc29ubGRGaWxlTmFtZSk7XHJcbn07XHJcblxyXG5leHBvcnQgY29uc3QgZG93bmxvYWRBbmRJbXBvcnRTY2hvb2xzID0gYXN5bmMgKFxyXG4gIG9wdGlvbnM6IE9wZW5EYXRhU3luY09wdGlvbnNQYXJ0aWFsID0ge30sXHJcbiAgc2F2ZUZvdW5kZXJzVG9Dc3Y6IGJvb2xlYW4gPSBmYWxzZSxcclxuICBzYXZlU2Nob29sc1dpdGhvdXRSdWlhblRvQ3N2OiBib29sZWFuID0gZmFsc2VcclxuKSA9PiB7XHJcbiAgYXdhaXQgcnVuU3luY1BhcnQoU3luY1BhcnQuU2Nob29scywgW1N5bmNQYXJ0LkFkZHJlc3NQb2ludHNdLCBhc3luYyAoKSA9PiB7XHJcbiAgICBjb25zdCBydW5PcHRpb25zID0gcHJlcGFyZU9wdGlvbnMob3B0aW9ucyk7XHJcblxyXG4gICAgYXdhaXQgZG93bmxvYWRKc29ubGQocnVuT3B0aW9ucyk7XHJcbiAgICBhd2FpdCBpbXBvcnREYXRhVG9EYihcclxuICAgICAgcnVuT3B0aW9ucyxcclxuICAgICAgc2F2ZUZvdW5kZXJzVG9Dc3YsXHJcbiAgICAgIHNhdmVTY2hvb2xzV2l0aG91dFJ1aWFuVG9Dc3ZcclxuICAgICk7XHJcbiAgICBkZWxldGVTY2hvb2xzSnNvbmxkRmlsZShydW5PcHRpb25zKTtcclxuICB9KTtcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBkZWxldGVTY2hvb2xzSnNvbmxkRmlsZSA9IChcclxuICBvcHRpb25zOiBPcGVuRGF0YVN5bmNPcHRpb25zUGFydGlhbCA9IHt9XHJcbikgPT4ge1xyXG4gIGNvbnN0IHJ1bk9wdGlvbnMgPSBwcmVwYXJlT3B0aW9ucyhvcHRpb25zKTtcclxuXHJcbiAgaWYgKGV4aXN0c1N5bmMoZ2V0SnNvbmxkRmlsZVBhdGgocnVuT3B0aW9ucykpKSB7XHJcbiAgICBybVN5bmMoZ2V0SnNvbmxkRmlsZVBhdGgocnVuT3B0aW9ucykpO1xyXG4gIH1cclxufTtcclxuIl19