Skip to content

Commit f8ebfc3

Browse files
committed
v0.1.3 update
added support for when multiple graphics are assigned to the same node added graphic filename to the error output
1 parent 1babb18 commit f8ebfc3

File tree

3 files changed

+96
-28
lines changed

3 files changed

+96
-28
lines changed

README.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,16 @@ graphics-error-scraper -u https://webctrl.example.com -U admin -p hvac1234 -o re
4848
```json
4949
[
5050
{
51-
"path": "Automatic Controls Equipment Systems / Office Building / Utilities / Building Electric Meter Totalizer",
51+
"displayPath": "Automatic Controls Equipment Systems / Office Building / Utilities / Building Electric Meter Totalizer",
52+
"gqlPath": "#aces_electric_meter_totalizer",
53+
"graphicName": "Default",
54+
"graphicFile": "aces_electric_meter_totalizer.view",
5255
"actionErrors": [
5356
{
5457
"details": "Uncaught Could not find trend report or an embedded trend at real_pwr_tn",
5558
"type": "UI_Error",
5659
"number": 0,
5760
"line": 449
58-
},
59-
{
60-
"details": "Uncaught Error #3004: The expression \"real_pwr/units\" cannot be evaluated - no child \"real_pwr\" under location \"/trees/geographic/#office_area/#utilities_area/#aces_electric_meter_totalizer\".",
61-
"type": "UI_Error",
62-
"number": 0,
63-
"line": 454
6461
}
6562
]
6663
}

index.ts

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const fs: typeof fsType = require('fs');
4646
// Special case: -v or --version with no other parameters
4747
const onlyVersion = (args.length === 1 && (args[0] === '-v' || args[0] === '--version'));
4848
if (hasFlag('version') || onlyVersion) {
49-
console.log('v0.1.2');
49+
console.log('v0.1.3');
5050
return;
5151
}
5252

@@ -60,7 +60,7 @@ const fs: typeof fsType = require('fs');
6060

6161
if (!url || !username || !password) {
6262
console.error(
63-
'Usage: graphics-error-scraper [options]\n' +
63+
'\nUsage: graphics-error-scraper [options]\n' +
6464
' -u, --url <url> Target URL (required)\n' +
6565
' -U, --username <username> Username (required)\n' +
6666
' -p, --password <password> Password (required)\n' +
@@ -70,7 +70,8 @@ const fs: typeof fsType = require('fs');
7070
' -t, --timeout <ms> Timeout for page actions in milliseconds (default: 180000)\n' +
7171
' -h, --headless <bool> Headless mode (true/false, default: true)\n' +
7272
' -v, --verbose Verbose logging (flag or "true")\n' +
73-
' --version, -v Print version and exit'
73+
' --version, -v Print version and exit\n' +
74+
'\nFor more information, see https://github.com/automatic-controls/graphics-error-scraper\n'
7475
);
7576
process.exit(1);
7677
}
@@ -184,16 +185,56 @@ const fs: typeof fsType = require('fs');
184185
return s;
185186
});
186187
}
187-
188-
const errors = [];
189-
for (const g of await navFrame.$$('.TreeCtrl-outer[id^=geoTree] .TreeCtrl-content')) {
190-
try {
191-
await g.click();
192-
} catch (ex) {
193-
continue;
188+
async function getLocationInfo() {
189+
return await page.evaluate(() => {
190+
const ret: Record<string, string> = {};
191+
const window = (document.getElementById('actionContent') as HTMLIFrameElement)?.contentWindow;
192+
if (!window) {
193+
return ret;
194+
}
195+
{
196+
const base = window?.document.getElementsByTagName('base');
197+
if (base && base.length > 0) {
198+
const href = base[0].href;
199+
if (href) {
200+
const m = /\/([^\/]+\.view)\/lvl5\//g.exec(href);
201+
if (m) {
202+
ret.graphicFile = m[1];
203+
}
204+
}
205+
}
206+
}
207+
{
208+
//@ts-ignore
209+
const gql = window.frameManager?.treeGqlLocation;
210+
if (gql) {
211+
ret.gqlPath = gql;
212+
}
213+
}
214+
{
215+
const m = /\[[^:]+:[^:]+:([^\]]+?):[^\]:]+\]/g.exec(window.document.title);
216+
if (m) {
217+
ret.graphicName = m[1];
218+
}
219+
}
220+
return ret;
221+
});
222+
}
223+
async function hasMultipleGraphics() {
224+
return await page.evaluate(() => {
225+
return (document.querySelector('#actButtonSpan > span[title="View graphics"] > img:last-child') as HTMLImageElement)?.style.display !== 'none';
226+
});
227+
}
228+
const errors: Record<string, any>[] = [];
229+
// Set url property to undefined if present in any error object
230+
const cleanList = (arr: any[]) => arr.map(obj => {
231+
if (obj && typeof obj === 'object' && 'url' in obj) {
232+
return { ...obj, url: undefined };
194233
}
195-
await wait();
196-
if (await page.$('#actButtonSpan > span[title="View graphics"]') && await page.$('#errorIndication:not([style*="display: none"])')) {
234+
return obj;
235+
});
236+
async function handleErrors(g: any) {
237+
if (await page.$('#errorIndication:not([style*="display: none"])')) {
197238
const e = await page.evaluate(() => {
198239
return {
199240
//@ts-ignore
@@ -204,20 +245,50 @@ const fs: typeof fsType = require('fs');
204245
infoMessages: DisplayError.getInfoMessages()
205246
};
206247
});
207-
// Set url property to undefined if present in any error object
208-
const cleanList = (arr: any[]) => arr.map(obj => {
209-
if (obj && typeof obj === 'object' && 'url' in obj) {
210-
return { ...obj, url: undefined };
211-
}
212-
return obj;
213-
});
214-
const errorObj: any = { path: await getDisplayPath(g) };
248+
const errorObj: Record<string, any> = { displayPath: await getDisplayPath(g) };
249+
const locInfo = await getLocationInfo();
250+
if (locInfo.gqlPath) errorObj.gqlPath = locInfo.gqlPath;
251+
if (locInfo.graphicName) errorObj.graphicName = locInfo.graphicName;
252+
if (locInfo.graphicFile) errorObj.graphicFile = locInfo.graphicFile;
215253
if (e.mainErrors && e.mainErrors.length > 0) errorObj.mainErrors = cleanList(e.mainErrors);
216254
if (e.actionErrors && e.actionErrors.length > 0) errorObj.actionErrors = cleanList(e.actionErrors);
217255
if (e.infoMessages && e.infoMessages.length > 0) errorObj.infoMessages = cleanList(e.infoMessages);
218256
errors.push(errorObj);
219257
}
220258
}
259+
for (const g of await navFrame.$$('.TreeCtrl-outer[id^=geoTree] .TreeCtrl-content')) {
260+
try {
261+
await g.click();
262+
} catch (ex) {
263+
continue;
264+
}
265+
await wait();
266+
if (await page.$('#actButtonSpan > span[title="View graphics"]')) {
267+
if (await hasMultipleGraphics()) {
268+
await page.locator('#actButtonSpan > span[title="View graphics"] > img:last-child').click();
269+
await wait();
270+
const frame = await (await page.mainFrame().$('#actioncategories'))?.contentFrame();
271+
if (frame) {
272+
const len = (await frame.$$('#content > .MenuItem-base')).length;
273+
for (let i = 1; i <= len; ++i) {
274+
if (i > 1) {
275+
await page.locator('#actButtonSpan > span[title="View graphics"] > img:last-child').click();
276+
await wait();
277+
}
278+
const x = await frame.$(`#content > .MenuItem-base:nth-child(${i})`);
279+
if (!x) {
280+
break;
281+
}
282+
await x.click();
283+
await wait();
284+
await handleErrors(g);
285+
}
286+
}
287+
} else {
288+
await handleErrors(g);
289+
}
290+
}
291+
}
221292
if (outputFile === '-') {
222293
console.log(JSON.stringify(errors, null, 2));
223294
} else {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "graphics-error-scraper",
3-
"version": "0.1.2",
3+
"version": "0.1.3",
44
"description": "Scrapes a WebCTRL system to look for graphics errors.",
55
"license": "BSD-3-Clause",
66
"author": "Cameron Vogt",

0 commit comments

Comments
 (0)