Skip to content

Commit fd320b5

Browse files
authored
Merge pull request #509 from KxSystems/ee-api
[ggplot] Move png detection to extension
2 parents aea31b2 + a8f96f4 commit fd320b5

File tree

8 files changed

+116
-73
lines changed

8 files changed

+116
-73
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ To use GGPlot2 in VSCode:
629629

630630
You can make changes to the script before exporting the plot. Re-running the script updates to reflect the changes.
631631

632-
**Note**: When executing GG script commands in PyKX, select the `KDB RESULTS` tab to display the plot.
632+
**Note**: When executing GG script commands, select the `KDB RESULTS` tab to display the plot.
633633

634634
## q REPL
635635

resources/evaluate.q

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{[ctx; code; returnFormat]
2+
if [`histogram in key `.qp;
3+
if [not `display2 in key `.qp;
4+
.qp.display2: (')[{x[`output][`bytes]}; .qp.display]
5+
]];
26
if [-10h ~ type ctx;
37
ctx: enlist ctx];
48
toString: {[data]
@@ -117,8 +121,7 @@
117121
(`result; ::);
118122
(`errored; 1b);
119123
(`error; err);
120-
(`backtrace; .Q.sbt userCode);
121-
(`base64; 0b))
124+
(`backtrace; .Q.sbt userCode))
122125
}[suffix; prefix]];
123126
if [isLastLine or result`errored;
124127
system "d ", cachedCtx;
@@ -197,23 +200,6 @@
197200
}
198201
result: evalInContext[ctx; splitExpression stripTrailingSemi wrapLines removeMultilineComments code];
199202
if [result `errored; :result];
200-
/ggplot - start
201-
if[type[result[`result]] = 99h;
202-
attrs: key[[result[`result]]];
203-
if[type[attrs] = 11h;
204-
if[`output in attrs;
205-
output: result[`result][`output];
206-
if[type[output] = 99h;
207-
attrs: key[output];
208-
if[type[attrs] = 11h;
209-
if[`bytes in attrs;
210-
bytes: output[`bytes];
211-
if[type[bytes] = 4h;
212-
if[0x89504E470D0A1A0A ~ bytes til 8;
213-
result[`base64]: 1b;
214-
result[`result]: .Q.btoa bytes;
215-
:result]]]]]]]];
216-
/ggplot - end
217203
if [returnFormat ~ "text";
218204
result[`result]: toString result `result];
219205
if [returnFormat ~ "structuredText";

resources/evaluatePy.q

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
{[isString;code]
2+
if [`histogram in key `.qp;
3+
if [not `display2 in key `.qp;
4+
.qp.display2: (')[{x[`output][`bytes]}; .qp.display]
5+
]];
26
if[()~key`.pykx;
37
:(!). flip(
48
(`result;::);
@@ -97,23 +101,6 @@
97101
if [`backtrace in key result;
98102
result[`backtrace]:string result`backtrace];
99103
if[result `errored; :result];
100-
/ggplot - start
101-
if[type[result[`result]] = 99h;
102-
attrs: key[[result[`result]]];
103-
if[type[attrs] = 11h;
104-
if[`output in attrs;
105-
output: result[`result][`output];
106-
if[type[output] = 99h;
107-
attrs: key[output];
108-
if[type[attrs] = 11h;
109-
if[`bytes in attrs;
110-
bytes: output[`bytes];
111-
if[type[bytes] = 4h;
112-
if[0x89504E470D0A1A0A ~ bytes til 8;
113-
result[`base64]: 1b;
114-
result[`result]: .Q.btoa bytes;
115-
:result]]]]]]]];
116-
/ggplot - end
117104
$[result`errored;
118105
::;
119106
returnResult;

src/classes/localConnection.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ export class LocalConnection {
175175
isPython?: boolean,
176176
): Promise<any> {
177177
let result;
178-
let base64 = false;
179178
await this.waitForConnection();
180179

181180
if (!this.connection) {
@@ -204,7 +203,6 @@ export class LocalConnection {
204203
);
205204
} else {
206205
result = res.result === null ? "" : res.result;
207-
base64 = res.base64 || false;
208206
}
209207
}
210208
});
@@ -222,10 +220,6 @@ export class LocalConnection {
222220
return result;
223221
}
224222

225-
if (base64) {
226-
return { base64, result };
227-
}
228-
229223
if (!stringify && !isPython) {
230224
return JSON.parse(result);
231225
}

src/commands/serverCommand.ts

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import {
5858
checkIfIsDatasource,
5959
addQueryHistory,
6060
formatScratchpadStacktrace,
61+
resultToBase64,
6162
} from "../utils/queryUtils";
6263
import {
6364
validateServerAlias,
@@ -894,37 +895,43 @@ async function _executeQuery(
894895
);
895896
} else {
896897
/* istanbul ignore next */
897-
if (results.base64) {
898-
const active = ext.activeTextEditor;
899-
if (active) {
900-
const data = `data:image/png;base64,${results.result}`;
901-
const plot = <Plot>{
902-
charts: [{ data }],
903-
};
904-
const uri = await addWorkspaceFile(
905-
active.document.uri,
906-
"plot",
907-
".plot",
908-
);
909-
if (!workspaceHas(uri)) {
910-
await workspace.openTextDocument(uri);
911-
await openWith(uri, ChartEditorProvider.viewType, ViewColumn.Beside);
898+
if (ext.isResultsTabVisible) {
899+
const data = resultToBase64(results);
900+
if (data) {
901+
const active = ext.activeTextEditor;
902+
if (active) {
903+
const plot = <Plot>{
904+
charts: [{ data }],
905+
};
906+
const uri = await addWorkspaceFile(
907+
active.document.uri,
908+
"plot",
909+
".plot",
910+
);
911+
if (!workspaceHas(uri)) {
912+
await workspace.openTextDocument(uri);
913+
await openWith(
914+
uri,
915+
ChartEditorProvider.viewType,
916+
ViewColumn.Beside,
917+
);
918+
}
919+
await setUriContent(uri, JSON.stringify(plot));
912920
}
913-
await setUriContent(uri, JSON.stringify(plot));
921+
} else {
922+
await writeQueryResultsToView(
923+
results,
924+
query,
925+
connLabel,
926+
executorName,
927+
isInsights,
928+
isWorkbook ? "WORKBOOK" : "SCRATCHPAD",
929+
isPython,
930+
duration,
931+
isFromConnTree,
932+
connVersion,
933+
);
914934
}
915-
} else if (ext.isResultsTabVisible) {
916-
await writeQueryResultsToView(
917-
results,
918-
query,
919-
connLabel,
920-
executorName,
921-
isInsights,
922-
isWorkbook ? "WORKBOOK" : "SCRATCHPAD",
923-
isPython,
924-
duration,
925-
isFromConnTree,
926-
connVersion,
927-
);
928935
} else {
929936
await writeQueryResultsToConsole(
930937
results,

src/models/queryResult.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ export type QueryResult = {
2626
text: string;
2727
index: number;
2828
}[];
29-
base64?: boolean;
3029
};
3130

3231
export enum QueryResultType {

src/utils/queryUtils.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,19 @@ export function formatScratchpadStacktrace(stacktrace: ScratchpadStacktrace) {
395395
})
396396
.join("\n");
397397
}
398+
399+
const PNG = ["0x89", "0x50", "0x4e", "0x47", "0x0d", "0x0a", "0x1a", "0x0a"];
400+
401+
export function resultToBase64(result: any): string | undefined {
402+
const bytes =
403+
result?.columns?.type === "bytes" ? result.columns.values : result;
404+
if (Array.isArray(bytes) && bytes.length > 66) {
405+
for (let i = 0; i < PNG.length; i++) {
406+
if (bytes[i] !== PNG[i] && bytes[i] !== parseInt(PNG[i], 16)) {
407+
return undefined;
408+
}
409+
}
410+
return `data:image/png;base64,${Buffer.from(bytes).toString("base64")}`;
411+
}
412+
return undefined;
413+
}

test/suite/utils.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1974,4 +1974,58 @@ describe("Utils", () => {
19741974
);
19751975
});
19761976
});
1977+
1978+
describe("resultToBase64", () => {
1979+
const png = [
1980+
"0x89",
1981+
"0x50",
1982+
"0x4e",
1983+
"0x47",
1984+
"0x0d",
1985+
"0x0a",
1986+
"0x1a",
1987+
"0x0a",
1988+
];
1989+
const img = Array.from({ length: 59 }, () => "0x00");
1990+
1991+
it("should return undefined for undefined", () => {
1992+
const result = queryUtils.resultToBase64(undefined);
1993+
assert.strictEqual(result, undefined);
1994+
});
1995+
it("should return undefined for just signature", () => {
1996+
const result = queryUtils.resultToBase64(png);
1997+
assert.strictEqual(result, undefined);
1998+
});
1999+
it("should return undefined for undefined for bad signature", () => {
2000+
const result = queryUtils.resultToBase64([
2001+
...png.map((v) => parseInt(v, 16) + 1),
2002+
,
2003+
...img,
2004+
]);
2005+
assert.strictEqual(result, undefined);
2006+
});
2007+
it("should return base64 for minimum img str", () => {
2008+
const result = queryUtils.resultToBase64([...png, ...img]);
2009+
assert.ok(result.startsWith("data:image/png;base64"));
2010+
});
2011+
it("should return base64 for minimum img num", () => {
2012+
const result = queryUtils.resultToBase64([
2013+
...png.map((v) => parseInt(v, 16)),
2014+
...img.map((v) => parseInt(v, 16)),
2015+
]);
2016+
assert.ok(result.startsWith("data:image/png;base64"));
2017+
});
2018+
it("should return base64 for minimum img str for structuredText", () => {
2019+
const result = queryUtils.resultToBase64({
2020+
columns: { type: "bytes", values: [...png, ...img] },
2021+
});
2022+
assert.ok(result.startsWith("data:image/png;base64"));
2023+
});
2024+
it("should return undefined for bogus structuredText", () => {
2025+
const result = queryUtils.resultToBase64({
2026+
columns: { type: "bytes" },
2027+
});
2028+
assert.strictEqual(result, undefined);
2029+
});
2030+
});
19772031
});

0 commit comments

Comments
 (0)