diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/documents/Documents.ts | 52 | ||||
-rw-r--r-- | src/scraping/buxton/final/BuxtonImporter.ts | 52 | ||||
-rw-r--r-- | src/server/ApiManagers/UtilManager.ts | 9 | ||||
-rw-r--r-- | src/server/Message.ts | 5 | ||||
-rw-r--r-- | src/server/Websocket/Websocket.ts | 7 |
5 files changed, 70 insertions, 55 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index b00a1a91d..cc18dc0a6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -55,6 +55,7 @@ import { InkingControl } from "../views/InkingControl"; import { RichTextField } from "../../new_fields/RichTextField"; import { Networking } from "../Network"; import { extname } from "path"; +import { MessageStore } from "../../server/Message"; const requestImageSize = require('../util/request-image-size'); const path = require('path'); @@ -346,6 +347,7 @@ export namespace Docs { export namespace Create { export function Buxton() { + let responded = false; const loading = new Doc; loading.title = "Please wait for the import script..."; const parent = TreeDocument([loading], { @@ -354,36 +356,36 @@ export namespace Docs { _height: 400, _LODdisable: true }); - Networking.FetchFromServer("/buxton").then(response => { - const devices = JSON.parse(response); - if (!Array.isArray(devices)) { - if ("error" in devices) { - loading.title = devices.error; - } else { - console.log(devices); - alert("The importer returned an unexpected import format. Check the console."); - } - return; + const parentProto = Doc.GetProto(parent); + const { _socket } = DocServer; + Utils.AddServerHandler(_socket, MessageStore.BuxtonDocumentResult, ({ device, errors }) => { + if (!responded) { + responded = true; + parentProto.data = new List<Doc>(); } - const parentProto = Doc.GetProto(parent); - parentProto.data = new List<Doc>(); - devices.forEach(device => { + if (device) { const { __images } = device; delete device.__images; const { ImageDocument, StackingDocument } = Docs.Create; - if (Array.isArray(__images)) { - const constructed = __images.map(relative => Utils.prepend(relative)); - const hero = constructed[0]; - constructed.splice(0, 1); - const deviceImages = constructed.map((url, i) => ImageDocument(url, { title: `image${i}.${extname(url)}` })); - const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); - const deviceProto = Doc.GetProto(doc); - deviceProto.hero = new ImageField(hero); - Docs.Get.DocumentHierarchyFromJson(device, undefined, deviceProto); - Doc.AddDocToList(parentProto, "data", doc); - } - }); + const constructed = __images.map(relative => Utils.prepend(relative)); + const deviceImages = constructed.map((url, i) => ImageDocument(url, { title: `image${i}.${extname(url)}` })); + const doc = StackingDocument(deviceImages, { title: device.title, _LODdisable: true }); + const deviceProto = Doc.GetProto(doc); + deviceProto.hero = new ImageField(constructed[0]); + Docs.Get.DocumentHierarchyFromJson(device, undefined, deviceProto); + Doc.AddDocToList(parentProto, "data", doc); + } else if (errors) { + console.log(errors); + } else { + alert("A Buxton document import was completely empty (??)"); + } + }); + Utils.AddServerHandler(_socket, MessageStore.BuxtonImportComplete, ({ deviceCount, errorCount }) => { + _socket.off(MessageStore.BuxtonDocumentResult.Message); + _socket.off(MessageStore.BuxtonImportComplete.Message); + alert(`Successfully imported ${deviceCount} device${deviceCount === 1 ? "" : "s"}, with ${errorCount} error${errorCount === 1 ? "" : "s"}.`); }); + Utils.Emit(_socket, MessageStore.BeginBuxtonImport, ""); return parent; } diff --git a/src/scraping/buxton/final/BuxtonImporter.ts b/src/scraping/buxton/final/BuxtonImporter.ts index 3d7421e90..d9d48d68c 100644 --- a/src/scraping/buxton/final/BuxtonImporter.ts +++ b/src/scraping/buxton/final/BuxtonImporter.ts @@ -8,7 +8,6 @@ const StreamZip = require('node-stream-zip'); const createImageSizeStream = require("image-size-stream"); import { parseXml } from "libxmljs"; import { strictEqual } from "assert"; -import { BatchedArray, TimeUnit } from "array-batcher"; interface DocumentContents { body: string; @@ -24,21 +23,33 @@ export interface DeviceDocument { longDescription: string; company: string; year: number; - originalPrice: number; - degreesOfFreedom: number; + originalPrice?: number; + degreesOfFreedom?: number; dimensions?: string; primaryKey: string; secondaryKey: string; attribute: string; + __images: string[]; + hyperlinks: string[]; + captions: string[]; + embeddedFileNames: string[]; } -interface AnalysisResult { +export interface AnalysisResult { device?: DeviceDocument; - errors?: any; + errors?: { [key: string]: string }; } type Transformer<T> = (raw: string) => { transformed?: T, error?: string }; +export interface ImportResults { + deviceCount: number, + errorCount: number +} + +type ResultCallback = (result: AnalysisResult) => void; +type TerminatorCallback = (result: ImportResults) => void; + interface Processor<T> { exp: RegExp; matchIndex?: number; @@ -168,7 +179,7 @@ const successOut = "buxton.json"; const failOut = "incomplete.json"; const deviceKeys = Array.from(RegexMap.keys()); -export default async function executeImport() { +export default async function executeImport(emitter: ResultCallback, terminator: TerminatorCallback) { try { const contents = readdirSync(sourceDir); const wordDocuments = contents.filter(file => /.*\.docx?$/.test(file)).map(file => `${sourceDir}/${file}`); @@ -176,7 +187,7 @@ export default async function executeImport() { rimraf.sync(dir); mkdirSync(dir); }); - return parseFiles(wordDocuments); + return parseFiles(wordDocuments, emitter, terminator); } catch (e) { const message = [ "Unable to find a source directory.", @@ -188,23 +199,22 @@ export default async function executeImport() { } } -async function parseFiles(wordDocuments: string[]): Promise<DeviceDocument[]> { - const imported = await BatchedArray.from(wordDocuments, { batchSize: 8 }).batchedMapPatientInterval<{ fileName: string, contents: DocumentContents }>({ magnitude: 10, unit: TimeUnit.Seconds }, async (batch, collector) => { - for (const filePath of batch) { - const fileName = path.basename(filePath).replace("Bill_Notes_", ""); - console.log(cyan(`\nExtracting contents from ${fileName}...`)); - collector.push({ fileName, contents: await extractFileContents(filePath) }); - } - }); - console.log(yellow("\nAnalyzing the extracted document text...\n")); - const results = imported.map(({ fileName, contents }) => analyze(fileName, contents)); +async function parseFiles(wordDocuments: string[], emitter: ResultCallback, terminator: TerminatorCallback): Promise<DeviceDocument[]> { + const results: AnalysisResult[] = []; + for (const filePath of wordDocuments) { + const fileName = path.basename(filePath).replace("Bill_Notes_", ""); + console.log(cyan(`\nExtracting contents from ${fileName}...`)); + const result = analyze(fileName, await extractFileContents(filePath)); + emitter(result); + results.push(result); + } const masterDevices: DeviceDocument[] = []; - const masterErrors: any[] = []; + const masterErrors: { [key: string]: string }[] = []; results.forEach(({ device, errors }) => { if (device) { masterDevices.push(device); - } else { + } else if (errors) { masterErrors.push(errors); } }); @@ -219,6 +229,8 @@ async function parseFiles(wordDocuments: string[]): Promise<DeviceDocument[]> { await writeOutputFile(failOut, masterErrors, total, false); console.log(); + terminator({ deviceCount: masterDevices.length, errorCount: masterErrors.length }); + return masterDevices; } @@ -311,7 +323,7 @@ function analyze(fileName: string, contents: DocumentContents): AnalysisResult { embeddedFileNames, __images: imageUrls }; - const errors: any = { fileName }; + const errors: { [key: string]: string } = { fileName }; for (const key of deviceKeys) { const { exp, transformer, matchIndex, required } = RegexMap.get(key)!; diff --git a/src/server/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts index 5aac8261e..8adc3da81 100644 --- a/src/server/ApiManagers/UtilManager.ts +++ b/src/server/ApiManagers/UtilManager.ts @@ -41,15 +41,6 @@ export default class UtilManager extends ApiManager { register({ method: Method.GET, - subscription: "/buxton", - secureHandler: async ({ req, res }) => { - req.setTimeout(300000); - res.send(await executeImport()); - } - }); - - register({ - method: Method.GET, subscription: "/version", secureHandler: ({ res }) => { return new Promise<void>(resolve => { diff --git a/src/server/Message.ts b/src/server/Message.ts index 79b6fa1e0..2a03e2311 100644 --- a/src/server/Message.ts +++ b/src/server/Message.ts @@ -1,4 +1,5 @@ import { Utils } from "../Utils"; +import { AnalysisResult, ImportResults } from "../scraping/buxton/final/BuxtonImporter"; export class Message<T> { private _name: string; @@ -56,6 +57,9 @@ export namespace MessageStore { export const GetDocument = new Message<string>("Get Document"); export const DeleteAll = new Message<any>("Delete All"); export const ConnectionTerminated = new Message<string>("Connection Terminated"); + export const BeginBuxtonImport = new Message<string>("Begin Buxton Import"); + export const BuxtonDocumentResult = new Message<AnalysisResult>("Buxton Document Result"); + export const BuxtonImportComplete = new Message<ImportResults>("Buxton Import Complete"); export const GetRefField = new Message<string>("Get Ref Field"); export const GetRefFields = new Message<string[]>("Get Ref Fields"); @@ -65,5 +69,4 @@ export namespace MessageStore { export const DeleteField = new Message<string>("Delete field"); export const DeleteFields = new Message<string[]>("Delete fields"); - } diff --git a/src/server/Websocket/Websocket.ts b/src/server/Websocket/Websocket.ts index ba7ca8f35..724221be1 100644 --- a/src/server/Websocket/Websocket.ts +++ b/src/server/Websocket/Websocket.ts @@ -12,6 +12,7 @@ import { timeMap } from "../ApiManagers/UserManager"; import { green } from "colors"; import { networkInterfaces, type } from "os"; import { object } from "serializr"; +import executeImport from "../../scraping/buxton/final/BuxtonImporter"; export namespace WebSocket { @@ -106,6 +107,12 @@ export namespace WebSocket { Utils.AddServerHandler(socket, MessageStore.DeleteFields, ids => DeleteFields(socket, ids)); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefField, GetRefField); Utils.AddServerHandlerCallback(socket, MessageStore.GetRefFields, GetRefFields); + Utils.AddServerHandler(socket, MessageStore.BeginBuxtonImport, () => { + executeImport( + deviceOrError => Utils.Emit(socket, MessageStore.BuxtonDocumentResult, deviceOrError), + results => Utils.Emit(socket, MessageStore.BuxtonImportComplete, results) + ); + }); disconnect = () => { socket.broadcast.emit("connection_terminated", Date.now()); |