diff --git a/src/utils/tana/convert-to-tana-node.ts b/src/utils/tana/convert-to-tana-node.ts index 9cbc3056..7429f56b 100644 --- a/src/utils/tana/convert-to-tana-node.ts +++ b/src/utils/tana/convert-to-tana-node.ts @@ -3,21 +3,12 @@ import { NodeType, TanaIntermediateFile, TanaIntermediateNode } from "./types" import { RuntimePropertiesSingleton } from '../../runtime-properties'; import { checkboxDone, checkboxTodo, tanaCodeBlock, tanaTableBlock, tanaTableColBlock, tanaTableRowBlock } from "../../constants"; import { createNewTanaFile } from "./create-new-tana-file"; +import { createTanaNode } from "./create-tana-node"; export const cleanTanaContent = (content: string, valueToClean: string):string => { return content.replace(valueToClean, '') } -const createTanaNode = (type: NodeType, content: string, data: NoteData, uid: string = 'uuid' + Math.random()): TanaIntermediateNode => { - return { - uid, - createdAt: new Date(data.createdAt).getTime(), - editedAt: new Date(data.updatedAt).getTime(), - type, - name: content, - refs: [], - children: [] - } -} + const convertString2TanaNode = (content: string, data: NoteData, addTags: boolean = false): TanaIntermediateNode => { const linkNameMap = RuntimePropertiesSingleton.getInstance(); const link = linkNameMap.getNoteIdNameMapByNoteTitle(content); @@ -34,28 +25,55 @@ const convertString2TanaCheckbox = (content: string, todoState: string, data: No const checkboxNode = createTanaNode( 'node' as NodeType, cleanTanaContent(content, todoState === 'todo' ? checkboxTodo: checkboxDone), - data) + data, undefined) checkboxNode.todoState = todoState as "todo"|"done" return checkboxNode; } const convertString2TanaCodeblock = (content: string, data: NoteData): TanaIntermediateNode => { - return createTanaNode('codeblock' as NodeType, cleanTanaContent(content, tanaCodeBlock), data); + return createTanaNode('codeblock' as NodeType, cleanTanaContent(content, tanaCodeBlock), data, undefined); } +const splitAndCleanArray = (content: string, splitBy: string): Array => { + return content.split(splitBy).filter(contentElem => contentElem !== ''); +} const convertString2TanaTable = (content: string, data: NoteData): TanaIntermediateNode => { - const mainTableNode: TanaIntermediateNode = createTanaNode('node' as NodeType, 'Table', data); - const rows = content.split(tanaTableRowBlock) - for (const row of rows){ - const cols = row.split(tanaTableColBlock) - const rowNode: TanaIntermediateNode = createTanaNode('node' as NodeType, row, data); + const mainTableNode: TanaIntermediateNode = createTanaNode('node' as NodeType, 'Table', data, undefined); + + const rows = splitAndCleanArray(content, tanaTableRowBlock) + const columns: Array = []; + for (const [rowIndex, row] of rows.entries()){ + + const cols = splitAndCleanArray(row, tanaTableColBlock) + + if (rowIndex === 0){ + + // create fields for this first row + for (const col of cols){ + + columns.push(createTanaNode('field' as NodeType, col, data, undefined)) + } + } + else { + let rowNode: TanaIntermediateNode; + for (const [index, col] of cols.entries()) { + if (index === 0){ + const rowItems = row.split(tanaTableColBlock) + rowNode = createTanaNode('node' as NodeType, rowItems && rowItems.length >= 1 ? rowItems[1]:'', data, undefined); + } else { + const cellNode: TanaIntermediateNode = createTanaNode('node' as NodeType, col, data, undefined); + const columnField = {...columns[index], uid: 'uid' + Math.random()} + columnField.children = []; + columnField.children.push(cellNode) + rowNode.children.push(columnField) + } + //rowNode.children.push(cellNode) + + } + mainTableNode.children.push(rowNode) - for (const col of cols) { - const colNode: TanaIntermediateNode =createTanaNode('field' as NodeType, col, data); - rowNode.children.push(colNode) } - mainTableNode.children.push(rowNode) } return mainTableNode diff --git a/src/utils/tana/create-new-tana-file.ts b/src/utils/tana/create-new-tana-file.ts index 266358c3..99fe0e5b 100644 --- a/src/utils/tana/create-new-tana-file.ts +++ b/src/utils/tana/create-new-tana-file.ts @@ -5,6 +5,7 @@ export const createNewTanaFile =(): TanaIntermediateFile => { version: 'TanaIntermediateFile V0.1', nodes: [], supertags: [], + attributes: [], summary: { leafNodes:0, topLevelNodes: 0, diff --git a/src/utils/tana/create-tana-node.ts b/src/utils/tana/create-tana-node.ts new file mode 100644 index 00000000..cd6645b6 --- /dev/null +++ b/src/utils/tana/create-tana-node.ts @@ -0,0 +1,16 @@ +import { NoteData } from "../../models/NoteData" +import { NodeType, TanaIntermediateNode } from "./types" + +export const createTanaNode = (type: NodeType, content: string, data: NoteData, uid: string): TanaIntermediateNode => { + if (!uid) + uid = 'uuid' + Math.random() + return { + uid, + createdAt: new Date(data.createdAt).getTime(), + editedAt: new Date(data.updatedAt).getTime(), + type, + name: content, + refs: [], + children: [] + } +} \ No newline at end of file diff --git a/src/utils/tana/create-tana-output.ts b/src/utils/tana/create-tana-output.ts index b175693a..3ba661d1 100644 --- a/src/utils/tana/create-tana-output.ts +++ b/src/utils/tana/create-tana-output.ts @@ -1,9 +1,10 @@ import * as fs from 'fs'; - +import * as _ from 'lodash'; import { YarleOptions } from "../../YarleOptions"; import { getAllOutputFilesWithExtension } from "../get-all-output-files"; -import { TanaIntermediateFile } from "./types"; +import { NodeType, TanaIntermediateAttribute, TanaIntermediateFile, TanaIntermediateNode } from "./types"; import { createNewTanaFile } from './create-new-tana-file'; +import { createTanaNode } from './create-tana-node'; const tanaNoteFileName = 'notes-in-TIF.json'; export const createTanaOutput = (options: YarleOptions, outputNotebookFolders: Array): void => { @@ -20,10 +21,22 @@ export const createTanaOutput = (options: YarleOptions, outputNotebookFolders: A saveMergedTanaNotes(options, mergedNotes) } } - +const cleanChildren = (nodes: Array): Array => { + const cleanNodes = _.cloneDeep(nodes) + for (const node of cleanNodes) + node.children = [] + return cleanNodes; +} const updateMergedNotes = (mergedNotes: TanaIntermediateFile, convertedTanaNote: TanaIntermediateFile): void => { mergedNotes.nodes.push(convertedTanaNote.nodes[0]) + const fieldNodes = addFieldNodesToRoot(convertedTanaNote.nodes[0]); + mergedNotes.attributes.push(...getAttributes(fieldNodes)) + mergedNotes.nodes.push(...cleanChildren(fieldNodes)) + + // handling attributes + + //handling supertags if(convertedTanaNote.supertags){ for (const supertag of convertedTanaNote.supertags){ if (!mergedNotes.supertags.find(mergedSupertag => mergedSupertag.uid === supertag.uid)) @@ -33,8 +46,69 @@ const updateMergedNotes = (mergedNotes: TanaIntermediateFile, convertedTanaNote: mergedNotes.summary.leafNodes += convertedTanaNote.summary.leafNodes mergedNotes.summary.totalNodes += convertedTanaNote.summary.totalNodes mergedNotes.summary.topLevelNodes += 1 + mergedNotes.summary.fields = fieldNodes.length; +} +const getAttributes = (fieldNodes: Array): Array => { + const fieldAttributes: Array = []; + for (const field of fieldNodes){ + for (const valueChild of field.children){ + const storedAttribute = fieldAttributes.find( attribute => attribute.name === field.name) + if (storedAttribute) { + storedAttribute.count +=1; + storedAttribute.values.push(valueChild.name) + }else { + fieldAttributes.push(createNewTanaAttribute(field.name, valueChild.name)) + } + } + } + return fieldAttributes; +} + +const createNewTanaAttribute = (name: string, value: string): TanaIntermediateAttribute => { + return { + name, + values: [value], + count: 1, + } } +// +const addFieldNodesToRoot = (tanaNode: TanaIntermediateNode): Array => { + const fields:Array = [] + deepFind([tanaNode], (node:TanaIntermediateNode) => { + return node.type === 'field' as NodeType; + }, fields); + //return fields || [] + const fieldNodes: Array = []; + for (const field of fields) { + const foundField = fieldNodes.find(fieldNode => fieldNode.name === field.name) + if (foundField){ + field.refs.push(foundField.uid) + foundField.children.push(...field.children) + } + else { + const node = createTanaNode('node' as NodeType, field.name, { createdAt: ''+new Date(field.createdAt).toISOString().slice(0, 10), updatedAt: ''+new Date(field.editedAt).toISOString().slice(0, 10), }, undefined); + node.children =[...field.children] + fieldNodes.push(node) + field.refs.push(node.uid) + } + } + + return fieldNodes; + +} +const deepFind = (arr: Array, searchFuncion: Function, fields: Array): void => { + for(const obj of arr) { + if(searchFuncion(obj)) { + fields.push(obj); + } + if(obj.children) { + deepFind(obj.children, searchFuncion, fields); + + } + } + return null; +} const getMergedTanaNotes = (options: YarleOptions): TanaIntermediateFile => { let mergedTanaNote diff --git a/test/data/test-tana-table.enex b/test/data/test-tana-table.enex new file mode 100644 index 00000000..eff37470 --- /dev/null +++ b/test/data/test-tana-table.enex @@ -0,0 +1,16 @@ + + + + + Table + 20230511T153412Z + 20230517T204040Z + + akos + + + +

TABLE0.0
Column1
Column2
Row1
Cell1.1
Cell1.2
Row2
Cell.2.1
Cell2.2

]]> +
+
+