@deepnote/blocks 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -0
- package/dist/index.cjs +598 -0
- package/dist/index.d.cts +381 -0
- package/dist/index.d.ts +381 -0
- package/dist/index.js +568 -0
- package/package.json +43 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
let zod = require("zod");
|
|
25
|
+
zod = __toESM(zod);
|
|
26
|
+
let yaml = require("yaml");
|
|
27
|
+
yaml = __toESM(yaml);
|
|
28
|
+
let ts_dedent = require("ts-dedent");
|
|
29
|
+
ts_dedent = __toESM(ts_dedent);
|
|
30
|
+
|
|
31
|
+
//#region src/deserialize-file/deepnote-file-schema.ts
|
|
32
|
+
const deepnoteBlockSchema = zod.z.object({
|
|
33
|
+
blockGroup: zod.z.string().optional(),
|
|
34
|
+
content: zod.z.string().optional(),
|
|
35
|
+
executionCount: zod.z.number().optional(),
|
|
36
|
+
id: zod.z.string(),
|
|
37
|
+
metadata: zod.z.record(zod.z.any()).optional(),
|
|
38
|
+
outputs: zod.z.array(zod.z.any()).optional(),
|
|
39
|
+
sortingKey: zod.z.string(),
|
|
40
|
+
type: zod.z.string(),
|
|
41
|
+
version: zod.z.number().optional()
|
|
42
|
+
});
|
|
43
|
+
const deepnoteFileSchema = zod.z.object({
|
|
44
|
+
metadata: zod.z.object({
|
|
45
|
+
checksum: zod.z.string().optional(),
|
|
46
|
+
createdAt: zod.z.string(),
|
|
47
|
+
exportedAt: zod.z.string().optional(),
|
|
48
|
+
modifiedAt: zod.z.string().optional()
|
|
49
|
+
}),
|
|
50
|
+
project: zod.z.object({
|
|
51
|
+
id: zod.z.string(),
|
|
52
|
+
initNotebookId: zod.z.string().optional(),
|
|
53
|
+
integrations: zod.z.array(zod.z.object({
|
|
54
|
+
id: zod.z.string(),
|
|
55
|
+
name: zod.z.string(),
|
|
56
|
+
type: zod.z.string()
|
|
57
|
+
})).optional(),
|
|
58
|
+
name: zod.z.string(),
|
|
59
|
+
notebooks: zod.z.array(zod.z.object({
|
|
60
|
+
blocks: zod.z.array(deepnoteBlockSchema),
|
|
61
|
+
executionMode: zod.z.enum(["block", "downstream"]).optional(),
|
|
62
|
+
id: zod.z.string(),
|
|
63
|
+
isModule: zod.z.boolean().optional(),
|
|
64
|
+
name: zod.z.string(),
|
|
65
|
+
workingDirectory: zod.z.string().optional()
|
|
66
|
+
})),
|
|
67
|
+
settings: zod.z.object({
|
|
68
|
+
environment: zod.z.object({
|
|
69
|
+
customImage: zod.z.string().optional(),
|
|
70
|
+
pythonVersion: zod.z.string().optional()
|
|
71
|
+
}).optional(),
|
|
72
|
+
requirements: zod.z.array(zod.z.string()).optional(),
|
|
73
|
+
sqlCacheMaxAge: zod.z.number().optional()
|
|
74
|
+
}).optional()
|
|
75
|
+
}),
|
|
76
|
+
version: zod.z.string()
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/deserialize-file/parse-yaml.ts
|
|
81
|
+
function parseYaml(yamlContent) {
|
|
82
|
+
try {
|
|
83
|
+
return (0, yaml.parse)(yamlContent);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86
|
+
throw new Error(`Failed to parse Deepnote file: ${message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/deserialize-file/deserialize-deepnote-file.ts
|
|
92
|
+
/**
|
|
93
|
+
* Deserialize a YAML string into a DeepnoteFile object.
|
|
94
|
+
*/
|
|
95
|
+
function deserializeDeepnoteFile(yamlContent) {
|
|
96
|
+
const parsed = parseYaml(yamlContent);
|
|
97
|
+
const result = deepnoteFileSchema.safeParse(parsed);
|
|
98
|
+
if (!result.success) {
|
|
99
|
+
const issue = result.error.issues[0];
|
|
100
|
+
if (!issue) throw new Error("Invalid Deepnote file.");
|
|
101
|
+
const path = issue.path.join(".");
|
|
102
|
+
const message = path ? `${path}: ${issue.message}` : issue.message;
|
|
103
|
+
throw new Error(`Failed to parse the Deepnote file: ${message}.`);
|
|
104
|
+
}
|
|
105
|
+
return result.data;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/blocks.ts
|
|
110
|
+
var UnsupportedBlockTypeError = class extends Error {
|
|
111
|
+
constructor(message) {
|
|
112
|
+
super(message);
|
|
113
|
+
this.name = "UnsupportedBlockTypeError";
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/blocks/image-blocks.ts
|
|
119
|
+
function escapeHtmlAttribute(value) {
|
|
120
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
121
|
+
}
|
|
122
|
+
function sanitizeWidth(width) {
|
|
123
|
+
return width.replace(/[^0-9]/g, "") || "";
|
|
124
|
+
}
|
|
125
|
+
function sanitizeAlignment(alignment) {
|
|
126
|
+
return [
|
|
127
|
+
"left",
|
|
128
|
+
"center",
|
|
129
|
+
"right"
|
|
130
|
+
].includes(alignment.toLowerCase()) ? alignment.toLowerCase() : "";
|
|
131
|
+
}
|
|
132
|
+
function createMarkdownForImageBlock(block) {
|
|
133
|
+
return `<img src="${escapeHtmlAttribute(block.metadata.deepnote_img_src ?? "")}" width="${sanitizeWidth(block.metadata.deepnote_img_width ?? "")}" align="${sanitizeAlignment(block.metadata.deepnote_img_alignment ?? "")}" />`;
|
|
134
|
+
}
|
|
135
|
+
function isImageBlock(block) {
|
|
136
|
+
return block.type === "image";
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/blocks/text-blocks.ts
|
|
141
|
+
function isTextBlock(block) {
|
|
142
|
+
return [
|
|
143
|
+
"text-cell-p",
|
|
144
|
+
"text-cell-h1",
|
|
145
|
+
"text-cell-h2",
|
|
146
|
+
"text-cell-h3",
|
|
147
|
+
"text-cell-bullet",
|
|
148
|
+
"text-cell-todo",
|
|
149
|
+
"text-cell-callout"
|
|
150
|
+
].includes(block.type.toLowerCase());
|
|
151
|
+
}
|
|
152
|
+
function escapeMarkdown(text) {
|
|
153
|
+
return text.replace(/([\\`*_{}[\]()#+\-.!|>])/g, "\\$1");
|
|
154
|
+
}
|
|
155
|
+
function createMarkdownForTextBlock(block) {
|
|
156
|
+
if (block.type === "text-cell-h1") return `# ${escapeMarkdown(block.content)}`;
|
|
157
|
+
if (block.type === "text-cell-h2") return `## ${escapeMarkdown(block.content)}`;
|
|
158
|
+
if (block.type === "text-cell-h3") return `### ${escapeMarkdown(block.content)}`;
|
|
159
|
+
if (block.type === "text-cell-bullet") return `- ${escapeMarkdown(block.content)}`;
|
|
160
|
+
if (block.type === "text-cell-todo") return `- ${block.metadata.checked ? "[x]" : "[ ]"} ${escapeMarkdown(block.content)}`;
|
|
161
|
+
if (block.type === "text-cell-callout") return `> ${escapeMarkdown(block.content)}`;
|
|
162
|
+
if (block.type === "text-cell-p") return escapeMarkdown(block.content);
|
|
163
|
+
throw new Error("Unhandled block type.");
|
|
164
|
+
}
|
|
165
|
+
function stripMarkdownFromTextBlock(block) {
|
|
166
|
+
if (block.type === "text-cell-h1") return block.content.replace(/^#\s+/, "").trim();
|
|
167
|
+
if (block.type === "text-cell-h2") return block.content.replace(/^##\s+/, "").trim();
|
|
168
|
+
if (block.type === "text-cell-h3") return block.content.replace(/^#{3,6}\s+/, "").trim();
|
|
169
|
+
if (block.type === "text-cell-bullet") return block.content.replace(/^-+\s+/, "").trim();
|
|
170
|
+
if (block.type === "text-cell-todo") return block.content.replace(/^-+\s+\[.\]\s+/, "").trim();
|
|
171
|
+
if (block.type === "text-cell-callout") return block.content.replace(/^>\s+/, "").trim();
|
|
172
|
+
if (block.type === "text-cell-p") return block.content.trim();
|
|
173
|
+
throw new Error("Unhandled block type.");
|
|
174
|
+
}
|
|
175
|
+
function createMarkdownForSeparatorBlock(_block) {
|
|
176
|
+
return "<hr>";
|
|
177
|
+
}
|
|
178
|
+
function isSeparatorBlock(block) {
|
|
179
|
+
return block.type === "separator";
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/markdown.ts
|
|
184
|
+
function createMarkdown(block) {
|
|
185
|
+
if (block.type === "markdown") return block.content ?? "";
|
|
186
|
+
if (isTextBlock(block)) return createMarkdownForTextBlock(block);
|
|
187
|
+
if (isSeparatorBlock(block)) return createMarkdownForSeparatorBlock(block);
|
|
188
|
+
if (isImageBlock(block)) return createMarkdownForImageBlock(block);
|
|
189
|
+
throw new UnsupportedBlockTypeError(`Creating markdown from block type ${block.type} is not supported yet.`);
|
|
190
|
+
}
|
|
191
|
+
function stripMarkdown(block) {
|
|
192
|
+
if (isTextBlock(block)) return stripMarkdownFromTextBlock(block);
|
|
193
|
+
throw new UnsupportedBlockTypeError(`Stripping markdown from block type ${block.type} is not supported yet.`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/blocks/python-utils.ts
|
|
198
|
+
function escapePythonString(value) {
|
|
199
|
+
return `'${value.replaceAll("\\", "\\\\").replaceAll("'", "\\'").replaceAll("\n", "\\n")}'`;
|
|
200
|
+
}
|
|
201
|
+
function sanitizePythonVariableName(name, options = {}) {
|
|
202
|
+
let sanitizedVariableName = name.replace(/\s+/g, "_").replace(/[^0-9a-zA-Z_]/g, "").replace(/^[^a-zA-Z_]+/g, "");
|
|
203
|
+
if (sanitizedVariableName === "" && !options.disableEmptyFallback) sanitizedVariableName = "input_1";
|
|
204
|
+
return sanitizedVariableName;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/python-snippets.ts
|
|
209
|
+
const pythonCode = {
|
|
210
|
+
setVariableContextValue: (variableName, value) => {
|
|
211
|
+
return `${sanitizePythonVariableName(variableName)} = ${value ? "True" : "False"}`;
|
|
212
|
+
},
|
|
213
|
+
executeBigNumber: (titleTemplate, valueVariableName, comparisonTitleTemplate = "", comparisonVariableName = "") => {
|
|
214
|
+
const sanitizedValueVariable = sanitizePythonVariableName(valueVariableName);
|
|
215
|
+
const valuePart = sanitizedValueVariable ? `f"{${sanitizedValueVariable}}"` : `""`;
|
|
216
|
+
if (!(comparisonTitleTemplate || comparisonVariableName)) return `
|
|
217
|
+
def __deepnote_big_number__():
|
|
218
|
+
import json
|
|
219
|
+
import jinja2
|
|
220
|
+
from jinja2 import meta
|
|
221
|
+
|
|
222
|
+
def render_template(template):
|
|
223
|
+
parsed_content = jinja2.Environment().parse(template)
|
|
224
|
+
|
|
225
|
+
required_variables = meta.find_undeclared_variables(parsed_content)
|
|
226
|
+
|
|
227
|
+
context = {
|
|
228
|
+
variable_name: globals().get(variable_name)
|
|
229
|
+
for variable_name in required_variables
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
result = jinja2.Environment().from_string(template).render(context)
|
|
233
|
+
|
|
234
|
+
return result
|
|
235
|
+
|
|
236
|
+
rendered_title = render_template(${escapePythonString(titleTemplate)})
|
|
237
|
+
|
|
238
|
+
return json.dumps({
|
|
239
|
+
"title": rendered_title,
|
|
240
|
+
"value": ${valuePart}
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
__deepnote_big_number__()
|
|
244
|
+
`;
|
|
245
|
+
const sanitizedComparisonVariable = sanitizePythonVariableName(comparisonVariableName);
|
|
246
|
+
const comparisonValuePart = sanitizedComparisonVariable ? `f"{${sanitizedComparisonVariable}}"` : `""`;
|
|
247
|
+
return `
|
|
248
|
+
def __deepnote_big_number__():
|
|
249
|
+
import json
|
|
250
|
+
import jinja2
|
|
251
|
+
from jinja2 import meta
|
|
252
|
+
|
|
253
|
+
def render_template(template):
|
|
254
|
+
parsed_content = jinja2.Environment().parse(template)
|
|
255
|
+
|
|
256
|
+
required_variables = meta.find_undeclared_variables(parsed_content)
|
|
257
|
+
|
|
258
|
+
context = {
|
|
259
|
+
variable_name: globals().get(variable_name)
|
|
260
|
+
for variable_name in required_variables
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
result = jinja2.Environment().from_string(template).render(context)
|
|
264
|
+
|
|
265
|
+
return result
|
|
266
|
+
|
|
267
|
+
rendered_title = render_template(${escapePythonString(titleTemplate)})
|
|
268
|
+
rendered_comparison_title = render_template(${escapePythonString(comparisonTitleTemplate)})
|
|
269
|
+
|
|
270
|
+
return json.dumps({
|
|
271
|
+
"comparisonTitle": rendered_comparison_title,
|
|
272
|
+
"comparisonValue": ${comparisonValuePart},
|
|
273
|
+
"title": rendered_title,
|
|
274
|
+
"value": ${valuePart}
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
__deepnote_big_number__()
|
|
278
|
+
`;
|
|
279
|
+
},
|
|
280
|
+
executeVisualization: (variableName, spec, filters) => {
|
|
281
|
+
return `_dntk.DeepnoteChart(${sanitizePythonVariableName(variableName)}, ${escapePythonString(spec)}, filters=${escapePythonString(filters)})`;
|
|
282
|
+
},
|
|
283
|
+
dateRangePast7days: (name) => {
|
|
284
|
+
return ts_dedent.dedent`
|
|
285
|
+
from datetime import datetime as _deepnote_datetime, timedelta as _deepnote_timedelta
|
|
286
|
+
${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - _deepnote_timedelta(days=7), _deepnote_datetime.now().date()]
|
|
287
|
+
`;
|
|
288
|
+
},
|
|
289
|
+
dateRangePast14days: (name) => {
|
|
290
|
+
return ts_dedent.dedent`
|
|
291
|
+
from datetime import datetime as _deepnote_datetime, timedelta as _deepnote_timedelta
|
|
292
|
+
${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - _deepnote_timedelta(days=14), _deepnote_datetime.now().date()]
|
|
293
|
+
`;
|
|
294
|
+
},
|
|
295
|
+
dateRangePastMonth: (name) => {
|
|
296
|
+
return ts_dedent.dedent`
|
|
297
|
+
from datetime import datetime as _deepnote_datetime
|
|
298
|
+
from dateutil.relativedelta import relativedelta
|
|
299
|
+
${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - relativedelta(months=1), _deepnote_datetime.now().date()]
|
|
300
|
+
`;
|
|
301
|
+
},
|
|
302
|
+
dateRangePast3months: (name) => {
|
|
303
|
+
return ts_dedent.dedent`
|
|
304
|
+
from datetime import datetime as _deepnote_datetime
|
|
305
|
+
from dateutil.relativedelta import relativedelta
|
|
306
|
+
${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - relativedelta(months=3), _deepnote_datetime.now().date()]
|
|
307
|
+
`;
|
|
308
|
+
},
|
|
309
|
+
dateRangePast6months: (name) => {
|
|
310
|
+
return ts_dedent.dedent`
|
|
311
|
+
from datetime import datetime as _deepnote_datetime
|
|
312
|
+
from dateutil.relativedelta import relativedelta
|
|
313
|
+
${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - relativedelta(months=6), _deepnote_datetime.now().date()]
|
|
314
|
+
`;
|
|
315
|
+
},
|
|
316
|
+
dateRangePastYear: (name) => {
|
|
317
|
+
return ts_dedent.dedent`
|
|
318
|
+
from datetime import datetime as _deepnote_datetime
|
|
319
|
+
from dateutil.relativedelta import relativedelta
|
|
320
|
+
${sanitizePythonVariableName(name)} = [_deepnote_datetime.now().date() - relativedelta(years=1), _deepnote_datetime.now().date()]
|
|
321
|
+
`;
|
|
322
|
+
},
|
|
323
|
+
dateRangeCustomDays: (name, days) => {
|
|
324
|
+
return ts_dedent.dedent`
|
|
325
|
+
from datetime import datetime, timedelta
|
|
326
|
+
${sanitizePythonVariableName(name)} = [datetime.now().date() - timedelta(days=${Math.floor(Number(days)) || 0}), datetime.now().date()]
|
|
327
|
+
`;
|
|
328
|
+
},
|
|
329
|
+
dateRangeAbsolute: (name, startDate, endDate) => {
|
|
330
|
+
const sanitizedName = sanitizePythonVariableName(name);
|
|
331
|
+
const escapedStartDate = escapePythonString(startDate);
|
|
332
|
+
const escapedEndDate = escapePythonString(endDate);
|
|
333
|
+
return ts_dedent.dedent`
|
|
334
|
+
from dateutil.parser import parse as _deepnote_parse
|
|
335
|
+
${sanitizedName} = [${startDate ? `_deepnote_parse(${escapedStartDate}).date()` : "None"}, ${endDate ? `_deepnote_parse(${escapedEndDate}).date()` : "None"}]
|
|
336
|
+
`;
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
//#endregion
|
|
341
|
+
//#region src/blocks/big-number-blocks.ts
|
|
342
|
+
function createPythonCodeForBigNumberBlock(block) {
|
|
343
|
+
return pythonCode.executeBigNumber(block.metadata.deepnote_big_number_title ?? "", block.metadata.deepnote_big_number_value ?? "", block.metadata.deepnote_big_number_comparison_title, block.metadata.deepnote_big_number_comparison_value);
|
|
344
|
+
}
|
|
345
|
+
function isBigNumberBlock(block) {
|
|
346
|
+
return block.type === "big-number";
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
//#endregion
|
|
350
|
+
//#region src/blocks/button-blocks.ts
|
|
351
|
+
function createPythonCodeForButtonBlock(block, executionContext) {
|
|
352
|
+
if (block.metadata.deepnote_button_behavior === "set_variable" && block.metadata.deepnote_variable_name) {
|
|
353
|
+
const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
|
|
354
|
+
if (executionContext?.variableContext?.includes(sanitizedPythonVariableName)) return pythonCode.setVariableContextValue(sanitizedPythonVariableName, true);
|
|
355
|
+
return pythonCode.setVariableContextValue(sanitizedPythonVariableName, false);
|
|
356
|
+
}
|
|
357
|
+
return "";
|
|
358
|
+
}
|
|
359
|
+
function isButtonBlock(block) {
|
|
360
|
+
return block.type === "button";
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
//#endregion
|
|
364
|
+
//#region src/blocks/data-frame.ts
|
|
365
|
+
function createDataFrameConfig(block) {
|
|
366
|
+
const tableState = block.metadata.deepnote_table_state ?? {};
|
|
367
|
+
const tableStateAsJson = JSON.stringify(tableState);
|
|
368
|
+
return ts_dedent.dedent`
|
|
369
|
+
if '_dntk' in globals():
|
|
370
|
+
_dntk.dataframe_utils.configure_dataframe_formatter(${escapePythonString(tableStateAsJson)})
|
|
371
|
+
else:
|
|
372
|
+
_deepnote_current_table_attrs = ${escapePythonString(tableStateAsJson)}
|
|
373
|
+
`;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/blocks/code-blocks.ts
|
|
378
|
+
function createPythonCodeForCodeBlock(block) {
|
|
379
|
+
return ts_dedent.dedent`
|
|
380
|
+
${createDataFrameConfig(block)}
|
|
381
|
+
|
|
382
|
+
${block.content}
|
|
383
|
+
`;
|
|
384
|
+
}
|
|
385
|
+
function isCodeBlock(block) {
|
|
386
|
+
return block.type === "code";
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
//#endregion
|
|
390
|
+
//#region src/blocks/input-blocks.ts
|
|
391
|
+
const DATE_RANGE_INPUT_RELATIVE_RANGES = [
|
|
392
|
+
{
|
|
393
|
+
value: "past7days",
|
|
394
|
+
pythonCode: pythonCode.dateRangePast7days
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
value: "past14days",
|
|
398
|
+
pythonCode: pythonCode.dateRangePast14days
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
value: "pastMonth",
|
|
402
|
+
pythonCode: pythonCode.dateRangePastMonth
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
value: "past3months",
|
|
406
|
+
pythonCode: pythonCode.dateRangePast3months
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
value: "past6months",
|
|
410
|
+
pythonCode: pythonCode.dateRangePast6months
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
value: "pastYear",
|
|
414
|
+
pythonCode: pythonCode.dateRangePastYear
|
|
415
|
+
}
|
|
416
|
+
];
|
|
417
|
+
function isCustomDateRange(value) {
|
|
418
|
+
if (typeof value !== "string") return false;
|
|
419
|
+
const days = Number.parseInt(value.split("customDays")[1] ?? "0", 10);
|
|
420
|
+
return value.startsWith("customDays") && !Number.isNaN(days) && days >= 0;
|
|
421
|
+
}
|
|
422
|
+
function isValidRelativeDateInterval(value) {
|
|
423
|
+
return typeof value === "string" && DATE_RANGE_INPUT_RELATIVE_RANGES.some((range) => range.value === value);
|
|
424
|
+
}
|
|
425
|
+
function isValidAbsoluteDateRange(value) {
|
|
426
|
+
const YYYY_MM_DD_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
427
|
+
return Array.isArray(value) && value.length === 2 && value.every((v) => typeof v === "string" && (v === "" || YYYY_MM_DD_REGEX.test(v)));
|
|
428
|
+
}
|
|
429
|
+
function createPythonCodeForInputTextBlock(block) {
|
|
430
|
+
return `${sanitizePythonVariableName(block.metadata.deepnote_variable_name)} = ${escapePythonString(block.metadata.deepnote_variable_value)}`;
|
|
431
|
+
}
|
|
432
|
+
function createPythonCodeForInputTextareaBlock(block) {
|
|
433
|
+
return `${sanitizePythonVariableName(block.metadata.deepnote_variable_name)} = ${escapePythonString(block.metadata.deepnote_variable_value)}`;
|
|
434
|
+
}
|
|
435
|
+
function createPythonCodeForInputCheckboxBlock(block) {
|
|
436
|
+
return `${sanitizePythonVariableName(block.metadata.deepnote_variable_name)} = ${block.metadata.deepnote_variable_value ? "True" : "False"}`;
|
|
437
|
+
}
|
|
438
|
+
function createPythonCodeForInputSelectBlock(block) {
|
|
439
|
+
const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
|
|
440
|
+
if (block.metadata.deepnote_allow_multiple_values || Array.isArray(block.metadata.deepnote_variable_value)) return `${sanitizedPythonVariableName} = [${(Array.isArray(block.metadata.deepnote_variable_value) ? block.metadata.deepnote_variable_value : [block.metadata.deepnote_variable_value]).map((value) => escapePythonString(value)).join(", ")}]`;
|
|
441
|
+
else if (!block.metadata.deepnote_allow_multiple_values && !block.metadata.deepnote_variable_value) return `${sanitizedPythonVariableName} = None`;
|
|
442
|
+
else return `${sanitizedPythonVariableName} = ${escapePythonString(block.metadata.deepnote_variable_value)}`;
|
|
443
|
+
}
|
|
444
|
+
function createPythonCodeForInputSliderBlock(block) {
|
|
445
|
+
const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
|
|
446
|
+
const value = block.metadata.deepnote_variable_value;
|
|
447
|
+
if (!/^-?\d+\.?\d*$|^-?\d*\.\d+$/.test(value)) throw new Error(`Invalid numeric value for slider input: "${value}". Expected a valid number (integer or float).`);
|
|
448
|
+
const numericValue = Number(value);
|
|
449
|
+
if (!Number.isFinite(numericValue)) throw new Error(`Invalid numeric value for slider input: "${value}". Value must be finite.`);
|
|
450
|
+
return `${sanitizedPythonVariableName} = ${numericValue}`;
|
|
451
|
+
}
|
|
452
|
+
function createPythonCodeForInputFileBlock(block) {
|
|
453
|
+
const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
|
|
454
|
+
if (block.metadata.deepnote_variable_value === "") return `${sanitizedPythonVariableName} = None`;
|
|
455
|
+
return `${sanitizedPythonVariableName} = ${escapePythonString(block.metadata.deepnote_variable_value)}`;
|
|
456
|
+
}
|
|
457
|
+
function createPythonCodeForInputDateBlock(block) {
|
|
458
|
+
const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
|
|
459
|
+
const escapedValue = escapePythonString(block.metadata.deepnote_variable_value);
|
|
460
|
+
if (!block.metadata.deepnote_variable_value) return `
|
|
461
|
+
${sanitizedPythonVariableName} = None
|
|
462
|
+
`;
|
|
463
|
+
if (block.metadata.deepnote_input_date_version === 2) return `
|
|
464
|
+
from dateutil.parser import parse as _deepnote_parse
|
|
465
|
+
${sanitizedPythonVariableName} = _deepnote_parse(${escapedValue}).date()
|
|
466
|
+
`;
|
|
467
|
+
return `
|
|
468
|
+
from datetime import datetime as _deepnote_datetime
|
|
469
|
+
${sanitizedPythonVariableName} = _deepnote_datetime.strptime(${escapedValue}, "%Y-%m-%dT%H:%M:%S.%fZ")
|
|
470
|
+
`;
|
|
471
|
+
}
|
|
472
|
+
function createPythonCodeForInputDateRangeBlock(block) {
|
|
473
|
+
const sanitizedPythonVariableName = sanitizePythonVariableName(block.metadata.deepnote_variable_name);
|
|
474
|
+
if (isValidAbsoluteDateRange(block.metadata.deepnote_variable_value)) {
|
|
475
|
+
const startDate = block.metadata.deepnote_variable_value[0];
|
|
476
|
+
const endDate = block.metadata.deepnote_variable_value[1];
|
|
477
|
+
return pythonCode.dateRangeAbsolute(sanitizedPythonVariableName, startDate, endDate);
|
|
478
|
+
} else if (isCustomDateRange(block.metadata.deepnote_variable_value)) {
|
|
479
|
+
const customDays = block.metadata.deepnote_variable_value.replace("customDays", "");
|
|
480
|
+
return pythonCode.dateRangeCustomDays(sanitizedPythonVariableName, Number(customDays));
|
|
481
|
+
} else if (isValidRelativeDateInterval(block.metadata.deepnote_variable_value)) {
|
|
482
|
+
const range = DATE_RANGE_INPUT_RELATIVE_RANGES.find((range$1) => range$1.value === block.metadata.deepnote_variable_value);
|
|
483
|
+
if (!range) throw new Error(`Invalid relative date interval: "${block.metadata.deepnote_variable_value}". Expected one of: ${DATE_RANGE_INPUT_RELATIVE_RANGES.map((r) => r.value).join(", ")}.`);
|
|
484
|
+
return ts_dedent.dedent`
|
|
485
|
+
${range.pythonCode(sanitizedPythonVariableName)}`;
|
|
486
|
+
} else return ts_dedent.dedent`
|
|
487
|
+
${sanitizedPythonVariableName} = [None, None]
|
|
488
|
+
`;
|
|
489
|
+
}
|
|
490
|
+
function isInputTextBlock(block) {
|
|
491
|
+
return block.type === "input-text";
|
|
492
|
+
}
|
|
493
|
+
function isInputTextareaBlock(block) {
|
|
494
|
+
return block.type === "input-textarea";
|
|
495
|
+
}
|
|
496
|
+
function isInputCheckboxBlock(block) {
|
|
497
|
+
return block.type === "input-checkbox";
|
|
498
|
+
}
|
|
499
|
+
function isInputSelectBlock(block) {
|
|
500
|
+
return block.type === "input-select";
|
|
501
|
+
}
|
|
502
|
+
function isInputSliderBlock(block) {
|
|
503
|
+
return block.type === "input-slider";
|
|
504
|
+
}
|
|
505
|
+
function isInputFileBlock(block) {
|
|
506
|
+
return block.type === "input-file";
|
|
507
|
+
}
|
|
508
|
+
function isInputDateBlock(block) {
|
|
509
|
+
return block.type === "input-date";
|
|
510
|
+
}
|
|
511
|
+
function isInputDateRangeBlock(block) {
|
|
512
|
+
return block.type === "input-date-range";
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
//#endregion
|
|
516
|
+
//#region src/blocks/sql-utils.ts
|
|
517
|
+
function convertToEnvironmentVariableName(str) {
|
|
518
|
+
return (/^\d/.test(str) ? `_${str}` : str).toUpperCase().replace(/[^\w]/g, "_");
|
|
519
|
+
}
|
|
520
|
+
function getSqlEnvVarName(integrationId) {
|
|
521
|
+
return `SQL_${integrationId}`;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
//#endregion
|
|
525
|
+
//#region src/blocks/sql-blocks.ts
|
|
526
|
+
function createPythonCodeForSqlBlock(block) {
|
|
527
|
+
const query = block.content;
|
|
528
|
+
const pythonVariableName = block.metadata.deepnote_variable_name;
|
|
529
|
+
const sanitizedPythonVariableName = pythonVariableName !== void 0 ? sanitizePythonVariableName(pythonVariableName) || "input_1" : void 0;
|
|
530
|
+
const returnVariableType = block.metadata.deepnote_return_variable_type ?? "dataframe";
|
|
531
|
+
const integrationId = block.metadata.sql_integration_id;
|
|
532
|
+
const connectionEnvVarName = integrationId ? convertToEnvironmentVariableName(getSqlEnvVarName(integrationId)) : "SQL_ALCHEMY_JSON_ENV_VAR";
|
|
533
|
+
const escapedQuery = escapePythonString(query);
|
|
534
|
+
const dataFrameConfig = createDataFrameConfig(block);
|
|
535
|
+
const executeSqlFunctionCall = ts_dedent.dedent`
|
|
536
|
+
_dntk.execute_sql(
|
|
537
|
+
${escapedQuery},
|
|
538
|
+
'${connectionEnvVarName}',
|
|
539
|
+
audit_sql_comment='',
|
|
540
|
+
sql_cache_mode='cache_disabled',
|
|
541
|
+
return_variable_type='${returnVariableType}'
|
|
542
|
+
)
|
|
543
|
+
`;
|
|
544
|
+
if (sanitizedPythonVariableName === void 0) return ts_dedent.dedent`
|
|
545
|
+
${dataFrameConfig}
|
|
546
|
+
|
|
547
|
+
${executeSqlFunctionCall}
|
|
548
|
+
`;
|
|
549
|
+
return ts_dedent.dedent`
|
|
550
|
+
${dataFrameConfig}
|
|
551
|
+
|
|
552
|
+
${sanitizedPythonVariableName} = ${executeSqlFunctionCall}
|
|
553
|
+
${sanitizedPythonVariableName}
|
|
554
|
+
`;
|
|
555
|
+
}
|
|
556
|
+
function isSqlBlock(block) {
|
|
557
|
+
return block.type === "sql";
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
//#endregion
|
|
561
|
+
//#region src/blocks/visualization-blocks.ts
|
|
562
|
+
function createPythonCodeForVisualizationBlock(block) {
|
|
563
|
+
const variableName = block.metadata.deepnote_variable_name;
|
|
564
|
+
const spec = block.metadata.deepnote_visualization_spec;
|
|
565
|
+
const filters = block.metadata.deepnote_chart_filter?.advancedFilters ?? [];
|
|
566
|
+
if (!variableName || !spec) return "";
|
|
567
|
+
const sanitizedVariableName = sanitizePythonVariableName(variableName);
|
|
568
|
+
return pythonCode.executeVisualization(sanitizedVariableName, JSON.stringify(spec), JSON.stringify(filters));
|
|
569
|
+
}
|
|
570
|
+
function isVisualizationBlock(block) {
|
|
571
|
+
return block.type === "visualization";
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/python-code.ts
|
|
576
|
+
function createPythonCode(block, executionContext) {
|
|
577
|
+
if (isCodeBlock(block)) return createPythonCodeForCodeBlock(block);
|
|
578
|
+
if (isSqlBlock(block)) return createPythonCodeForSqlBlock(block);
|
|
579
|
+
if (isInputTextBlock(block)) return createPythonCodeForInputTextBlock(block);
|
|
580
|
+
if (isInputTextareaBlock(block)) return createPythonCodeForInputTextareaBlock(block);
|
|
581
|
+
if (isInputCheckboxBlock(block)) return createPythonCodeForInputCheckboxBlock(block);
|
|
582
|
+
if (isInputSelectBlock(block)) return createPythonCodeForInputSelectBlock(block);
|
|
583
|
+
if (isInputSliderBlock(block)) return createPythonCodeForInputSliderBlock(block);
|
|
584
|
+
if (isInputFileBlock(block)) return createPythonCodeForInputFileBlock(block);
|
|
585
|
+
if (isInputDateBlock(block)) return createPythonCodeForInputDateBlock(block);
|
|
586
|
+
if (isInputDateRangeBlock(block)) return createPythonCodeForInputDateRangeBlock(block);
|
|
587
|
+
if (isVisualizationBlock(block)) return createPythonCodeForVisualizationBlock(block);
|
|
588
|
+
if (isButtonBlock(block)) return createPythonCodeForButtonBlock(block, executionContext);
|
|
589
|
+
if (isBigNumberBlock(block)) return createPythonCodeForBigNumberBlock(block);
|
|
590
|
+
throw new UnsupportedBlockTypeError(`Creating python code from block type ${block.type} is not supported yet.`);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
//#endregion
|
|
594
|
+
exports.createMarkdown = createMarkdown;
|
|
595
|
+
exports.createPythonCode = createPythonCode;
|
|
596
|
+
exports.deepnoteBlockSchema = deepnoteBlockSchema;
|
|
597
|
+
exports.deserializeDeepnoteFile = deserializeDeepnoteFile;
|
|
598
|
+
exports.stripMarkdown = stripMarkdown;
|