UNPKG

@tricoteuses/assemblee

Version:

Retrieve, clean up & handle French Assemblée nationale's open data

242 lines (239 loc) 35.9 kB
import assert from "assert"; import commandLineArgs from "command-line-args"; import { createHash } from "crypto"; import { differenceInDays } from "date-fns"; import fs from "fs-extra"; import path from "path"; import { walkDocumentAndDivisions } from "../dossiers_legislatifs.mjs"; import * as git from "../git.mjs"; import { iterLoadAssembleeDocuments, pathFromDocumentUid } from "../loaders.mjs"; import { parseTexte } from "../parsers/index.mjs"; import { DocumentUrlFormat, iterDocumentOrDivisionUrls } from "../urls.mjs"; import { commitOption, dataDirDefaultOption, legislatureOption, pullOption, remoteOption, silentOption, verboseOption, documentFormatOption } from "./shared/cli_helpers.mjs"; function parseArgs(argv) { const optionsDefinitions = [commitOption, legislatureOption, remoteOption, silentOption, verboseOption, dataDirDefaultOption, pullOption, { alias: "f", help: "retrieve all documents, even already retrieved ones", name: "full", type: Boolean }, { alias: "n", help: "try to also retrieve documents that were previously not found", name: "not-found", type: Boolean }, documentFormatOption, { alias: "T", help: "type of documents to retrieve (for example: PION)", multiple: true, name: "document-type", type: String }]; const options = commandLineArgs(optionsDefinitions, { argv: argv }); return options; } const today = new Date(); async function fetchWithRetry(url, retries = 3, backoff = 300) { for (let attempt = 0; attempt < retries; attempt++) { try { return await fetch(url); } catch (error) { if (attempt === retries - 1) { throw error; } console.warn(`Fetch attempt ${attempt + 1} for ${url} failed. Retrying in ${backoff}ms...`); await new Promise(resolve => setTimeout(resolve, backoff)); backoff *= 2; } } throw new Error(`Failed to fetch ${url} after ${retries} attempts`); } export async function downloadAndParse(document, documentsDir, options) { if (document.uid.substring(4, 6) === "SN") { return; } if (["RAPP", "RINF"].includes(document.uid.substring(0, 4))) { return; } if (options.legislature?.toString() !== document.legislature) { return; } if (options.fetchDocuments) { const dataDir = options.dataDir; const documentsFilesDir = path.join(dataDir, "Documents"); const documentDir = pathFromDocumentUid(`${documentsDir}/documents`, document.uid); const documentFileDir = pathFromDocumentUid(documentsFilesDir, document.uid); fs.ensureDirSync(documentsFilesDir); await processDocumentOrDivision(document, documentsFilesDir, options); if (options.parseDocuments) { const documentPath = `${documentDir}.json`; const htmlPath = path.join(documentFileDir, `dyn-opendata.html`); if (fs.existsSync(htmlPath)) { const html = fs.readFileSync(htmlPath, { encoding: "utf-8" }); try { const parsedDocument = parseTexte("https://www.assemblee-nationale.fr", html); if (parsedDocument && fs.existsSync(documentPath)) { const json = fs.readFileSync(documentPath, { encoding: "utf-8" }); const documentJson = JSON.parse(json); documentJson.subdivisions = parsedDocument?.subdivisions; fs.writeFileSync(documentPath, JSON.stringify(documentJson, null, 2)); } } catch (e) { console.log(`Unable to parse document ${document.uid}`); return; } } } } } async function retrieveDocuments(options) { assert(!options.commit || !options.uid, 'Options "commit" & "uid" are incompatible'); const dataDir = options.dataDir; const documentsDir = path.join(dataDir, "Documents"); if (options.pull) { git.resetAndPull(documentsDir); } fs.ensureDirSync(documentsDir); if (options.full && !options.uid) { for (const filename of fs.readdirSync(documentsDir)) { if (filename[0] === ".") { continue; } fs.removeSync(path.join(documentsDir, filename)); } } const firstUid = options.uid; let skip = Boolean(firstUid); for (const { document } of iterLoadAssembleeDocuments(dataDir, options.legislature)) { for (const documentOrDivision of walkDocumentAndDivisions(document)) { // Ignore documents from Sénat. if (documentOrDivision.uid.substring(4, 6) === "SN") { continue; } if (skip) { if (documentOrDivision.uid === firstUid) { skip = false; } else { continue; } } await processDocumentOrDivision(documentOrDivision, documentsDir, options); } } if (options.commit) { return git.commitAndPush(documentsDir, "Nouvelle moisson", options.remote); } return 0; } async function processDocumentOrDivision(documentOrDivision, documentsDir, options) { const documentDir = pathFromDocumentUid(documentsDir, documentOrDivision.uid); fs.ensureDirSync(documentDir); const filenameBySha256 = {}; const indexPath = path.join(documentDir, "index.json"); const index = fs.pathExistsSync(indexPath) ? fs.readJsonSync(indexPath) : {}; for (const { format, type, url } of iterDocumentOrDivisionUrls(documentOrDivision)) { const filename = `${type}.${format === DocumentUrlFormat.Pdf ? "pdf" : "html"}`; // Filter by file extension if option is passed const fileExtension = url.split(".").pop(); if (options.format && fileExtension && options.format !== fileExtension) { continue; } if (options.legislature && options.legislature.toString() !== documentOrDivision.legislature) { continue; } // Filter by document type if option is passed const documentType = documentOrDivision.classification?.type?.code; if (options["document-type"] !== undefined && !options["document-type"].includes(documentType)) { continue; } let formatFilesInfos = index[format] ?? (index[format] = []); let fileInfos = formatFilesInfos.find(file => file.url === url) ?? {}; if (!formatFilesInfos.includes(fileInfos)) { formatFilesInfos.push(fileInfos); } fileInfos.url = url; if (fileInfos.status === 200 && !options.full) { filenameBySha256[fileInfos.sha256] = filename; continue; } if (fileInfos.status === 404 && !options["not-found"] && differenceInDays(today, documentOrDivision.cycleDeVie.chrono.dateCreation ?? documentOrDivision.cycleDeVie.chrono.dateDepot) > 10) { continue; } if (!options.silent) { console.log(`Retrieving document or division ${documentOrDivision.uid} at ${url}…`); } const response = await fetchWithRetry(url); const filePath = path.join(documentDir, filename); if (response.ok) { const arrayBuffer = await response.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); if (format === DocumentUrlFormat.Pdf && !buffer.subarray(0, 4).toString().startsWith("%PDF")) { // Instead of a PDF, the received data may be an HTML page with a message like // "Document non encore publié". if (!options.silent) { console.warn(` PDF "${url}" not found.`); } fs.removeSync(filePath); delete fileInfos.filename; delete fileInfos.sha256; fileInfos.status = 404; } else { const sha256 = createHash("sha256").update(buffer).digest("hex"); const existingFilename = filenameBySha256[sha256]; if (existingFilename === undefined) { fs.writeFileSync(filePath, buffer, { encoding: "utf8" }); fileInfos.filename = filename; filenameBySha256[sha256] = filename; } else { fileInfos.filename = existingFilename; } fileInfos.sha256 = sha256; fileInfos.status = response.status; } } else { if (response.status === 404) { if (!options.silent) { console.warn(` Page "${url}" not found.`); } } else { console.error(` Error:\n${JSON.stringify({ code: response.status, message: response.statusText }, null, 2)}`); } fs.removeSync(filePath); delete fileInfos.filename; delete fileInfos.sha256; fileInfos.status = response.status; } } fs.writeJsonSync(indexPath, index, { encoding: "utf-8", spaces: 2 }); } function main(argv) { const options = parseArgs(argv); return retrieveDocuments(options); } /* istanbul ignore if */ if (process.argv[1].endsWith("retrieve_documents.ts")) { main(process.argv).then(exitCode => process.exit(exitCode)).catch(error => { console.log(error); process.exit(1); }); } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhc3NlcnQiLCJjb21tYW5kTGluZUFyZ3MiLCJjcmVhdGVIYXNoIiwiZGlmZmVyZW5jZUluRGF5cyIsImZzIiwicGF0aCIsIndhbGtEb2N1bWVudEFuZERpdmlzaW9ucyIsImdpdCIsIml0ZXJMb2FkQXNzZW1ibGVlRG9jdW1lbnRzIiwicGF0aEZyb21Eb2N1bWVudFVpZCIsInBhcnNlVGV4dGUiLCJEb2N1bWVudFVybEZvcm1hdCIsIml0ZXJEb2N1bWVudE9yRGl2aXNpb25VcmxzIiwiY29tbWl0T3B0aW9uIiwiZGF0YURpckRlZmF1bHRPcHRpb24iLCJsZWdpc2xhdHVyZU9wdGlvbiIsInB1bGxPcHRpb24iLCJyZW1vdGVPcHRpb24iLCJzaWxlbnRPcHRpb24iLCJ2ZXJib3NlT3B0aW9uIiwiZG9jdW1lbnRGb3JtYXRPcHRpb24iLCJwYXJzZUFyZ3MiLCJhcmd2Iiwib3B0aW9uc0RlZmluaXRpb25zIiwiYWxpYXMiLCJoZWxwIiwibmFtZSIsInR5cGUiLCJCb29sZWFuIiwibXVsdGlwbGUiLCJTdHJpbmciLCJvcHRpb25zIiwidG9kYXkiLCJEYXRlIiwiZmV0Y2hXaXRoUmV0cnkiLCJ1cmwiLCJyZXRyaWVzIiwiYmFja29mZiIsImF0dGVtcHQiLCJmZXRjaCIsImVycm9yIiwiY29uc29sZSIsIndhcm4iLCJQcm9taXNlIiwicmVzb2x2ZSIsInNldFRpbWVvdXQiLCJFcnJvciIsImRvd25sb2FkQW5kUGFyc2UiLCJkb2N1bWVudCIsImRvY3VtZW50c0RpciIsInVpZCIsInN1YnN0cmluZyIsImluY2x1ZGVzIiwibGVnaXNsYXR1cmUiLCJ0b1N0cmluZyIsImZldGNoRG9jdW1lbnRzIiwiZGF0YURpciIsImRvY3VtZW50c0ZpbGVzRGlyIiwiam9pbiIsImRvY3VtZW50RGlyIiwiZG9jdW1lbnRGaWxlRGlyIiwiZW5zdXJlRGlyU3luYyIsInByb2Nlc3NEb2N1bWVudE9yRGl2aXNpb24iLCJwYXJzZURvY3VtZW50cyIsImRvY3VtZW50UGF0aCIsImh0bWxQYXRoIiwiZXhpc3RzU3luYyIsImh0bWwiLCJyZWFkRmlsZVN5bmMiLCJlbmNvZGluZyIsInBhcnNlZERvY3VtZW50IiwianNvbiIsImRvY3VtZW50SnNvbiIsIkpTT04iLCJwYXJzZSIsInN1YmRpdmlzaW9ucyIsIndyaXRlRmlsZVN5bmMiLCJzdHJpbmdpZnkiLCJlIiwibG9nIiwicmV0cmlldmVEb2N1bWVudHMiLCJjb21taXQiLCJwdWxsIiwicmVzZXRBbmRQdWxsIiwiZnVsbCIsImZpbGVuYW1lIiwicmVhZGRpclN5bmMiLCJyZW1vdmVTeW5jIiwiZmlyc3RVaWQiLCJza2lwIiwiZG9jdW1lbnRPckRpdmlzaW9uIiwiY29tbWl0QW5kUHVzaCIsInJlbW90ZSIsImZpbGVuYW1lQnlTaGEyNTYiLCJpbmRleFBhdGgiLCJpbmRleCIsInBhdGhFeGlzdHNTeW5jIiwicmVhZEpzb25TeW5jIiwiZm9ybWF0IiwiUGRmIiwiZmlsZUV4dGVuc2lvbiIsInNwbGl0IiwicG9wIiwiZG9jdW1lbnRUeXBlIiwiY2xhc3NpZmljYXRpb24iLCJjb2RlIiwidW5kZWZpbmVkIiwiZm9ybWF0RmlsZXNJbmZvcyIsImZpbGVJbmZvcyIsImZpbmQiLCJmaWxlIiwicHVzaCIsInN0YXR1cyIsInNoYTI1NiIsImN5Y2xlRGVWaWUiLCJjaHJvbm8iLCJkYXRlQ3JlYXRpb24iLCJkYXRlRGVwb3QiLCJzaWxlbnQiLCJyZXNwb25zZSIsImZpbGVQYXRoIiwib2siLCJhcnJheUJ1ZmZlciIsImJ1ZmZlciIsIkJ1ZmZlciIsImZyb20iLCJzdWJhcnJheSIsInN0YXJ0c1dpdGgiLCJ1cGRhdGUiLCJkaWdlc3QiLCJleGlzdGluZ0ZpbGVuYW1lIiwibWVzc2FnZSIsInN0YXR1c1RleHQiLCJ3cml0ZUpzb25TeW5jIiwic3BhY2VzIiwibWFpbiIsInByb2Nlc3MiLCJlbmRzV2l0aCIsInRoZW4iLCJleGl0Q29kZSIsImV4aXQiLCJjYXRjaCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zY3JpcHRzL3JldHJpZXZlX2RvY3VtZW50cy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgYXNzZXJ0IGZyb20gXCJhc3NlcnRcIlxuaW1wb3J0IGNvbW1hbmRMaW5lQXJncyBmcm9tIFwiY29tbWFuZC1saW5lLWFyZ3NcIlxuaW1wb3J0IHsgY3JlYXRlSGFzaCB9IGZyb20gXCJjcnlwdG9cIlxuaW1wb3J0IHsgZGlmZmVyZW5jZUluRGF5cyB9IGZyb20gXCJkYXRlLWZuc1wiXG5pbXBvcnQgZnMgZnJvbSBcImZzLWV4dHJhXCJcbmltcG9ydCBwYXRoIGZyb20gXCJwYXRoXCJcblxuaW1wb3J0IHtcbiAgRG9jdW1lbnRGaWxlSW5mb3MsXG4gIERvY3VtZW50RmlsZXNJbmRleCxcbiAgd2Fsa0RvY3VtZW50QW5kRGl2aXNpb25zLFxufSBmcm9tIFwiLi4vZG9zc2llcnNfbGVnaXNsYXRpZnNcIlxuaW1wb3J0ICogYXMgZ2l0IGZyb20gXCIuLi9naXRcIlxuaW1wb3J0IHsgaXRlckxvYWRBc3NlbWJsZWVEb2N1bWVudHMsIHBhdGhGcm9tRG9jdW1lbnRVaWQgfSBmcm9tIFwiLi4vbG9hZGVyc1wiXG5pbXBvcnQgeyBwYXJzZVRleHRlIH0gZnJvbSBcIi4uL3BhcnNlcnNcIlxuaW1wb3J0IHsgRG9jdW1lbnRVcmxGb3JtYXQsIGl0ZXJEb2N1bWVudE9yRGl2aXNpb25VcmxzIH0gZnJvbSBcIi4uL3VybHNcIlxuaW1wb3J0IHtcbiAgY29tbWl0T3B0aW9uLFxuICBkYXRhRGlyRGVmYXVsdE9wdGlvbixcbiAgbGVnaXNsYXR1cmVPcHRpb24sXG4gIHB1bGxPcHRpb24sXG4gIHJlbW90ZU9wdGlvbixcbiAgc2lsZW50T3B0aW9uLFxuICB2ZXJib3NlT3B0aW9uLFxuICBkb2N1bWVudEZvcm1hdE9wdGlvbixcbn0gZnJvbSBcIi4vc2hhcmVkL2NsaV9oZWxwZXJzXCJcblxuZnVuY3Rpb24gcGFyc2VBcmdzKGFyZ3Y6IHN0cmluZ1tdKTogYW55IHtcbiAgY29uc3Qgb3B0aW9uc0RlZmluaXRpb25zID0gW1xuICAgIGNvbW1pdE9wdGlvbixcbiAgICBsZWdpc2xhdHVyZU9wdGlvbixcbiAgICByZW1vdGVPcHRpb24sXG4gICAgc2lsZW50T3B0aW9uLFxuICAgIHZlcmJvc2VPcHRpb24sXG4gICAgZGF0YURpckRlZmF1bHRPcHRpb24sXG4gICAgcHVsbE9wdGlvbixcbiAgICB7XG4gICAgICBhbGlhczogXCJmXCIsXG4gICAgICBoZWxwOiBcInJldHJpZXZlIGFsbCBkb2N1bWVudHMsIGV2ZW4gYWxyZWFkeSByZXRyaWV2ZWQgb25lc1wiLFxuICAgICAgbmFtZTogXCJmdWxsXCIsXG4gICAgICB0eXBlOiBCb29sZWFuLFxuICAgIH0sXG4gICAge1xuICAgICAgYWxpYXM6IFwiblwiLFxuICAgICAgaGVscDogXCJ0cnkgdG8gYWxzbyByZXRyaWV2ZSBkb2N1bWVudHMgdGhhdCB3ZXJlIHByZXZpb3VzbHkgbm90IGZvdW5kXCIsXG4gICAgICBuYW1lOiBcIm5vdC1mb3VuZFwiLFxuICAgICAgdHlwZTogQm9vbGVhbixcbiAgICB9LFxuICAgIGRvY3VtZW50Rm9ybWF0T3B0aW9uLFxuICAgIHtcbiAgICAgIGFsaWFzOiBcIlRcIixcbiAgICAgIGhlbHA6IFwidHlwZSBvZiBkb2N1bWVudHMgdG8gcmV0cmlldmUgKGZvciBleGFtcGxlOiBQSU9OKVwiLFxuICAgICAgbXVsdGlwbGU6IHRydWUsXG4gICAgICBuYW1lOiBcImRvY3VtZW50LXR5cGVcIixcbiAgICAgIHR5cGU6IFN0cmluZyxcbiAgICB9LFxuICBdXG4gIGNvbnN0IG9wdGlvbnMgPSBjb21tYW5kTGluZUFyZ3Mob3B0aW9uc0RlZmluaXRpb25zLCB7XG4gICAgYXJndjogYXJndixcbiAgfSlcbiAgcmV0dXJuIG9wdGlvbnNcbn1cblxuY29uc3QgdG9kYXkgPSBuZXcgRGF0ZSgpXG5cbmFzeW5jIGZ1bmN0aW9uIGZldGNoV2l0aFJldHJ5KFxuICB1cmw6IHN0cmluZyxcbiAgcmV0cmllczogbnVtYmVyID0gMyxcbiAgYmFja29mZjogbnVtYmVyID0gMzAwLFxuKTogUHJvbWlzZTxSZXNwb25zZT4ge1xuICBmb3IgKGxldCBhdHRlbXB0ID0gMDsgYXR0ZW1wdCA8IHJldHJpZXM7IGF0dGVtcHQrKykge1xuICAgIHRyeSB7XG4gICAgICByZXR1cm4gYXdhaXQgZmV0Y2godXJsKVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoYXR0ZW1wdCA9PT0gcmV0cmllcyAtIDEpIHtcbiAgICAgICAgdGhyb3cgZXJyb3JcbiAgICAgIH1cbiAgICAgIGNvbnNvbGUud2FybihcbiAgICAgICAgYEZldGNoIGF0dGVtcHQgJHthdHRlbXB0ICsgMX0gZm9yICR7dXJsfSBmYWlsZWQuIFJldHJ5aW5nIGluICR7YmFja29mZn1tcy4uLmAsXG4gICAgICApXG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4gc2V0VGltZW91dChyZXNvbHZlLCBiYWNrb2ZmKSlcbiAgICAgIGJhY2tvZmYgKj0gMlxuICAgIH1cbiAgfVxuICB0aHJvdyBuZXcgRXJyb3IoYEZhaWxlZCB0byBmZXRjaCAke3VybH0gYWZ0ZXIgJHtyZXRyaWVzfSBhdHRlbXB0c2ApXG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBkb3dubG9hZEFuZFBhcnNlKFxuICBkb2N1bWVudDogYW55LFxuICBkb2N1bWVudHNEaXI6IHN0cmluZyxcbiAgb3B0aW9uczogYW55LFxuKTogUHJvbWlzZTx2b2lkPiB7XG4gIGlmIChkb2N1bWVudC51aWQuc3Vic3RyaW5nKDQsIDYpID09PSBcIlNOXCIpIHtcbiAgICByZXR1cm5cbiAgfVxuXG4gIGlmIChbXCJSQVBQXCIsIFwiUklORlwiXS5pbmNsdWRlcyhkb2N1bWVudC51aWQuc3Vic3RyaW5nKDAsIDQpKSkge1xuICAgIHJldHVyblxuICB9XG5cbiAgaWYgKG9wdGlvbnMubGVnaXNsYXR1cmU/LnRvU3RyaW5nKCkgIT09IGRvY3VtZW50LmxlZ2lzbGF0dXJlKSB7XG4gICAgcmV0dXJuXG4gIH1cblxuICBpZiAob3B0aW9ucy5mZXRjaERvY3VtZW50cykge1xuICAgIGNvbnN0IGRhdGFEaXIgPSBvcHRpb25zLmRhdGFEaXJcbiAgICBjb25zdCBkb2N1bWVudHNGaWxlc0RpciA9IHBhdGguam9pbihkYXRhRGlyLCBcIkRvY3VtZW50c1wiKVxuICAgIGNvbnN0IGRvY3VtZW50RGlyID0gcGF0aEZyb21Eb2N1bWVudFVpZChcbiAgICAgIGAke2RvY3VtZW50c0Rpcn0vZG9jdW1lbnRzYCxcbiAgICAgIGRvY3VtZW50LnVpZCxcbiAgICApXG4gICAgY29uc3QgZG9jdW1lbnRGaWxlRGlyID0gcGF0aEZyb21Eb2N1bWVudFVpZChkb2N1bWVudHNGaWxlc0RpciwgZG9jdW1lbnQudWlkKVxuXG4gICAgZnMuZW5zdXJlRGlyU3luYyhkb2N1bWVudHNGaWxlc0RpcilcbiAgICBhd2FpdCBwcm9jZXNzRG9jdW1lbnRPckRpdmlzaW9uKGRvY3VtZW50LCBkb2N1bWVudHNGaWxlc0Rpciwgb3B0aW9ucylcblxuICAgIGlmIChvcHRpb25zLnBhcnNlRG9jdW1lbnRzKSB7XG4gICAgICBjb25zdCBkb2N1bWVudFBhdGggPSBgJHtkb2N1bWVudERpcn0uanNvbmBcbiAgICAgIGNvbnN0IGh0bWxQYXRoID0gcGF0aC5qb2luKGRvY3VtZW50RmlsZURpciwgYGR5bi1vcGVuZGF0YS5odG1sYClcblxuICAgICAgaWYgKGZzLmV4aXN0c1N5bmMoaHRtbFBhdGgpKSB7XG4gICAgICAgIGNvbnN0IGh0bWwgPSBmcy5yZWFkRmlsZVN5bmMoaHRtbFBhdGgsIHsgZW5jb2Rpbmc6IFwidXRmLThcIiB9KVxuICAgICAgICB0cnkge1xuICAgICAgICAgIGNvbnN0IHBhcnNlZERvY3VtZW50ID0gcGFyc2VUZXh0ZShcbiAgICAgICAgICAgIFwiaHR0cHM6Ly93d3cuYXNzZW1ibGVlLW5hdGlvbmFsZS5mclwiLFxuICAgICAgICAgICAgaHRtbCxcbiAgICAgICAgICApXG4gICAgICAgICAgaWYgKHBhcnNlZERvY3VtZW50ICYmIGZzLmV4aXN0c1N5bmMoZG9jdW1lbnRQYXRoKSkge1xuICAgICAgICAgICAgY29uc3QganNvbiA9IGZzLnJlYWRGaWxlU3luYyhkb2N1bWVudFBhdGgsIHsgZW5jb2Rpbmc6IFwidXRmLThcIiB9KVxuICAgICAgICAgICAgY29uc3QgZG9jdW1lbnRKc29uID0gSlNPTi5wYXJzZShqc29uKVxuICAgICAgICAgICAgZG9jdW1lbnRKc29uLnN1YmRpdmlzaW9ucyA9IHBhcnNlZERvY3VtZW50Py5zdWJkaXZpc2lvbnNcbiAgICAgICAgICAgIGZzLndyaXRlRmlsZVN5bmMoXG4gICAgICAgICAgICAgIGRvY3VtZW50UGF0aCxcbiAgICAgICAgICAgICAgSlNPTi5zdHJpbmdpZnkoZG9jdW1lbnRKc29uLCBudWxsLCAyKSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICBjb25zb2xlLmxvZyhgVW5hYmxlIHRvIHBhcnNlIGRvY3VtZW50ICR7ZG9jdW1lbnQudWlkfWApXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gcmV0cmlldmVEb2N1bWVudHMob3B0aW9uczogYW55KTogUHJvbWlzZTxudW1iZXI+IHtcbiAgYXNzZXJ0KFxuICAgICFvcHRpb25zLmNvbW1pdCB8fCAhb3B0aW9ucy51aWQsXG4gICAgJ09wdGlvbnMgXCJjb21taXRcIiAmIFwidWlkXCIgYXJlIGluY29tcGF0aWJsZScsXG4gIClcblxuICBjb25zdCBkYXRhRGlyID0gb3B0aW9ucy5kYXRhRGlyXG4gIGNvbnN0IGRvY3VtZW50c0RpciA9IHBhdGguam9pbihkYXRhRGlyLCBcIkRvY3VtZW50c1wiKVxuICBpZiAob3B0aW9ucy5wdWxsKSB7XG4gICAgZ2l0LnJlc2V0QW5kUHVsbChkb2N1bWVudHNEaXIpXG4gIH1cbiAgZnMuZW5zdXJlRGlyU3luYyhkb2N1bWVudHNEaXIpXG5cbiAgaWYgKG9wdGlvbnMuZnVsbCAmJiAhb3B0aW9ucy51aWQpIHtcbiAgICBmb3IgKGNvbnN0IGZpbGVuYW1lIG9mIGZzLnJlYWRkaXJTeW5jKGRvY3VtZW50c0RpcikpIHtcbiAgICAgIGlmIChmaWxlbmFtZVswXSA9PT0gXCIuXCIpIHtcbiAgICAgICAgY29udGludWVcbiAgICAgIH1cbiAgICAgIGZzLnJlbW92ZVN5bmMocGF0aC5qb2luKGRvY3VtZW50c0RpciwgZmlsZW5hbWUpKVxuICAgIH1cbiAgfVxuXG4gIGNvbnN0IGZpcnN0VWlkID0gb3B0aW9ucy51aWRcbiAgbGV0IHNraXAgPSBCb29sZWFuKGZpcnN0VWlkKVxuICBmb3IgKGNvbnN0IHsgZG9jdW1lbnQgfSBvZiBpdGVyTG9hZEFzc2VtYmxlZURvY3VtZW50cyhcbiAgICBkYXRhRGlyLFxuICAgIG9wdGlvbnMubGVnaXNsYXR1cmUsXG4gICkpIHtcbiAgICBmb3IgKGNvbnN0IGRvY3VtZW50T3JEaXZpc2lvbiBvZiB3YWxrRG9jdW1lbnRBbmREaXZpc2lvbnMoZG9jdW1lbnQpKSB7XG4gICAgICAvLyBJZ25vcmUgZG9jdW1lbnRzIGZyb20gU8OpbmF0LlxuICAgICAgaWYgKGRvY3VtZW50T3JEaXZpc2lvbi51aWQuc3Vic3RyaW5nKDQsIDYpID09PSBcIlNOXCIpIHtcbiAgICAgICAgY29udGludWVcbiAgICAgIH1cblxuICAgICAgaWYgKHNraXApIHtcbiAgICAgICAgaWYgKGRvY3VtZW50T3JEaXZpc2lvbi51aWQgPT09IGZpcnN0VWlkKSB7XG4gICAgICAgICAgc2tpcCA9IGZhbHNlXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29udGludWVcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBhd2FpdCBwcm9jZXNzRG9jdW1lbnRPckRpdmlzaW9uKGRvY3VtZW50T3JEaXZpc2lvbiwgZG9jdW1lbnRzRGlyLCBvcHRpb25zKVxuICAgIH1cbiAgfVxuXG4gIGlmIChvcHRpb25zLmNvbW1pdCkge1xuICAgIHJldHVybiBnaXQuY29tbWl0QW5kUHVzaChkb2N1bWVudHNEaXIsIFwiTm91dmVsbGUgbW9pc3NvblwiLCBvcHRpb25zLnJlbW90ZSlcbiAgfVxuICByZXR1cm4gMFxufVxuXG5hc3luYyBmdW5jdGlvbiBwcm9jZXNzRG9jdW1lbnRPckRpdmlzaW9uKFxuICBkb2N1bWVudE9yRGl2aXNpb246IGFueSxcbiAgZG9jdW1lbnRzRGlyOiBzdHJpbmcsXG4gIG9wdGlvbnM6IGFueSxcbik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCBkb2N1bWVudERpciA9IHBhdGhGcm9tRG9jdW1lbnRVaWQoZG9jdW1lbnRzRGlyLCBkb2N1bWVudE9yRGl2aXNpb24udWlkKVxuICBmcy5lbnN1cmVEaXJTeW5jKGRvY3VtZW50RGlyKVxuXG4gIGNvbnN0IGZpbGVuYW1lQnlTaGEyNTY6IHsgW2RpZ2VzdDogc3RyaW5nXTogc3RyaW5nIH0gPSB7fVxuICBjb25zdCBpbmRleFBhdGggPSBwYXRoLmpvaW4oZG9jdW1lbnREaXIsIFwiaW5kZXguanNvblwiKVxuICBjb25zdCBpbmRleCA9IChcbiAgICBmcy5wYXRoRXhpc3RzU3luYyhpbmRleFBhdGgpID8gZnMucmVhZEpzb25TeW5jKGluZGV4UGF0aCkgOiB7fVxuICApIGFzIERvY3VtZW50RmlsZXNJbmRleFxuXG4gIGZvciAoY29uc3QgeyBmb3JtYXQsIHR5cGUsIHVybCB9IG9mIGl0ZXJEb2N1bWVudE9yRGl2aXNpb25VcmxzKFxuICAgIGRvY3VtZW50T3JEaXZpc2lvbixcbiAgKSkge1xuICAgIGNvbnN0IGZpbGVuYW1lID0gYCR7dHlwZX0uJHtmb3JtYXQgPT09IERvY3VtZW50VXJsRm9ybWF0LlBkZiA/IFwicGRmXCIgOiBcImh0bWxcIn1gXG5cbiAgICAvLyBGaWx0ZXIgYnkgZmlsZSBleHRlbnNpb24gaWYgb3B0aW9uIGlzIHBhc3NlZFxuICAgIGNvbnN0IGZpbGVFeHRlbnNpb24gPSB1cmwuc3BsaXQoXCIuXCIpLnBvcCgpXG4gICAgaWYgKG9wdGlvbnMuZm9ybWF0ICYmIGZpbGVFeHRlbnNpb24gJiYgb3B0aW9ucy5mb3JtYXQgIT09IGZpbGVFeHRlbnNpb24pIHtcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgaWYgKFxuICAgICAgb3B0aW9ucy5sZWdpc2xhdHVyZSAmJlxuICAgICAgb3B0aW9ucy5sZWdpc2xhdHVyZS50b1N0cmluZygpICE9PSBkb2N1bWVudE9yRGl2aXNpb24ubGVnaXNsYXR1cmVcbiAgICApIHtcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgLy8gRmlsdGVyIGJ5IGRvY3VtZW50IHR5cGUgaWYgb3B0aW9uIGlzIHBhc3NlZFxuICAgIGNvbnN0IGRvY3VtZW50VHlwZSA9IGRvY3VtZW50T3JEaXZpc2lvbi5jbGFzc2lmaWNhdGlvbj8udHlwZT8uY29kZVxuICAgIGlmIChcbiAgICAgIG9wdGlvbnNbXCJkb2N1bWVudC10eXBlXCJdICE9PSB1bmRlZmluZWQgJiZcbiAgICAgICFvcHRpb25zW1wiZG9jdW1lbnQtdHlwZVwiXS5pbmNsdWRlcyhkb2N1bWVudFR5cGUpXG4gICAgKSB7XG4gICAgICBjb250aW51ZVxuICAgIH1cblxuICAgIGxldCBmb3JtYXRGaWxlc0luZm9zID0gaW5kZXhbZm9ybWF0XSA/PyAoaW5kZXhbZm9ybWF0XSA9IFtdKVxuICAgIGxldCBmaWxlSW5mb3MgPVxuICAgICAgZm9ybWF0RmlsZXNJbmZvcy5maW5kKChmaWxlKSA9PiBmaWxlLnVybCA9PT0gdXJsKSA/P1xuICAgICAgKHt9IGFzIERvY3VtZW50RmlsZUluZm9zKVxuICAgIGlmICghZm9ybWF0RmlsZXNJbmZvcy5pbmNsdWRlcyhmaWxlSW5mb3MpKSB7XG4gICAgICBmb3JtYXRGaWxlc0luZm9zLnB1c2goZmlsZUluZm9zKVxuICAgIH1cbiAgICBmaWxlSW5mb3MudXJsID0gdXJsXG5cbiAgICBpZiAoZmlsZUluZm9zLnN0YXR1cyA9PT0gMjAwICYmICFvcHRpb25zLmZ1bGwpIHtcbiAgICAgIGZpbGVuYW1lQnlTaGEyNTZbZmlsZUluZm9zLnNoYTI1NiBhcyBzdHJpbmddID0gZmlsZW5hbWVcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuICAgIGlmIChcbiAgICAgIGZpbGVJbmZvcy5zdGF0dXMgPT09IDQwNCAmJlxuICAgICAgIW9wdGlvbnNbXCJub3QtZm91bmRcIl0gJiZcbiAgICAgIGRpZmZlcmVuY2VJbkRheXMoXG4gICAgICAgIHRvZGF5LFxuICAgICAgICBkb2N1bWVudE9yRGl2aXNpb24uY3ljbGVEZVZpZS5jaHJvbm8uZGF0ZUNyZWF0aW9uID8/XG4gICAgICAgICAgKGRvY3VtZW50T3JEaXZpc2lvbi5jeWNsZURlVmllLmNocm9uby5kYXRlRGVwb3QgYXMgRGF0ZSB8IHN0cmluZyksXG4gICAgICApID4gMTBcbiAgICApIHtcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgaWYgKCFvcHRpb25zLnNpbGVudCkge1xuICAgICAgY29uc29sZS5sb2coXG4gICAgICAgIGBSZXRyaWV2aW5nIGRvY3VtZW50IG9yIGRpdmlzaW9uICR7ZG9jdW1lbnRPckRpdmlzaW9uLnVpZH0gYXQgJHt1cmx94oCmYCxcbiAgICAgIClcbiAgICB9XG5cbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoV2l0aFJldHJ5KHVybClcbiAgICBjb25zdCBmaWxlUGF0aCA9IHBhdGguam9pbihkb2N1bWVudERpciwgZmlsZW5hbWUpXG5cbiAgICBpZiAocmVzcG9uc2Uub2spIHtcbiAgICAgIGNvbnN0IGFycmF5QnVmZmVyID0gYXdhaXQgcmVzcG9uc2UuYXJyYXlCdWZmZXIoKVxuICAgICAgY29uc3QgYnVmZmVyID0gQnVmZmVyLmZyb20oYXJyYXlCdWZmZXIpXG4gICAgICBpZiAoXG4gICAgICAgIGZvcm1hdCA9PT0gRG9jdW1lbnRVcmxGb3JtYXQuUGRmICYmXG4gICAgICAgICFidWZmZXIuc3ViYXJyYXkoMCwgNCkudG9TdHJpbmcoKS5zdGFydHNXaXRoKFwiJVBERlwiKVxuICAgICAgKSB7XG4gICAgICAgIC8vIEluc3RlYWQgb2YgYSBQREYsIHRoZSByZWNlaXZlZCBkYXRhIG1heSBiZSBhbiBIVE1MIHBhZ2Ugd2l0aCBhIG1lc3NhZ2UgbGlrZVxuICAgICAgICAvLyBcIkRvY3VtZW50IG5vbiBlbmNvcmUgcHVibGnDqVwiLlxuICAgICAgICBpZiAoIW9wdGlvbnMuc2lsZW50KSB7XG4gICAgICAgICAgY29uc29sZS53YXJuKGAgIFBERiBcIiR7dXJsfVwiIG5vdCBmb3VuZC5gKVxuICAgICAgICB9XG4gICAgICAgIGZzLnJlbW92ZVN5bmMoZmlsZVBhdGgpXG4gICAgICAgIGRlbGV0ZSBmaWxlSW5mb3MuZmlsZW5hbWVcbiAgICAgICAgZGVsZXRlIGZpbGVJbmZvcy5zaGEyNTZcbiAgICAgICAgZmlsZUluZm9zLnN0YXR1cyA9IDQwNFxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3Qgc2hhMjU2ID0gY3JlYXRlSGFzaChcInNoYTI1NlwiKS51cGRhdGUoYnVmZmVyKS5kaWdlc3QoXCJoZXhcIilcbiAgICAgICAgY29uc3QgZXhpc3RpbmdGaWxlbmFtZSA9IGZpbGVuYW1lQnlTaGEyNTZbc2hhMjU2XVxuICAgICAgICBpZiAoZXhpc3RpbmdGaWxlbmFtZSA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgZnMud3JpdGVGaWxlU3luYyhmaWxlUGF0aCwgYnVmZmVyLCB7XG4gICAgICAgICAgICBlbmNvZGluZzogXCJ1dGY4XCIsXG4gICAgICAgICAgfSlcbiAgICAgICAgICBmaWxlSW5mb3MuZmlsZW5hbWUgPSBmaWxlbmFtZVxuICAgICAgICAgIGZpbGVuYW1lQnlTaGEyNTZbc2hhMjU2XSA9IGZpbGVuYW1lXG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZmlsZUluZm9zLmZpbGVuYW1lID0gZXhpc3RpbmdGaWxlbmFtZVxuICAgICAgICB9XG4gICAgICAgIGZpbGVJbmZvcy5zaGEyNTYgPSBzaGEyNTZcbiAgICAgICAgZmlsZUluZm9zLnN0YXR1cyA9IHJlc3BvbnNlLnN0YXR1c1xuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICBpZiAocmVzcG9uc2Uuc3RhdHVzID09PSA0MDQpIHtcbiAgICAgICAgaWYgKCFvcHRpb25zLnNpbGVudCkge1xuICAgICAgICAgIGNvbnNvbGUud2FybihgICBQYWdlIFwiJHt1cmx9XCIgbm90IGZvdW5kLmApXG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnNvbGUuZXJyb3IoXG4gICAgICAgICAgYCAgRXJyb3I6XFxuJHtKU09OLnN0cmluZ2lmeShcbiAgICAgICAgICAgIHsgY29kZTogcmVzcG9uc2Uuc3RhdHVzLCBtZXNzYWdlOiByZXNwb25zZS5zdGF0dXNUZXh0IH0sXG4gICAgICAgICAgICBudWxsLFxuICAgICAgICAgICAgMixcbiAgICAgICAgICApfWAsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIGZzLnJlbW92ZVN5bmMoZmlsZVBhdGgpXG4gICAgICBkZWxldGUgZmlsZUluZm9zLmZpbGVuYW1lXG4gICAgICBkZWxldGUgZmlsZUluZm9zLnNoYTI1NlxuICAgICAgZmlsZUluZm9zLnN0YXR1cyA9IHJlc3BvbnNlLnN0YXR1c1xuICAgIH1cbiAgfVxuXG4gIGZzLndyaXRlSnNvblN5bmMoaW5kZXhQYXRoLCBpbmRleCwgeyBlbmNvZGluZzogXCJ1dGYtOFwiLCBzcGFjZXM6IDIgfSlcbn1cblxuZnVuY3Rpb24gbWFpbihhcmd2OiBhbnkpOiBQcm9taXNlPG51bWJlcj4ge1xuICBjb25zdCBvcHRpb25zID0gcGFyc2VBcmdzKGFyZ3YpXG4gIHJldHVybiByZXRyaWV2ZURvY3VtZW50cyhvcHRpb25zKVxufVxuXG4vKiBpc3RhbmJ1bCBpZ25vcmUgaWYgKi9cbmlmIChwcm9jZXNzLmFyZ3ZbMV0uZW5kc1dpdGgoXCJyZXRyaWV2ZV9kb2N1bWVudHMudHNcIikpIHtcbiAgbWFpbihwcm9jZXNzLmFyZ3YpXG4gICAgLnRoZW4oKGV4aXRDb2RlKSA9PiBwcm9jZXNzLmV4aXQoZXhpdENvZGUpKVxuICAgIC5jYXRjaCgoZXJyb3IpID0+IHtcbiAgICAgIGNvbnNvbGUubG9nKGVycm9yKVxuICAgICAgcHJvY2Vzcy5leGl0KDEpXG4gICAgfSlcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEs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