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

461 lines 68.9 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; const schoolsOutsideTheirFounderMunicipality = [ "181042096", "107516306", "181147246", "150076924", "107583992", "181024161", "181064588", "107629062", ]; 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) { // also filter out known exceptions if (!schoolsOutsideTheirFounderMunicipality.includes(school.izo)) { 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm91bmRlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZGIvZm91bmRlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxRQUFRLE1BQU0sZ0JBQWdCLENBQUM7QUFDdEMsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBTXhELE9BQU8sRUFDTCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLHdCQUF3QixHQUN6QixNQUFNLGtCQUFrQixDQUFDO0FBQzFCLE9BQU8sRUFBRSxTQUFTLEVBQUUsc0JBQXNCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDN0UsT0FBTyxFQUVMLGdCQUFnQixHQUtqQixNQUFNLFNBQVMsQ0FBQztBQUVqQixNQUFNLFlBQVksR0FBRyxHQUFHLENBQUM7QUFDekIsTUFBTSxvQkFBb0IsR0FBRyxHQUFHLENBQUM7QUFFakMsTUFBTSxzQ0FBc0MsR0FBRztJQUM3QyxXQUFXO0lBQ1gsV0FBVztJQUNYLFdBQVc7SUFDWCxXQUFXO0lBQ1gsV0FBVztJQUNYLFdBQVc7SUFDWCxXQUFXO0lBQ1gsV0FBVztDQUNaLENBQUM7QUFFRixNQUFNLENBQUMsTUFBTSxjQUFjLEdBQUcsS0FBSyxFQUFFLFFBQW1CLEVBQW1CLEVBQUU7SUFDM0UsSUFBSSxnQkFBZ0IsR0FBRyxDQUFDLENBQUM7SUFDekIsTUFBTSwyQkFBMkIsR0FBRyxFQUFFLENBQUM7SUFFdkMsS0FBSyxNQUFNLE9BQU8sSUFBSSxRQUFRLEVBQUU7UUFDOUIsSUFDRSxPQUFPLENBQUMsZ0JBQWdCLEtBQUssZ0JBQWdCLENBQUMsSUFBSTtZQUNsRCxPQUFPLENBQUMsZ0JBQWdCLEtBQUssZ0JBQWdCLENBQUMsUUFBUSxFQUN0RDtZQUNBLFNBQVM7U0FDVjtRQUNELE1BQU0seUJBQXlCLEdBQUcsdUJBQXVCLENBQUMsT0FBTyxDQUFDLENBQUM7UUFFbkUsd0ZBQXdGO1FBQ3hGLElBQUksZ0JBQWdC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LGdCQUFnQixDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxlQUFlLENBQUMsQ0FDbkUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDZCxJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUk7UUFDZCxJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUk7S0FDZixDQUFDLENBQUMsQ0FBQztBQUNOLENBQUMsQ0FBQztBQUVGLE1BQU0sMkJBQTJCLEdBQUcsS0FBSyxFQUN2QyxRQUFnQixFQUMyQixFQUFFO0lBQzdDLE9BQU8sQ0FDTCxNQUFNLFNBQVMsRUFBRTtTQUNkLE1BQU0sQ0FBQyxNQUFNLEVBQUUsTUFBTSxDQUFDO1NBQ3RCLElBQUksQ0FBQyxtQkFBbUIsQ0FBQztTQUN6QixLQUFLLENBQUMsV0FBVyxFQUFFLFFBQVEsQ0FBQyxDQUNoQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNkLElBQUksRUFBRSxHQUFHLENBQUMsSUFBSTtRQUNkLElBQUksRUFBRSxHQUFHLENBQUMsSUFBSTtLQUNmLENBQUMsQ0FBQyxDQUFDO0FBQ04sQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGRpc3RhbmNlIGZyb20gXCJAdHVyZi9kaXN0YW5jZVwiO1xyXG5pbXBvcnQgeyB3aG9sZUxpbmVFcnJvciB9IGZyb20gXCIuLi9zdHJlZXQtbWFya2Rvd24vc21kXCI7XHJcbmltcG9ydCB7XHJcbiAgRGJNdW5pY2lwYWxpdHlSZXN1bHQsXHJcbiAgTXVuaWNpcGFsaXR5UGFydFJlc3VsdCxcclxuICBTbWRFcnJvcixcclxufSBmcm9tIFwiLi4vc3RyZWV0LW1hcmtkb3duL3R5cGVzXCI7XHJcbmltcG9ydCB7XHJcbiAgZXh0cmFjdE11bmljaXBhbGl0eU5hbWUsXHJcbiAgZmluZENsb3Nlc3RTdHJpbmcsXHJcbiAgc2FuaXRpemVNdW5pY2lwYWxpdHlOYW1lLFxyXG59IGZyb20gXCIuLi91dGlscy9oZWxwZXJzXCI7XHJcbmltcG9ydCB7IGdldEtuZXhEYiwgaW5zZXJ0QXV0b2luY3JlbWVudFJvdywgaW5zZXJ0TXVsdGlwbGVSb3dzIH0gZnJvbSBcIi4vZGJcIjtcclxuaW1wb3J0IHtcclxuICBGb3VuZGVyLFxyXG4gIE11bmljaXBhbGl0eVR5cGUsXHJcbiAgTXVuaWNpcGFsaXR5V2l0aFBvc2l0aW9uLFxyXG4gIFBsYWNlV2l0aFBvc2l0aW9uLFxyXG4gIFNjaG9vbCxcclxuICBTY2hvb2xUeXBlLFxyXG59IGZyb20gXCIuL3R5cGVzXCI7XHJcblxyXG5jb25zdCBjaXR5VHlwZUNvZGUgPSAyNjE7XHJcbmNvbnN0IGNpdHlEaXN0cmljdFR5cGVDb2RlID0gMjYzO1xyXG5cclxuY29uc3Qgc2Nob29sc091dHNpZGVUaGVpckZvdW5kZXJNdW5pY2lwYWxpdHkgPSBbXHJcbiAgXCIxODEwNDIwOTZcIixcclxuICBcIjEwNzUxNjMwNlwiLFxyXG4gIFwiMTgxMTQ3MjQ2XCIsXHJcbiAgXCIxNTAwNzY5MjRcIixcclxuICBcIjEwNzU4Mzk5MlwiLFxyXG4gIFwiMTgxMDI0MTYxXCIsXHJcbiAgXCIxODEwNjQ1ODhcIixcclxuICBcIjEwNzYyOTA2MlwiLFxyXG5dO1xyXG5cclxuZXhwb3J0IGNvbnN0IGluc2VydEZvdW5kZXJzID0gYXN5bmMgKGZvdW5kZXJzOiBGb3VuZGVyW10pOiBQcm9taXNlPG51bWJlcj4gPT4ge1xyXG4gIGxldCBpbnNlcnRlZEZvdW5kZXJzID0gMDtcclxuICBjb25zdCBzY2hvb2xGb3VuZGVyQ29ubmVjdGlvbkRhdGEgPSBbXTtcclxuXHJcbiAgZm9yIChjb25zdCBmb3VuZGVyIG9mIGZvdW5kZXJzKSB7XHJcbiAgICBpZiAoXHJcbiAgICAgIGZvdW5kZXIubXVuaWNpcGFsaXR5VHlwZSAhPT0gTXVuaWNpcGFsaXR5VHlwZS5DaXR5ICYmXHJcbiAgICAgIGZvdW5kZXIubXVuaWNpcGFsaXR5VHlwZSAhPT0gTXVuaWNpcGFsaXR5VHlwZS5EaXN0cmljdFxyXG4gICAgKSB7XHJcbiAgICAgIGNvbnRpbnVlO1xyXG4gICAgfVxyXG4gICAgY29uc3QgZXh0cmFjdGVkTXVuaWNpcGFsaXR5TmFtZSA9IGV4dHJhY3RNdW5pY2lwYWxpdHlOYW1lKGZvdW5kZXIpO1xyXG5cclxuICAgIC8vIGNoZWNrIGlmIHRoZSBleHRyYWN0ZWQgbXVuaWNpcGFsaXR5IG5hbWUgaXMgdGhlIHNhbWUgYXMgaW4gYWxsIHRoZSBzY2hvb2xzJyBsb2NhdGlvbnNcclxuICAgIGxldCBkaWZmZXJpbmdTY2hvb2xzID0gW107XHJcbiAgICBsZXQgbXVuaWNpcGFsaXR5Q29kZSA9IC0xO1xyXG5cclxuICAgIGZvciAoY29uc3Qgc2Nob29sIG9mIGZvdW5kZXIuc2Nob29scykge1xyXG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCAoZm91bmRlci5tdW5pY2lwYWxpdHlUeXBlID09PSBNdW5pY2lwYWxpdHlUeXBlLkNpdHlcclxuICAgICAgICA/IGdldENpdHlPZlNjaG9vbChzY2hvb2wuaXpvKVxyXG4gICAgICAgIDogZ2V0RGlzdHJpY3RPZlNjaG9vbChzY2hvb2wuaXpvKSk7XHJcbiAgICAgIGlmICghcmVzdWx0KSB7XHJcbiAgICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgICBgaXpvOiAke3NjaG9vbC5pem99LCBleHRyYWN0ZWQ6ICR7ZXh0cmFjdGVkTXVuaWNpcGFsaXR5TmFtZX0sIFJVSUFOOiBVTkRFRklORURgXHJcbiAgICAgICAgKTtcclxuICAgICAgICBkaWZmZXJpbmdTY2hvb2xzLnB1c2goc2Nob29sKTtcclxuICAgICAgfSBlbHNlIHtcclxuICAgICAgICBjb25zdCB7IG5hbWUsIGNvZGUgfSA9IHJlc3VsdDtcclxuICAgICAgICBpZiAobmFtZSAhPT0gZXh0cmFjdGVkTXVuaWNpcGFsaXR5TmFtZSkge1xyXG4gICAgICAgICAgLy8gYWxzbyBmaWx0ZXIgb3V0IGtub3duIGV4Y2VwdGlvbnNcclxuICAgICAgICAgIGlmICghc2Nob29sc091dHNpZGVUaGVpckZvdW5kZXJNdW5pY2lwYWxpdHkuaW5jbHVkZXMoc2Nob29sLml6bykpIHtcclxuICAgICAgICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgICAgICAgYGl6bzogJHtzY2hvb2wuaXpvfSwgZXh0cmFjdGVkOiAke2V4dHJhY3RlZE11bmljaXBhbGl0eU5hbWV9LCBSVUlBTjogJHtuYW1lfWBcclxuICAgICAgICAgICAgKTtcclxuICAgICAgICAgICAgZGlmZmVyaW5nU2Nob29scy5wdXNoKHNjaG9vbCk7XHJcbiAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIC8vIHN0b3JlIG11bmljaXBhbGl0eUNvZGUgZXZlbiBpZiB0aGUgbmFtZXMgZG9uJ3QgbWF0Y2gsIHdlIHdpbGwgdXNlIGl0IGxhdGVyXHJcbiAgICAgICAgbXVuaWNpcGFsaXR5Q29kZSA9IHBhcnNlSW50KGNvZGUpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgbXVuaWNpcGFsaXR5Q29kZSA9IGF3YWl0IGZpeEZvdW5kZXJQcm9ibGVtcyhcclxuICAgICAgZm91bmRlcixcclxuICAgICAgbXVuaWNpcGFsaXR5Q29kZSxcclxuICAgICAgZGlmZmVyaW5nU2Nob29scyxcclxuICAgICAgZXh0cmFjdGVkTXVuaWNpcGFsaXR5TmFtZVxyXG4gICAgKTtcclxuXHJcbiAgICBjb25zdCBjaXR5RGlzdHJpY3RDb2RlID1cclxuICAgICAgZm91bmRlci5tdW5pY2lwYWxpdHlUeXBlID09PSBNdW5pY2lwYWxpdHlUeXBlLkRpc3RyaWN0XHJcbiAgICAgICAgPyBtdW5pY2lwYWxpdHlDb2RlLnRvU3RyaW5nKClcclxuICAgICAgICA6IG51bGw7XHJcbiAgICBsZXQgY2l0eUNvZGUgPSBudWxsO1xyXG4gICAgaWYgKGZvdW5kZXIubXVuaWNpcGFsaXR5VHlwZSA9PT0gTXVuaWNpcGFsaXR5VHlwZS5DaXR5KSB7XHJcbiAgICAgIGNpdHlDb2RlID0gbXVuaWNpcGFsaXR5Q29kZT8udG9TdHJpbmcoKSA/PyBudWxsO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgY2l0eUNvZGUgPSBhd2FpdCBnZXRDaXR5Q29kZUJ5RGlzdHJpY3RDb2RlKG11bmljaXBhbGl0eUNvZGUpO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IGV4aXN0aW5nID0gYXdhaXQgZ2V0S25leERiKClcclxuICAgICAgLnNlbGVjdChcIipcIilcclxuICAgICAgLmZyb20oXCJmb3VuZGVyXCIpXHJcbiAgICAgIC53aGVyZSh7XHJcbiAgICAgICAgbmFtZTogc2FuaXRpemVNdW5pY2lwYWxpdHlOYW1lKGZvdW5kZXIubmFtZSksXHJcbiAgICAgICAgaWNvOiBmb3VuZGVyLmljbyxcclxuICAgICAgfSk7XHJcblxyXG4gICAgbGV0IGZvdW5kZXJJZCA9IGV4aXN0aW5nWzBdPy5pZCA/PyBudWxsO1xyXG5cclxuICAgIGlmIChleGlzdGluZy5sZW5ndGggPT09IDApIHtcclxuICAgICAgZm91bmRlcklkID0gYXdhaXQgaW5zZXJ0QXV0b2luY3JlbWVudFJvdyhcclxuICAgICAgICBbXHJcbiAgICAgICAgICBzYW5pdGl6ZU11bmljaXBhbGl0eU5hbWUoZm91bmRlci5uYW1lKSxcclxuICAgICAgICAgIHNhbml0aXplTXVuaWNpcGFsaXR5TmFtZShleHRyYWN0ZWRNdW5pY2lwYWxpdHlOYW1lKSxcclxuICAgICAgICAgIGZvdW5kZXIuaWNvLFxyXG4gICAgICAgICAgU3RyaW5nKGZvdW5kZXIub3JpZ2luYWxUeXBlKSxcclxuICAgICAgICAgIGNpdHlDb2RlLFxyXG4gICAgICAgICAgY2l0eURpc3RyaWN0Q29kZSxcclxuICAgICAgICBdLFxyXG4gICAgICAgIFwiZm91bmRlclwiLFxyXG4gICAgICAgIFtcclxuICAgICAgICAgIFwibmFtZVwiLFxyXG4gICAgICAgICAgXCJzaG9ydF9uYW1lXCIsXHJcbiAgICAgICAgICBcImljb1wiLFxyXG4gICAgICAgICAgXCJmb3VuZGVyX3R5cGVfY29kZVwiLFxyXG4gICAgICAgICAgXCJjaXR5X2NvZGVcIixcclxuICAgICAgICAgIFwiY2l0eV9kaXN0cmljdF9jb2RlXCIsXHJcbiAgICAgICAgXVxyXG4gICAgICApO1xyXG4gICAgICBpbnNlcnRlZEZvdW5kZXJzKys7XHJcbiAgICB9XHJcblxyXG4gICAgZm91bmRlci5zY2hvb2xzLmZvckVhY2goKHNjaG9vbCkgPT4ge1xyXG4gICAgICBzY2hvb2xGb3VuZGVyQ29ubmVjdGlvbkRhdGEucHVzaChbc2Nob29sLml6bywgZm91bmRlcklkXSk7XHJcbiAgICB9KTtcclxuICB9XHJcblxyXG4gIGNvbnN0IGluc2VydGVkQ29ubmVjdGlvbnMgPSBhd2FpdCBpbnNlcnRNdWx0aXBsZVJvd3MoXHJcbiAgICBzY2hvb2xGb3VuZGVyQ29ubmVjdGlvbkRhdGEsXHJcbiAgICBcInNjaG9vbF9mb3VuZGVyXCIsXHJcbiAgICBbXCJzY2hvb2xfaXpvXCIsIFwiZm91bmRlcl9pZFwiXSxcclxuICAgIHRydWVcclxuICApO1xyXG5cclxuICByZXR1cm4gaW5zZXJ0ZWRGb3VuZGVycyArIGluc2VydGVkQ29ubmVjdGlvbnM7XHJcbn07XHJcblxyXG5jb25zdCBnZXRDaXR5T2ZTY2hvb2wgPSBhc3luYyAoaXpvOiBzdHJpbmcpOiBQcm9taXNlPGFueT4gPT4ge1xyXG4gIHJldHVybiBhd2FpdCBnZXRLbmV4RGIoKVxyXG4gICAgLnNlbGVjdChcImMubmFtZVwiLCBcImMuY29kZVwiKVxyXG4gICAgLmZyb20oXCJzY2hvb2wgYXMgc1wiKVxyXG4gICAgLmpvaW4oXCJzY2hvb2xfbG9jYXRpb24gYXMgbFwiLCBcInMuaXpvXCIsIFwibC5zY2hvb2xfaXpvXCIpXHJcbiAgICAuam9pbihcImFkZHJlc3NfcG9pbnQgYXMgYVwiLCBcImwuYWRkcmVzc19wb2ludF9pZFwiLCBcImEuaWRcIilcclxuICAgIC5qb2luKFwiY2l0eSBhcyBjXCIsIFwiYS5jaXR5X2NvZGVcIiwgXCJjLmNvZGVcIilcclxuICAgIC53aGVyZShcInMuaXpvXCIsIGl6bylcclxuICAgIC5saW1pdCgxKVxyXG4gICAgLmZpcnN0KCk7XHJcbn07XHJcblxyXG5jb25zdCBnZXREaXN0cmljdE9mU2Nob29sID0gYXN5bmMgKGl6bzogc3RyaW5nKTogUHJvbWlzZTxhbnk+ID0+IHtcclxuICByZXR1cm4gYXdhaXQgZ2V0S25leERiKClcclxuICAgIC5zZWxlY3QoXCJkLm5hbWVcIiwgXCJkLmNvZGVcIilcclxuICAgIC5mcm9tKFwic2Nob29sIGFzIHNcIilcclxuICAgIC5qb2luKFwic2Nob29sX2xvY2F0aW9uIGFzIGxcIiwgXCJzLml6b1wiLCBcImwuc2Nob29sX2l6b1wiKVxyXG4gICAgLmpvaW4oXCJhZGRyZXNzX3BvaW50IGFzIGFcIiwgXCJsLmFkZHJlc3NfcG9pbnRfaWRcIiwgXCJhLmlkXCIpXHJcbiAgICAuam9pbihcImNpdHlfZGlzdHJpY3QgYXMgZFwiLCBcImEuY2l0eV9kaXN0cmljdF9jb2RlXCIsIFwiZC5jb2RlXCIpXHJcbiAgICAud2hlcmUoXCJzLml6b1wiLCBpem8pXHJcbiAgICAubGltaXQoMSlcclxuICAgIC5maXJzdCgpO1xyXG59O1xyXG5cclxuY29uc3QgZml4Rm91bmRlclByb2JsZW1zID0gYXN5bmMgKFxyXG4gIGZvdW5kZXI6IEZvdW5kZXIsXHJcbiAgbXVuaWNpcGFsaXR5Q29kZTogbnVtYmVyLFxyXG4gIGRpZmZlcmluZ1NjaG9vbHM6IFNjaG9vbFtdLFxyXG4gIGV4dHJhY3RlZE11bmljaXBhbGl0eU5hbWU6IHN0cmluZ1xyXG4pOiBQcm9taXNlPG51bWJlciB8IG51bGw+ID0+IHtcclxuICBpZiAoXHJcbiAgICBkaWZmZXJpbmdTY2hvb2xzLmxlbmd0aCA9PT0gMCB8fFxyXG4gICAgZGlmZmVyaW5nU2Nob29scy5sZW5ndGggPCBmb3VuZGVyLnNjaG9vbHMubGVuZ3RoXHJcbiAgKSB7XHJcbiAgICByZXR1cm4gbXVuaWNpcGFsaXR5Q29kZTtcclxuICB9XHJcblxyXG4gIC8vIGVpdGhlciB0aGUgc2Nob29sIGRvZXMgbm90IGhhdmUgYSBwb3NpdGlvbiAoaW52YWxpZCBSVUlBTiBvciBtaXNzaW5nIGJ1aWxkaW5nKVxyXG4gIC8vIG9yIHRoZSBzY2hvb2wgaXMgbm90IGluIHRoZSBzYW1lIG11bmljaXBhbGl0eSBhcyB0aGUgZm91bmRlclxyXG5cclxuICAvLyBmaW5kIGFsbCBjaXRpZXMgYW5kIHRoZWlyIHBvc2l0aW9uIHdpdGggdGhlIHNhbWUgbmFtZSBhcyBtdW5pY2lwYWxpdHlOYW1lXHJcbiAgY29uc3QgbXVuaWNpcGFsaXRpZXMgPSBhd2FpdCBmaW5kTXVuaWNpcGFsaXRpZXNBbmRQb3NpdGlvbnNCeU5hbWVBbmRUeXBlKFxyXG4gICAgZXh0cmFjdGVkTXVuaWNpcGFsaXR5TmFtZSxcclxuICAgIGZvdW5kZXIubXVuaWNpcGFsaXR5VHlwZVxyXG4gICk7XHJcblxyXG4gIC8vIGdldCBvbmUgc2Nob29sIHBvc2l0aW9uIChpZiB0aGVyZSBhcmUgbW9yZSBzY2hvb2xzLCB0aGV5IHNob3VsZCBiZSBjbG9zZSB0byBlYWNoIG90aGVyKVxyXG4gIGNvbnN0IHNjaG9vbFBvc2l0aW9uOlxyXG4gICAgfCB7IHdnczg0X2xhdGl0dWRlOiBudW1iZXI7IHdnczg0X2xvbmdpdHVkZTogbnVtYmVyIH1cclxuICAgIHwgdW5kZWZpbmVkID0gYXdhaXQgZ2V0S25leERiKClcclxuICAgIC5zZWxlY3QoXCJhZGRyZXNzX3BvaW50Lndnczg0X2xhdGl0dWRlXCIsIFwiYWRkcmVzc19wb2ludC53Z3M4NF9sb25naXR1ZGVcIilcclxuICAgIC5mcm9tKFwic2Nob29sXCIpXHJcbiAgICAuam9pbihcInNjaG9vbF9sb2NhdGlvblwiLCBcInNjaG9vbC5pem9cIiwgXCJzY2hvb2xfbG9jYXRpb24uc2Nob29sX2l6b1wiKVxyXG4gICAgLmpvaW4oXHJcbiAgICAgIFwiYWRkcmVzc19wb2ludFwiLFxyXG4gICAgICBcInNjaG9vbF9sb2NhdGlvbi5hZGRyZXNzX3BvaW50X2lkXCIsXHJcbiAgICAgIFwiYWRkcmVzc19wb2ludC5pZFwiXHJcbiAgICApXHJcbiAgICAud2hlcmVJbihcclxuICAgICAgXCJzY2hvb2wuaXpvXCIsXHJcbiAgICAgIGRpZmZlcmluZ1NjaG9vbHMubWFwKChzY2hvb2wpID0+IHNjaG9vbC5pem8pXHJcbiAgICApXHJcbiAgICAubGltaXQoMSlcclxuICAgIC5maXJzdCgpO1xyXG5cclxuICBpZiAoc2Nob29sUG9zaXRpb24pIHtcclxuICAgIGlmIChtdW5pY2lwYWxpdGllcy5sZW5ndGggPT09IDApIHtcclxuICAgICAgaWYgKG11bmljaXBhbGl0eUNvZGUgPT09IC0xKSB7XHJcbiAgICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgICBgbm8gbXVuaWNpcGFsaXR5IGJ5IG5hbWUgb3IgYnkgbG9jYXRpb24gZm91bmQgZm9yICR7Zm91bmRlci5uYW1lfWBcclxuICAgICAgICApO1xyXG4gICAgICAgIHJldHVybiBudWxsO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGNvbnNvbGUubG9nKFxyXG4gICAgICAgICAgXCJubyBtdW5pY2lwYWxpdHkgbWF0Y2hpbmcgdGhlIGV4dHJhY3RlZCBuYW1lLCB1c2luZyB0aGUgbXVuaWNpcGFsaXR5IGZyb20gUlVJQU4gY29kZVwiXHJcbiAgICAgICAgKTtcclxuICAgICAgfVxyXG4gICAgfSBlbHNlIGlmIChtdW5pY2lwYWxpdGllcy5sZW5ndGggPT09IDEpIHtcclxuICAgICAgY29uc29sZS5sb2coXHJcbiAgICAgICAgXCJ1c2luZyB0aGUgb25seSBtdW5pY2lwYWxpdHkgZm91bmQgbWF0Y2hpbmcgdGhlIGV4dHJhY3RlZCBuYW1lXCJcclxuICAgICAgKTtcclxuICAgICAgcmV0dXJuIG11bmljaXBhbGl0aWVzWzBd