UNPKG

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

448 lines 67.8 kB
import distance from "@turf/distance"; import { wholeLineError } from "../street-markdown/smd"; import { extractMunicipalityName, findClosestString, sanitizeMunicipalityName, } from "../utils/helpers"; import { getKnexDb, insertAutoincrementRow, insertMultipleRows } from "./db"; import { MunicipalityType, } from "./types"; const cityTypeCode = 261; const cityDistrictTypeCode = 263; export const insertFounders = async (founders) => { let insertedFounders = 0; const schoolFounderConnectionData = []; for (const founder of founders) { if (founder.municipalityType !== MunicipalityType.City && founder.municipalityType !== MunicipalityType.District) { continue; } const extractedMunicipalityName = extractMunicipalityName(founder); // check if the extracted municipality name is the same as in all the schools' locations let differingSchools = []; let municipalityCode = -1; for (const school of founder.schools) { const result = await (founder.municipalityType === MunicipalityType.City ? getCityOfSchool(school.izo) : getDistrictOfSchool(school.izo)); if (!result) { console.log(`izo: ${school.izo}, extracted: ${extractedMunicipalityName}, RUIAN: UNDEFINED`); differingSchools.push(school); } else { const { name, code } = result; if (name !== extractedMunicipalityName) { console.log(`izo: ${school.izo}, extracted: ${extractedMunicipalityName}, RUIAN: ${name}`); differingSchools.push(school); } // store municipalityCode even if the names don't match, we will use it later municipalityCode = parseInt(code); } } municipalityCode = await fixFounderProblems(founder, municipalityCode, differingSchools, extractedMunicipalityName); const cityDistrictCode = founder.municipalityType === MunicipalityType.District ? municipalityCode.toString() : null; let cityCode = null; if (founder.municipalityType === MunicipalityType.City) { cityCode = municipalityCode?.toString() ?? null; } else { cityCode = await getCityCodeByDistrictCode(municipalityCode); } const existing = await getKnexDb() .select("*") .from("founder") .where({ name: sanitizeMunicipalityName(founder.name), ico: founder.ico, }); let founderId = existing[0]?.id ?? null; if (existing.length === 0) { founderId = await insertAutoincrementRow([ sanitizeMunicipalityName(founder.name), sanitizeMunicipalityName(extractedMunicipalityName), founder.ico, String(founder.originalType), cityCode, cityDistrictCode, ], "founder", [ "name", "short_name", "ico", "founder_type_code", "city_code", "city_district_code", ]); insertedFounders++; } founder.schools.forEach((school) => { schoolFounderConnectionData.push([school.izo, founderId]); }); } const insertedConnections = await insertMultipleRows(schoolFounderConnectionData, "school_founder", ["school_izo", "founder_id"], true); return insertedFounders + insertedConnections; }; const getCityOfSchool = async (izo) => { return await getKnexDb() .select("c.name", "c.code") .from("school as s") .join("school_location as l", "s.izo", "l.school_izo") .join("address_point as a", "l.address_point_id", "a.id") .join("city as c", "a.city_code", "c.code") .where("s.izo", izo) .limit(1) .first(); }; const getDistrictOfSchool = async (izo) => { return await getKnexDb() .select("d.name", "d.code") .from("school as s") .join("school_location as l", "s.izo", "l.school_izo") .join("address_point as a", "l.address_point_id", "a.id") .join("city_district as d", "a.city_district_code", "d.code") .where("s.izo", izo) .limit(1) .first(); }; const fixFounderProblems = async (founder, municipalityCode, differingSchools, extractedMunicipalityName) => { if (differingSchools.length === 0 || differingSchools.length < founder.schools.length) { return municipalityCode; } // either the school does not have a position (invalid RUIAN or missing building) // or the school is not in the same municipality as the founder // find all cities and their position with the same name as municipalityName const municipalities = await findMunicipalitiesAndPositionsByNameAndType(extractedMunicipalityName, founder.municipalityType); // get one school position (if there are more schools, they should be close to each other) const schoolPosition = await getKnexDb() .select("address_point.wgs84_latitude", "address_point.wgs84_longitude") .from("school") .join("school_location", "school.izo", "school_location.school_izo") .join("address_point", "school_location.address_point_id", "address_point.id") .whereIn("school.izo", differingSchools.map((school) => school.izo)) .limit(1) .first(); if (schoolPosition) { if (municipalities.length === 0) { if (municipalityCode === -1) { console.log(`no municipality by name or by location found for ${founder.name}`); return null; } else { console.log("no municipality matching the extracted name, using the municipality from RUIAN code"); } } else if (municipalities.length === 1) { console.log("using the only municipality found matching the extracted name"); return municipalities[0].code; } else { console.log("using the closest municipality matching the extracted name"); return getClosestCode({ code: 1, lat: schoolPosition.wgs84_latitude, lng: schoolPosition.wgs84_longitude, }, municipalities.map((municipality) => ({ code: municipality.code, lat: municipality.lat, lng: municipality.lng, }))); } } else { if (municipalities.length > 0) { if (municipalities.length > 1) { console.log(`using the first municipality matching the extracted name (${municipalities.length} matches) - possibly incorrect!`); } else { console.log("using the only municipality found matching the extracted name"); } return municipalities[0].code; } else { console.log(`no municipality by name or by location found for ${founder.name}`); return null; } } }; const getClosestCode = async (from, toList) => { let lowestDistance = Number.MAX_SAFE_INTEGER; let code = null; for (const place of toList) { let municipalityDistance = distance([place.lat, place.lng], [from.lat, from.lng]); if (municipalityDistance < lowestDistance) { lowestDistance = municipalityDistance; code = place.code; } } return code; }; const getCityCodeByDistrictCode = async (districtCode) => { const result = await getKnexDb() .first("city.code") .from("city_district") .join("city", "city_district.city_code", "city.code") .where("city_district.code", districtCode) .limit(1); return result?.code ?? null; }; const findMunicipalitiesAndPositionsByNameAndType = async (name, type) => { const knex = getKnexDb(); return (type === MunicipalityType.City ? await knex .select("city.name", "city.code", "address_point.wgs84_latitude", "address_point.wgs84_longitude") .from("city") .join("address_point", "city.code", "address_point.city_code") .where("city.name", name) .groupBy("city.code") : await knex .select("city_district.name", "city_district.code", "address_point.wgs84_latitude", "address_point.wgs84_longitude") .from("city_district") .join("address_point", "city_district.code", "address_point.city_district_code") .where("city_district.name", name) .groupBy("city_district.code")).map((row) => ({ code: row.code, type, lat: row.wgs84_latitude, lng: row.wgs84_longitude, })); }; const extractFounderName = (line) => { if (line[0] === "#") { return line.substring(1).trim(); } else { return line.trim(); } }; const getBaseFounderQuery = () => { return getKnexDb() .select("f.id", "f.name", "f.ico", "f.founder_type_code", "f.city_code", "f.city_district_code") .from("founder as f") .leftJoin("city as c", "c.code", "f.city_code") .leftJoin("city_district as d", "d.code", "f.city_district_code") .orderBy("f.founder_type_code"); }; export const getFounderById = async (id, schoolType) => { const result = await getBaseFounderQuery().where("f.id", id).first(); const founder = await resultToFounder(result, schoolType); return { founder: founder ?? null, errors: founder ? [] : [wholeLineError(`Zřizovatel s id ${id} neexistuje.`, "")], }; }; export const findFounder = async (nameWithHashtag, schoolType) => { const errors = []; // special case where we search for a city instead of a founder if (nameWithHashtag[1] === "#") { const cityName = nameWithHashtag.substring(2).trim(); const result = await getKnexDb() .select("code") .from("city") .where("name", cityName) .first(); return { founder: { name: cityName, ico: "00000000", originalType: cityTypeCode, municipalityType: MunicipalityType.City, municipalityCode: result.code, schools: await getSchoolsByCityCode(result.code, schoolType), }, errors, }; } const name = extractFounderName(nameWithHashtag); const result = await getBaseFounderQuery() .where("f.name", name) .orWhere("c.name", name) .orWhere("d.name", name) .first(); if (result) { return { founder: await resultToFounder(result, schoolType), errors }; } else { const allFounderNames = await getAllFounderNames(); const namesList = allFounderNames .map((row) => row.founderName) .concat(allFounderNames.map((row) => row.municipalityName)); const bestMatch = findClosestString(name, namesList); const bestMatchRow = allFounderNames.find((foundersNames) => foundersNames.founderName === bestMatch || foundersNames.municipalityName === bestMatch); if (!bestMatchRow) { errors.push(wholeLineError(`Nenašli jsme žádné zřizovatele, nejspíš jste zapomněli inicializovat databázi.`, nameWithHashtag)); return { founder: null, errors, }; } errors.push(wholeLineError(`Zřizovatel '${name}' neexistuje, mysleli jste '${bestMatch}'?`, nameWithHashtag)); const founder = await getKnexDb() .select("id", "name", "ico", "founder_type_code", "city_code", "city_district_code") .from("founder") .where("id", bestMatchRow.id) .first(); return { founder: await resultToFounder(founder, schoolType), errors, }; } }; let cachedCityCode = null; let lastCode = null; export const getFounderCityCode = async (type, code) => { if (type === MunicipalityType.District) { if (lastCode !== code) { lastCode = code; cachedCityCode = (await getKnexDb().from("city_district").where("code", code).first()).city_code; } return cachedCityCode; } else { return code; } }; const resultToFounder = async (result, schoolType) => { return { name: result.name, ico: result.ico, originalType: result.founder_type_code, municipalityType: result.founder_type_code === cityTypeCode ? MunicipalityType.City : MunicipalityType.District, municipalityCode: result.founder_type_code === cityTypeCode ? result.city_code : result.city_district_code, schools: await getSchoolsByCityCode(result.city_code, schoolType), }; }; // in case of cities, load all schools in the city, not just the ones connected to the founder const getSchoolsByCityCode = async (cityCode, schoolType) => { const result = await getKnexDb() .select("s.izo", "s.redizo", "s.name", "s.capacity", "sl.address_point_id") .from("school as s") .join("school_founder as sf", "s.izo", "sf.school_izo") .join("school_location as sl", "s.izo", "sl.school_izo") .join("founder as f", "sf.founder_id", "f.id") .where("f.city_code", cityCode) .where("s.type", schoolType); return result.map((row) => ({ izo: String(row.izo), redizo: String(row.redizo), name: String(row.name), capacity: parseInt(row.capacity), locations: [ { addressPointId: parseInt(row.address_point_id), }, ], type: schoolType, })); }; const getAllFounderNames = async () => { const result = await getKnexDb() .select("f.id", "f.name as founder_name", "c.name as city_name", "d.name as city_district_name") .from("founder as f") .leftJoin("city as c", "c.code", "f.city_code") .leftJoin("city_district as d", "d.code", "f.city_district_code"); return result.map((row) => ({ id: parseInt(row.id), founderName: String(row.founder_name), municipalityName: String(row.city_name ? row.city_name : row.city_district_name), })); }; export const findMunicipalityPartByName = async (name, cityCode) => { const errors = []; const result = await getKnexDb() .first("code") .from("municipality_part") .where({ name, city_code: cityCode }); if (result) { return { municipalityPartCode: result.code, errors }; } else { const allNames = await getAllMunicipalityPartNames(cityCode); const namesList = allNames.map((row) => row.name); const bestMatch = findClosestString(name, namesList); errors.push({ message: `Obec ani městská část '${name}' neexistuje, mysleli jste '${bestMatch}'?`, startOffset: 0, endOffset: 0, }); return { municipalityPartCode: null, errors, }; } }; export const findMunicipalityByNameAndType = async (name, type, founder) => { const errors = []; const result = type === MunicipalityType.City ? await getKnexDb().select("code").from("city").where("name", name) : await getKnexDb() .select("code") .from("city_district") .where("name", name) .andWhere("city_code", await getFounderCityCode(founder.municipalityType, founder.municipalityCode)); if (result.length > 0) { if (result.length > 1) { const cityCode = await getFounderCityCode(founder.municipalityType, founder.municipalityCode); const positions = await getKnexDb() .select("city_code", "wgs84_latitude", "wgs84_longitude") .from("address_point") .groupBy("city_code") .whereIn("city_code", result.map((row) => row.code)); const founderPosition = await getKnexDb() .first("wgs84_latitude", "wgs84_longitude") .from("address_point") .groupBy("city_code") .where("city_code", cityCode); const closestCode = await getClosestCode({ code: cityCode, lat: founderPosition.wgs84_latitude, lng: founderPosition.wgs84_longitude, }, positions.map((row) => ({ code: row.city_code, lat: row.wgs84_latitude, lng: row.wgs84_longitude, }))); return { municipality: { code: closestCode, type }, errors }; } else { return { municipality: { code: result[0].code, type }, errors }; } } else { const allNames = await getAllMunicipalityNames(type); const namesList = allNames.map((row) => row.name); const bestMatch = findClosestString(name, namesList); const bestMatchRow = allNames.find((municipality) => municipality.name === bestMatch); errors.push({ message: `Obec ani městská část '${name}' neexistuje, mysleli jste '${bestMatch}'?`, startOffset: 0, endOffset: 0, }); return { municipality: { code: bestMatchRow.code, type }, errors, }; } }; const getAllMunicipalityNames = async (type) => { return (await getKnexDb() .select("name", "code") .from(type === MunicipalityType.City ? "city" : "city_district")).map((row) => ({ name: row.name, code: row.code, })); }; const getAllMunicipalityPartNames = async (cityCode) => { return (await getKnexDb() .select("name", "code") .from("municipality_part") .where("city_code", cityCode)).map((row) => ({ name: row.name, code: row.code, })); }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm91bmRlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZGIvZm91bmRlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxRQUFRLE1BQU0sZ0JBQWdCLENBQUM7QUFDdEMsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBTXhELE9BQU8sRUFDTCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLHdCQUF3QixHQUN6QixNQUFNLGtCQUFrQixDQUFDO0FBQzFCLE9BQU8sRUFBRSxTQUFTLEVBQUUsc0JBQXNCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDN0UsT0FBTyxFQUVMLGdCQUFnQixHQUtqQixNQUFNLFNBQVMsQ0FBQztBQUVqQixNQUFNLFlBQVksR0FBRyxHQUFHLENBQUM7QUFDekIsTUFBTSxvQkFBb0IsR0FBRyxHQUFHLENBQUM7QUFFakMsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLEtBQUssRUFBRSxRQUFtQixFQUFtQixFQUFFO0lBQzNFLElBQUksZ0JBQWdCLEdBQUcsQ0FBQyxDQUFDO0lBQ3pCLE1BQU0sMkJBQTJCLEdBQUcsRUFBRSxDQUFDO0lBRXZDLEtBQUssTUFBTSxPQUFPLElBQUksUUFBUSxFQUFFO1FBQzlCLElBQ0UsT0FBTyxDQUFDLGdCQUFnQixLQUFLLGdCQUFnQixDQUFDLElBQUk7WUFDbEQsT0FBTyxDQUFDLGdCQUFnQixLQUFLLGdCQUFnQixDQUFDLFFBQVEsRUFDdEQ7WUFDQSxTQUFTO1NBQ1Y7UUFDRCxNQUFNLHlCQUF5QixHQUFHLHVCQUF1QixDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBRW5FLHdGQUF3RjtRQUN4RixJQUFJLGdCQUFnQixHQUFHLEVBQUUsQ0FBQztRQUMxQixJQUFJLGdCQUFnQixHQUFHLENBQUMsQ0FBQyxDQUFDO1FBRTFCLEtBQUssTUFBTSxNQUFNLElBQUksT0FBTyxDQUFDLE9BQU8sRUFBRTtZQUNwQyxNQUFNLE1B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TixDQUFDLENBQUM7QUFFRixNQUFNLDJCQUEyQixHQUFHLEtBQUssRUFDdkMsUUFBZ0IsRUFDMkIsRUFBRTtJQUM3QyxPQUFPLENBQ0wsTUFBTSxTQUFTLEVBQUU7U0FDZCxNQUFNLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQztTQUN0QixJQUFJLENBQUMsbUJBQW1CLENBQUM7U0FDekIsS0FBSyxDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsQ0FDaEMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDZCxJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUk7UUFDZCxJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUk7S0FDZixDQUFDLENBQUMsQ0FBQztBQUNOLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBkaXN0YW5jZSBmcm9tIFwiQHR1cmYvZGlzdGFuY2VcIjtcclxuaW1wb3J0IHsgd2hvbGVMaW5lRXJyb3IgfSBmcm9tIFwiLi4vc3RyZWV0LW1hcmtkb3duL3NtZFwiO1xyXG5pbXBvcnQge1xyXG4gIERiTXVuaWNpcGFsaXR5UmVzdWx0LFxyXG4gIE11bmljaXBhbGl0eVBhcnRSZXN1bHQsXHJcbiAgU21kRXJyb3IsXHJcbn0gZnJvbSBcIi4uL3N0cmVldC1tYXJrZG93bi90eXBlc1wiO1xyXG5pbXBvcnQge1xyXG4gIGV4dHJhY3RNdW5pY2lwYWxpdHlOYW1lLFxyXG4gIGZpbmRDbG9zZXN0U3RyaW5nLFxyXG4gIHNhbml0aXplTXVuaWNpcGFsaXR5TmFtZSxcclxufSBmcm9tIFwiLi4vdXRpbHMvaGVscGVyc1wiO1xyXG5pbXBvcnQgeyBnZXRLbmV4RGIsIGluc2VydEF1dG9pbmNyZW1lbnRSb3csIGluc2VydE11bHRpcGxlUm93cyB9IGZyb20gXCIuL2RiXCI7XHJcbmltcG9ydCB7XHJcbiAgRm91bmRlcixcclxuICBNdW5pY2lwYWxpdHlUeXBlLFxyXG4gIE11bmljaXBhbGl0eVdpdGhQb3NpdGlvbixcclxuICBQbGFjZVdpdGhQb3NpdGlvbixcclxuICBTY2hvb2wsXHJcbiAgU2Nob29sVHlwZSxcclxufSBmcm9tIFwiLi90eXBlc1wiO1xyXG5cclxuY29uc3QgY2l0eVR5cGVDb2RlID0gMjYxO1xyXG5jb25zdCBjaXR5RGlzdHJpY3RUeXBlQ29kZSA9IDI2MztcclxuXHJcbmV4cG9ydCBjb25zdCBpbnNlcnRGb3VuZGVycyA9IGFzeW5jIChmb3VuZGVyczogRm91bmRlcltdKTogUHJvbWlzZTxudW1iZXI+ID0+IHtcclxuICBsZXQgaW5zZXJ0ZWRGb3VuZGVycyA9IDA7XHJcbiAgY29uc3Qgc2Nob29sRm91bmRlckNvbm5lY3Rpb25EYXRhID0gW107XHJcblxyXG4gIGZvciAoY29uc3QgZm91bmRlciBvZiBmb3VuZGVycykge1xyXG4gICAgaWYgKFxyXG4gICAgICBmb3VuZGVyLm11bmljaXBhbGl0eVR5cGUgIT09IE11bmljaXBhbGl0eVR5cGUuQ2l0eSAmJlxyXG4gICAgICBmb3VuZGVyLm11bmljaXBhbGl0eVR5cGUgIT09IE11bmljaXBhbGl0eVR5cGUuRGlzdHJpY3RcclxuICAgICkge1xyXG4gICAgICBjb250aW51ZTtcclxuICAgIH1cclxuICAgIGNvbnN0IGV4dHJhY3RlZE11bmljaXBhbGl0eU5hbWUgPSBleHRyYWN0TXVuaWNpcGFsaXR5TmFtZShmb3VuZGVyKTtcclxuXHJcbiAgICAvLyBjaGVjayBpZiB0aGUgZXh0cmFjdGVkIG11bmljaXBhbGl0eSBuYW1lIGlzIHRoZSBzYW1lIGFzIGluIGFsbCB0aGUgc2Nob29scycgbG9jYXRpb25zXHJcbiAgICBsZXQgZGlmZmVyaW5nU2Nob29scyA9IFtdO1xyXG4gICAgbGV0IG11bmljaXBhbGl0eUNvZGUgPSAtMTtcclxuXHJcbiAgICBmb3IgKGNvbnN0IHNjaG9vbCBvZiBmb3VuZGVyLnNjaG9vbHMpIHtcclxuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgKGZvdW5kZXIubXVuaWNpcGFsaXR5VHlwZSA9PT0gTXVuaWNpcGFsaXR5VHlwZS5DaXR5XHJcbiAgICAgICAgPyBnZXRDaXR5T2ZTY2hvb2woc2Nob29sLml6bylcclxuICAgICAgICA6IGdldERpc3RyaWN0T2ZTY2hvb2woc2Nob29sLml6bykpO1xyXG4gICAgICBpZiAoIXJlc3VsdCkge1xyXG4gICAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICAgYGl6bzogJHtzY2hvb2wuaXpvfSwgZXh0cmFjdGVkOiAke2V4dHJhY3RlZE11bmljaXBhbGl0eU5hbWV9LCBSVUlBTjogVU5ERUZJTkVEYFxyXG4gICAgICAgICk7XHJcbiAgICAgICAgZGlmZmVyaW5nU2Nob29scy5wdXNoKHNjaG9vbCk7XHJcbiAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgY29uc3QgeyBuYW1lLCBjb2RlIH0gPSByZXN1bHQ7XHJcbiAgICAgICAgaWYgKG5hbWUgIT09IGV4dHJhY3RlZE11bmljaXBhbGl0eU5hbWUpIHtcclxuICAgICAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICAgICBgaXpvOiAke3NjaG9vbC5pem99LCBleHRyYWN0ZWQ6ICR7ZXh0cmFjdGVkTXVuaWNpcGFsaXR5TmFtZX0sIFJVSUFOOiAke25hbWV9YFxyXG4gICAgICAgICAgKTtcclxuICAgICAgICAgIGRpZmZlcmluZ1NjaG9vbHMucHVzaChzY2hvb2wpO1xyXG4gICAgICAgIH1cclxuICAgICAgICAvLyBzdG9yZSBtdW5pY2lwYWxpdHlDb2RlIGV2ZW4gaWYgdGhlIG5hbWVzIGRvbid0IG1hdGNoLCB3ZSB3aWxsIHVzZSBpdCBsYXRlclxyXG4gICAgICAgIG11bmljaXBhbGl0eUNvZGUgPSBwYXJzZUludChjb2RlKTtcclxuICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIG11bmljaXBhbGl0eUNvZGUgPSBhd2FpdCBmaXhGb3VuZGVyUHJvYmxlbXMoXHJcbiAgICAgIGZvdW5kZXIsXHJcbiAgICAgIG11bmljaXBhbGl0eUNvZGUsXHJcbiAgICAgIGRpZmZlcmluZ1NjaG9vbHMsXHJcbiAgICAgIGV4dHJhY3RlZE11bmljaXBhbGl0eU5hbWVcclxuICAgICk7XHJcblxyXG4gICAgY29uc3QgY2l0eURpc3RyaWN0Q29kZSA9XHJcbiAgICAgIGZvdW5kZXIubXVuaWNpcGFsaXR5VHlwZSA9PT0gTXVuaWNpcGFsaXR5VHlwZS5EaXN0cmljdFxyXG4gICAgICAgID8gbXVuaWNpcGFsaXR5Q29kZS50b1N0cmluZygpXHJcbiAgICAgICAgOiBudWxsO1xyXG4gICAgbGV0IGNpdHlDb2RlID0gbnVsbDtcclxuICAgIGlmIChmb3VuZGVyLm11bmljaXBhbGl0eVR5cGUgPT09IE11bmljaXBhbGl0eVR5cGUuQ2l0eSkge1xyXG4gICAgICBjaXR5Q29kZSA9IG11bmljaXBhbGl0eUNvZGU/LnRvU3RyaW5nKCkgPz8gbnVsbDtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGNpdHlDb2RlID0gYXdhaXQgZ2V0Q2l0eUNvZGVCeURpc3RyaWN0Q29kZShtdW5pY2lwYWxpdHlDb2RlKTtcclxuICAgIH1cclxuXHJcbiAgICBjb25zdCBleGlzdGluZyA9IGF3YWl0IGdldEtuZXhEYigpXHJcbiAgICAgIC5zZWxlY3QoXCIqXCIpXHJcbiAgICAgIC5mcm9tKFwiZm91bmRlclwiKVxyXG4gICAgICAud2hlcmUoe1xyXG4gICAgICAgIG5hbWU6IHNhbml0aXplTXVuaWNpcGFsaXR5TmFtZShmb3VuZGVyLm5hbWUpLFxyXG4gICAgICAgIGljbzogZm91bmRlci5pY28sXHJcbiAgICAgIH0pO1xyXG5cclxuICAgIGxldCBmb3VuZGVySWQgPSBleGlzdGluZ1swXT8uaWQgPz8gbnVsbDtcclxuXHJcbiAgICBpZiAoZXhpc3RpbmcubGVuZ3RoID09PSAwKSB7XHJcbiAgICAgIGZvdW5kZXJJZCA9IGF3YWl0IGluc2VydEF1dG9pbmNyZW1lbnRSb3coXHJcbiAgICAgICAgW1xyXG4gICAgICAgICAgc2FuaXRpemVNdW5pY2lwYWxpdHlOYW1lKGZvdW5kZXIubmFtZSksXHJcbiAgICAgICAgICBzYW5pdGl6ZU11bmljaXBhbGl0eU5hbWUoZXh0cmFjdGVkTXVuaWNpcGFsaXR5TmFtZSksXHJcbiAgICAgICAgICBmb3VuZGVyLmljbyxcclxuICAgICAgICAgIFN0cmluZyhmb3VuZGVyLm9yaWdpbmFsVHlwZSksXHJcbiAgICAgICAgICBjaXR5Q29kZSxcclxuICAgICAgICAgIGNpdHlEaXN0cmljdENvZGUsXHJcbiAgICAgICAgXSxcclxuICAgICAgICBcImZvdW5kZXJcIixcclxuICAgICAgICBbXHJcbiAgICAgICAgICBcIm5hbWVcIixcclxuICAgICAgICAgIFwic2hvcnRfbmFtZVwiLFxyXG4gICAgICAgICAgXCJpY29cIixcclxuICAgICAgICAgIFwiZm91bmRlcl90eXBlX2NvZGVcIixcclxuICAgICAgICAgIFwiY2l0eV9jb2RlXCIsXHJcbiAgICAgICAgICBcImNpdHlfZGlzdHJpY3RfY29kZVwiLFxyXG4gICAgICAgIF1cclxuICAgICAgKTtcclxuICAgICAgaW5zZXJ0ZWRGb3VuZGVycysrO1xyXG4gICAgfVxyXG5cclxuICAgIGZvdW5kZXIuc2Nob29scy5mb3JFYWNoKChzY2hvb2wpID0+IHtcclxuICAgICAgc2Nob29sRm91bmRlckNvbm5lY3Rpb25EYXRhLnB1c2goW3NjaG9vbC5pem8sIGZvdW5kZXJJZF0pO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICBjb25zdCBpbnNlcnRlZENvbm5lY3Rpb25zID0gYXdhaXQgaW5zZXJ0TXVsdGlwbGVSb3dzKFxyXG4gICAgc2Nob29sRm91bmRlckNvbm5lY3Rpb25EYXRhLFxyXG4gICAgXCJzY2hvb2xfZm91bmRlclwiLFxyXG4gICAgW1wic2Nob29sX2l6b1wiLCBcImZvdW5kZXJfaWRcIl0sXHJcbiAgICB0cnVlXHJcbiAgKTtcclxuXHJcbiAgcmV0dXJuIGluc2VydGVkRm91bmRlcnMgKyBpbnNlcnRlZENvbm5lY3Rpb25zO1xyXG59O1xyXG5cclxuY29uc3QgZ2V0Q2l0eU9mU2Nob29sID0gYXN5bmMgKGl6bzogc3RyaW5nKTogUHJvbWlzZTxhbnk+ID0+IHtcclxuICByZXR1cm4gYXdhaXQgZ2V0S25leERiKClcclxuICAgIC5zZWxlY3QoXCJjLm5hbWVcIiwgXCJjLmNvZGVcIilcclxuICAgIC5mcm9tKFwic2Nob29sIGFzIHNcIilcclxuICAgIC5qb2luKFwic2Nob29sX2xvY2F0aW9uIGFzIGxcIiwgXCJzLml6b1wiLCBcImwuc2Nob29sX2l6b1wiKVxyXG4gICAgLmpvaW4oXCJhZGRyZXNzX3BvaW50IGFzIGFcIiwgXCJsLmFkZHJlc3NfcG9pbnRfaWRcIiwgXCJhLmlkXCIpXHJcbiAgICAuam9pbihcImNpdHkgYXMgY1wiLCBcImEuY2l0eV9jb2RlXCIsIFwiYy5jb2RlXCIpXHJcbiAgICAud2hlcmUoXCJzLml6b1wiLCBpem8pXHJcbiAgICAubGltaXQoMSlcclxuICAgIC5maXJzdCgpO1xyXG59O1xyXG5cclxuY29uc3QgZ2V0RGlzdHJpY3RPZlNjaG9vbCA9IGFzeW5jIChpem86IHN0cmluZyk6IFByb21pc2U8YW55PiA9PiB7XHJcbiAgcmV0dXJuIGF3YWl0IGdldEtuZXhEYigpXHJcbiAgICAuc2VsZWN0KFwiZC5uYW1lXCIsIFwiZC5jb2RlXCIpXHJcbiAgICAuZnJvbShcInNjaG9vbCBhcyBzXCIpXHJcbiAgICAuam9pbihcInNjaG9vbF9sb2NhdGlvbiBhcyBsXCIsIFwicy5pem9cIiwgXCJsLnNjaG9vbF9pem9cIilcclxuICAgIC5qb2luKFwiYWRkcmVzc19wb2ludCBhcyBhXCIsIFwibC5hZGRyZXNzX3BvaW50X2lkXCIsIFwiYS5pZFwiKVxyXG4gICAgLmpvaW4oXCJjaXR5X2Rpc3RyaWN0IGFzIGRcIiwgXCJhLmNpdHlfZGlzdHJpY3RfY29kZVwiLCBcImQuY29kZVwiKVxyXG4gICAgLndoZXJlKFwicy5pem9cIiwgaXpvKVxyXG4gICAgLmxpbWl0KDEpXHJcbiAgICAuZmlyc3QoKTtcclxufTtcclxuXHJcbmNvbnN0IGZpeEZvdW5kZXJQcm9ibGVtcyA9IGFzeW5jIChcclxuICBmb3VuZGVyOiBGb3VuZGVyLFxyXG4gIG11bmljaXBhbGl0eUNvZGU6IG51bWJlcixcclxuICBkaWZmZXJpbmdTY2hvb2xzOiBTY2hvb2xbXSxcclxuICBleHRyYWN0ZWRNdW5pY2lwYWxpdHlOYW1lOiBzdHJpbmdcclxuKTogUHJvbWlzZTxudW1iZXIgfCBudWxsPiA9PiB7XHJcbiAgaWYgKFxyXG4gICAgZGlmZmVyaW5nU2Nob29scy5sZW5ndGggPT09IDAgfHxcclxuICAgIGRpZmZlcmluZ1NjaG9vbHMubGVuZ3RoIDwgZm91bmRlci5zY2hvb2xzLmxlbmd0aFxyXG4gICkge1xyXG4gICAgcmV0dXJuIG11bmljaXBhbGl0eUNvZGU7XHJcbiAgfVxyXG5cclxuICAvLyBlaXRoZXIgdGhlIHNjaG9vbCBkb2VzIG5vdCBoYXZlIGEgcG9zaXRpb24gKGludmFsaWQgUlVJQU4gb3IgbWlzc2luZyBidWlsZGluZylcclxuICAvLyBvciB0aGUgc2Nob29sIGlzIG5vdCBpbiB0aGUgc2FtZSBtdW5pY2lwYWxpdHkgYXMgdGhlIGZvdW5kZXJcclxuXHJcbiAgLy8gZmluZCBhbGwgY2l0aWVzIGFuZCB0aGVpciBwb3NpdGlvbiB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgbXVuaWNpcGFsaXR5TmFtZVxyXG4gIGNvbnN0IG11bmljaXBhbGl0aWVzID0gYXdhaXQgZmluZE11bmljaXBhbGl0aWVzQW5kUG9zaXRpb25zQnlOYW1lQW5kVHlwZShcclxuICAgIGV4dHJhY3RlZE11bmljaXBhbGl0eU5hbWUsXHJcbiAgICBmb3VuZGVyLm11bmljaXBhbGl0eVR5cGVcclxuICApO1xyXG5cclxuICAvLyBnZXQgb25lIHNjaG9vbCBwb3NpdGlvbiAoaWYgdGhlcmUgYXJlIG1vcmUgc2Nob29scywgdGhleSBzaG91bGQgYmUgY2xvc2UgdG8gZWFjaCBvdGhlcilcclxuICBjb25zdCBzY2hvb2xQb3NpdGlvbjpcclxuICAgIHwgeyB3Z3M4NF9sYXRpdHVkZTogbnVtYmVyOyB3Z3M4NF9sb25naXR1ZGU6IG51bWJlciB9XHJcbiAgICB8IHVuZGVmaW5lZCA9IGF3YWl0IGdldEtuZXhEYigpXHJcbiAgICAuc2VsZWN0KFwiYWRkcmVzc19wb2ludC53Z3M4NF9sYXRpdHVkZVwiLCBcImFkZHJlc3NfcG9pbnQud2dzODRfbG9uZ2l0dWRlXCIpXHJcbiAgICAuZnJvbShcInNjaG9vbFwiKVxyXG4gICAgLmpvaW4oXCJzY2hvb2xfbG9jYXRpb25cIiwgXCJzY2hvb2wuaXpvXCIsIFwic2Nob29sX2xvY2F0aW9uLnNjaG9vbF9pem9cIilcclxuICAgIC5qb2luKFxyXG4gICAgICBcImFkZHJlc3NfcG9pbnRcIixcclxuICAgICAgXCJzY2hvb2xfbG9jYXRpb24uYWRkcmVzc19wb2ludF9pZFwiLFxyXG4gICAgICBcImFkZHJlc3NfcG9pbnQuaWRcIlxyXG4gICAgKVxyXG4gICAgLndoZXJlSW4oXHJcbiAgICAgIFwic2Nob29sLml6b1wiLFxyXG4gICAgICBkaWZmZXJpbmdTY2hvb2xzLm1hcCgoc2Nob29sKSA9PiBzY2hvb2wuaXpvKVxyXG4gICAgKVxyXG4gICAgLmxpbWl0KDEpXHJcbiAgICAuZmlyc3QoKTtcclxuXHJcbiAgaWYgKHNjaG9vbFBvc2l0aW9uKSB7XHJcbiAgICBpZiAobXVuaWNpcGFsaXRpZXMubGVuZ3RoID09PSAwKSB7XHJcbiAgICAgIGlmIChtdW5pY2lwYWxpdHlDb2RlID09PSAtMSkge1xyXG4gICAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICAgYG5vIG11bmljaXBhbGl0eSBieSBuYW1lIG9yIGJ5IGxvY2F0aW9uIGZvdW5kIGZvciAke2ZvdW5kZXIubmFtZX1gXHJcbiAgICAgICAgKTtcclxuICAgICAgICByZXR1cm4gbnVsbDtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICAgIFwibm8gbXVuaWNpcGFsaXR5IG1hdGNoaW5nIHRoZSBleHRyYWN0ZWQgbmFtZSwgdXNpbmcgdGhlIG11bmljaXBhbGl0eSBmcm9tIFJVSUFOIGNvZGVcIlxyXG4gICAgICAgICk7XHJcbiAgICAgIH1cclxuICAgIH0gZWxzZSBpZiAobXVuaWNpcGFsaXRpZXMubGVuZ3RoID09PSAxKSB7XHJcbiAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgIFwidXNpbmcgdGhlIG9ubHkgbXVuaWNpcGFsaXR5IGZvdW5kIG1hdGNoaW5nIHRoZSBleHRyYWN0ZWQgbmFtZVwiXHJcbiAgICAgICk7XHJcbiAgICAgIHJldHVybiBtdW5pY2lwYWxpdGllc1swXS5jb2RlO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgY29uc29sZS5sb2coXCJ1c2luZyB0aGUgY2xvc2VzdCBtdW5pY2lwYWxpdHkgbWF0Y2hpbmcgdGhlIGV4dHJhY3RlZCBuYW1lXCIpO1xyXG4gICAgICByZXR1cm4gZ2V0Q2xvc2VzdENvZGUoXHJcbiAgICAgICAge1xyXG4gICAgICAgICAgY29kZTogMSxcclxuICAgICAgICAgIGxhdDogc2Nob29sUG9zaXRpb24ud2dzODRfbGF0aXR1ZGUsXHJcbiAgICAgICAgICBsbmc6IHNjaG9vbFBvc2l0aW9uLndnczg0X2xvbmdpdHVkZSxcclxuICAgICAgICB9LFxyXG4gICAgICAgIG11bmljaXBhbGl0aWVzLm1hcCgobXVuaWNpcGFsaXR5KSA9PiAoe1xyXG4gICAgICAgICAgY29kZTogbXVuaWNpcGFsaXR5LmNvZGUsXHJcbiAgICAgICAgICBsYXQ6IG11bmljaXBhbGl0eS5sYXQsXHJcbiAgICAgICAgICBsbmc6IG11bmljaXBhbGl0eS5sbmcsXHJcbiAgICAgICAgfSkpXHJcbiAgICAgICk7XHJcbiAgICB9XHJcbiAgfSBlbHNlIHtcclxuICAgIGlmIChtdW5pY2lwYWxpdGllcy5sZW5ndGggPiAwKSB7XHJcbiAgICAgIGlmIChtdW5pY2lwYWxpdGllcy5sZW5ndGggPiAxKSB7XHJcbiAgICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgICBgdXNpbmcgdGhlIGZpcnN0IG11bmljaXBhbGl0eSBtYXRjaGluZyB0aGUgZXh0cmFjdGVkIG5hbWUgKCR7bXVuaWNpcGFsaXRpZXMubGVuZ3RofSBtYXRjaGVzKSAtIHBvc3NpYmx5IGluY29ycmVjdCFgXHJcbiAgICAgICAgKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBjb25zb2xlLmxvZyhcclxuICAgICAgICAgIFwidXNpbmcgdGhlIG9ubHkgbXVuaWNpcGFsaXR5IGZvdW5kIG1hdGNoaW5nIHRoZSB