From 9fff34896b035bf5afeea2c37ffb43366ab9ad4b Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Wed, 2 Apr 2025 17:35:04 +0700 Subject: [PATCH 01/50] wip: integrate with import-tool Signed-off-by: Anna Khismatullina --- packages/importer/package.json | 1 + packages/importer/src/huly/cards.ts.bak | 75 +++++++++ packages/importer/src/huly/converters.ts | 42 +++++ .../src/huly/converters/master-tag.ts | 43 ++++++ packages/importer/src/huly/huly.ts | 143 ++++++++++++++++-- packages/importer/src/importer/builder.ts | 72 ++++++++- packages/importer/src/importer/importer.ts | 3 +- packages/importer/src/importer/unified.ts | 0 packages/importer/src/types.ts | 19 +++ 9 files changed, 383 insertions(+), 15 deletions(-) create mode 100644 packages/importer/src/huly/cards.ts.bak create mode 100644 packages/importer/src/huly/converters.ts create mode 100644 packages/importer/src/huly/converters/master-tag.ts create mode 100644 packages/importer/src/importer/unified.ts create mode 100644 packages/importer/src/types.ts diff --git a/packages/importer/package.json b/packages/importer/package.json index 3533bec328b..b0a674678d0 100644 --- a/packages/importer/package.json +++ b/packages/importer/package.json @@ -42,6 +42,7 @@ }, "dependencies": { "@hcengineering/attachment": "^0.6.14", + "@hcengineering/card": "^0.6.0", "@hcengineering/chunter": "^0.6.20", "@hcengineering/collaboration": "^0.6.0", "@hcengineering/contact": "^0.6.24", diff --git a/packages/importer/src/huly/cards.ts.bak b/packages/importer/src/huly/cards.ts.bak new file mode 100644 index 00000000000..7b101524f34 --- /dev/null +++ b/packages/importer/src/huly/cards.ts.bak @@ -0,0 +1,75 @@ +import { TxOperations } from '@hcengineering/core' +import { readFileSync } from 'fs' +import { parse as parseFrontmatter } from 'gray-matter' +import { join } from 'path' +import { parse as parseYaml } from 'yaml' +import { type Logger } from '../importer/logger' +import { ImportContext } from '../types' +import { CardConverter, MasterTagConverter } from './converters' + +export class CardsImporter { + private readonly converters = new Map() + private readonly context: ImportContext = { + vars: {}, + defaults: new Map() + } + + constructor ( + private readonly client: TxOperations, + private readonly logger: Logger + ) { + // Register converters + this.converters.set('card:class:MasterTag', new MasterTagConverter()) + this.converters.set('card:class:Card', new CardConverter()) + } + + async importFromDirectory (dir: string): Promise { + this.logger.log('Starting cards import from directory: ' + dir) + + // Read all yaml files for MasterTags + const files = await this.readDirectory(dir) + + for (const file of files) { + if (file.endsWith('.yaml')) { + this.logger.log('Processing YAML file: ' + file) + const content = readFileSync(join(dir, file), 'utf-8') + const data = parseYaml(content) + const converter = this.converters.get(data.class) + if (converter !== undefined) { + const unifiedDoc = converter.convert(data, this.context) + this.logger.log('Converted to UnifiedDoc: ' + JSON.stringify(unifiedDoc, null, 2)) + } else { + this.logger.log('No converter found for class: ' + data.class) + } + } else if (file.endsWith('.md')) { + this.logger.log('Processing Markdown file: ' + file) + const content = readFileSync(join(dir, file), 'utf-8') + const { data } = parseFrontmatter(content) + const converter = this.converters.get(data.class) + if (converter !== undefined) { + const unifiedDoc = converter.convert(data, this.context) + this.logger.log('Converted to UnifiedDoc: ' + JSON.stringify(unifiedDoc, null, 2)) + } else { + this.logger.log('No converter found for class: ' + data.class) + } + } + } + } + + private async readDirectory (dir: string): Promise { + const entries = fs.readdirSync(dir, { withFileTypes: true }) + const files: string[] = [] + + for (const entry of entries) { + const fullPath = join(dir, entry.name) + if (entry.isDirectory() === true) { + const subFiles = await this.readDirectory(fullPath) + files.push(...subFiles) + } else if (entry.isFile() === true && (entry.name.endsWith('.yaml') || entry.name.endsWith('.md'))) { + files.push(fullPath) + } + } + + return files + } +} diff --git a/packages/importer/src/huly/converters.ts b/packages/importer/src/huly/converters.ts new file mode 100644 index 00000000000..804e03447a1 --- /dev/null +++ b/packages/importer/src/huly/converters.ts @@ -0,0 +1,42 @@ +import core, { Doc } from '@hcengineering/core' +import card from '@hcengineering/card' +import { Converter, UnifiedDoc } from '../types' + +export class MasterTagConverter implements Converter { + convert (data: Record): UnifiedDoc { + if (data.class !== card.class.MasterTag) { + throw new Error('Invalid master tag data') + } + + return { + _class: card.class.MasterTag, + data: { + space: core.space.Model, + label: data.title, + extends: card.class.Card, + kind: 0, + icon: 'card:icon:MasterTag', + ...data.properties + } + } + } +} + +export class CardConverter implements Converter { + convert (data: any): UnifiedDoc { + const { class: cardClass, title, content, ...customFields } = data + + return { + _class: cardClass, + data: { + space: core.space.Workspace, + title, + content, + rank: '#nextRank', + parentInfo: [], + blobs: {}, + ...customFields + } + } + } +} diff --git a/packages/importer/src/huly/converters/master-tag.ts b/packages/importer/src/huly/converters/master-tag.ts new file mode 100644 index 00000000000..d55386972c3 --- /dev/null +++ b/packages/importer/src/huly/converters/master-tag.ts @@ -0,0 +1,43 @@ +import card, { MasterTag } from '@hcengineering/card' +import core, { Attribute, generateId } from '@hcengineering/core' +import { converter, UnifiedDoc } from '../../types' +import { IntlString } from '../../../../platform/types' + +export const masterTagConverter: converter = (data: Record) => { + const { class: _class, title, properties } = data + if (_class !== card.class.MasterTag) { + throw new Error('Invalid master tag data') + } + + const masterTagId = generateId(card.class.MasterTag) + const masterTag: UnifiedDoc = { + _class: card.class.MasterTag, + props: { + _id: masterTagId, + space: core.space.Model, + extends: card.class.Card, + label: 'embedded:embedded:' + title as IntlString, // todo: check if it's correct + kind: 0, + icon: card.icon.MasterTag + } + } + + const attributes: UnifiedDoc>[] = [] + for (const property of properties) { + attributes.push({ + _class: core.class.Attribute, + props: { + space: core.space.Model, + attributeOf: masterTagId, + name: generateId(core.class.Attribute), + label: 'embedded:embedded:' + property.label as IntlString, // todo: check if it's correct + isCustom: true, + type: { + _class: 'core:class:Type' + property.type + }, + defaultValue: property.defaultValue ?? null + } + }) + } + return [masterTag, ...attributes] +} diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 0d09a78cb65..5b13a57ad48 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -14,10 +14,17 @@ // /* eslint-disable @typescript-eslint/no-unused-vars */ import { type Attachment } from '@hcengineering/attachment' -import contact, { Employee, SocialIdentity, type Person } from '@hcengineering/contact' -import { +import card, { MasterTag } from '@hcengineering/card' +import contact, { Employee, type Person, SocialIdentity } from '@hcengineering/contact' +import documents, { + ControlledDocument, + DocumentCategory, + DocumentMeta, + DocumentState +} from '@hcengineering/controlled-documents' +import core, { AccountUuid, - buildSocialIdString, + Attribute, type Class, type Doc, generateId, @@ -35,6 +42,7 @@ import sizeOf from 'image-size' import * as yaml from 'js-yaml' import { contentType } from 'mime-types' import * as path from 'path' +import { IntlString } from '../../../platform/types' import { ImportWorkspaceBuilder } from '../importer/builder' import { type ImportAttachment, @@ -44,22 +52,17 @@ import { type ImportDocument, ImportDrawing, type ImportIssue, + ImportOrgSpace, type ImportProject, type ImportProjectType, type ImportTeamspace, type ImportWorkspace, - WorkspaceImporter, - ImportOrgSpace + WorkspaceImporter } from '../importer/importer' import { type Logger } from '../importer/logger' import { BaseMarkdownPreprocessor } from '../importer/preprocessor' import { type FileUploader } from '../importer/uploader' -import documents, { - DocumentState, - DocumentCategory, - ControlledDocument, - DocumentMeta -} from '@hcengineering/controlled-documents' +import { UnifiedDoc } from '../types' export interface HulyComment { author: string @@ -335,6 +338,7 @@ interface AttachmentMetadata { export class HulyFormatImporter { private readonly importerEmailPlaceholder = 'newuser@huly.io' private readonly importerNamePlaceholder = 'New User' + private readonly pathById = new Map, string>() private readonly refMetaByPath = new Map() private readonly fileMetaByPath = new Map() @@ -343,6 +347,7 @@ export class HulyFormatImporter { private personsByName = new Map>() private employeesByName = new Map>() private accountsByEmail = new Map() + private readonly personIdByEmail = new Map() constructor ( @@ -351,7 +356,8 @@ export class HulyFormatImporter { private readonly logger: Logger, private readonly importerSocialId?: PersonId, private readonly importerPerson?: Ref - ) {} + ) { + } private async initCaches (): Promise { await this.cachePersonsByNames() @@ -521,6 +527,23 @@ export class HulyFormatImporter { break } + case card.class.MasterTag: { + const masterTag = await this.processMasterTag(spaceConfig) + const { _id: masterTagId } = masterTag.props + if (masterTagId === undefined) { + throw new Error('Master tag ID is undefined') + } + builder.addMasterTag(spacePath, masterTag) + + const attributesByLabel = await this.processMasterTagAttributes(spaceConfig, masterTagId) + builder.addMasterTagAttributes(spacePath, Array.from(attributesByLabel.values())) + + if (fs.existsSync(spacePath) && fs.statSync(spacePath).isDirectory()) { + await this.processCardsRecursively(builder, spacePath, spacePath, masterTagId as Ref, attributesByLabel) + } + break + } + default: { throw new Error(`Unknown space class ${spaceConfig.class} in ${spaceName}`) } @@ -534,6 +557,102 @@ export class HulyFormatImporter { return builder.build() } + private async processMasterTag (yamlData: Record): Promise>> { + const { class: _class, title } = yamlData + if (_class !== card.class.MasterTag) { + throw new Error('Invalid master tag data') + } + + const masterTag: UnifiedDoc = { + _class: card.class.MasterTag, + props: { + _id: generateId(), + space: core.space.Model, + extends: card.class.Card, + label: 'embedded:embedded:' + title as IntlString, // todo: check if it's correct + kind: 0, + icon: card.icon.MasterTag + } + } + + return masterTag + } + + private async processMasterTagAttributes (yamlConfig: Record, masterTagId: Ref): Promise>>> { + const { properties } = yamlConfig + + const attributesByLabel = new Map>>() + for (const property of properties) { + const attr: UnifiedDoc> = { + _class: core.class.Attribute, + props: { + space: core.space.Model, + attributeOf: masterTagId, + name: generateId(core.class.Attribute), + label: 'embedded:embedded:' + property.label as IntlString, // todo: check if it's correct + isCustom: true, + type: { + _class: 'core:class:Type' + property.type + }, + defaultValue: property.defaultValue ?? null + } + } + attributesByLabel.set(property.label, attr) + } + return attributesByLabel + } + + private async processCardsRecursively ( // todo: process masterTag children recursively + builder: ImportWorkspaceBuilder, + tagPath: string, + currentPath: string, + masterTagId: Ref, + attributesByLabel: Map>> + ): Promise { + const cardFiles = fs.readdirSync(currentPath).filter((f) => f.endsWith('.md')) + + for (const cardFile of cardFiles) { + const cardPath = path.join(currentPath, cardFile) + const cardHeader = (await this.readYamlHeader(cardPath)) as Record + + if (cardHeader.class === undefined) { // means it's a card of class MasterTag + const card = await this.processCard(cardHeader, cardPath, masterTagId, attributesByLabel) + builder.addCard(tagPath, card) + } else { + // todo: check if it's a child master tag, or else throw error + throw new Error(`Unknown card class ${cardHeader.class} in ${cardFile}`) + } + + if (fs.existsSync(cardPath) && fs.statSync(cardPath).isDirectory()) { + await this.processCardsRecursively(builder, tagPath, cardPath, masterTagId, attributesByLabel) + } + } + } + + private async processCard (cardHeader: Record, cardPath: string, masterTagId: Ref, attributesByTitle: Map>>): Promise { + const { _class, title, ...customProperties } = cardHeader + + const props: Record = { + _id: generateId(), + space: core.space.Workspace, + title + } + + for (const [key, value] of Object.entries(customProperties)) { + const attributeName = attributesByTitle.get(key)?.props.name + if (attributeName === undefined) { + throw new Error(`Attribute not found: ${key}`) // todo: keep the error till builder validation + } + props[attributeName] = value + } + + return { + _class: masterTagId, + contentProvider: () => this.readMarkdownContent(cardPath), + props + } + } + private async processIssuesRecursively ( builder: ImportWorkspaceBuilder, projectIdentifier: string, diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 3442468f51c..4e5dccfb9fa 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. // +import card, { Card, MasterTag } from '@hcengineering/card' import documents, { ControlledDocument, DocumentState } from '@hcengineering/controlled-documents' -import { type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' +import { Attribute, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' import document from '@hcengineering/document' import tracker, { IssuePriority, type IssueStatus } from '@hcengineering/tracker' import { @@ -28,6 +29,7 @@ import { type ImportTeamspace, type ImportWorkspace } from './importer' +import { UnifiedDoc } from '../types' export interface ValidationError { path: string @@ -56,6 +58,11 @@ export class ImportWorkspaceBuilder { private readonly qmsDocsBySpace = new Map>() private readonly qmsDocsParents = new Map() + private readonly masterTags = new Map>() + private readonly masterTagAttributes = new Map>>() + private readonly cards = new Map>() + // private readonly cardParents = new Map() + private readonly projectTypes = new Map() private readonly issueStatusCache = new Map>() private readonly errors = new Map() @@ -221,10 +228,28 @@ export class ImportWorkspaceBuilder { return this } + addMasterTag (path: string, masterTag: UnifiedDoc): this { + this.validateAndAdd('masterTag', path, masterTag, (mt) => this.validateMasterTag(mt), this.masterTags, path) + return this + } + + addMasterTagAttributes (path: string, attributes: UnifiedDoc>[]): this { + for (const attribute of attributes) { + this.validateAndAdd('masterTagAttribute', path, attribute, (a) => this.validateMasterTagAttribute(a), this.masterTags, path) + } + return this + } + + addCard (path: string, card: UnifiedDoc): this { + this.validateAndAdd('card', path, card, (c) => this.validateCard(c), this.cards, path) + return this + } + validate (): ValidationResult { // Perform cross-entity validation this.validateSpacesReferences() this.validateDocumentsReferences() + this.validateCardsReferences() return { isValid: this.errors.size === 0, @@ -287,7 +312,13 @@ export class ImportWorkspaceBuilder { spaces: [ ...Array.from(this.projects.values()), ...Array.from(this.teamspaces.values()), - ...Array.from(this.qmsSpaces.values()) + ...Array.from(this.qmsSpaces.values()), + ...Array.from(this.cards.values()) + ], + unifiedDocs: [ + ...Array.from(this.masterTags.values()), + ...Array.from(this.masterTagAttributes.values()), + ...Array.from(this.cards.values()) ], attachments: [] } @@ -570,6 +601,13 @@ export class ImportWorkspaceBuilder { } } + private validateCardsReferences (): void { + // TODO: Validate cards references (master tag, attributes? parent-child, field references?) + // for (const [cardPath] of this.cards) { + // Check parent document exists + // } + } + private addError (path: string, error: string): void { this.errors.set(path, { path, error }) } @@ -675,6 +713,36 @@ export class ImportWorkspaceBuilder { return errors } + private validateMasterTag (masterTag: UnifiedDoc): string[] { + const errors: string[] = [] + + if (masterTag._class !== card.class.MasterTag) { + errors.push('Invalid class: ' + masterTag._class) + } + + // todo: validate master tag + return errors + } + + private validateMasterTagAttribute (attribute: UnifiedDoc>): string[] { + const errors: string[] = [] + + // todo: validate master tag attribute + return errors + } + + private validateCard (card: UnifiedDoc): string[] { // todo: pass validator separately (same level as converter) + const errors: string[] = [] + + // if (card._class !== card.class.Card) { + // validate class is a ref to master tag + // errors.push('Invalid class: ' + card._class) + // } + + // todo: validate card + return errors + } + private validateOrgSpace (space: ImportOrgSpace): string[] { const errors: string[] = [] diff --git a/packages/importer/src/importer/importer.ts b/packages/importer/src/importer/importer.ts index 2ab4e1b103e..81e35645f54 100644 --- a/packages/importer/src/importer/importer.ts +++ b/packages/importer/src/importer/importer.ts @@ -72,11 +72,12 @@ import view from '@hcengineering/view' import { type MarkdownPreprocessor, NoopMarkdownPreprocessor } from './preprocessor' import { type FileUploader } from './uploader' import { Logger } from './logger' - +import { UnifiedDoc } from '../types' export interface ImportWorkspace { projectTypes?: ImportProjectType[] spaces?: ImportSpace[] attachments?: ImportAttachment[] + unifiedDocs?: UnifiedDoc>[] } export interface ImportProjectType { diff --git a/packages/importer/src/importer/unified.ts b/packages/importer/src/importer/unified.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/importer/src/types.ts b/packages/importer/src/types.ts new file mode 100644 index 00000000000..68096acd0e5 --- /dev/null +++ b/packages/importer/src/types.ts @@ -0,0 +1,19 @@ +import { Class, Data, Doc, Ref, Space } from '@hcengineering/core' + +export interface UnifiedDoc { + _class: Ref> + props: Props + markdownFields?: string[] + collabField?: string + contentProvider?: () => Promise +} + +export type Props = Data & Partial & { space: Ref } + +export interface ImportContext { + vars: Record + defaults: Map>, Props> +} + +export type contentProvider = () => Promise +export type converter = (data: Record, contentProvider?: contentProvider) => UnifiedDoc[] From e0825269e6fd809dc96bae554cff18a9278dd98f Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Wed, 2 Apr 2025 17:35:18 +0700 Subject: [PATCH 02/50] wip: build fine Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/converters.ts | 42 ------------------ .../src/huly/converters/master-tag.ts | 43 ------------------- packages/importer/src/huly/huly.ts | 19 +++++--- packages/importer/src/importer/builder.ts | 3 +- packages/importer/src/types.ts | 1 - 5 files changed, 13 insertions(+), 95 deletions(-) delete mode 100644 packages/importer/src/huly/converters.ts delete mode 100644 packages/importer/src/huly/converters/master-tag.ts diff --git a/packages/importer/src/huly/converters.ts b/packages/importer/src/huly/converters.ts deleted file mode 100644 index 804e03447a1..00000000000 --- a/packages/importer/src/huly/converters.ts +++ /dev/null @@ -1,42 +0,0 @@ -import core, { Doc } from '@hcengineering/core' -import card from '@hcengineering/card' -import { Converter, UnifiedDoc } from '../types' - -export class MasterTagConverter implements Converter { - convert (data: Record): UnifiedDoc { - if (data.class !== card.class.MasterTag) { - throw new Error('Invalid master tag data') - } - - return { - _class: card.class.MasterTag, - data: { - space: core.space.Model, - label: data.title, - extends: card.class.Card, - kind: 0, - icon: 'card:icon:MasterTag', - ...data.properties - } - } - } -} - -export class CardConverter implements Converter { - convert (data: any): UnifiedDoc { - const { class: cardClass, title, content, ...customFields } = data - - return { - _class: cardClass, - data: { - space: core.space.Workspace, - title, - content, - rank: '#nextRank', - parentInfo: [], - blobs: {}, - ...customFields - } - } - } -} diff --git a/packages/importer/src/huly/converters/master-tag.ts b/packages/importer/src/huly/converters/master-tag.ts deleted file mode 100644 index d55386972c3..00000000000 --- a/packages/importer/src/huly/converters/master-tag.ts +++ /dev/null @@ -1,43 +0,0 @@ -import card, { MasterTag } from '@hcengineering/card' -import core, { Attribute, generateId } from '@hcengineering/core' -import { converter, UnifiedDoc } from '../../types' -import { IntlString } from '../../../../platform/types' - -export const masterTagConverter: converter = (data: Record) => { - const { class: _class, title, properties } = data - if (_class !== card.class.MasterTag) { - throw new Error('Invalid master tag data') - } - - const masterTagId = generateId(card.class.MasterTag) - const masterTag: UnifiedDoc = { - _class: card.class.MasterTag, - props: { - _id: masterTagId, - space: core.space.Model, - extends: card.class.Card, - label: 'embedded:embedded:' + title as IntlString, // todo: check if it's correct - kind: 0, - icon: card.icon.MasterTag - } - } - - const attributes: UnifiedDoc>[] = [] - for (const property of properties) { - attributes.push({ - _class: core.class.Attribute, - props: { - space: core.space.Model, - attributeOf: masterTagId, - name: generateId(core.class.Attribute), - label: 'embedded:embedded:' + property.label as IntlString, // todo: check if it's correct - isCustom: true, - type: { - _class: 'core:class:Type' + property.type - }, - defaultValue: property.defaultValue ?? null - } - }) - } - return [masterTag, ...attributes] -} diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 5b13a57ad48..8c1a3f095da 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -14,7 +14,7 @@ // /* eslint-disable @typescript-eslint/no-unused-vars */ import { type Attachment } from '@hcengineering/attachment' -import card, { MasterTag } from '@hcengineering/card' +import card, { Card, MasterTag } from '@hcengineering/card' import contact, { Employee, type Person, SocialIdentity } from '@hcengineering/contact' import documents, { ControlledDocument, @@ -62,7 +62,7 @@ import { import { type Logger } from '../importer/logger' import { BaseMarkdownPreprocessor } from '../importer/preprocessor' import { type FileUploader } from '../importer/uploader' -import { UnifiedDoc } from '../types' +import { Props, UnifiedDoc } from '../types' export interface HulyComment { author: string @@ -529,7 +529,7 @@ export class HulyFormatImporter { case card.class.MasterTag: { const masterTag = await this.processMasterTag(spaceConfig) - const { _id: masterTagId } = masterTag.props + const masterTagId = masterTag.props._id as Ref if (masterTagId === undefined) { throw new Error('Master tag ID is undefined') } @@ -539,7 +539,7 @@ export class HulyFormatImporter { builder.addMasterTagAttributes(spacePath, Array.from(attributesByLabel.values())) if (fs.existsSync(spacePath) && fs.statSync(spacePath).isDirectory()) { - await this.processCardsRecursively(builder, spacePath, spacePath, masterTagId as Ref, attributesByLabel) + await this.processCardsRecursively(builder, spacePath, spacePath, masterTagId, attributesByLabel) } break } @@ -557,7 +557,7 @@ export class HulyFormatImporter { return builder.build() } - private async processMasterTag (yamlData: Record): Promise>> { + private async processMasterTag (yamlData: Record): Promise> { const { class: _class, title } = yamlData if (_class !== card.class.MasterTag) { throw new Error('Invalid master tag data') @@ -629,7 +629,12 @@ export class HulyFormatImporter { } } - private async processCard (cardHeader: Record, cardPath: string, masterTagId: Ref, attributesByTitle: Map>>): Promise { + private async processCard ( + cardHeader: Record, + cardPath: string, + masterTagId: Ref, + attributesByTitle: Map>> + ): Promise> { const { _class, title, ...customProperties } = cardHeader const props: Record = { @@ -649,7 +654,7 @@ export class HulyFormatImporter { return { _class: masterTagId, contentProvider: () => this.readMarkdownContent(cardPath), - props + props: props as Props // todo: what is the correct props type? } } diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 4e5dccfb9fa..fda45a3c9a8 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -312,8 +312,7 @@ export class ImportWorkspaceBuilder { spaces: [ ...Array.from(this.projects.values()), ...Array.from(this.teamspaces.values()), - ...Array.from(this.qmsSpaces.values()), - ...Array.from(this.cards.values()) + ...Array.from(this.qmsSpaces.values()) ], unifiedDocs: [ ...Array.from(this.masterTags.values()), diff --git a/packages/importer/src/types.ts b/packages/importer/src/types.ts index 68096acd0e5..e7bdc4bb93f 100644 --- a/packages/importer/src/types.ts +++ b/packages/importer/src/types.ts @@ -16,4 +16,3 @@ export interface ImportContext { } export type contentProvider = () => Promise -export type converter = (data: Record, contentProvider?: contentProvider) => UnifiedDoc[] From 34a6e1956f8c45d3cebe55ac2b3ca551497a778f Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Thu, 3 Apr 2025 12:16:40 +0700 Subject: [PATCH 03/50] update example-files Signed-off-by: Anna Khismatullina --- .../docs/huly/example-workspace/Custom.yaml | 7 +++ .../Card Custom 1.md} | 15 ++--- .../huly/example-workspace/Documentation.yaml | 10 ---- .../Documentation/Getting Started.md | 19 ------ .../Documentation/User Guide.md | 16 ----- .../Documentation/User Guide/Installation.md | 19 ------ .../Documentation/files/architecture.png | Bin 40292 -> 0 bytes .../huly/example-workspace/Project Alpha.yaml | 12 ---- .../Project Alpha/1.Project Setup.md | 30 ---------- .../1.Project Setup/2.Configure CI.md | 13 ----- .../Project Alpha/4.Update Docs.md | 11 ---- .../Project Alpha/files/config.yaml | 18 ------ .../Project Alpha/files/screenshot.png | Bin 39710 -> 0 bytes .../files/screenshot/drawing1.json | 55 ------------------ .../huly/example-workspace/QMS Documents.yaml | 11 ---- .../[SOP-002] Document Review.md | 42 ------------- .../[WI-001] Document Template Usage.md | 37 ------------ 17 files changed, 11 insertions(+), 304 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/Custom.yaml rename dev/import-tool/docs/huly/example-workspace/{QMS Documents/[SOP-001] Document Control.md => Custom/Card Custom 1.md} (56%) delete mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/1.Project Setup.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/1.Project Setup/2.Configure CI.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/4.Update Docs.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/config.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot.png delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json delete mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md diff --git a/dev/import-tool/docs/huly/example-workspace/Custom.yaml b/dev/import-tool/docs/huly/example-workspace/Custom.yaml new file mode 100644 index 00000000000..b2bf6c6fad0 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Custom.yaml @@ -0,0 +1,7 @@ +class: card:class:MasterTag +title: Custom Type +properties: + - label: aaa # embedded:embedded:aaa + type: TypeString # human readable format + core:class^ + - label: bbb + type: TypeBoolean diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md b/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md similarity index 56% rename from dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md rename to dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md index 8daf8101ade..28dd4ecf2b9 100644 --- a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md +++ b/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md @@ -1,16 +1,9 @@ --- -class: documents:mixin:DocumentTemplate -title: 'Standard Operating Procedure Template' -docPrefix: SOP -category: DOC -author: John Appleseed -owner: John Appleseed -abstract: Template for Standard Operating Procedures -reviewers: - - John Appleseed -approvers: - - John Appleseed +title: Custom Card 1 +aaa: some text +bbb: true --- + # Standard Operating Procedure ## 1. Purpose diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation.yaml b/dev/import-tool/docs/huly/example-workspace/Documentation.yaml deleted file mode 100644 index 781c2b6f123..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Documentation.yaml +++ /dev/null @@ -1,10 +0,0 @@ -class: document:class:Teamspace -title: Documentation -emoji: 📖 -private: false -autoJoin: true -owners: - - john.doe@example.com -members: - - joe.shmoe@example.com -description: Technical documentation and guides diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md b/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md deleted file mode 100644 index 4b0f5dab76d..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -class: document:class:Document -title: Getting Started Guide ---- -# Getting Started - -Welcome to our project! This guide will help you get started with development. - -## Setup Steps - -1. Clone the repository -2. Install dependencies -3. Set up your environment - -## Project Communication -We use Huly for all project communication: -- Team discussions in Virtual Office -- Technical discussions in issue comments -- Documentation in Huly Documents \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md deleted file mode 100644 index 5cbe18d0340..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -class: document:class:Document -title: User Guide ---- -# User Guide - -Our platform architecture and key components. - -## System Overview - - - -## Development Workflow -- Code reviews via GitHub integration -- CI/CD status in Huly Activity Feed -- Team sync-ups in Huly Virtual Office diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md deleted file mode 100644 index 0c39873c056..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -class: document:class:Document -title: Installation Guide ---- -# Installation - -## System Requirements -- Node.js 18 or higher -- Docker Desktop -- Git - -## Setup Steps - -1. Clone the repository -2. Install dependencies -3. Configure your environment - -## Need Help? -Contact @Joe Shmoe for technical support \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png b/dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png deleted file mode 100644 index 131b4072aa97c69e9075cc05b4a91b95eea3a54e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40292 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 VNc#M!*-5xl(=2CMoE3X)`#vi%=pn*%YOW2pLJp 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%0At-fNf1Ku}>_1&Gj;m?&OmPh`mH_Kl%VOaUq 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 diff --git a/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json b/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json deleted file mode 100644 index 4848657f095..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "class": "drawing:class:Drawing", - "content": [ - { - "lineWidth": 4, - "erasing": false, - "penColor": "#0000ff", - "points": [ - { "x": 213, "y": 181.5 }, - { "x": 203, "y": 181.5 }, - { "x": 193, "y": 181.5 }, - { "x": 168, "y": 181.5 }, - { "x": 136, "y": 181.5 }, - { "x": 108, "y": 181.5 }, - { "x": 84, "y": 188.5 }, - { "x": 59, "y": 196.5 }, - { "x": 42, "y": 205.5 }, - { "x": 35, "y": 220.5 }, - { "x": 32, "y": 227.5 }, - { "x": 31, "y": 233.5 }, - { "x": 31, "y": 241.5 }, - { "x": 31, "y": 248.5 }, - { "x": 34, "y": 257.5 }, - { "x": 41, "y": 269.5 }, - { "x": 53, "y": 280.5 }, - { "x": 69, "y": 290.5 }, - { "x": 84, "y": 297.5 }, - { "x": 112, "y": 305.5 }, - { "x": 190, "y": 319.5 }, - { "x": 250, "y": 323.5 }, - { "x": 314, "y": 323.5 }, - { "x": 372, "y": 317.5 }, - { "x": 410, "y": 303.5 }, - { "x": 424, "y": 292.5 }, - { "x": 427, "y": 279.5 }, - { "x": 427, "y": 265.5 }, - { "x": 425, "y": 250.5 }, - { "x": 413, "y": 225.5 }, - { "x": 407, "y": 214.5 }, - { "x": 398, "y": 203.5 }, - { "x": 380, "y": 189.5 }, - { "x": 361, "y": 181.5 }, - { "x": 317, "y": 168.5 }, - { "x": 261, "y": 154.5 }, - { "x": 207, "y": 148.5 }, - { "x": 168, "y": 148.5 }, - { "x": 153, "y": 148.5 }, - { "x": 147, "y": 151.5 }, - { "x": 143, "y": 157.5 }, - { "x": 142, "y": 165.5 }, - { "x": 142, "y": 165.5 } - ] - } - ] -} diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml b/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml deleted file mode 100644 index 3074c615960..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml +++ /dev/null @@ -1,11 +0,0 @@ -class: documents:class:OrgSpace -title: QMS Documents -description: Quality Management System Documentation -private: false -owners: - - user1 -members: - - user1 -qualified: user1 -manager: user1 -qara: user1 diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md deleted file mode 100644 index 3741298f42c..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -class: documents:class:ControlledDocument -title: Document Review Procedure -template: '../[SOP-001] Document Control.md' -author: John Appleseed -owner: John Appleseed -abstract: Procedure for document review and approval process -reviewers: - - John Appleseed -approvers: - - John Appleseed -changeControl: - description: Initial document creation - reason: Need for standardized review process - impact: Improved document quality control ---- -# Document Review Procedure - -## 1. Purpose -This procedure defines the process for reviewing quality management system documents. - -## 2. Scope -Applies to all controlled documents within the QMS. - -## 3. Responsibilities -- Document Owner: Responsible for content -- Reviewers: Technical review -- QA Manager: Final approval - -## 4. Procedure -1. Author prepares document -2. Technical review -3. QA review -4. Final approval -5. Document release - -## 5. References -- Quality Manual -- Document Control Procedure - -## 6. Revision History -Rev 0.1 - Initial draft diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md deleted file mode 100644 index b8963023acd..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -class: documents:class:ControlledDocument -title: Document Template Usage Guide -template: '[SOP-001] Document Control.md' -author: John Appleseed -owner: John Appleseed -abstract: Work instruction for using document templates -reviewers: - - John Appleseed -approvers: - - John Appleseed ---- -# Document Template Usage Guide - -## 1. Purpose -Guide users in proper usage of QMS document templates. - -## 2. Scope -All personnel creating QMS documentation. - -## 3. Procedure -1. Select appropriate template -2. Fill in required sections -3. Submit for review - -## 4. Document Review Changes - -| Step | Current Text | Updated Text | Comments | -| --- | --- | --- | --- | -| Initial Review | Select a template from library | Select a template that matches your document type | Clarified selection criteria | -| Metadata | Fill in required fields | Complete all required metadata and content fields | Added metadata specification | -| Review Process | Submit for review | Submit document for review according to procedure | Added reference to procedure | -| Approval | Wait for approval | Submit for approval after receiving all reviews | Process detail added | - -## 5. References -- [Document Control SOP](./[SOP-001]%20Document%20Control.md) -- [Document Review Procedure](./[SOP-001]%20Document%20Control/[SOP-002]%20Document%20Review.md) From 13bd1cbc5f78f257512d7cc7d9279025d596f711 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Thu, 3 Apr 2025 12:56:11 +0700 Subject: [PATCH 04/50] Type and Cards imported (no child cards, nor references) Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/huly.ts | 4 ++-- packages/importer/src/importer/builder.ts | 3 ++- packages/importer/src/importer/importer.ts | 28 ++++++++++++++++++++-- packages/importer/src/types.ts | 1 - 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 8c1a3f095da..7e5a05d4f21 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -588,11 +588,11 @@ export class HulyFormatImporter { props: { space: core.space.Model, attributeOf: masterTagId, - name: generateId(core.class.Attribute), + name: generateId>(), label: 'embedded:embedded:' + property.label as IntlString, // todo: check if it's correct isCustom: true, type: { - _class: 'core:class:Type' + property.type + _class: 'core:class:' + property.type }, defaultValue: property.defaultValue ?? null } diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index fda45a3c9a8..63cbf9a9f9f 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -235,7 +235,8 @@ export class ImportWorkspaceBuilder { addMasterTagAttributes (path: string, attributes: UnifiedDoc>[]): this { for (const attribute of attributes) { - this.validateAndAdd('masterTagAttribute', path, attribute, (a) => this.validateMasterTagAttribute(a), this.masterTags, path) + const key = path + '/' + attribute.props.name + this.validateAndAdd('masterTagAttribute', key, attribute, (a) => this.validateMasterTagAttribute(a), this.masterTagAttributes, key) } return this } diff --git a/packages/importer/src/importer/importer.ts b/packages/importer/src/importer/importer.ts index 81e35645f54..53c41e72f81 100644 --- a/packages/importer/src/importer/importer.ts +++ b/packages/importer/src/importer/importer.ts @@ -48,7 +48,8 @@ import core, { type Timestamp, type TxOperations, type PersonId, - type AccountUuid + type AccountUuid, + AttachedDoc } from '@hcengineering/core' import document, { type Document, getFirstRank, type Teamspace } from '@hcengineering/document' import task, { @@ -72,7 +73,7 @@ import view from '@hcengineering/view' import { type MarkdownPreprocessor, NoopMarkdownPreprocessor } from './preprocessor' import { type FileUploader } from './uploader' import { Logger } from './logger' -import { UnifiedDoc } from '../types' +import { Props, UnifiedDoc } from '../types' export interface ImportWorkspace { projectTypes?: ImportProjectType[] spaces?: ImportSpace[] @@ -242,6 +243,7 @@ export class WorkspaceImporter { await this.importProjectTypes() await this.importSpaces() await this.importAttachments() + await this.importUnifiedDocs() } private async importProjectTypes (): Promise { @@ -1130,4 +1132,26 @@ export class WorkspaceImporter { return await this.client.createDoc(documents.class.ChangeControl, spaceId, changeControlData) } + + private async importUnifiedDocs (): Promise { + if (this.workspaceData.unifiedDocs === undefined) return + + for (const unifiedDoc of this.workspaceData.unifiedDocs) { + await this.importUnifiedDoc(unifiedDoc) + } + } + + private async importUnifiedDoc (unifiedDoc: UnifiedDoc>): Promise { + const { _class, props } = unifiedDoc + const _id = props._id ?? generateId>() + if (unifiedDoc.collabField !== undefined) { + if ((props as any)[unifiedDoc.collabField] !== undefined) { + const collabId = makeCollabId(_class, _id, unifiedDoc.collabField) + const collabContent = await unifiedDoc.contentProvider?.() ?? '' + const res = await this.createCollaborativeContent(_id, collabId, collabContent, props.space) + ;(props as any)[unifiedDoc.collabField] = res + } + } + await this.client.createDoc(_class, props.space, props as Data>, _id) + } } diff --git a/packages/importer/src/types.ts b/packages/importer/src/types.ts index e7bdc4bb93f..d7243eba747 100644 --- a/packages/importer/src/types.ts +++ b/packages/importer/src/types.ts @@ -3,7 +3,6 @@ import { Class, Data, Doc, Ref, Space } from '@hcengineering/core' export interface UnifiedDoc { _class: Ref> props: Props - markdownFields?: string[] collabField?: string contentProvider?: () => Promise } From 213a6f9d268929aa8d340151926fdb5ab04a8b9f Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Thu, 3 Apr 2025 14:35:46 +0700 Subject: [PATCH 05/50] Fix description Signed-off-by: Anna Khismatullina --- .vscode/launch.json | 2 +- .../docs/huly/example-workspace/Custom.yaml | 2 +- .../huly/example-workspace/Custom/Card Custom 1.md | 2 +- packages/importer/src/huly/huly.ts | 1 + packages/importer/src/importer/importer.ts | 10 ++++------ 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 164f4d12d1d..6586d5bfd89 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -839,7 +839,7 @@ "-pw", "1234", "-ws", - "ws1" + "ws2" ], "env": { "FRONT_URL": "http://localhost:8087" diff --git a/dev/import-tool/docs/huly/example-workspace/Custom.yaml b/dev/import-tool/docs/huly/example-workspace/Custom.yaml index b2bf6c6fad0..4f4fb3444ff 100644 --- a/dev/import-tool/docs/huly/example-workspace/Custom.yaml +++ b/dev/import-tool/docs/huly/example-workspace/Custom.yaml @@ -1,5 +1,5 @@ class: card:class:MasterTag -title: Custom Type +title: Custom Type 4 properties: - label: aaa # embedded:embedded:aaa type: TypeString # human readable format + core:class^ diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md b/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md index 28dd4ecf2b9..65fd090d2a0 100644 --- a/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md +++ b/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md @@ -1,5 +1,5 @@ --- -title: Custom Card 1 +title: Custom Card 4 aaa: some text bbb: true --- diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 7e5a05d4f21..c7153ccb564 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -653,6 +653,7 @@ export class HulyFormatImporter { return { _class: masterTagId, + collabField: 'content', contentProvider: () => this.readMarkdownContent(cardPath), props: props as Props // todo: what is the correct props type? } diff --git a/packages/importer/src/importer/importer.ts b/packages/importer/src/importer/importer.ts index 53c41e72f81..2cfd261a895 100644 --- a/packages/importer/src/importer/importer.ts +++ b/packages/importer/src/importer/importer.ts @@ -1145,12 +1145,10 @@ export class WorkspaceImporter { const { _class, props } = unifiedDoc const _id = props._id ?? generateId>() if (unifiedDoc.collabField !== undefined) { - if ((props as any)[unifiedDoc.collabField] !== undefined) { - const collabId = makeCollabId(_class, _id, unifiedDoc.collabField) - const collabContent = await unifiedDoc.contentProvider?.() ?? '' - const res = await this.createCollaborativeContent(_id, collabId, collabContent, props.space) - ;(props as any)[unifiedDoc.collabField] = res - } + const collabId = makeCollabId(_class, _id, unifiedDoc.collabField) + const collabContent = await unifiedDoc.contentProvider?.() ?? '' + const res = await this.createCollaborativeContent(_id, collabId, collabContent, props.space) + ;(props as any)[unifiedDoc.collabField] = res } await this.client.createDoc(_class, props.space, props as Data>, _id) } From a4712c7ec2fb6a518582828b99231ae8c84ce0c3 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Thu, 3 Apr 2025 17:12:19 +0700 Subject: [PATCH 06/50] Support parent-child for mastertags Signed-off-by: Anna Khismatullina --- .../docs/huly/example-workspace/Custom.yaml | 2 +- .../example-workspace/Custom/Card Custom 1.md | 2 +- packages/importer/src/huly/huly.ts | 60 ++++++++++++++++++- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/dev/import-tool/docs/huly/example-workspace/Custom.yaml b/dev/import-tool/docs/huly/example-workspace/Custom.yaml index 4f4fb3444ff..ab70a289306 100644 --- a/dev/import-tool/docs/huly/example-workspace/Custom.yaml +++ b/dev/import-tool/docs/huly/example-workspace/Custom.yaml @@ -1,5 +1,5 @@ class: card:class:MasterTag -title: Custom Type 4 +title: Master Tag 1 properties: - label: aaa # embedded:embedded:aaa type: TypeString # human readable format + core:class^ diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md b/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md index 65fd090d2a0..2244c53c664 100644 --- a/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md +++ b/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md @@ -1,5 +1,5 @@ --- -title: Custom Card 4 +title: Card of MT 1 aaa: some text bbb: true --- diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index c7153ccb564..02c0bcf6659 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -539,6 +539,9 @@ export class HulyFormatImporter { builder.addMasterTagAttributes(spacePath, Array.from(attributesByLabel.values())) if (fs.existsSync(spacePath) && fs.statSync(spacePath).isDirectory()) { + // Сначала обрабатываем дочерние мастер-теги + await this.processSubTagsRecursively(builder, spacePath, spacePath, masterTagId, attributesByLabel) + // Затем обрабатываем карточки await this.processCardsRecursively(builder, spacePath, spacePath, masterTagId, attributesByLabel) } break @@ -557,7 +560,60 @@ export class HulyFormatImporter { return builder.build() } - private async processMasterTag (yamlData: Record): Promise> { + private async processSubTagsRecursively ( + builder: ImportWorkspaceBuilder, + tagPath: string, + currentPath: string, + parentMasterTagId: Ref, + parentAttributesByLabel: Map>> + ): Promise { + // Ищем YAML файлы, которые могут быть дочерними мастер-тегами + const yamlFiles = fs.readdirSync(currentPath).filter((f) => f.endsWith('.yaml')) + + for (const yamlFile of yamlFiles) { + const yamlPath = path.join(currentPath, yamlFile) + const dirName = path.basename(yamlFile, '.yaml') + const dirPath = path.join(currentPath, dirName) + + try { + // Читаем конфигурацию YAML файла + const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record + + // Проверяем, является ли файл мастер-тегом + if (yamlConfig.class !== card.class.MasterTag) { + continue // Пропускаем, если это не мастер-тег + } + + // Создаем дочерний мастер-тег с передачей parentMasterTagId + const childMasterTag = await this.processMasterTag(yamlConfig, parentMasterTagId) + const childMasterTagId = childMasterTag.props._id as Ref + if (childMasterTagId === undefined) { + throw new Error('Child master tag ID is undefined') + } + + // Добавляем мастер-тег в билдер + builder.addMasterTag(yamlPath, childMasterTag) + + // Обрабатываем атрибуты дочернего мастер-тега + const childAttributesByLabel = await this.processMasterTagAttributes(yamlConfig, childMasterTagId) + builder.addMasterTagAttributes(yamlPath, Array.from(childAttributesByLabel.values())) + + // Проверяем наличие директории для обработки карточек и сабтегов + if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) { + // Рекурсивно обрабатываем содержимое директории дочернего мастер-тега + // Сначала обрабатываем сабтеги + await this.processSubTagsRecursively(builder, yamlPath, dirPath, childMasterTagId, childAttributesByLabel) + // Затем обрабатываем карточки + await this.processCardsRecursively(builder, yamlPath, dirPath, childMasterTagId, childAttributesByLabel) + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + this.logger.error(`Invalid master tag configuration in ${yamlFile}: ${message}`) + } + } + } + + private async processMasterTag (yamlData: Record, parentMasterTagId?: Ref): Promise> { const { class: _class, title } = yamlData if (_class !== card.class.MasterTag) { throw new Error('Invalid master tag data') @@ -568,7 +624,7 @@ export class HulyFormatImporter { props: { _id: generateId(), space: core.space.Model, - extends: card.class.Card, + extends: parentMasterTagId ?? card.class.Card, label: 'embedded:embedded:' + title as IntlString, // todo: check if it's correct kind: 0, icon: card.icon.MasterTag From b2c808b9e0632ce65a0466f2ceeeed285ea5ccc4 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Thu, 3 Apr 2025 17:21:53 +0700 Subject: [PATCH 07/50] grant child examples Signed-off-by: Anna Khismatullina --- .../example-workspace/Custom/Child Type.yaml | 7 +++++ .../Custom/Child Type/Card of Child 1.md | 27 +++++++++++++++++++ .../Custom/Child Type/Child Child Type.yaml | 7 +++++ .../Card of Child of Child.md | 27 +++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100644 dev/import-tool/docs/huly/example-workspace/Custom/Child Type.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Card of Child 1.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type/Card of Child of Child.md diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Child Type.yaml b/dev/import-tool/docs/huly/example-workspace/Custom/Child Type.yaml new file mode 100644 index 00000000000..8a72d43d8f8 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Custom/Child Type.yaml @@ -0,0 +1,7 @@ +class: card:class:MasterTag +title: Child Type 2 +properties: + - label: ccc # embedded:embedded:aaa + type: TypeString # human readable format + core:class^ + - label: ddd + type: TypeString diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Card of Child 1.md b/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Card of Child 1.md new file mode 100644 index 00000000000..be7a63d7e52 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Card of Child 1.md @@ -0,0 +1,27 @@ +--- +title: Card from Child 2 +# aaa: aaa +# bbb: true +ccc: ccc +ddd: ddd +--- + +# Standard Operating Procedure + +## 1. Purpose +[Describe the purpose of the procedure] + +## 2. Scope +[Define the scope and applicability] + +## 3. Responsibilities +[List key roles and responsibilities] + +## 4. Procedure +[Detail the step-by-step procedure] + +## 5. References +[List related documents] + +## 6. Revision History +[Document revision history] diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type.yaml b/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type.yaml new file mode 100644 index 00000000000..630a356763d --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type.yaml @@ -0,0 +1,7 @@ +class: card:class:MasterTag +title: Child Child Type 2 +properties: + - label: ccc # embedded:embedded:aaa + type: TypeString # human readable format + core:class^ + - label: ddd + type: TypeString diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type/Card of Child of Child.md b/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type/Card of Child of Child.md new file mode 100644 index 00000000000..be7a63d7e52 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type/Card of Child of Child.md @@ -0,0 +1,27 @@ +--- +title: Card from Child 2 +# aaa: aaa +# bbb: true +ccc: ccc +ddd: ddd +--- + +# Standard Operating Procedure + +## 1. Purpose +[Describe the purpose of the procedure] + +## 2. Scope +[Define the scope and applicability] + +## 3. Responsibilities +[List key roles and responsibilities] + +## 4. Procedure +[Detail the step-by-step procedure] + +## 5. References +[List related documents] + +## 6. Revision History +[Document revision history] From 7be55ef3539d35b8b3d0d4f3f95252aa9831b6f5 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Thu, 3 Apr 2025 19:44:42 +0700 Subject: [PATCH 08/50] working state after refactoring Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/huly.ts | 80 ++++++------ packages/importer/src/huly/parsing.ts | 17 +++ packages/importer/src/huly/unified.ts | 169 ++++++++++++++++++++++++++ 3 files changed, 225 insertions(+), 41 deletions(-) create mode 100644 packages/importer/src/huly/parsing.ts create mode 100644 packages/importer/src/huly/unified.ts diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 02c0bcf6659..ab8b9063c73 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -27,6 +27,7 @@ import core, { Attribute, type Class, type Doc, + isId, generateId, PersonId, type Ref, @@ -63,6 +64,8 @@ import { type Logger } from '../importer/logger' import { BaseMarkdownPreprocessor } from '../importer/preprocessor' import { type FileUploader } from '../importer/uploader' import { Props, UnifiedDoc } from '../types' +import { readMarkdownContent, readYamlHeader } from './parsing' +import { UnifiedDocProcessor } from './unified' export interface HulyComment { author: string @@ -350,6 +353,8 @@ export class HulyFormatImporter { private readonly personIdByEmail = new Map() + private readonly unifiedDocImporter: UnifiedDocProcessor + constructor ( private readonly client: TxOperations, private readonly fileUploader: FileUploader, @@ -357,6 +362,7 @@ export class HulyFormatImporter { private readonly importerSocialId?: PersonId, private readonly importerPerson?: Ref ) { + this.unifiedDocImporter = new UnifiedDocProcessor(this.client, this.logger) } private async initCaches (): Promise { @@ -482,6 +488,29 @@ export class HulyFormatImporter { } } + // Импортируем UnifiedDoc сущности + const unifiedResult = await this.unifiedDocImporter.importFromDirectory(folderPath) + + // Разбираем и добавляем в билдер по классу + for (const [path, docs] of unifiedResult.entries()) { + for (const doc of docs) { + switch (doc._class) { + case card.class.MasterTag: + builder.addMasterTag(path, doc as UnifiedDoc) + break + case core.class.Attribute: + builder.addMasterTagAttributes(path, [doc as UnifiedDoc>]) + break + default: + if (isId(doc._class)) { + builder.addCard(path, doc as UnifiedDoc) + } else { + this.logger.error(`Unknown doc class ${String(doc._class)} for path ${path}`) + } + } + } + } + // Process all yaml files first const yamlFiles = fs.readdirSync(folderPath).filter((f) => f.endsWith('.yaml') && f !== 'settings.yaml') @@ -528,22 +557,7 @@ export class HulyFormatImporter { } case card.class.MasterTag: { - const masterTag = await this.processMasterTag(spaceConfig) - const masterTagId = masterTag.props._id as Ref - if (masterTagId === undefined) { - throw new Error('Master tag ID is undefined') - } - builder.addMasterTag(spacePath, masterTag) - - const attributesByLabel = await this.processMasterTagAttributes(spaceConfig, masterTagId) - builder.addMasterTagAttributes(spacePath, Array.from(attributesByLabel.values())) - - if (fs.existsSync(spacePath) && fs.statSync(spacePath).isDirectory()) { - // Сначала обрабатываем дочерние мастер-теги - await this.processSubTagsRecursively(builder, spacePath, spacePath, masterTagId, attributesByLabel) - // Затем обрабатываем карточки - await this.processCardsRecursively(builder, spacePath, spacePath, masterTagId, attributesByLabel) - } + this.logger.log(`Skipping ${spaceName}: master tag already processed`) break } @@ -669,7 +683,7 @@ export class HulyFormatImporter { for (const cardFile of cardFiles) { const cardPath = path.join(currentPath, cardFile) - const cardHeader = (await this.readYamlHeader(cardPath)) as Record + const cardHeader = await readYamlHeader(cardPath) if (cardHeader.class === undefined) { // means it's a card of class MasterTag const card = await this.processCard(cardHeader, cardPath, masterTagId, attributesByLabel) @@ -710,7 +724,7 @@ export class HulyFormatImporter { return { _class: masterTagId, collabField: 'content', - contentProvider: () => this.readMarkdownContent(cardPath), + contentProvider: () => readMarkdownContent(cardPath), props: props as Props // todo: what is the correct props type? } } @@ -726,7 +740,7 @@ export class HulyFormatImporter { for (const issueFile of issueFiles) { const issuePath = path.join(currentPath, issueFile) - const issueHeader = (await this.readYamlHeader(issuePath)) as HulyIssueHeader + const issueHeader = (await readYamlHeader(issuePath)) as HulyIssueHeader if (issueHeader.class === undefined) { this.logger.error(`Skipping ${issueFile}: not an issue`) @@ -750,7 +764,7 @@ export class HulyFormatImporter { class: tracker.class.Issue, title: issueHeader.title, number: parseInt(issueNumber ?? 'NaN'), - descrProvider: async () => await this.readMarkdownContent(issuePath), + descrProvider: async () => await readMarkdownContent(issuePath), status: { name: issueHeader.status }, priority: issueHeader.priority, estimation: issueHeader.estimation, @@ -838,7 +852,7 @@ export class HulyFormatImporter { for (const docFile of docFiles) { const docPath = path.join(currentPath, docFile) - const docHeader = (await this.readYamlHeader(docPath)) as HulyDocumentHeader + const docHeader = (await readYamlHeader(docPath)) as HulyDocumentHeader if (docHeader.class === undefined) { this.logger.error(`Skipping ${docFile}: not a document`) @@ -859,7 +873,7 @@ export class HulyFormatImporter { id: docMeta.id as Ref, class: document.class.Document, title: docHeader.title, - descrProvider: async () => await this.readMarkdownContent(docPath), + descrProvider: async () => await readMarkdownContent(docPath), subdocs: [] // Will be added via builder } @@ -886,7 +900,7 @@ export class HulyFormatImporter { for (const docFile of docFiles) { const docPath = path.join(currentPath, docFile) - const docHeader = (await this.readYamlHeader(docPath)) as + const docHeader = (await readYamlHeader(docPath)) as | HulyControlledDocumentHeader | HulyDocumentTemplateHeader @@ -1087,7 +1101,7 @@ export class HulyFormatImporter { reviewers: header.reviewers?.map((email) => this.findEmployeeByName(email)) ?? [], approvers: header.approvers?.map((email) => this.findEmployeeByName(email)) ?? [], coAuthors: header.coAuthors?.map((email) => this.findEmployeeByName(email)) ?? [], - descrProvider: async () => await this.readMarkdownContent(docPath), + descrProvider: async () => await readMarkdownContent(docPath), ccReason: header.changeControl?.reason, ccImpact: header.changeControl?.impact, ccDescription: header.changeControl?.description, @@ -1125,7 +1139,7 @@ export class HulyFormatImporter { reviewers: header.reviewers?.map((email) => this.findEmployeeByName(email)) ?? [], approvers: header.approvers?.map((email) => this.findEmployeeByName(email)) ?? [], coAuthors: header.coAuthors?.map((email) => this.findEmployeeByName(email)) ?? [], - descrProvider: async () => await this.readMarkdownContent(docPath), + descrProvider: async () => await readMarkdownContent(docPath), ccReason: header.changeControl?.reason, ccImpact: header.changeControl?.impact, ccDescription: header.changeControl?.description, @@ -1133,22 +1147,6 @@ export class HulyFormatImporter { } } - private async readYamlHeader (filePath: string): Promise { - this.logger.log('Read YAML header from: ' + filePath) - const content = fs.readFileSync(filePath, 'utf8') - const match = content.match(/^---\n([\s\S]*?)\n---/) - if (match != null) { - return yaml.load(match[1]) - } - return {} - } - - private async readMarkdownContent (filePath: string): Promise { - const content = fs.readFileSync(filePath, 'utf8') - const match = content.match(/^---\n[\s\S]*?\n---\n(.*)$/s) - return match != null ? match[1] : content - } - private async cacheAccountsByEmails (): Promise { const employees = await this.client.findAll( contact.mixin.Employee, diff --git a/packages/importer/src/huly/parsing.ts b/packages/importer/src/huly/parsing.ts new file mode 100644 index 00000000000..0e94b2afa87 --- /dev/null +++ b/packages/importer/src/huly/parsing.ts @@ -0,0 +1,17 @@ +import * as fs from 'fs' +import * as yaml from 'js-yaml' + +export async function readYamlHeader (filePath: string): Promise { + const content = fs.readFileSync(filePath, 'utf8') + const match = content.match(/^---\n([\s\S]*?)\n---/) + if (match != null) { + return yaml.load(match[1]) + } + return {} +} + +export async function readMarkdownContent (filePath: string): Promise { + const content = fs.readFileSync(filePath, 'utf8') + const match = content.match(/^---\n[\s\S]*?\n---\n(.*)$/s) + return match != null ? match[1] : content +} diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts new file mode 100644 index 00000000000..23420100843 --- /dev/null +++ b/packages/importer/src/huly/unified.ts @@ -0,0 +1,169 @@ +// unified.ts +import { UnifiedDoc, Props } from '../types' +import card, { Card, MasterTag } from '@hcengineering/card' +import core, { + Attribute, + type Doc, + generateId, + type Ref, + type TxOperations +} from '@hcengineering/core' +import * as fs from 'fs' +import * as path from 'path' +import * as yaml from 'js-yaml' +import { Logger } from '../importer/logger' +import { IntlString } from '../../../platform/types' +import { readMarkdownContent, readYamlHeader } from './parsing' + +export type UnifiedDocProcessResult = Map>> + +export class UnifiedDocProcessor { + constructor ( + private readonly client: TxOperations, + private readonly logger: Logger + ) {} + + async importFromDirectory (directoryPath: string): Promise { + const unifiedDocs: UnifiedDocProcessResult = new Map() + await this.processDirectory(directoryPath, unifiedDocs) + return unifiedDocs + } + + private async processDirectory ( + currentPath: string, + result: UnifiedDocProcessResult, + parentMasterTagId?: Ref, + parentAttributesByLabel?: Map>> + ): Promise { + const entries = fs.readdirSync(currentPath, { withFileTypes: true }) + + // Сначала обрабатываем YAML файлы (потенциальные мастер-теги) + for (const entry of entries) { + if (!entry.isFile() || !entry.name.endsWith('.yaml')) continue // todo: filter entries by extension + + const yamlPath = path.join(currentPath, entry.name) + const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record + + if (yamlConfig?.class === card.class.MasterTag) { + const masterTag = await this.createMasterTag(yamlConfig, parentMasterTagId) + const masterTagId = masterTag.props._id as Ref + const attributesByLabel = await this.createMasterTagAttributes(yamlConfig, masterTagId) + + // Добавляем мастер-тег и его атрибуты + const docs = result.get(yamlPath) ?? [] + docs.push( + masterTag, + ...Array.from(attributesByLabel.values()) + ) + result.set(yamlPath, docs) + + // Рекурсивно обрабатываем содержимое директории мастер-тега + const tagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) + if (fs.existsSync(tagDir) && fs.statSync(tagDir).isDirectory()) { + await this.processDirectory(tagDir, result, masterTagId, attributesByLabel) + } + } + } + + if (parentMasterTagId === undefined || parentAttributesByLabel === undefined) { + // means we are in the root directory + return + } + + // Затем обрабатываем markdown файлы (карточки) + for (const entry of entries) { + if (!entry.isFile() || !entry.name.endsWith('.md')) continue + + const cardPath = path.join(currentPath, entry.name) + const cardHeader = await readYamlHeader(cardPath) + const unifiedDoc = await this.createCard(cardHeader, cardPath, parentMasterTagId, parentAttributesByLabel) + + if (unifiedDoc != null) { + const docs = result.get(cardPath) ?? [] + docs.push(unifiedDoc) + result.set(cardPath, docs) + } + } + } + + private async createMasterTag ( + data: Record, + parentMasterTagId?: Ref + ): Promise> { + const { class: _class, title } = data + if (_class !== card.class.MasterTag) { + throw new Error('Invalid master tag data') + } + + return { + _class: card.class.MasterTag, + props: { + _id: generateId(), + space: core.space.Model, + extends: parentMasterTagId ?? card.class.Card, + label: 'embedded:embedded:' + title as IntlString, // todo: check if it's correct + kind: 0, + icon: card.icon.MasterTag + } + } + } + + private async createMasterTagAttributes ( + data: Record, + masterTagId: Ref + ): Promise>>> { + if (data.properties === undefined) { + return new Map() + } + + const attributesByLabel = new Map>>() + for (const property of data.properties) { + const attr: UnifiedDoc> = { + _class: core.class.Attribute, + props: { + space: core.space.Model, + attributeOf: masterTagId, + name: generateId>(), + label: 'embedded:embedded:' + property.label as IntlString, // todo: check if it's correct + isCustom: true, + type: { + _class: 'core:class:' + property.type + }, + defaultValue: property.defaultValue ?? null + } + } + attributesByLabel.set(property.label, attr) + } + return attributesByLabel + } + + private async createCard ( + cardHeader: Record, + cardPath: string, + masterTagId: Ref, + attributesByLabel: Map>> + ): Promise> { + const { _class, title, ...customProperties } = cardHeader + + const props: Record = { + _id: generateId(), + space: core.space.Workspace, + title + } + + for (const [key, value] of Object.entries(customProperties)) { + const attributeName = attributesByLabel.get(key)?.props.name + if (attributeName === undefined) { + throw new Error(`Attribute not found: ${key}`) // todo: keep the error till builder validation + } + props[attributeName] = value + } + + return { + _class: masterTagId, + collabField: 'content', + contentProvider: () => readMarkdownContent(cardPath), + props: props as Props // todo: what is the correct props type? + } + } +} From 287f2f24675a391156edadd058a28ff50fce0b1e Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Thu, 3 Apr 2025 22:28:44 +0700 Subject: [PATCH 09/50] minor cleanup Signed-off-by: Anna Khismatullina --- dev/import-tool/docs/huly/example-workspace/Custom.yaml | 2 +- packages/importer/src/huly/huly.ts | 3 +-- packages/importer/src/huly/unified.ts | 7 ------- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/dev/import-tool/docs/huly/example-workspace/Custom.yaml b/dev/import-tool/docs/huly/example-workspace/Custom.yaml index ab70a289306..fc258d5ae2b 100644 --- a/dev/import-tool/docs/huly/example-workspace/Custom.yaml +++ b/dev/import-tool/docs/huly/example-workspace/Custom.yaml @@ -1,5 +1,5 @@ class: card:class:MasterTag -title: Master Tag 1 +title: Master Tag 3 properties: - label: aaa # embedded:embedded:aaa type: TypeString # human readable format + core:class^ diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index ab8b9063c73..359a725748a 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -353,7 +353,7 @@ export class HulyFormatImporter { private readonly personIdByEmail = new Map() - private readonly unifiedDocImporter: UnifiedDocProcessor + private readonly unifiedDocImporter = new UnifiedDocProcessor() constructor ( private readonly client: TxOperations, @@ -362,7 +362,6 @@ export class HulyFormatImporter { private readonly importerSocialId?: PersonId, private readonly importerPerson?: Ref ) { - this.unifiedDocImporter = new UnifiedDocProcessor(this.client, this.logger) } private async initCaches (): Promise { diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 23420100843..caa8ff399be 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -6,23 +6,16 @@ import core, { type Doc, generateId, type Ref, - type TxOperations } from '@hcengineering/core' import * as fs from 'fs' import * as path from 'path' import * as yaml from 'js-yaml' -import { Logger } from '../importer/logger' import { IntlString } from '../../../platform/types' import { readMarkdownContent, readYamlHeader } from './parsing' export type UnifiedDocProcessResult = Map>> export class UnifiedDocProcessor { - constructor ( - private readonly client: TxOperations, - private readonly logger: Logger - ) {} - async importFromDirectory (directoryPath: string): Promise { const unifiedDocs: UnifiedDocProcessResult = new Map() await this.processDirectory(directoryPath, unifiedDocs) From 65d88cb8d113f9b928b61ad3f3c0f0945f224c19 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Thu, 3 Apr 2025 22:46:06 +0700 Subject: [PATCH 10/50] draft PR Signed-off-by: Anna Khismatullina --- .../huly/example-workspace/Documentation.yaml | 10 ++ .../Documentation/Getting Started.md | 19 +++ .../Documentation/User Guide.md | 16 ++ .../Documentation/User Guide/Installation.md | 19 +++ .../Documentation/files/architecture.png | Bin 0 -> 40292 bytes .../huly/example-workspace/Project Alpha.yaml | 12 ++ .../Project Alpha/1.Project Setup.md | 30 ++++ .../1.Project Setup/2.Configure CI.md | 13 ++ .../Project Alpha/4.Update Docs.md | 11 ++ .../Project Alpha/files/config.yaml | 18 ++ .../Project Alpha/files/screenshot.png | Bin 0 -> 39710 bytes .../files/screenshot/drawing1.json | 55 ++++++ .../huly/example-workspace/QMS Documents.yaml | 11 ++ .../[SOP-001] Document Control.md | 32 ++++ .../[SOP-002] Document Review.md | 42 +++++ .../[WI-001] Document Template Usage.md | 37 ++++ packages/importer/src/huly/cards.ts.bak | 75 -------- packages/importer/src/huly/huly.ts | 160 +----------------- 18 files changed, 327 insertions(+), 233 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/1.Project Setup.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/1.Project Setup/2.Configure CI.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/4.Update Docs.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/config.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot.png create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json create mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md create mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md create mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md delete mode 100644 packages/importer/src/huly/cards.ts.bak diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation.yaml b/dev/import-tool/docs/huly/example-workspace/Documentation.yaml new file mode 100644 index 00000000000..781c2b6f123 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Documentation.yaml @@ -0,0 +1,10 @@ +class: document:class:Teamspace +title: Documentation +emoji: 📖 +private: false +autoJoin: true +owners: + - john.doe@example.com +members: + - joe.shmoe@example.com +description: Technical documentation and guides diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md b/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md new file mode 100644 index 00000000000..4b0f5dab76d --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md @@ -0,0 +1,19 @@ +--- +class: document:class:Document +title: Getting Started Guide +--- +# Getting Started + +Welcome to our project! This guide will help you get started with development. + +## Setup Steps + +1. Clone the repository +2. Install dependencies +3. Set up your environment + +## Project Communication +We use Huly for all project communication: +- Team discussions in Virtual Office +- Technical discussions in issue comments +- Documentation in Huly Documents \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md new file mode 100644 index 00000000000..5cbe18d0340 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md @@ -0,0 +1,16 @@ +--- +class: document:class:Document +title: User Guide +--- +# User Guide + +Our platform architecture and key components. + +## System Overview + + + +## Development Workflow +- Code reviews via GitHub integration +- CI/CD status in Huly Activity Feed +- Team sync-ups in Huly Virtual Office diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md new file mode 100644 index 00000000000..0c39873c056 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md @@ -0,0 +1,19 @@ +--- +class: document:class:Document +title: Installation Guide +--- +# Installation + +## System Requirements +- Node.js 18 or higher +- Docker Desktop +- Git + +## Setup Steps + +1. Clone the repository +2. Install dependencies +3. Configure your environment + +## Need Help? +Contact @Joe Shmoe for technical support \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png b/dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..131b4072aa97c69e9075cc05b4a91b95eea3a54e GIT binary patch literal 40292 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 VNc#M!*-5xl(=2CMoE3X)`#vi%=pn*%YOW2pLJp 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%0At-fNf1Ku}>_1&Gj;m?&OmPh`mH_Kl%VOaUq z&XJj(PQ$!jeY!vP{C35TV&g?yv|5(+Te->h#}(O+btcE&x*yfxm>+ygbyq^>wV^9+ z&&e=4#v;ssDHT&x-0`?K=}q$x6V1d^k#nbN_t)d3C@V>P4bI4>N83@h`a|c(g*yAe z8pO?0l~(s>=Y0WfFr=3Z9h}*NeO+@M>(ghnHM|hK?&^khOzA6vQdeJ#1^a&Vi)$@a zcdoY6>-OkvQ}59uV;bHs9Y1tPTKI6E@|*{rlVY_~&!69>J8|JL=kxn}y9b3*Wq?R&T!AIA1xbup4HLm5ux_3uE{l`|E{;%IxAN6VeN_W6du!-!>TPn|T8zjG^dRJfT z3S0fy+b)LNojQGtko_2ODtL~vp79sQpI$7}i*?)BF~}v!dH1JoJ?j44JYmw7E(SR{ zt^EcLEZ(6Rwr0GYy+Ks=fCGO9p?J0Tf`^CE_6K zT(adj5VP=jR`llO!;g-fu}Qu>Axw4G+gz{IuxmfcAFeGe&eJoj-rHAddeda9!ps@3 z-VCfa>(WWPebvKLp0?9$-Yj#8Ja)ju@%@`t#}5k+JxXyf`mL!Xdd@X9nP=;RX0-W9 z=8W^Xo@+X#z)G`8BY*bo`1+)gWBqz|-!*i?==HW*O&f>aI@n-pwb40dhHi(v;%M`g z7f;G;*3O>hzv4}3TweKs7u{;MrIal(?ACLOopNiCr1k#d%wnr)_Lf(DJa_xuiLmxp zbkmf%d%O7k=IORt8Kv)+hwRkqSFDq@*6Hs1ij|4`^Ig#my_gKbw&L-qumEXi1CE4# z{rWvc1}2VnF$XYNX@zX0f(t_k&JJ-5!Rq@Q-U|sIc1wF#RS_Kt1W->Vlzb{Hw?VHa zlF4<5DJ|TJ%(bNXyfTT%7TY7T!Z~n+kb5k0r)5QTqRU8RK2+DjVj}FnI4yL|_yH0K z5nWO!Xv+$r#}W{;!1~V_6pg%Np`%nyqocGR|gl`!`|5VjIEZ*h6$rFL0&f;^`JYy(Z=njq}07hlTV$ zTw6OBcarDEV5b2Q*_HR+vT(eiqtRlO1HTpk)XkSKJx-k(V(3scRmf>{r$Q%0Pcv(- zM2{0LMsgi^?8G5F5`>}+NR?P1OT$Ho)mYzO4p|K2GMbv1aTZYH)lxCsB8nDRX$?vb zBRA#&EWxsi9A=uT7tOZjk(ub&U$XPKNPs zixW;vd*VCJdJz5B_9Y%YO&{vGR%%$7RC&yaRB`a=Uz?WKDMtO4%Fsc-*-g;!yPvc@ zt#a_=Xt!}IP2EC_P8m%Pvvc>cbRKX1@R83#I~D)rA9~N9Fg-uxnxkT|o~f;ot%7gQ z%MaaquP*XtwlwBNTTCgwv!1L$DM6{5z!8WOZM<1{eFqL)fcXiyaRyy;&GoXDOpC~R z7LIVA@eow#-M|A020}dzW{}bQ_;De)2v8_LL%?FK){i=3WGOr%B5z-ku(xf@loy>h zect62=l&RHr?LAjO*0eLnY2@!2&+h+vU7UJY9prvXy$BkBan;N!|RlkmHFk!pdS8& z8-@E40U;o7U(!doN5Gxg+1ZXCAIOOEM?S*GG}X+HOv_kP^cFWx5Qm{@Kn5_uBj&ql z0m2}BQ=2O_$ZfgEQzFcFsCMG?86Z~P6S4|V^LZjTKuR!?yw+6YK1pJwd0GtpKs1WU z1$w>Yw6yWwBW~et5ez^!ux$01x+GKYX6VGgoIz>HcN=E3d>`~Gg$aXE7qHv@c$sAUo;@T{RZu#vb!X4~0K zgDwzux8S|ahhQ@$;wrOa3(gzkq412bphd1Lu90<7IiOm<(Ak0XPApn~#_YtvcHerV4rAw$`u3yzuGKuc(PPt(g%8>y?N?5Cy0F-)chJ2Tp8eM@ zmbue)-ZHhyi6v1JU4QQyz-D)7d%2tE#bY;m_4m1)xoL`WNXyd4cRreb4jYlE`0nXA zE%)2gubDm3dYkAsX!dA}%jL8EI_k|ctv@%P%(`vmZB#xrFYm9weG$EW%liCyTbJb# zuG1~TZg%AFT0K3>d8^rbBdNO@e=Yf>J9Ix4Kv2xw58o#S?`o%S?qRP+ErKej*`jib@HSxCmU#JXy6jBX9+w9rC~m>$47?Q z`NG;ZN+lT#r%-llnP7@Q@Qf^m7!tvz5=vqJ1CI*twK$38Cb|`2;H5a*z5nyTLX;iM zpnRRMK{Eg}hjcNh`B9jcgkFE#sfSlUz#=Rh^c+R%0ThIH^0?%+Np|AY1gK zqWtLOj7dW)?Hmkm?uxf+&u~2S=+HB}L#?s}ub1!ou;1?P{edg8m$d84Y^>AmcwMzj zdi;*eKkxWVOG%#CG2GaE{e16OYlDg)=YigP z3LGUxREAkDOW0O;yNfx2{cceu?EnoH>9LWaW^L z&_Z)AuHU0VVwrPyrfr(?%k2+Mr-RbWBXaCH5O0o!Md(_mfxS0Kb?*G+gzNMC6$r#R zO%l-@HrD{wZtiW{6CNH;*(`bY&ag4B9viVAlkp4%F2foT_371GgE6{(6-8TNFGDU} zf-m|$>snEQ8Nmj#VVQn@Q^s4fFryQ@Og0)`!?Wb)#fMnVrFZG*)m2*E3#Tt!HJ%wI z+qCi>80-&cMsAm*H0OwEcc)~ey8I7qIW)0gNuLzaC}yDsaJwr%=?h!Aci`?MbS55?}pA8RiQ_Rabeav8v`LZTe1;1YyKqR~@U`F$} z+Po_~wHOU|wJc+fo6+{3^s`;&p9@QCjN;{XowBx3^Uoc_)aBEprND6*7hj~{d}Jy! zET?jjwRNpomeHzJbMd9VXXaSs?E$s6y}8v*!Y*_1;>DZ{?b50r^nNaAzvh9Lq_DKK zRBiEU!vNcI17XX-;`+L(%(=JT)t5f+@a<*EvzGiX2JJZvNvr}?73VqOnvTa$DAGAV z$WppB>o6huGaG>6Ie|t}WFjutYAP`cPWqBRQ|r3k)vd-xHw&T{(ZOh zq+zN_vo9uwY}KfaOF!V$F>$nYv`^X)85z~em~geg<)7PQXQcz-7brMk=DQ$5+DMq7 zB^!o27lOD${iWHIL1V^_wVBYm9gZCnY|NJ;cVX-1RG0Sl&vA5sk`o!ec~lF4?|v4# zupUoP@ za{vDQ7E(2~hIWNr=||=w&anI8W4ZUdH~K}B3x~@3phO9c0r^a1iURUvbhFlqeU(pZh3yx5GOfp?m{|~F`e zt)ej|=e9w4#r=KjDwM~z3=U}gvh*j%u zE|QY)g|bk?k@;{^HakS?!%PlyTY2cL&FN|0YuiufyUD2;HVyNexb%Lxo%{YL?I)G4 ze&-_vG){RfKpL|gy> literal 0 HcmV?d00001 diff --git a/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json b/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json new file mode 100644 index 00000000000..4848657f095 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json @@ -0,0 +1,55 @@ +{ + "class": "drawing:class:Drawing", + "content": [ + { + "lineWidth": 4, + "erasing": false, + "penColor": "#0000ff", + "points": [ + { "x": 213, "y": 181.5 }, + { "x": 203, "y": 181.5 }, + { "x": 193, "y": 181.5 }, + { "x": 168, "y": 181.5 }, + { "x": 136, "y": 181.5 }, + { "x": 108, "y": 181.5 }, + { "x": 84, "y": 188.5 }, + { "x": 59, "y": 196.5 }, + { "x": 42, "y": 205.5 }, + { "x": 35, "y": 220.5 }, + { "x": 32, "y": 227.5 }, + { "x": 31, "y": 233.5 }, + { "x": 31, "y": 241.5 }, + { "x": 31, "y": 248.5 }, + { "x": 34, "y": 257.5 }, + { "x": 41, "y": 269.5 }, + { "x": 53, "y": 280.5 }, + { "x": 69, "y": 290.5 }, + { "x": 84, "y": 297.5 }, + { "x": 112, "y": 305.5 }, + { "x": 190, "y": 319.5 }, + { "x": 250, "y": 323.5 }, + { "x": 314, "y": 323.5 }, + { "x": 372, "y": 317.5 }, + { "x": 410, "y": 303.5 }, + { "x": 424, "y": 292.5 }, + { "x": 427, "y": 279.5 }, + { "x": 427, "y": 265.5 }, + { "x": 425, "y": 250.5 }, + { "x": 413, "y": 225.5 }, + { "x": 407, "y": 214.5 }, + { "x": 398, "y": 203.5 }, + { "x": 380, "y": 189.5 }, + { "x": 361, "y": 181.5 }, + { "x": 317, "y": 168.5 }, + { "x": 261, "y": 154.5 }, + { "x": 207, "y": 148.5 }, + { "x": 168, "y": 148.5 }, + { "x": 153, "y": 148.5 }, + { "x": 147, "y": 151.5 }, + { "x": 143, "y": 157.5 }, + { "x": 142, "y": 165.5 }, + { "x": 142, "y": 165.5 } + ] + } + ] +} diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml b/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml new file mode 100644 index 00000000000..3074c615960 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml @@ -0,0 +1,11 @@ +class: documents:class:OrgSpace +title: QMS Documents +description: Quality Management System Documentation +private: false +owners: + - user1 +members: + - user1 +qualified: user1 +manager: user1 +qara: user1 diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md new file mode 100644 index 00000000000..8daf8101ade --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md @@ -0,0 +1,32 @@ +--- +class: documents:mixin:DocumentTemplate +title: 'Standard Operating Procedure Template' +docPrefix: SOP +category: DOC +author: John Appleseed +owner: John Appleseed +abstract: Template for Standard Operating Procedures +reviewers: + - John Appleseed +approvers: + - John Appleseed +--- +# Standard Operating Procedure + +## 1. Purpose +[Describe the purpose of the procedure] + +## 2. Scope +[Define the scope and applicability] + +## 3. Responsibilities +[List key roles and responsibilities] + +## 4. Procedure +[Detail the step-by-step procedure] + +## 5. References +[List related documents] + +## 6. Revision History +[Document revision history] diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md new file mode 100644 index 00000000000..3741298f42c --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md @@ -0,0 +1,42 @@ +--- +class: documents:class:ControlledDocument +title: Document Review Procedure +template: '../[SOP-001] Document Control.md' +author: John Appleseed +owner: John Appleseed +abstract: Procedure for document review and approval process +reviewers: + - John Appleseed +approvers: + - John Appleseed +changeControl: + description: Initial document creation + reason: Need for standardized review process + impact: Improved document quality control +--- +# Document Review Procedure + +## 1. Purpose +This procedure defines the process for reviewing quality management system documents. + +## 2. Scope +Applies to all controlled documents within the QMS. + +## 3. Responsibilities +- Document Owner: Responsible for content +- Reviewers: Technical review +- QA Manager: Final approval + +## 4. Procedure +1. Author prepares document +2. Technical review +3. QA review +4. Final approval +5. Document release + +## 5. References +- Quality Manual +- Document Control Procedure + +## 6. Revision History +Rev 0.1 - Initial draft diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md new file mode 100644 index 00000000000..b8963023acd --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md @@ -0,0 +1,37 @@ +--- +class: documents:class:ControlledDocument +title: Document Template Usage Guide +template: '[SOP-001] Document Control.md' +author: John Appleseed +owner: John Appleseed +abstract: Work instruction for using document templates +reviewers: + - John Appleseed +approvers: + - John Appleseed +--- +# Document Template Usage Guide + +## 1. Purpose +Guide users in proper usage of QMS document templates. + +## 2. Scope +All personnel creating QMS documentation. + +## 3. Procedure +1. Select appropriate template +2. Fill in required sections +3. Submit for review + +## 4. Document Review Changes + +| Step | Current Text | Updated Text | Comments | +| --- | --- | --- | --- | +| Initial Review | Select a template from library | Select a template that matches your document type | Clarified selection criteria | +| Metadata | Fill in required fields | Complete all required metadata and content fields | Added metadata specification | +| Review Process | Submit for review | Submit document for review according to procedure | Added reference to procedure | +| Approval | Wait for approval | Submit for approval after receiving all reviews | Process detail added | + +## 5. References +- [Document Control SOP](./[SOP-001]%20Document%20Control.md) +- [Document Review Procedure](./[SOP-001]%20Document%20Control/[SOP-002]%20Document%20Review.md) diff --git a/packages/importer/src/huly/cards.ts.bak b/packages/importer/src/huly/cards.ts.bak deleted file mode 100644 index 7b101524f34..00000000000 --- a/packages/importer/src/huly/cards.ts.bak +++ /dev/null @@ -1,75 +0,0 @@ -import { TxOperations } from '@hcengineering/core' -import { readFileSync } from 'fs' -import { parse as parseFrontmatter } from 'gray-matter' -import { join } from 'path' -import { parse as parseYaml } from 'yaml' -import { type Logger } from '../importer/logger' -import { ImportContext } from '../types' -import { CardConverter, MasterTagConverter } from './converters' - -export class CardsImporter { - private readonly converters = new Map() - private readonly context: ImportContext = { - vars: {}, - defaults: new Map() - } - - constructor ( - private readonly client: TxOperations, - private readonly logger: Logger - ) { - // Register converters - this.converters.set('card:class:MasterTag', new MasterTagConverter()) - this.converters.set('card:class:Card', new CardConverter()) - } - - async importFromDirectory (dir: string): Promise { - this.logger.log('Starting cards import from directory: ' + dir) - - // Read all yaml files for MasterTags - const files = await this.readDirectory(dir) - - for (const file of files) { - if (file.endsWith('.yaml')) { - this.logger.log('Processing YAML file: ' + file) - const content = readFileSync(join(dir, file), 'utf-8') - const data = parseYaml(content) - const converter = this.converters.get(data.class) - if (converter !== undefined) { - const unifiedDoc = converter.convert(data, this.context) - this.logger.log('Converted to UnifiedDoc: ' + JSON.stringify(unifiedDoc, null, 2)) - } else { - this.logger.log('No converter found for class: ' + data.class) - } - } else if (file.endsWith('.md')) { - this.logger.log('Processing Markdown file: ' + file) - const content = readFileSync(join(dir, file), 'utf-8') - const { data } = parseFrontmatter(content) - const converter = this.converters.get(data.class) - if (converter !== undefined) { - const unifiedDoc = converter.convert(data, this.context) - this.logger.log('Converted to UnifiedDoc: ' + JSON.stringify(unifiedDoc, null, 2)) - } else { - this.logger.log('No converter found for class: ' + data.class) - } - } - } - } - - private async readDirectory (dir: string): Promise { - const entries = fs.readdirSync(dir, { withFileTypes: true }) - const files: string[] = [] - - for (const entry of entries) { - const fullPath = join(dir, entry.name) - if (entry.isDirectory() === true) { - const subFiles = await this.readDirectory(fullPath) - files.push(...subFiles) - } else if (entry.isFile() === true && (entry.name.endsWith('.yaml') || entry.name.endsWith('.md'))) { - files.push(fullPath) - } - } - - return files - } -} diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 359a725748a..c4808ee3dee 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -27,8 +27,8 @@ import core, { Attribute, type Class, type Doc, - isId, generateId, + isId, PersonId, type Ref, SocialIdType, @@ -43,7 +43,6 @@ import sizeOf from 'image-size' import * as yaml from 'js-yaml' import { contentType } from 'mime-types' import * as path from 'path' -import { IntlString } from '../../../platform/types' import { ImportWorkspaceBuilder } from '../importer/builder' import { type ImportAttachment, @@ -63,7 +62,7 @@ import { import { type Logger } from '../importer/logger' import { BaseMarkdownPreprocessor } from '../importer/preprocessor' import { type FileUploader } from '../importer/uploader' -import { Props, UnifiedDoc } from '../types' +import { UnifiedDoc } from '../types' import { readMarkdownContent, readYamlHeader } from './parsing' import { UnifiedDocProcessor } from './unified' @@ -573,161 +572,6 @@ export class HulyFormatImporter { return builder.build() } - private async processSubTagsRecursively ( - builder: ImportWorkspaceBuilder, - tagPath: string, - currentPath: string, - parentMasterTagId: Ref, - parentAttributesByLabel: Map>> - ): Promise { - // Ищем YAML файлы, которые могут быть дочерними мастер-тегами - const yamlFiles = fs.readdirSync(currentPath).filter((f) => f.endsWith('.yaml')) - - for (const yamlFile of yamlFiles) { - const yamlPath = path.join(currentPath, yamlFile) - const dirName = path.basename(yamlFile, '.yaml') - const dirPath = path.join(currentPath, dirName) - - try { - // Читаем конфигурацию YAML файла - const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record - - // Проверяем, является ли файл мастер-тегом - if (yamlConfig.class !== card.class.MasterTag) { - continue // Пропускаем, если это не мастер-тег - } - - // Создаем дочерний мастер-тег с передачей parentMasterTagId - const childMasterTag = await this.processMasterTag(yamlConfig, parentMasterTagId) - const childMasterTagId = childMasterTag.props._id as Ref - if (childMasterTagId === undefined) { - throw new Error('Child master tag ID is undefined') - } - - // Добавляем мастер-тег в билдер - builder.addMasterTag(yamlPath, childMasterTag) - - // Обрабатываем атрибуты дочернего мастер-тега - const childAttributesByLabel = await this.processMasterTagAttributes(yamlConfig, childMasterTagId) - builder.addMasterTagAttributes(yamlPath, Array.from(childAttributesByLabel.values())) - - // Проверяем наличие директории для обработки карточек и сабтегов - if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) { - // Рекурсивно обрабатываем содержимое директории дочернего мастер-тега - // Сначала обрабатываем сабтеги - await this.processSubTagsRecursively(builder, yamlPath, dirPath, childMasterTagId, childAttributesByLabel) - // Затем обрабатываем карточки - await this.processCardsRecursively(builder, yamlPath, dirPath, childMasterTagId, childAttributesByLabel) - } - } catch (error) { - const message = error instanceof Error ? error.message : String(error) - this.logger.error(`Invalid master tag configuration in ${yamlFile}: ${message}`) - } - } - } - - private async processMasterTag (yamlData: Record, parentMasterTagId?: Ref): Promise> { - const { class: _class, title } = yamlData - if (_class !== card.class.MasterTag) { - throw new Error('Invalid master tag data') - } - - const masterTag: UnifiedDoc = { - _class: card.class.MasterTag, - props: { - _id: generateId(), - space: core.space.Model, - extends: parentMasterTagId ?? card.class.Card, - label: 'embedded:embedded:' + title as IntlString, // todo: check if it's correct - kind: 0, - icon: card.icon.MasterTag - } - } - - return masterTag - } - - private async processMasterTagAttributes (yamlConfig: Record, masterTagId: Ref): Promise>>> { - const { properties } = yamlConfig - - const attributesByLabel = new Map>>() - for (const property of properties) { - const attr: UnifiedDoc> = { - _class: core.class.Attribute, - props: { - space: core.space.Model, - attributeOf: masterTagId, - name: generateId>(), - label: 'embedded:embedded:' + property.label as IntlString, // todo: check if it's correct - isCustom: true, - type: { - _class: 'core:class:' + property.type - }, - defaultValue: property.defaultValue ?? null - } - } - attributesByLabel.set(property.label, attr) - } - return attributesByLabel - } - - private async processCardsRecursively ( // todo: process masterTag children recursively - builder: ImportWorkspaceBuilder, - tagPath: string, - currentPath: string, - masterTagId: Ref, - attributesByLabel: Map>> - ): Promise { - const cardFiles = fs.readdirSync(currentPath).filter((f) => f.endsWith('.md')) - - for (const cardFile of cardFiles) { - const cardPath = path.join(currentPath, cardFile) - const cardHeader = await readYamlHeader(cardPath) - - if (cardHeader.class === undefined) { // means it's a card of class MasterTag - const card = await this.processCard(cardHeader, cardPath, masterTagId, attributesByLabel) - builder.addCard(tagPath, card) - } else { - // todo: check if it's a child master tag, or else throw error - throw new Error(`Unknown card class ${cardHeader.class} in ${cardFile}`) - } - - if (fs.existsSync(cardPath) && fs.statSync(cardPath).isDirectory()) { - await this.processCardsRecursively(builder, tagPath, cardPath, masterTagId, attributesByLabel) - } - } - } - - private async processCard ( - cardHeader: Record, - cardPath: string, - masterTagId: Ref, - attributesByTitle: Map>> - ): Promise> { - const { _class, title, ...customProperties } = cardHeader - - const props: Record = { - _id: generateId(), - space: core.space.Workspace, - title - } - - for (const [key, value] of Object.entries(customProperties)) { - const attributeName = attributesByTitle.get(key)?.props.name - if (attributeName === undefined) { - throw new Error(`Attribute not found: ${key}`) // todo: keep the error till builder validation - } - props[attributeName] = value - } - - return { - _class: masterTagId, - collabField: 'content', - contentProvider: () => readMarkdownContent(cardPath), - props: props as Props // todo: what is the correct props type? - } - } - private async processIssuesRecursively ( builder: ImportWorkspaceBuilder, projectIdentifier: string, From 088f9bb65049434729efc9195d678c572ac9163e Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Thu, 3 Apr 2025 23:27:08 +0700 Subject: [PATCH 11/50] recipes example Signed-off-by: Anna Khismatullina --- .../docs/huly/example-workspace/Custom.yaml | 29 ++++++++++++++ .../docs/huly/example-workspace/Recipes.yaml | 15 +++++++ .../Recipes/Chocolate Lava Cake.md | 35 ++++++++++++++++ .../Recipes/Classic Margherita Pizza.md | 34 ++++++++++++++++ .../Recipes/Vegan/Mushroom Risotto.md | 40 +++++++++++++++++++ .../Recipes/Vegan/Vegan Recipe.yaml | 9 +++++ 6 files changed, 162 insertions(+) create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml diff --git a/dev/import-tool/docs/huly/example-workspace/Custom.yaml b/dev/import-tool/docs/huly/example-workspace/Custom.yaml index fc258d5ae2b..2303c4ce1c5 100644 --- a/dev/import-tool/docs/huly/example-workspace/Custom.yaml +++ b/dev/import-tool/docs/huly/example-workspace/Custom.yaml @@ -5,3 +5,32 @@ properties: type: TypeString # human readable format + core:class^ - label: bbb type: TypeBoolean + +# Property types: +# - TypeString +# - TypeHyperlink +# - TypeNumber +# - TypeBoolean +# - TypeDate +# - TypeBlob ? +# - TypeRelation ? +# - TypeRecord ? +# - TypeRank ? +# - TypePersonId ? +# - TypeAccountUuid ? +# - TypeCollaborativeDoc ? +# - TypeRef ? +# - TypeEnum ? +# - TypeFileSize ? +# - TypeIntlString ? +# - TypeMarkup ? +# - TypeTimestamp ? +# - TypeDate ? +# - TypeRefTo ? +# - TypeCollection ? +# - TypeCollaborativeDoc ? +# - TypeRank ? +# - TypePersonId ? +# - TypeAccountUuid ? + + diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes.yaml b/dev/import-tool/docs/huly/example-workspace/Recipes.yaml new file mode 100644 index 00000000000..5e231728696 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes.yaml @@ -0,0 +1,15 @@ +class: card:class:MasterTag +title: Recipe +properties: + - label: cookingTime + type: TypeString + - label: servings + type: TypeNumber + - label: difficulty + type: TypeString + - label: category + type: TypeString + - label: calories + type: TypeNumber + - label: chef + type: TypeString diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md b/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md new file mode 100644 index 00000000000..6f34d8b02fa --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md @@ -0,0 +1,35 @@ +--- +title: Chocolate Lava Cake +cookingTime: 25 minutes +servings: 4 +difficulty: Medium +category: Dessert +calories: 450 +chef: Anna Smith +--- + +# Chocolate Lava Cake + +## Ingredients +- 200g dark chocolate (70% cocoa) +- 200g butter +- 4 eggs +- 200g sugar +- 120g flour +- 1 tsp vanilla extract +- Pinch of salt +- Butter for ramekins +- Cocoa powder for dusting + +## Instructions +1. Melt chocolate and butter together +2. Whisk eggs and sugar until pale +3. Fold in chocolate mixture +4. Add flour and vanilla +5. Pour into buttered ramekins +6. Bake at 200°C (400°F) for 12 minutes + +## Notes +- Serve immediately while warm +- Can be prepared ahead and refrigerated +- Perfect with vanilla ice cream \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md b/dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md new file mode 100644 index 00000000000..d842bd2c611 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md @@ -0,0 +1,34 @@ +--- +title: Classic Margherita Pizza +cookingTime: 30 minutes +servings: 4 +difficulty: Medium +category: Italian +calories: 850 +chef: Mario Rossi +--- + +# Classic Margherita Pizza + +## Ingredients +- 2 1/2 cups (300g) all-purpose flour +- 1 tsp salt +- 1 tsp active dry yeast +- 1 cup warm water +- 2 tbsp olive oil +- 1 cup tomato sauce +- 2 cups mozzarella cheese +- Fresh basil leaves +- Extra virgin olive oil + +## Instructions +1. Mix flour, salt, and yeast in a large bowl +2. Add warm water and olive oil, knead for 10 minutes +3. Let rise for 1 hour +4. Roll out dough and add toppings +5. Bake at 450°F (230°C) for 15-20 minutes + +## Notes +- For best results, use San Marzano tomatoes for the sauce +- Fresh mozzarella is preferred over pre-shredded +- Add basil leaves after baking \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md b/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md new file mode 100644 index 00000000000..4d38d8f0a75 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md @@ -0,0 +1,40 @@ +--- +title: Vegan Mushroom Risotto +cookingTime: 45 minutes +servings: 4 +difficulty: Medium +category: Italian +calories: 380 +chef: Maria Green +proteinSource: Mushrooms +isGlutenFree: true +allergens: None +--- + +# Vegan Mushroom Risotto + +## Ingredients +- 300g Arborio rice +- 500g mixed mushrooms +- 1 onion, finely chopped +- 2 cloves garlic, minced +- 1 cup white wine +- 6 cups vegetable stock +- 2 tbsp nutritional yeast +- 2 tbsp olive oil +- Salt and pepper to taste +- Fresh parsley + +## Instructions +1. Sauté mushrooms until golden +2. Add onion and garlic, cook until soft +3. Add rice and toast for 2 minutes +4. Gradually add wine and stock +5. Cook until rice is creamy +6. Finish with nutritional yeast + +## Notes +- Use a variety of mushrooms for better flavor +- Keep stock warm while adding +- Stir constantly for creamy texture +- Nutritional yeast adds cheesy flavor \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml b/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml new file mode 100644 index 00000000000..a3e275d172b --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml @@ -0,0 +1,9 @@ +class: card:class:MasterTag +title: Vegan Recipe +properties: + - label: proteinSource + type: TypeString + - label: isGlutenFree + type: TypeBoolean + - label: allergens + type: TypeString \ No newline at end of file From dfbca1d3379b116718c4ded1936b17fa7e6941b9 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Thu, 3 Apr 2025 23:53:58 +0700 Subject: [PATCH 12/50] temp instructions for card import structure Signed-off-by: Anna Khismatullina --- .../docs/huly/CARDS_INSTRUCTIONS.md | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 dev/import-tool/docs/huly/CARDS_INSTRUCTIONS.md diff --git a/dev/import-tool/docs/huly/CARDS_INSTRUCTIONS.md b/dev/import-tool/docs/huly/CARDS_INSTRUCTIONS.md new file mode 100644 index 00000000000..a4a31058758 --- /dev/null +++ b/dev/import-tool/docs/huly/CARDS_INSTRUCTIONS.md @@ -0,0 +1,135 @@ +# Card Import Format Guide + +## Directory Structure + + +All files are organized in the following structure: + +``` +workspace/ +├── Recipes.yaml # Base type configuration +├── Recipes/ # Base type cards folder +│ ├── Classic Margherita Pizza.md +│ └── Chocolate Lava Cake.md +└── Recipes/Vegan/ # Child type folder + ├── Vegan Recipe.yaml # Child type configuration + └── Mushroom Risotto.md # Child type card +``` + +## Types (Master Tags) + +Types are described in YAML files and define the structure of cards. + +### Base Type +Create file `Recipes.yaml`: + +``` +class: card:class:MasterTag +title: Recipe +properties: + - label: cookingTime # Property name + type: TypeString # Data type + - label: servings + type: TypeNumber + # ... other properties +``` + +### Child Type +Create file `Recipes/Vegan/Vegan Recipe.yaml`: + +``` +class: card:class:MasterTag +title: Vegan Recipe +properties: + - label: proteinSource + type: TypeString + # ... additional properties +``` + +## Cards + +Cards are Markdown files with YAML header and content. + +### Base Type Card +Create file `Recipes/Classic Margherita Pizza.md`: + +``` +title: Classic Margherita Pizza +cookingTime: 30 minutes +servings: 4 +difficulty: Medium +category: Italian +calories: 850 +chef: Mario Rossi +``` + +# Content in Markdown format +## Sections +- Lists +- Instructions +- Notes + +### Child Type Card +Create file `Recipes/Vegan/Mushroom Risotto.md`: + +``` +title: Vegan Mushroom Risotto +cookingTime: 45 minutes +servings: 4 +difficulty: Medium +category: Italian +calories: 380 +chef: Maria Green +proteinSource: Mushrooms # Child type properties +isGlutenFree: true +allergens: None +``` + +# Content in Markdown format + +## Important Rules + +1. File Names: + - Type YAML files must end with `.yaml` + - Cards must have `.md` extension + - File names can contain spaces + +2. Directory Structure: + - Child types must be in a subfolder named after the type + - Child type cards must be in the same folder as its configuration + +3. Card YAML Header: + - Must start and end with `---` + - Must contain all properties defined in the type + - Values must match specified data types + +4. Card Content: + - After YAML header goes regular Markdown text + - Can use all Markdown features (headings, lists, tables, etc.) + +## Examples from Our System + +1. Base Recipe Type: + - File: `Recipes.yaml` + - Defines basic recipe properties (cooking time, servings, difficulty, etc.) + +2. Base Type Cards: + - `Recipes/Classic Margherita Pizza.md` - pizza recipe + - `Recipes/Chocolate Lava Cake.md` - dessert recipe + +3. Vegan Recipe Child Type: + - File: `Recipes/Vegan/Vegan Recipe.yaml` + - Adds specific properties (protein source, gluten-free, allergens) + +4. Child Type Card: + - `Recipes/Vegan/Mushroom Risotto.md` - vegan risotto + - Uses both base properties and vegan type properties + +## Supported Data Types (to be extended) + +- TypeString - text values +- TypeNumber - numeric values +- TypeBoolean - yes/no (true/false) +- TypeDate - dates +- TypeHyperlink - links +- TypeEnum - enumeration (list of possible values) (not supported yet) \ No newline at end of file From f8b8c7528e6176cb34ab1452c67cd25d70d8ebed Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Fri, 4 Apr 2025 12:00:29 +0700 Subject: [PATCH 13/50] initial tags support Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/huly.ts | 5 ++- packages/importer/src/huly/unified.ts | 51 +++++++++++++++++++---- packages/importer/src/importer/builder.ts | 20 ++++++++- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index c4808ee3dee..09dfd79c8ac 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -14,7 +14,7 @@ // /* eslint-disable @typescript-eslint/no-unused-vars */ import { type Attachment } from '@hcengineering/attachment' -import card, { Card, MasterTag } from '@hcengineering/card' +import card, { Card, MasterTag, Tag } from '@hcengineering/card' import contact, { Employee, type Person, SocialIdentity } from '@hcengineering/contact' import documents, { ControlledDocument, @@ -496,6 +496,9 @@ export class HulyFormatImporter { case card.class.MasterTag: builder.addMasterTag(path, doc as UnifiedDoc) break + case card.class.Tag: + builder.addTag(path, doc as UnifiedDoc) + break case core.class.Attribute: builder.addMasterTagAttributes(path, [doc as UnifiedDoc>]) break diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index caa8ff399be..273ae97d9fe 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -1,6 +1,6 @@ // unified.ts import { UnifiedDoc, Props } from '../types' -import card, { Card, MasterTag } from '@hcengineering/card' +import card, { Card, MasterTag, Tag } from '@hcengineering/card' import core, { Attribute, type Doc, @@ -40,9 +40,9 @@ export class UnifiedDocProcessor { if (yamlConfig?.class === card.class.MasterTag) { const masterTag = await this.createMasterTag(yamlConfig, parentMasterTagId) const masterTagId = masterTag.props._id as Ref - const attributesByLabel = await this.createMasterTagAttributes(yamlConfig, masterTagId) + const attributesByLabel = await this.createAttributes(yamlConfig, masterTagId) - // Добавляем мастер-тег и его атрибуты + // Add master tag and its attributes const docs = result.get(yamlPath) ?? [] docs.push( masterTag, @@ -50,11 +50,24 @@ export class UnifiedDocProcessor { ) result.set(yamlPath, docs) - // Рекурсивно обрабатываем содержимое директории мастер-тега - const tagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) - if (fs.existsSync(tagDir) && fs.statSync(tagDir).isDirectory()) { - await this.processDirectory(tagDir, result, masterTagId, attributesByLabel) + // Recursively process the master tag directory + const masterTagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) + if (fs.existsSync(masterTagDir) && fs.statSync(masterTagDir).isDirectory()) { + await this.processDirectory(masterTagDir, result, masterTagId, attributesByLabel) } + } else if (yamlConfig?.class === card.class.Tag) { + if (parentMasterTagId === undefined) { + throw new Error('Tag should be inside master tag folder: ' + currentPath) // todo: confirm this error message + } + + const tag = await this.createTag(yamlConfig, parentMasterTagId) + + const tagId = tag.props._id as Ref + const attributes = await this.createAttributes(yamlConfig, tagId) + + const docs = result.get(yamlPath) ?? [] + docs.push(tag, ...Array.from(attributes.values())) + result.set(yamlPath, docs) } } @@ -101,7 +114,29 @@ export class UnifiedDocProcessor { } } - private async createMasterTagAttributes ( + private async createTag ( + data: Record, + parentMasterTagId: Ref + ): Promise> { + const { class: _class, title } = data + if (_class !== card.class.Tag) { + throw new Error('Invalid tag data') + } + + return { + _class: card.class.Tag, + props: { + _id: generateId(), + space: core.space.Model, + extends: parentMasterTagId, + label: 'embedded:embedded:' + title as IntlString, // todo: check if it's correct + kind: 2, + icon: card.icon.Tag + } + } + } + + private async createAttributes ( // todo: check if it's the same structure for tags and master tag attributes data: Record, masterTagId: Ref ): Promise>>> { diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 63cbf9a9f9f..61922bd9d72 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -import card, { Card, MasterTag } from '@hcengineering/card' +import card, { Card, MasterTag, Tag } from '@hcengineering/card' import documents, { ControlledDocument, DocumentState } from '@hcengineering/controlled-documents' import { Attribute, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' import document from '@hcengineering/document' @@ -60,6 +60,7 @@ export class ImportWorkspaceBuilder { private readonly masterTags = new Map>() private readonly masterTagAttributes = new Map>>() + private readonly tags = new Map>() private readonly cards = new Map>() // private readonly cardParents = new Map() @@ -233,6 +234,11 @@ export class ImportWorkspaceBuilder { return this } + addTag (path: string, tag: UnifiedDoc): this { + this.validateAndAdd('tag', path, tag, (t) => this.validateTag(t), this.tags, path) + return this + } + addMasterTagAttributes (path: string, attributes: UnifiedDoc>[]): this { for (const attribute of attributes) { const key = path + '/' + attribute.props.name @@ -318,6 +324,7 @@ export class ImportWorkspaceBuilder { unifiedDocs: [ ...Array.from(this.masterTags.values()), ...Array.from(this.masterTagAttributes.values()), + ...Array.from(this.tags.values()), ...Array.from(this.cards.values()) ], attachments: [] @@ -724,6 +731,17 @@ export class ImportWorkspaceBuilder { return errors } + private validateTag (tag: UnifiedDoc): string[] { + const errors: string[] = [] + + if (tag._class !== card.class.Tag) { + errors.push('Invalid class: ' + tag._class) + } + + // todo: validate tag + return errors + } + private validateMasterTagAttribute (attribute: UnifiedDoc>): string[] { const errors: string[] = [] From a9063bdd1f3cc6b346026e4aeba9f2aee25bff84 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Fri, 4 Apr 2025 12:37:37 +0700 Subject: [PATCH 14/50] wip: validation Signed-off-by: Anna Khismatullina --- packages/importer/src/importer/builder.ts | 242 ++++++++++++++++++++-- 1 file changed, 226 insertions(+), 16 deletions(-) diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 61922bd9d72..5d039e24e62 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -14,7 +14,7 @@ // import card, { Card, MasterTag, Tag } from '@hcengineering/card' import documents, { ControlledDocument, DocumentState } from '@hcengineering/controlled-documents' -import { Attribute, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' +import core, { Attribute, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' import document from '@hcengineering/document' import tracker, { IssuePriority, type IssueStatus } from '@hcengineering/tracker' import { @@ -62,7 +62,7 @@ export class ImportWorkspaceBuilder { private readonly masterTagAttributes = new Map>>() private readonly tags = new Map>() private readonly cards = new Map>() - // private readonly cardParents = new Map() + private readonly cardParents = new Map() private readonly projectTypes = new Map() private readonly issueStatusCache = new Map>() @@ -247,8 +247,13 @@ export class ImportWorkspaceBuilder { return this } - addCard (path: string, card: UnifiedDoc): this { + addCard (path: string, card: UnifiedDoc, parentCardPath?: string): this { this.validateAndAdd('card', path, card, (c) => this.validateCard(c), this.cards, path) + + if (parentCardPath !== undefined) { + this.cardParents.set(path, parentCardPath) + } + return this } @@ -256,6 +261,7 @@ export class ImportWorkspaceBuilder { // Perform cross-entity validation this.validateSpacesReferences() this.validateDocumentsReferences() + this.validateTagsReferences() this.validateCardsReferences() return { @@ -314,6 +320,14 @@ export class ImportWorkspaceBuilder { } } + // Добавляем обработку иерархии карточек + const rootCardPaths = Array.from(this.cards.keys()) + .filter(cardPath => !this.cardParents.has(cardPath)) + + for (const rootPath of rootCardPaths) { + this.buildCardHierarchy(rootPath, this.cards) + } + return { projectTypes: Array.from(this.projectTypes.values()), spaces: [ @@ -609,10 +623,88 @@ export class ImportWorkspaceBuilder { } private validateCardsReferences (): void { - // TODO: Validate cards references (master tag, attributes? parent-child, field references?) - // for (const [cardPath] of this.cards) { - // Check parent document exists - // } + // Проверка существования атрибутов + for (const [cardPath, card] of this.cards) { + if (card._class !== undefined) { + const masterTag = this.masterTags.get(card._class) + if (masterTag !== undefined) { + const attributes = Array.from(this.masterTagAttributes.values()) + .filter(a => a.props.attributeOf === card._class) + + // Проверяем, что все используемые атрибуты существуют + for (const [attrName] of Object.entries(card.props)) { + if (attrName !== 'title' && !attributes.some(a => a.props.name === attrName)) { + this.addError(cardPath, `Card uses non-existent attribute: ${attrName}`) + } + } + } + } + + // Проверка существования родительской карточки по ID + if (card.props.parentId !== undefined) { + const parentExists = Array.from(this.cards.values()).some(c => c.props.id === card.props.parentId) + if (!parentExists) { + this.addError(cardPath, `Parent card with ID ${card.props.parentId} does not exist`) + } + } + + // Проверка на циклические зависимости + if (card.props.parentId !== undefined) { + let currentCard = card + const visitedIds = new Set() + + while (currentCard.props.parentId !== undefined) { + if (visitedIds.has(currentCard.props.parentId)) { + this.addError(cardPath, 'Circular dependency detected in card hierarchy') + break + } + + visitedIds.add(currentCard.props.parentId) + const parentCard = Array.from(this.cards.values()).find(c => c.props.id === currentCard.props.parentId) + if (!parentCard) break + currentCard = parentCard + } + } + } + } + + private validateTagsReferences (): void { + // Проверка ссылок MasterTag + for (const [path, masterTag] of this.masterTags) { + if (masterTag.props.extends !== undefined) { + if (masterTag.props.extends !== card.class.Card && + !this.masterTags.has(masterTag.props.extends)) { + this.addError(path, `Invalid extends reference: ${masterTag.props.extends}`) + } + } + } + + // Проверка ссылок Tag + for (const [path, tag] of this.tags) { + if (tag.props.extends === undefined) { + this.addError(path, 'extends (MasterTag reference) is required') + } else if (!this.masterTags.has(tag.props.extends)) { + this.addError(path, `Invalid MasterTag reference: ${tag.props.extends}`) + } + } + + // Проверка ссылок атрибутов + for (const [path, attribute] of this.masterTagAttributes) { + if (attribute.props.attributeOf === undefined) { + this.addError(path, 'attributeOf (MasterTag reference) is required') + } else if (!this.masterTags.has(attribute.props.attributeOf)) { + this.addError(path, `Invalid MasterTag reference: ${attribute.props.attributeOf}`) + } + } + + // Проверка ссылок карточек + for (const [path, card] of this.cards) { + if (card._class === undefined) { + this.addError(path, 'class (MasterTag reference) is required') + } else if (!this.masterTags.has(card._class)) { + this.addError(path, `Invalid MasterTag reference: ${card._class}`) + } + } } private addError (path: string, error: string): void { @@ -723,41 +815,159 @@ export class ImportWorkspaceBuilder { private validateMasterTag (masterTag: UnifiedDoc): string[] { const errors: string[] = [] + // Проверка класса if (masterTag._class !== card.class.MasterTag) { errors.push('Invalid class: ' + masterTag._class) } - // todo: validate master tag + // Проверка обязательных полей + if (!this.validateStringDefined(masterTag.props.label)) { + errors.push('label is required') + } + + // Проверка уникальности имени + const existingTags = Array.from(this.masterTags.values()) + .filter(tag => tag.props.label === masterTag.props.label) + if (existingTags.length > 0) { + errors.push(`MasterTag with label "${masterTag.props.label}" already exists`) + } + return errors } private validateTag (tag: UnifiedDoc): string[] { const errors: string[] = [] + // Проверка класса if (tag._class !== card.class.Tag) { errors.push('Invalid class: ' + tag._class) } - // todo: validate tag + // Проверка обязательных полей + if (!this.validateStringDefined(tag.props.label)) { + errors.push('label is required') + } + + // Проверка уникальности имени в рамках MasterTag + const existingTags = Array.from(this.tags.values()) + .filter(t => t.props.extends === tag.props.extends && + t.props.label === tag.props.label) + if (existingTags.length > 0) { + errors.push(`Tag with label "${tag.props.label}" already exists for this MasterTag`) + } + return errors } private validateMasterTagAttribute (attribute: UnifiedDoc>): string[] { const errors: string[] = [] - // todo: validate master tag attribute + // Проверка класса + if (attribute._class !== core.class.Attribute) { + errors.push('Invalid class: ' + attribute._class) + } + + // Проверка обязательных полей + if (!this.validateStringDefined(attribute.props.name)) { + errors.push('name is required') + } + if (!this.validateStringDefined(attribute.props.label)) { + errors.push('label is required') + } + + // Проверка связи с MasterTag + if (attribute.props.attributeOf === undefined) { + errors.push('attributeOf (MasterTag reference) is required') + } else if (!this.masterTags.has(attribute.props.attributeOf)) { + errors.push(`Invalid MasterTag reference: ${attribute.props.attributeOf}`) + } + + // Проверка типа атрибута + if (attribute.props.type === undefined) { + errors.push('type is required') + } else { + const validTypes = [ // todo: double check valid types + 'TypeString', 'TypeNumber', 'TypeBoolean', 'TypeDate', + 'TypeHyperlink', 'TypeEnum', 'TypeFileSize', 'TypeIntlString', + 'TypeMarkup', 'TypeTimestamp', 'TypeRef', 'TypeCollection' + ] + if (!validTypes.includes(attribute.props.type)) { + errors.push(`Invalid attribute type: ${attribute.props.type}`) + } + } + + // Проверка уникальности имени атрибута в рамках MasterTag + const existingAttributes = Array.from(this.masterTagAttributes.values()) + .filter(a => a.props.attributeOf === attribute.props.attributeOf && + a.props.name === attribute.props.name) + if (existingAttributes.length > 0) { + errors.push(`Attribute with name "${attribute.props.name}" already exists for this MasterTag`) + } + return errors } - private validateCard (card: UnifiedDoc): string[] { // todo: pass validator separately (same level as converter) + private validateCard (card: UnifiedDoc): string[] { const errors: string[] = [] - // if (card._class !== card.class.Card) { - // validate class is a ref to master tag - // errors.push('Invalid class: ' + card._class) - // } + // Проверка класса (должен быть ссылкой на MasterTag) + if (card._class === undefined) { + errors.push('class (MasterTag reference) is required') + } else if (!this.masterTags.has(card._class)) { + errors.push(`Invalid MasterTag reference: ${card._class}`) + } + + // Проверка обязательных полей + if (!this.validateStringDefined(card.props.title)) { + errors.push('title is required') + } + + // Получаем MasterTag и его атрибуты + const masterTag = this.masterTags.get(card._class) + if (masterTag !== undefined) { + const attributes = Array.from(this.masterTagAttributes.values()) + .filter(a => a.props.attributeOf === card._class) + + // Проверяем значения атрибутов + for (const attribute of attributes) { + const value = (card.props as Record)[attribute.props.name] + + // Проверка обязательных атрибутов + // todo: check if required attribute is missing + // if (attribute.props.required && value === undefined) { + // errors.push(`Required attribute "${attribute.props.label}" is missing`) + // continue + // } + + // Проверка типов данных + if (value !== undefined) { + switch (attribute.props.type) { + case 'TypeString': + if (typeof value !== 'string') { + errors.push(`Attribute "${attribute.props.label}" must be a string`) + } + break + case 'TypeNumber': + if (typeof value !== 'number') { + errors.push(`Attribute "${attribute.props.label}" must be a number`) + } + break + case 'TypeBoolean': + if (typeof value !== 'boolean') { + errors.push(`Attribute "${attribute.props.label}" must be a boolean`) + } + break + case 'TypeDate': + if (!(value instanceof Date)) { + errors.push(`Attribute "${attribute.props.label}" must be a date`) + } + break + // todo: add other types as needed + } + } + } + } - // todo: validate card return errors } From bf8432f88c7f9ee6f24afbdaaca5ecd82ceec867 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Fri, 4 Apr 2025 12:39:47 +0700 Subject: [PATCH 15/50] wip: remove parent validation Signed-off-by: Anna Khismatullina --- packages/importer/src/importer/builder.ts | 42 +---------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 5d039e24e62..31ca715d330 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -62,7 +62,6 @@ export class ImportWorkspaceBuilder { private readonly masterTagAttributes = new Map>>() private readonly tags = new Map>() private readonly cards = new Map>() - private readonly cardParents = new Map() private readonly projectTypes = new Map() private readonly issueStatusCache = new Map>() @@ -247,13 +246,8 @@ export class ImportWorkspaceBuilder { return this } - addCard (path: string, card: UnifiedDoc, parentCardPath?: string): this { + addCard (path: string, card: UnifiedDoc): this { this.validateAndAdd('card', path, card, (c) => this.validateCard(c), this.cards, path) - - if (parentCardPath !== undefined) { - this.cardParents.set(path, parentCardPath) - } - return this } @@ -320,14 +314,6 @@ export class ImportWorkspaceBuilder { } } - // Добавляем обработку иерархии карточек - const rootCardPaths = Array.from(this.cards.keys()) - .filter(cardPath => !this.cardParents.has(cardPath)) - - for (const rootPath of rootCardPaths) { - this.buildCardHierarchy(rootPath, this.cards) - } - return { projectTypes: Array.from(this.projectTypes.values()), spaces: [ @@ -639,32 +625,6 @@ export class ImportWorkspaceBuilder { } } } - - // Проверка существования родительской карточки по ID - if (card.props.parentId !== undefined) { - const parentExists = Array.from(this.cards.values()).some(c => c.props.id === card.props.parentId) - if (!parentExists) { - this.addError(cardPath, `Parent card with ID ${card.props.parentId} does not exist`) - } - } - - // Проверка на циклические зависимости - if (card.props.parentId !== undefined) { - let currentCard = card - const visitedIds = new Set() - - while (currentCard.props.parentId !== undefined) { - if (visitedIds.has(currentCard.props.parentId)) { - this.addError(cardPath, 'Circular dependency detected in card hierarchy') - break - } - - visitedIds.add(currentCard.props.parentId) - const parentCard = Array.from(this.cards.values()).find(c => c.props.id === currentCard.props.parentId) - if (!parentCard) break - currentCard = parentCard - } - } } } From c7297f26912e8a50e08fe6649c317055f25cc8a2 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Fri, 4 Apr 2025 15:20:44 +0700 Subject: [PATCH 16/50] wip: apply tags Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/unified.ts | 24 +++- packages/importer/src/importer/builder.ts | 10 ++ packages/importer/src/types.ts | 15 +- plugins/export-assets/lang/zh.json | 167 ++++++++++++++++++---- 4 files changed, 183 insertions(+), 33 deletions(-) diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 273ae97d9fe..bc1759de8fd 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -16,6 +16,8 @@ import { readMarkdownContent, readYamlHeader } from './parsing' export type UnifiedDocProcessResult = Map>> export class UnifiedDocProcessor { + private tagPaths: Map> = new Map() + async importFromDirectory (directoryPath: string): Promise { const unifiedDocs: UnifiedDocProcessResult = new Map() await this.processDirectory(directoryPath, unifiedDocs) @@ -60,9 +62,10 @@ export class UnifiedDocProcessor { throw new Error('Tag should be inside master tag folder: ' + currentPath) // todo: confirm this error message } - const tag = await this.createTag(yamlConfig, parentMasterTagId) + const tagId = this.tagPaths.get(yamlPath) ?? generateId() + const tag = await this.createTag(yamlConfig, tagId, parentMasterTagId) + this.tagPaths.set(yamlPath, tagId) - const tagId = tag.props._id as Ref const attributes = await this.createAttributes(yamlConfig, tagId) const docs = result.get(yamlPath) ?? [] @@ -116,6 +119,7 @@ export class UnifiedDocProcessor { private async createTag ( data: Record, + tagId: Ref, parentMasterTagId: Ref ): Promise> { const { class: _class, title } = data @@ -126,7 +130,7 @@ export class UnifiedDocProcessor { return { _class: card.class.Tag, props: { - _id: generateId(), + _id: tagId, space: core.space.Model, extends: parentMasterTagId, label: 'embedded:embedded:' + title as IntlString, // todo: check if it's correct @@ -187,6 +191,20 @@ export class UnifiedDocProcessor { props[attributeName] = value } + if (cardHeader.tags !== undefined) { + const tagIds: Ref[] = [] + for (const tagPath of cardHeader.tags) { + let tagId = this.tagPaths.get(tagPath) + if (tagId === undefined) { + // Если тег еще не обработан, генерируем ID и сохраняем его + tagId = generateId() + this.tagPaths.set(tagPath, tagId) + } + tagIds.push(tagId) + } + props.tags = tagIds + } + return { _class: masterTagId, collabField: 'content', diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 31ca715d330..b84749a75dc 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -625,6 +625,16 @@ export class ImportWorkspaceBuilder { } } } + + // Проверка существования тегов + if (card.props.tags !== undefined) { + for (const tagId of card.props.tags) { + const tagExists = Array.from(this.tags.values()).some(t => t.props._id === tagId) + if (!tagExists) { + this.addError(cardPath, `Card references non-existent tag: ${tagId}`) + } + } + } } } diff --git a/packages/importer/src/types.ts b/packages/importer/src/types.ts index d7243eba747..70c9b70b4c3 100644 --- a/packages/importer/src/types.ts +++ b/packages/importer/src/types.ts @@ -1,4 +1,6 @@ -import { Class, Data, Doc, Ref, Space } from '@hcengineering/core' +import { Class, Data, Doc, Mixin, Ref, Space } from '@hcengineering/core' + +export type Props = Data & Partial & { space: Ref } export interface UnifiedDoc { _class: Ref> @@ -7,11 +9,12 @@ export interface UnifiedDoc { contentProvider?: () => Promise } -export type Props = Data & Partial & { space: Ref } - -export interface ImportContext { - vars: Record - defaults: Map>, Props> +export interface UnifiedMixin { + _id: Ref + _class: Ref> + space: Ref + mixin: Ref> + props: Props } export type contentProvider = () => Promise diff --git a/plugins/export-assets/lang/zh.json b/plugins/export-assets/lang/zh.json index 08e383d4dc9..a93e89558f0 100644 --- a/plugins/export-assets/lang/zh.json +++ b/plugins/export-assets/lang/zh.json @@ -1,26 +1,145 @@ { - "string": { - "WorkspaceNamePattern": "名称必须为 40 个字符或更少,不能为空,不能包含特殊字符(<、>、/)", - "Export": "导出", - "DataToExport": "要导出的数据", - "ExportDocuments": "文档", - "ExportMilestones": "里程碑", - "ExportIssues": "问题", - "ExportTestCases": "测试用例", - "ExportTestRuns": "测试运行", - "ExportTestPlans": "测试计划", - "ExportFormat": "导出格式", - "ExportJSON": "JSON", - "ExportCSV": "CSV", - "ExportUnifiedFormat": "Huly 统一格式", - "ExportIncludeContent": "包含内容", - "ExportEverything": "所有内容", - "ExportAttributesOnly": "仅属性", - "ExportRequestSuccess": "导出已开始。", - "ExportRequestSuccessMessage": "导出已开始。完成后您将在收件箱中收到通知。", - "ExportRequestFailed": "提交导出失败。", - "ExportRequestFailedMessage": "提交导出时发生错误。请稍后重试。", - "ExportCompleted": "导出完成。您的文件已准备就绪。您可以从驱动器下载它们。", - "ExportFailed": "导出数据失败。请稍后重试。" - } + "projectTypes": [], + "spaces": [], + "unifiedDocs": [ + { + "_class": "card:class:MasterTag", + "props": { + "_id": "67ef664b667b87c74d9a3fc5", + "space": "core:space:Model", + "extends": "card:class:Card", + "label": "embedded:embedded:Master Tag 4", + "kind": 0, + "icon": "card:icon:MasterTag" + } + }, + { + "_class": "card:class:MasterTag", + "props": { + "_id": "67ef664b667b87c74d9a3fc8", + "space": "core:space:Model", + "extends": "67ef664b667b87c74d9a3fc5", + "label": "embedded:embedded:Child Type 2", + "kind": 0, + "icon": "card:icon:MasterTag" + } + }, + { + "_class": "card:class:MasterTag", + "props": { + "_id": "67ef664b667b87c74d9a3fcb", + "space": "core:space:Model", + "extends": "67ef664b667b87c74d9a3fc8", + "label": "embedded:embedded:Child Child Type 2", + "kind": 0, + "icon": "card:icon:MasterTag" + } + }, + { + "_class": "core:class:Attribute", + "props": { + "space": "core:space:Model", + "attributeOf": "67ef664b667b87c74d9a3fc5", + "name": "67ef664b667b87c74d9a3fc6", + "label": "embedded:embedded:aaa", + "isCustom": true, + "type": { "_class": "core:class:TypeString" }, + "defaultValue": null + } + }, + { + "_class": "core:class:Attribute", + "props": { + "space": "core:space:Model", + "attributeOf": "67ef664b667b87c74d9a3fc5", + "name": "67ef664b667b87c74d9a3fc7", + "label": "embedded:embedded:bbb", + "isCustom": true, + "type": { "_class": "core:class:TypeBoolean" }, + "defaultValue": null + } + }, + { + "_class": "core:class:Attribute", + "props": { + "space": "core:space:Model", + "attributeOf": "67ef664b667b87c74d9a3fc8", + "name": "67ef664b667b87c74d9a3fc9", + "label": "embedded:embedded:ccc", + "isCustom": true, + "type": { "_class": "core:class:TypeString" }, + "defaultValue": null + } + }, + { + "_class": "core:class:Attribute", + "props": { + "space": "core:space:Model", + "attributeOf": "67ef664b667b87c74d9a3fc8", + "name": "67ef664b667b87c74d9a3fca", + "label": "embedded:embedded:ddd", + "isCustom": true, + "type": { "_class": "core:class:TypeString" }, + "defaultValue": null + } + }, + { + "_class": "core:class:Attribute", + "props": { + "space": "core:space:Model", + "attributeOf": "67ef664b667b87c74d9a3fcb", + "name": "67ef664b667b87c74d9a3fcc", + "label": "embedded:embedded:ccc", + "isCustom": true, + "type": { "_class": "core:class:TypeString" }, + "defaultValue": null + } + }, + { + "_class": "core:class:Attribute", + "props": { + "space": "core:space:Model", + "attributeOf": "67ef664b667b87c74d9a3fcb", + "name": "67ef664b667b87c74d9a3fcd", + "label": "embedded:embedded:ddd", + "isCustom": true, + "type": { "_class": "core:class:TypeString" }, + "defaultValue": null + } + }, + { + "_class": "67ef664b667b87c74d9a3fcb", + "collabField": "content", + "props": { + "_id": "67ef664b667b87c74d9a3fce", + "space": "core:space:Workspace", + "title": "Card from Child 2", + "67ef664b667b87c74d9a3fcc": "ccc", + "67ef664b667b87c74d9a3fcd": "ddd" + } + }, + { + "_class": "67ef664b667b87c74d9a3fc8", + "collabField": "content", + "props": { + "_id": "67ef664b667b87c74d9a3fcf", + "space": "core:space:Workspace", + "title": "Card from Child 2", + "67ef664b667b87c74d9a3fc9": "ccc", + "67ef664b667b87c74d9a3fca": "ddd" + } + }, + { + "_class": "67ef664b667b87c74d9a3fc5", + "collabField": "content", + "props": { + "_id": "67ef664b667b87c74d9a3fd0", + "space": "core:space:Workspace", + "title": "Card of MT 1", + "67ef664b667b87c74d9a3fc6": "some text", + "67ef664b667b87c74d9a3fc7": true + } + } + ], + "attachments": [] } From f7a066393a546885de53407b89ca595cc634a953 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Fri, 4 Apr 2025 19:16:20 +0700 Subject: [PATCH 17/50] wip Signed-off-by: Anna Khismatullina --- .../huly/example-workspace/Custom/Tag.yaml | 5 + packages/importer/src/huly/unified.ts | 104 +++++++++++------- 2 files changed, 71 insertions(+), 38 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/Custom/Tag.yaml diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Tag.yaml b/dev/import-tool/docs/huly/example-workspace/Custom/Tag.yaml new file mode 100644 index 00000000000..622ec3f0e1b --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Custom/Tag.yaml @@ -0,0 +1,5 @@ +class: card:class:Tag +title: XXX +properties: + - label: x + type: TypeString diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index bc1759de8fd..487f518db10 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -1,27 +1,34 @@ // unified.ts -import { UnifiedDoc, Props } from '../types' import card, { Card, MasterTag, Tag } from '@hcengineering/card' import core, { Attribute, - type Doc, + Doc, generateId, - type Ref, + Mixin, + Ref } from '@hcengineering/core' import * as fs from 'fs' -import * as path from 'path' import * as yaml from 'js-yaml' +import * as path from 'path' import { IntlString } from '../../../platform/types' +import { Props, UnifiedDoc, UnifiedMixin } from '../types' import { readMarkdownContent, readYamlHeader } from './parsing' -export type UnifiedDocProcessResult = Map>> +export interface UnifiedDocProcessResult { + docs: Map>> + mixins: Map>> +} export class UnifiedDocProcessor { - private tagPaths: Map> = new Map() + private readonly tagPaths = new Map>() async importFromDirectory (directoryPath: string): Promise { - const unifiedDocs: UnifiedDocProcessResult = new Map() - await this.processDirectory(directoryPath, unifiedDocs) - return unifiedDocs + const result: UnifiedDocProcessResult = { + docs: new Map(), + mixins: new Map() + } + await this.processDirectory(directoryPath, result) + return result } private async processDirectory ( @@ -45,12 +52,12 @@ export class UnifiedDocProcessor { const attributesByLabel = await this.createAttributes(yamlConfig, masterTagId) // Add master tag and its attributes - const docs = result.get(yamlPath) ?? [] + const docs = result.docs.get(yamlPath) ?? [] docs.push( masterTag, ...Array.from(attributesByLabel.values()) ) - result.set(yamlPath, docs) + result.docs.set(yamlPath, docs) // Recursively process the master tag directory const masterTagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) @@ -68,29 +75,31 @@ export class UnifiedDocProcessor { const attributes = await this.createAttributes(yamlConfig, tagId) - const docs = result.get(yamlPath) ?? [] + const docs = result.docs.get(yamlPath) ?? [] docs.push(tag, ...Array.from(attributes.values())) - result.set(yamlPath, docs) + result.docs.set(yamlPath, docs) } } if (parentMasterTagId === undefined || parentAttributesByLabel === undefined) { - // means we are in the root directory + // Means we are in the root directory return } - // Затем обрабатываем markdown файлы (карточки) + // Then process markdown files (cards) for (const entry of entries) { if (!entry.isFile() || !entry.name.endsWith('.md')) continue const cardPath = path.join(currentPath, entry.name) const cardHeader = await readYamlHeader(cardPath) - const unifiedDoc = await this.createCard(cardHeader, cardPath, parentMasterTagId, parentAttributesByLabel) + const card = await this.createCard(cardHeader, cardPath, parentMasterTagId, parentAttributesByLabel) + + if (card != null) { + const docs = result.docs.get(cardPath) ?? [] + docs.push(card) + result.docs.set(cardPath, docs) - if (unifiedDoc != null) { - const docs = result.get(cardPath) ?? [] - docs.push(unifiedDoc) - result.set(cardPath, docs) + await this.applyTags(card, cardHeader, cardPath, result) } } } @@ -133,14 +142,14 @@ export class UnifiedDocProcessor { _id: tagId, space: core.space.Model, extends: parentMasterTagId, - label: 'embedded:embedded:' + title as IntlString, // todo: check if it's correct + label: 'embedded:embedded:' + title as IntlString, kind: 2, icon: card.icon.Tag } } } - private async createAttributes ( // todo: check if it's the same structure for tags and master tag attributes + private async createAttributes ( data: Record, masterTagId: Ref ): Promise>>> { @@ -156,7 +165,7 @@ export class UnifiedDocProcessor { space: core.space.Model, attributeOf: masterTagId, name: generateId>(), - label: 'embedded:embedded:' + property.label as IntlString, // todo: check if it's correct + label: 'embedded:embedded:' + property.label as IntlString, isCustom: true, type: { _class: 'core:class:' + property.type @@ -184,27 +193,13 @@ export class UnifiedDocProcessor { } for (const [key, value] of Object.entries(customProperties)) { - const attributeName = attributesByLabel.get(key)?.props.name + const attributeName = attributesByLabel.get(key)?.props.name // todo: handle tag attributes separately if (attributeName === undefined) { throw new Error(`Attribute not found: ${key}`) // todo: keep the error till builder validation } props[attributeName] = value } - if (cardHeader.tags !== undefined) { - const tagIds: Ref[] = [] - for (const tagPath of cardHeader.tags) { - let tagId = this.tagPaths.get(tagPath) - if (tagId === undefined) { - // Если тег еще не обработан, генерируем ID и сохраняем его - tagId = generateId() - this.tagPaths.set(tagPath, tagId) - } - tagIds.push(tagId) - } - props.tags = tagIds - } - return { _class: masterTagId, collabField: 'content', @@ -212,4 +207,37 @@ export class UnifiedDocProcessor { props: props as Props // todo: what is the correct props type? } } + + private async applyTags ( + card: UnifiedDoc, + cardHeader: Record, + cardPath: string, + result: UnifiedDocProcessResult + ): Promise { + if (cardHeader.tags === undefined) return + + const mixins: UnifiedMixin[] = [] + for (const tagPath of cardHeader.tags) { + let tagId = this.tagPaths.get(tagPath) + if (tagId === undefined) { + tagId = generateId() + this.tagPaths.set(tagPath, tagId) + } + + const mixin: UnifiedMixin = { + _id: card.props._id as Ref, + _class: card._class, + space: card.props.space, + mixin: tagId, + props: { + __mixin: 'true' + } + } + mixins.push(mixin) + } + + if (mixins.length > 0) { + result.mixins.set(cardPath, mixins) + } + } } From e7510ee7a96f42cdb07dee478cb4088c2ec33df0 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Sun, 6 Apr 2025 15:58:39 +0700 Subject: [PATCH 18/50] wip: mixins Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/huly.ts | 10 ++++++-- packages/importer/src/importer/builder.ts | 19 ++++++++++++-- packages/importer/src/importer/importer.ts | 29 ++++++++++++++++------ packages/importer/src/types.ts | 2 -- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 09dfd79c8ac..7bc6b479d80 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -487,10 +487,10 @@ export class HulyFormatImporter { } // Импортируем UnifiedDoc сущности - const unifiedResult = await this.unifiedDocImporter.importFromDirectory(folderPath) + const { docs: unifiedDocs, mixins: unifiedMixins } = await this.unifiedDocImporter.importFromDirectory(folderPath) // Разбираем и добавляем в билдер по классу - for (const [path, docs] of unifiedResult.entries()) { + for (const [path, docs] of unifiedDocs.entries()) { for (const doc of docs) { switch (doc._class) { case card.class.MasterTag: @@ -512,6 +512,12 @@ export class HulyFormatImporter { } } + for (const [path, mixins] of unifiedMixins.entries()) { + for (const mixin of mixins) { + builder.addTagMixin(path, mixin) + } + } + // Process all yaml files first const yamlFiles = fs.readdirSync(folderPath).filter((f) => f.endsWith('.yaml') && f !== 'settings.yaml') diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index b84749a75dc..4f8ade11f88 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -14,7 +14,7 @@ // import card, { Card, MasterTag, Tag } from '@hcengineering/card' import documents, { ControlledDocument, DocumentState } from '@hcengineering/controlled-documents' -import core, { Attribute, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' +import core, { Attribute, Doc, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' import document from '@hcengineering/document' import tracker, { IssuePriority, type IssueStatus } from '@hcengineering/tracker' import { @@ -29,7 +29,7 @@ import { type ImportTeamspace, type ImportWorkspace } from './importer' -import { UnifiedDoc } from '../types' +import { UnifiedDoc, UnifiedMixin } from '../types' export interface ValidationError { path: string @@ -62,6 +62,7 @@ export class ImportWorkspaceBuilder { private readonly masterTagAttributes = new Map>>() private readonly tags = new Map>() private readonly cards = new Map>() + private readonly mixins = new Map>() private readonly projectTypes = new Map() private readonly issueStatusCache = new Map>() @@ -251,6 +252,11 @@ export class ImportWorkspaceBuilder { return this } + addTagMixin (path: string, mixin: UnifiedMixin): this { + this.validateAndAdd('tagMixin', path, mixin, (m) => this.validateTagMixin(m), this.mixins, path) + return this + } + validate (): ValidationResult { // Perform cross-entity validation this.validateSpacesReferences() @@ -327,6 +333,7 @@ export class ImportWorkspaceBuilder { ...Array.from(this.tags.values()), ...Array.from(this.cards.values()) ], + mixins: Array.from(this.mixins.values()), attachments: [] } } @@ -941,6 +948,14 @@ export class ImportWorkspaceBuilder { return errors } + private validateTagMixin (mixin: UnifiedMixin): string[] { + const errors: string[] = [] + + // todo: validate tag mixin + + return errors + } + private validateOrgSpace (space: ImportOrgSpace): string[] { const errors: string[] = [] diff --git a/packages/importer/src/importer/importer.ts b/packages/importer/src/importer/importer.ts index 2cfd261a895..c6f8dace045 100644 --- a/packages/importer/src/importer/importer.ts +++ b/packages/importer/src/importer/importer.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // -import attachment, { Drawing, type Attachment } from '@hcengineering/attachment' +import attachment, { type Attachment, Drawing } from '@hcengineering/attachment' import chunter, { type ChatMessage } from '@hcengineering/chunter' import { Employee, type Person } from '@hcengineering/contact' import documents, { @@ -30,6 +30,7 @@ import documents, { useDocumentTemplate } from '@hcengineering/controlled-documents' import core, { + type AccountUuid, type AttachedData, type Class, type CollaborativeDoc, @@ -39,6 +40,7 @@ import core, { generateId, makeCollabId, type Mixin, + type PersonId, type Blob as PlatformBlob, type Ref, RolesAssignment, @@ -46,10 +48,7 @@ import core, { type Space, type Status, type Timestamp, - type TxOperations, - type PersonId, - type AccountUuid, - AttachedDoc + type TxOperations } from '@hcengineering/core' import document, { type Document, getFirstRank, type Teamspace } from '@hcengineering/document' import task, { @@ -70,15 +69,16 @@ import tracker, { TimeReportDayType } from '@hcengineering/tracker' import view from '@hcengineering/view' +import { UnifiedDoc, UnifiedMixin } from '../types' +import { Logger } from './logger' import { type MarkdownPreprocessor, NoopMarkdownPreprocessor } from './preprocessor' import { type FileUploader } from './uploader' -import { Logger } from './logger' -import { Props, UnifiedDoc } from '../types' export interface ImportWorkspace { projectTypes?: ImportProjectType[] spaces?: ImportSpace[] attachments?: ImportAttachment[] unifiedDocs?: UnifiedDoc>[] + mixins?: UnifiedMixin, Doc>[] } export interface ImportProjectType { @@ -244,6 +244,7 @@ export class WorkspaceImporter { await this.importSpaces() await this.importAttachments() await this.importUnifiedDocs() + await this.importUnifiedMixins() } private async importProjectTypes (): Promise { @@ -1152,4 +1153,18 @@ export class WorkspaceImporter { } await this.client.createDoc(_class, props.space, props as Data>, _id) } + + private async importUnifiedMixins (): Promise { + if (this.workspaceData.mixins === undefined) return + + for (const mixin of this.workspaceData.mixins) { + await this.importUnifiedMixin(mixin) + } + } + + private async importUnifiedMixin (mixin: UnifiedMixin, Doc>): Promise { + const { _class, mixin: mixinClass, props } = mixin + const { _id, space, ...data } = props + await this.client.createMixin(_id ?? generateId>(), _class, space, mixinClass, data as Data>) + } } diff --git a/packages/importer/src/types.ts b/packages/importer/src/types.ts index 70c9b70b4c3..28a230fdddf 100644 --- a/packages/importer/src/types.ts +++ b/packages/importer/src/types.ts @@ -10,9 +10,7 @@ export interface UnifiedDoc { } export interface UnifiedMixin { - _id: Ref _class: Ref> - space: Ref mixin: Ref> props: Props } From 3c6857427d92fce330586d15365e3ddf64ed269e Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Sun, 6 Apr 2025 16:03:14 +0700 Subject: [PATCH 19/50] qfix build Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/unified.ts | 6 +++--- packages/importer/src/importer/builder.ts | 18 +++++++++--------- packages/importer/src/types.ts | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 487f518db10..6dc0db67c53 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -225,13 +225,13 @@ export class UnifiedDocProcessor { } const mixin: UnifiedMixin = { - _id: card.props._id as Ref, _class: card._class, - space: card.props.space, mixin: tagId, props: { + _id: tagId, + space: core.space.Model, __mixin: 'true' - } + } as unknown as Props // todo: what is the correct props type? } mixins.push(mixin) } diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 4f8ade11f88..9f64e793e79 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -633,15 +633,15 @@ export class ImportWorkspaceBuilder { } } - // Проверка существования тегов - if (card.props.tags !== undefined) { - for (const tagId of card.props.tags) { - const tagExists = Array.from(this.tags.values()).some(t => t.props._id === tagId) - if (!tagExists) { - this.addError(cardPath, `Card references non-existent tag: ${tagId}`) - } - } - } + // todo: check if tags are valid + // if (card.props.tags !== undefined) { + // for (const tagId of card.props.tags) { + // const tagExists = Array.from(this.tags.values()).some(t => t.props._id === tagId) + // if (!tagExists) { + // this.addError(cardPath, `Card references non-existent tag: ${tagId}`) + // } + // } + // } } } diff --git a/packages/importer/src/types.ts b/packages/importer/src/types.ts index 28a230fdddf..e595ac05ef0 100644 --- a/packages/importer/src/types.ts +++ b/packages/importer/src/types.ts @@ -9,7 +9,7 @@ export interface UnifiedDoc { contentProvider?: () => Promise } -export interface UnifiedMixin { +export interface UnifiedMixin { // todo: extends T _class: Ref> mixin: Ref> props: Props From 3e9b4d285aae4310c94f792bc7e23ff8bd18b5f6 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Sun, 6 Apr 2025 16:56:55 +0700 Subject: [PATCH 20/50] tags applyed (no attributes) Signed-off-by: Anna Khismatullina --- .../{Custom.yaml => MasterTag.yaml} | 2 +- .../{Custom/Card Custom 1.md => MasterTag/Card1.md} | 5 ++++- .../{Custom => MasterTag}/Child Type.yaml | 0 .../Child Type/Card of Child 1.md | 0 .../Child Type/Child Child Type.yaml | 0 .../Child Child Type/Card of Child of Child.md | 0 .../{Custom => MasterTag}/Tag.yaml | 0 packages/importer/src/huly/unified.ts | 13 +++++++------ 8 files changed, 12 insertions(+), 8 deletions(-) rename dev/import-tool/docs/huly/example-workspace/{Custom.yaml => MasterTag.yaml} (97%) rename dev/import-tool/docs/huly/example-workspace/{Custom/Card Custom 1.md => MasterTag/Card1.md} (87%) rename dev/import-tool/docs/huly/example-workspace/{Custom => MasterTag}/Child Type.yaml (100%) rename dev/import-tool/docs/huly/example-workspace/{Custom => MasterTag}/Child Type/Card of Child 1.md (100%) rename dev/import-tool/docs/huly/example-workspace/{Custom => MasterTag}/Child Type/Child Child Type.yaml (100%) rename dev/import-tool/docs/huly/example-workspace/{Custom => MasterTag}/Child Type/Child Child Type/Card of Child of Child.md (100%) rename dev/import-tool/docs/huly/example-workspace/{Custom => MasterTag}/Tag.yaml (100%) diff --git a/dev/import-tool/docs/huly/example-workspace/Custom.yaml b/dev/import-tool/docs/huly/example-workspace/MasterTag.yaml similarity index 97% rename from dev/import-tool/docs/huly/example-workspace/Custom.yaml rename to dev/import-tool/docs/huly/example-workspace/MasterTag.yaml index 2303c4ce1c5..578f4d7d9e9 100644 --- a/dev/import-tool/docs/huly/example-workspace/Custom.yaml +++ b/dev/import-tool/docs/huly/example-workspace/MasterTag.yaml @@ -1,5 +1,5 @@ class: card:class:MasterTag -title: Master Tag 3 +title: Master Tag 5 properties: - label: aaa # embedded:embedded:aaa type: TypeString # human readable format + core:class^ diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md b/dev/import-tool/docs/huly/example-workspace/MasterTag/Card1.md similarity index 87% rename from dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md rename to dev/import-tool/docs/huly/example-workspace/MasterTag/Card1.md index 2244c53c664..73727e0c643 100644 --- a/dev/import-tool/docs/huly/example-workspace/Custom/Card Custom 1.md +++ b/dev/import-tool/docs/huly/example-workspace/MasterTag/Card1.md @@ -1,7 +1,10 @@ --- -title: Card of MT 1 +title: Card of MT 6 +tags: + - ./Tag.yaml aaa: some text bbb: true +# x: oololo --- # Standard Operating Procedure diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Child Type.yaml b/dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type.yaml similarity index 100% rename from dev/import-tool/docs/huly/example-workspace/Custom/Child Type.yaml rename to dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type.yaml diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Card of Child 1.md b/dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type/Card of Child 1.md similarity index 100% rename from dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Card of Child 1.md rename to dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type/Card of Child 1.md diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type.yaml b/dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type/Child Child Type.yaml similarity index 100% rename from dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type.yaml rename to dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type/Child Child Type.yaml diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type/Card of Child of Child.md b/dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type/Child Child Type/Card of Child of Child.md similarity index 100% rename from dev/import-tool/docs/huly/example-workspace/Custom/Child Type/Child Child Type/Card of Child of Child.md rename to dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type/Child Child Type/Card of Child of Child.md diff --git a/dev/import-tool/docs/huly/example-workspace/Custom/Tag.yaml b/dev/import-tool/docs/huly/example-workspace/MasterTag/Tag.yaml similarity index 100% rename from dev/import-tool/docs/huly/example-workspace/Custom/Tag.yaml rename to dev/import-tool/docs/huly/example-workspace/MasterTag/Tag.yaml diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 6dc0db67c53..aa40e86d2c4 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -4,7 +4,6 @@ import core, { Attribute, Doc, generateId, - Mixin, Ref } from '@hcengineering/core' import * as fs from 'fs' @@ -184,7 +183,7 @@ export class UnifiedDocProcessor { masterTagId: Ref, attributesByLabel: Map>> ): Promise> { - const { _class, title, ...customProperties } = cardHeader + const { _class, title, tags, ...customProperties } = cardHeader const props: Record = { _id: generateId(), @@ -218,18 +217,20 @@ export class UnifiedDocProcessor { const mixins: UnifiedMixin[] = [] for (const tagPath of cardHeader.tags) { - let tagId = this.tagPaths.get(tagPath) + const cardDir = path.dirname(cardPath) + const fullTagPath = path.resolve(cardDir, tagPath) + let tagId = this.tagPaths.get(fullTagPath) if (tagId === undefined) { tagId = generateId() - this.tagPaths.set(tagPath, tagId) + this.tagPaths.set(fullTagPath, tagId) } const mixin: UnifiedMixin = { _class: card._class, mixin: tagId, props: { - _id: tagId, - space: core.space.Model, + _id: card.props._id as Ref, + space: core.space.Workspace, __mixin: 'true' } as unknown as Props // todo: what is the correct props type? } From 1b82de382b004efe05e9e97901556ea388615b46 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Sun, 6 Apr 2025 16:57:25 +0700 Subject: [PATCH 21/50] disable invalid validations Signed-off-by: Anna Khismatullina --- packages/importer/src/importer/builder.ts | 120 +++++++++++----------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 9f64e793e79..8a901c4cfb9 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -647,41 +647,41 @@ export class ImportWorkspaceBuilder { private validateTagsReferences (): void { // Проверка ссылок MasterTag - for (const [path, masterTag] of this.masterTags) { - if (masterTag.props.extends !== undefined) { - if (masterTag.props.extends !== card.class.Card && - !this.masterTags.has(masterTag.props.extends)) { - this.addError(path, `Invalid extends reference: ${masterTag.props.extends}`) - } - } - } - - // Проверка ссылок Tag - for (const [path, tag] of this.tags) { - if (tag.props.extends === undefined) { - this.addError(path, 'extends (MasterTag reference) is required') - } else if (!this.masterTags.has(tag.props.extends)) { - this.addError(path, `Invalid MasterTag reference: ${tag.props.extends}`) - } - } - - // Проверка ссылок атрибутов - for (const [path, attribute] of this.masterTagAttributes) { - if (attribute.props.attributeOf === undefined) { - this.addError(path, 'attributeOf (MasterTag reference) is required') - } else if (!this.masterTags.has(attribute.props.attributeOf)) { - this.addError(path, `Invalid MasterTag reference: ${attribute.props.attributeOf}`) - } - } - - // Проверка ссылок карточек - for (const [path, card] of this.cards) { - if (card._class === undefined) { - this.addError(path, 'class (MasterTag reference) is required') - } else if (!this.masterTags.has(card._class)) { - this.addError(path, `Invalid MasterTag reference: ${card._class}`) - } - } + // for (const [path, masterTag] of this.masterTags) { + // if (masterTag.props.extends !== undefined) { + // if (masterTag.props.extends !== card.class.Card && + // !this.masterTags.has(masterTag.props.extends)) { + // this.addError(path, `Invalid extends reference: ${masterTag.props.extends}`) + // } + // } + // } + + // // Проверка ссылок Tag + // for (const [path, tag] of this.tags) { + // if (tag.props.extends === undefined) { + // this.addError(path, 'extends (MasterTag reference) is required') + // } else if (!this.masterTags.has(tag.props.extends)) { + // this.addError(path, `Invalid MasterTag reference: ${tag.props.extends}`) + // } + // } + + // // Проверка ссылок атрибутов + // for (const [path, attribute] of this.masterTagAttributes) { + // if (attribute.props.attributeOf === undefined) { + // this.addError(path, 'attributeOf (MasterTag reference) is required') + // } else if (!this.masterTags.has(attribute.props.attributeOf)) { + // this.addError(path, `Invalid MasterTag reference: ${attribute.props.attributeOf}`) + // } + // } + + // // Проверка ссылок карточек + // for (const [path, card] of this.cards) { + // if (card._class === undefined) { + // this.addError(path, 'class (MasterTag reference) is required') + // } else if (!this.masterTags.has(card._class)) { + // this.addError(path, `Invalid MasterTag reference: ${card._class}`) + // } + // } } private addError (path: string, error: string): void { @@ -852,26 +852,26 @@ export class ImportWorkspaceBuilder { errors.push('label is required') } - // Проверка связи с MasterTag - if (attribute.props.attributeOf === undefined) { - errors.push('attributeOf (MasterTag reference) is required') - } else if (!this.masterTags.has(attribute.props.attributeOf)) { - errors.push(`Invalid MasterTag reference: ${attribute.props.attributeOf}`) - } - - // Проверка типа атрибута - if (attribute.props.type === undefined) { - errors.push('type is required') - } else { - const validTypes = [ // todo: double check valid types - 'TypeString', 'TypeNumber', 'TypeBoolean', 'TypeDate', - 'TypeHyperlink', 'TypeEnum', 'TypeFileSize', 'TypeIntlString', - 'TypeMarkup', 'TypeTimestamp', 'TypeRef', 'TypeCollection' - ] - if (!validTypes.includes(attribute.props.type)) { - errors.push(`Invalid attribute type: ${attribute.props.type}`) - } - } + // todo: fix Проверка связи с MasterTag + // if (attribute.props.attributeOf === undefined) { + // errors.push('attributeOf (MasterTag reference) is required') + // } else if (!this.masterTags.has(attribute.props.attributeOf)) { + // errors.push(`Invalid MasterTag reference: ${attribute.props.attributeOf}`) + // } + + // todo: fix Проверка типа атрибута + // if (attribute.props.type === undefined) { + // errors.push('type is required') + // } else { + // const validTypes = [ // todo: double check valid types + // 'TypeString', 'TypeNumber', 'TypeBoolean', 'TypeDate', + // 'TypeHyperlink', 'TypeEnum', 'TypeFileSize', 'TypeIntlString', + // 'TypeMarkup', 'TypeTimestamp', 'TypeRef', 'TypeCollection' + // ] + // if (!validTypes.includes(attribute.props.type)) { + // errors.push(`Invalid attribute type: ${attribute.props.type}`) + // } + // } // Проверка уникальности имени атрибута в рамках MasterTag const existingAttributes = Array.from(this.masterTagAttributes.values()) @@ -888,11 +888,11 @@ export class ImportWorkspaceBuilder { const errors: string[] = [] // Проверка класса (должен быть ссылкой на MasterTag) - if (card._class === undefined) { - errors.push('class (MasterTag reference) is required') - } else if (!this.masterTags.has(card._class)) { - errors.push(`Invalid MasterTag reference: ${card._class}`) - } + // if (card._class === undefined) { + // errors.push('class (MasterTag reference) is required') + // } else if (!this.masterTags.has(card._class)) { + // errors.push(`Invalid MasterTag reference: ${card._class}`) + // } // Проверка обязательных полей if (!this.validateStringDefined(card.props.title)) { From c28cb8927f0ccbc3f45d06f85eab00c0576b1167 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Sun, 6 Apr 2025 17:36:24 +0700 Subject: [PATCH 22/50] ssuppot parent-child for cards Signed-off-by: Anna Khismatullina --- .../MasterTag/Card1/SubCard1.md | 28 +++++++ packages/importer/src/huly/unified.ts | 73 ++++++++++++++----- 2 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag/Card1/SubCard1.md diff --git a/dev/import-tool/docs/huly/example-workspace/MasterTag/Card1/SubCard1.md b/dev/import-tool/docs/huly/example-workspace/MasterTag/Card1/SubCard1.md new file mode 100644 index 00000000000..dfbec162679 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/MasterTag/Card1/SubCard1.md @@ -0,0 +1,28 @@ +--- +title: Child of Card of MT 7 +tags: + - ../Tag.yaml +aaa: some text +bbb: true +# x: oololo +--- + +# Standard Operating Procedure + +## 1. Purpose +[Describe the purpose of the procedure] + +## 2. Scope +[Define the scope and applicability] + +## 3. Responsibilities +[List key roles and responsibilities] + +## 4. Procedure +[Detail the step-by-step procedure] + +## 5. References +[List related documents] + +## 6. Revision History +[Document revision history] diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index aa40e86d2c4..9da0a5ede81 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -34,7 +34,7 @@ export class UnifiedDocProcessor { currentPath: string, result: UnifiedDocProcessResult, parentMasterTagId?: Ref, - parentAttributesByLabel?: Map>> + parentMasterTagAttrs?: Map>> ): Promise { const entries = fs.readdirSync(currentPath, { withFileTypes: true }) @@ -48,20 +48,20 @@ export class UnifiedDocProcessor { if (yamlConfig?.class === card.class.MasterTag) { const masterTag = await this.createMasterTag(yamlConfig, parentMasterTagId) const masterTagId = masterTag.props._id as Ref - const attributesByLabel = await this.createAttributes(yamlConfig, masterTagId) + const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) // Add master tag and its attributes const docs = result.docs.get(yamlPath) ?? [] docs.push( masterTag, - ...Array.from(attributesByLabel.values()) + ...Array.from(masterTagAttrs.values()) ) result.docs.set(yamlPath, docs) // Recursively process the master tag directory const masterTagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) if (fs.existsSync(masterTagDir) && fs.statSync(masterTagDir).isDirectory()) { - await this.processDirectory(masterTagDir, result, masterTagId, attributesByLabel) + await this.processDirectory(masterTagDir, result, masterTagId, masterTagAttrs) } } else if (yamlConfig?.class === card.class.Tag) { if (parentMasterTagId === undefined) { @@ -80,29 +80,60 @@ export class UnifiedDocProcessor { } } - if (parentMasterTagId === undefined || parentAttributesByLabel === undefined) { + if (parentMasterTagId === undefined || parentMasterTagAttrs === undefined) { // Means we are in the root directory return } // Then process markdown files (cards) - for (const entry of entries) { - if (!entry.isFile() || !entry.name.endsWith('.md')) continue + await this.processCardDirectory(currentPath, result, parentMasterTagId, parentMasterTagAttrs) + } + + private async processCard ( + cardPath: string, + result: UnifiedDocProcessResult, + masterTagId: Ref, + masterTagAttrs: Map>>, + parentCardId?: Ref + ): Promise { + const cardHeader = await readYamlHeader(cardPath) + const card = await this.createCard(cardHeader, cardPath, masterTagId, masterTagAttrs, parentCardId) + + if (card != null) { + if (parentCardId !== undefined) { + card.props.parent = parentCardId + } - const cardPath = path.join(currentPath, entry.name) - const cardHeader = await readYamlHeader(cardPath) - const card = await this.createCard(cardHeader, cardPath, parentMasterTagId, parentAttributesByLabel) + const docs = result.docs.get(cardPath) ?? [] + docs.push(card) + result.docs.set(cardPath, docs) - if (card != null) { - const docs = result.docs.get(cardPath) ?? [] - docs.push(card) - result.docs.set(cardPath, docs) + await this.applyTags(card, cardHeader, cardPath, result) - await this.applyTags(card, cardHeader, cardPath, result) + // Проверяем наличие дочерних карточек + const cardDir = path.join(path.dirname(cardPath), path.basename(cardPath, '.md')) + if (fs.existsSync(cardDir) && fs.statSync(cardDir).isDirectory()) { + await this.processCardDirectory(cardDir, result, masterTagId, masterTagAttrs, card.props._id as Ref) } } } + private async processCardDirectory ( + cardDir: string, + result: UnifiedDocProcessResult, + masterTagId: Ref, + masterTagAttrs: Map>>, + parentCardId?: Ref + ): Promise { + const entries = fs.readdirSync(cardDir, { withFileTypes: true }) + + for (const entry of entries) { + if (!entry.isFile() || !entry.name.endsWith('.md')) continue + const childCardPath = path.join(cardDir, entry.name) + await this.processCard(childCardPath, result, masterTagId, masterTagAttrs, parentCardId) + } + } + private async createMasterTag ( data: Record, parentMasterTagId?: Ref @@ -181,22 +212,24 @@ export class UnifiedDocProcessor { cardHeader: Record, cardPath: string, masterTagId: Ref, - attributesByLabel: Map>> + masterTagAttrs: Map>>, + parentCardId?: Ref ): Promise> { const { _class, title, tags, ...customProperties } = cardHeader const props: Record = { _id: generateId(), space: core.space.Workspace, - title + title, + parent: parentCardId } for (const [key, value] of Object.entries(customProperties)) { - const attributeName = attributesByLabel.get(key)?.props.name // todo: handle tag attributes separately - if (attributeName === undefined) { + const attrName = masterTagAttrs.get(key)?.props.name // todo: handle tag attributes separately + if (attrName === undefined) { throw new Error(`Attribute not found: ${key}`) // todo: keep the error till builder validation } - props[attributeName] = value + props[attrName] = value } return { From dbc864bddf8c3dee7339cf027d43fcd437597c9c Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Sun, 6 Apr 2025 17:53:53 +0700 Subject: [PATCH 23/50] ssuppot parent-child for tags Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/unified.ts | 60 +++++++++++++++++++++------ 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 9da0a5ede81..ee64a2001ca 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -65,18 +65,10 @@ export class UnifiedDocProcessor { } } else if (yamlConfig?.class === card.class.Tag) { if (parentMasterTagId === undefined) { - throw new Error('Tag should be inside master tag folder: ' + currentPath) // todo: confirm this error message + throw new Error('Tag should be inside master tag folder: ' + currentPath) } - const tagId = this.tagPaths.get(yamlPath) ?? generateId() - const tag = await this.createTag(yamlConfig, tagId, parentMasterTagId) - this.tagPaths.set(yamlPath, tagId) - - const attributes = await this.createAttributes(yamlConfig, tagId) - - const docs = result.docs.get(yamlPath) ?? [] - docs.push(tag, ...Array.from(attributes.values())) - result.docs.set(yamlPath, docs) + await this.processTag(yamlPath, yamlConfig, result, parentMasterTagId) } } @@ -156,10 +148,54 @@ export class UnifiedDocProcessor { } } + private async processTag ( + tagPath: string, + tagConfig: Record, + result: UnifiedDocProcessResult, + masterTagId: Ref, + parentTagId?: Ref + ): Promise { + const tagId = this.tagPaths.get(tagPath) ?? generateId() + const tag = await this.createTag(tagConfig, tagId, masterTagId, parentTagId) + this.tagPaths.set(tagPath, tagId) + + const attributes = await this.createAttributes(tagConfig, tagId) + + const docs = result.docs.get(tagPath) ?? [] + docs.push(tag, ...Array.from(attributes.values())) + result.docs.set(tagPath, docs) + + // Обрабатываем дочерние теги + const tagDir = path.join(path.dirname(tagPath), path.basename(tagPath, '.yaml')) + if (fs.existsSync(tagDir) && fs.statSync(tagDir).isDirectory()) { + await this.processTagDirectory(tagDir, result, masterTagId, tagId) + } + } + + private async processTagDirectory ( + tagDir: string, + result: UnifiedDocProcessResult, + parentMasterTagId: Ref, + parentTagId: Ref + ): Promise { + const entries = fs.readdirSync(tagDir, { withFileTypes: true }) + + for (const entry of entries) { + if (!entry.isFile() || !entry.name.endsWith('.yaml')) continue + const childTagPath = path.join(tagDir, entry.name) + const childTagConfig = yaml.load(fs.readFileSync(childTagPath, 'utf8')) as Record + + if (childTagConfig?.class === card.class.Tag) { + await this.processTag(childTagPath, childTagConfig, result, parentMasterTagId, parentTagId) + } + } + } + private async createTag ( data: Record, tagId: Ref, - parentMasterTagId: Ref + masterTagId: Ref, + parentTagId?: Ref ): Promise> { const { class: _class, title } = data if (_class !== card.class.Tag) { @@ -171,7 +207,7 @@ export class UnifiedDocProcessor { props: { _id: tagId, space: core.space.Model, - extends: parentMasterTagId, + extends: parentTagId ?? masterTagId, label: 'embedded:embedded:' + title as IntlString, kind: 2, icon: card.icon.Tag From de48eea8dc2623ee3bac35595978591a6effaeb3 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Sun, 6 Apr 2025 18:53:45 +0700 Subject: [PATCH 24/50] ssuppot system type cards import Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/huly.ts | 6 +++- packages/importer/src/huly/unified.ts | 44 ++++++++++++++++++--------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 7bc6b479d80..789055efcd0 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -502,8 +502,12 @@ export class HulyFormatImporter { case core.class.Attribute: builder.addMasterTagAttributes(path, [doc as UnifiedDoc>]) break + case 'card:types:File': + case 'card:types:Document': + builder.addCard(path, doc as UnifiedDoc) + break default: - if (isId(doc._class)) { + if (isId(doc._class) || (doc._class as string).startsWith('card:type:')) { // todo: fix system cards validation builder.addCard(path, doc as UnifiedDoc) } else { this.logger.error(`Unknown doc class ${String(doc._class)} for path ${path}`) diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index ee64a2001ca..28c1c03c351 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -73,46 +73,59 @@ export class UnifiedDocProcessor { } if (parentMasterTagId === undefined || parentMasterTagAttrs === undefined) { - // Means we are in the root directory - return + await this.processSystemCards(currentPath, result) + } else { + // Then process markdown files (cards) + await this.processCardDirectory(result, currentPath, parentMasterTagId, parentMasterTagAttrs) } + } + + private async processSystemCards ( + currentDir: string, + result: UnifiedDocProcessResult + ): Promise { + const entries = fs.readdirSync(currentDir, { withFileTypes: true }) - // Then process markdown files (cards) - await this.processCardDirectory(currentPath, result, parentMasterTagId, parentMasterTagAttrs) + for (const entry of entries) { + if (!entry.isFile() || !entry.name.endsWith('.md')) continue + const cardPath = path.join(currentDir, entry.name) + const { class: cardType, ...cardProps } = await readYamlHeader(cardPath) + + // todo: supported types + if (cardType === card.types.File || cardType === card.types.Document) { + await this.processCard(result, cardPath, cardProps, cardType, new Map()) // todo: get right master tag attributes + } + } } private async processCard ( - cardPath: string, result: UnifiedDocProcessResult, + cardPath: string, + cardProps: Record, masterTagId: Ref, masterTagAttrs: Map>>, parentCardId?: Ref ): Promise { - const cardHeader = await readYamlHeader(cardPath) - const card = await this.createCard(cardHeader, cardPath, masterTagId, masterTagAttrs, parentCardId) + const card = await this.createCard(cardProps, cardPath, masterTagId, masterTagAttrs, parentCardId) if (card != null) { - if (parentCardId !== undefined) { - card.props.parent = parentCardId - } - const docs = result.docs.get(cardPath) ?? [] docs.push(card) result.docs.set(cardPath, docs) - await this.applyTags(card, cardHeader, cardPath, result) + await this.applyTags(card, cardProps, cardPath, result) // Проверяем наличие дочерних карточек const cardDir = path.join(path.dirname(cardPath), path.basename(cardPath, '.md')) if (fs.existsSync(cardDir) && fs.statSync(cardDir).isDirectory()) { - await this.processCardDirectory(cardDir, result, masterTagId, masterTagAttrs, card.props._id as Ref) + await this.processCardDirectory(result, cardDir, masterTagId, masterTagAttrs, card.props._id as Ref) } } } private async processCardDirectory ( - cardDir: string, result: UnifiedDocProcessResult, + cardDir: string, masterTagId: Ref, masterTagAttrs: Map>>, parentCardId?: Ref @@ -122,7 +135,8 @@ export class UnifiedDocProcessor { for (const entry of entries) { if (!entry.isFile() || !entry.name.endsWith('.md')) continue const childCardPath = path.join(cardDir, entry.name) - await this.processCard(childCardPath, result, masterTagId, masterTagAttrs, parentCardId) + const { class: cardClass, ...cardProps } = await readYamlHeader(childCardPath) + await this.processCard(result, childCardPath, cardProps, masterTagId, masterTagAttrs, parentCardId) } } From aedc9ecd2a1832505021050ce118a5a265667e70 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Sun, 6 Apr 2025 18:58:45 +0700 Subject: [PATCH 25/50] ssuppot system type cards import Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/huly.ts | 8 +++----- packages/importer/src/huly/unified.ts | 7 ++++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 789055efcd0..b34043d9b4c 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -502,12 +502,8 @@ export class HulyFormatImporter { case core.class.Attribute: builder.addMasterTagAttributes(path, [doc as UnifiedDoc>]) break - case 'card:types:File': - case 'card:types:Document': - builder.addCard(path, doc as UnifiedDoc) - break default: - if (isId(doc._class) || (doc._class as string).startsWith('card:type:')) { // todo: fix system cards validation + if (isId(doc._class) || (doc._class as string).startsWith('card:types:')) { // todo: fix system cards validation builder.addCard(path, doc as UnifiedDoc) } else { this.logger.error(`Unknown doc class ${String(doc._class)} for path ${path}`) @@ -516,6 +512,8 @@ export class HulyFormatImporter { } } + // todo: attachments + for (const [path, mixins] of unifiedMixins.entries()) { for (const mixin of mixins) { builder.addTagMixin(path, mixin) diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 28c1c03c351..5f8e9bece33 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -91,10 +91,11 @@ export class UnifiedDocProcessor { const cardPath = path.join(currentDir, entry.name) const { class: cardType, ...cardProps } = await readYamlHeader(cardPath) - // todo: supported types - if (cardType === card.types.File || cardType === card.types.Document) { - await this.processCard(result, cardPath, cardProps, cardType, new Map()) // todo: get right master tag attributes + if (cardType.startsWith('card:types:') === false) { + throw new Error('Unsupported card type: ' + cardType) } + + await this.processCard(result, cardPath, cardProps, cardType, new Map()) // todo: get right master tag attributes } } From f408e5af39f0b596e5fbaee17d0d676f58505e67 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Mon, 7 Apr 2025 12:13:44 +0700 Subject: [PATCH 26/50] renamings Signed-off-by: Anna Khismatullina --- packages/importer/src/importer/importer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/importer/src/importer/importer.ts b/packages/importer/src/importer/importer.ts index c6f8dace045..9cdfb70bfba 100644 --- a/packages/importer/src/importer/importer.ts +++ b/packages/importer/src/importer/importer.ts @@ -1138,11 +1138,11 @@ export class WorkspaceImporter { if (this.workspaceData.unifiedDocs === undefined) return for (const unifiedDoc of this.workspaceData.unifiedDocs) { - await this.importUnifiedDoc(unifiedDoc) + await this.createUnifiedDoc(unifiedDoc) } } - private async importUnifiedDoc (unifiedDoc: UnifiedDoc>): Promise { + private async createUnifiedDoc (unifiedDoc: UnifiedDoc>): Promise { const { _class, props } = unifiedDoc const _id = props._id ?? generateId>() if (unifiedDoc.collabField !== undefined) { @@ -1158,11 +1158,11 @@ export class WorkspaceImporter { if (this.workspaceData.mixins === undefined) return for (const mixin of this.workspaceData.mixins) { - await this.importUnifiedMixin(mixin) + await this.createUnifiedMixin(mixin) } } - private async importUnifiedMixin (mixin: UnifiedMixin, Doc>): Promise { + private async createUnifiedMixin (mixin: UnifiedMixin, Doc>): Promise { const { _class, mixin: mixinClass, props } = mixin const { _id, space, ...data } = props await this.client.createMixin(_id ?? generateId>(), _class, space, mixinClass, data as Data>) From 7a3db5b75429313972d9a93242aceff1d90feab8 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Mon, 7 Apr 2025 12:14:09 +0700 Subject: [PATCH 27/50] test examples Signed-off-by: Anna Khismatullina --- dev/import-tool/docs/huly/example-workspace/File1.md | 9 +++++++++ .../huly/example-workspace/MasterTag/Tag/SubTag1.yaml | 5 +++++ 2 files changed, 14 insertions(+) create mode 100644 dev/import-tool/docs/huly/example-workspace/File1.md create mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag/Tag/SubTag1.yaml diff --git a/dev/import-tool/docs/huly/example-workspace/File1.md b/dev/import-tool/docs/huly/example-workspace/File1.md new file mode 100644 index 00000000000..65d9a8fe45d --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/File1.md @@ -0,0 +1,9 @@ +--- +class: card:types:File +title: File3 +--- + +kljdfnk +;lk;sjkdjfnksj +s'lekjgslkkn + diff --git a/dev/import-tool/docs/huly/example-workspace/MasterTag/Tag/SubTag1.yaml b/dev/import-tool/docs/huly/example-workspace/MasterTag/Tag/SubTag1.yaml new file mode 100644 index 00000000000..8370a595cbe --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/MasterTag/Tag/SubTag1.yaml @@ -0,0 +1,5 @@ +class: card:class:Tag +title: YYY +properties: + - label: y + type: TypeNumber From 7cc37778462f9558c09faac601d76638ef8fed1e Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Mon, 7 Apr 2025 14:33:23 +0700 Subject: [PATCH 28/50] association supported for mastertags Signed-off-by: Anna Khismatullina --- .../huly/example-workspace/MasterCard.yaml | 6 ++ .../huly/example-workspace/MasterSlave11.yaml | 6 ++ .../huly/example-workspace/MasterSlave1N.yaml | 6 ++ .../huly/example-workspace/MasterSlaveNN.yaml | 6 ++ .../huly/example-workspace/SlaveCard.yaml | 9 ++ packages/importer/src/huly/huly.ts | 5 + packages/importer/src/huly/unified.ts | 92 ++++++++++++++----- packages/importer/src/importer/builder.ts | 19 +++- 8 files changed, 124 insertions(+), 25 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/MasterCard.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/MasterSlave11.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/MasterSlave1N.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/MasterSlaveNN.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml diff --git a/dev/import-tool/docs/huly/example-workspace/MasterCard.yaml b/dev/import-tool/docs/huly/example-workspace/MasterCard.yaml new file mode 100644 index 00000000000..05b41e1e8e8 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/MasterCard.yaml @@ -0,0 +1,6 @@ +class: card:class:MasterTag +title: Master Card 4 +properties: + - label: aaa + type: TypeString + defaultValue: "" \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/MasterSlave11.yaml b/dev/import-tool/docs/huly/example-workspace/MasterSlave11.yaml new file mode 100644 index 00000000000..c1ab4b98295 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/MasterSlave11.yaml @@ -0,0 +1,6 @@ +class: core:class:Association +typeA: MasterCard.yaml +typeB: SlaveCard.yaml +nameA: Main Master +nameB: Main Slave +type: 1:1 \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/MasterSlave1N.yaml b/dev/import-tool/docs/huly/example-workspace/MasterSlave1N.yaml new file mode 100644 index 00000000000..4bd646446ac --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/MasterSlave1N.yaml @@ -0,0 +1,6 @@ +class: core:class:Association +typeA: MasterCard.yaml +typeB: SlaveCard.yaml +nameA: Masters +nameB: Slaves +type: 1:N \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/MasterSlaveNN.yaml b/dev/import-tool/docs/huly/example-workspace/MasterSlaveNN.yaml new file mode 100644 index 00000000000..208618c40c0 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/MasterSlaveNN.yaml @@ -0,0 +1,6 @@ +class: core:class:Association +typeA: MasterCard.yaml +typeB: SlaveCard.yaml +nameA: Masters +nameB: Slaves +type: N:N \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml new file mode 100644 index 00000000000..38c6ab55797 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml @@ -0,0 +1,9 @@ +class: card:class:MasterTag +title: Slave Card 4 +properties: + - label: Sex + type: TypeBoolean + defaultValue: false + - label: color + type: TypeString + defaultValue: "black" # todo: is default value supported? \ No newline at end of file diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index b34043d9b4c..2114caabec0 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -24,6 +24,7 @@ import documents, { } from '@hcengineering/controlled-documents' import core, { AccountUuid, + Association, Attribute, type Class, type Doc, @@ -502,6 +503,9 @@ export class HulyFormatImporter { case core.class.Attribute: builder.addMasterTagAttributes(path, [doc as UnifiedDoc>]) break + case core.class.Association: + builder.addAssociation(path, doc as UnifiedDoc) + break default: if (isId(doc._class) || (doc._class as string).startsWith('card:types:')) { // todo: fix system cards validation builder.addCard(path, doc as UnifiedDoc) @@ -565,6 +569,7 @@ export class HulyFormatImporter { break } + case core.class.Association: case card.class.MasterTag: { this.logger.log(`Skipping ${spaceName}: master tag already processed`) break diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 5f8e9bece33..68162c1968a 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -1,6 +1,7 @@ // unified.ts import card, { Card, MasterTag, Tag } from '@hcengineering/card' import core, { + Association, Attribute, Doc, generateId, @@ -20,6 +21,7 @@ export interface UnifiedDocProcessResult { export class UnifiedDocProcessor { private readonly tagPaths = new Map>() + private readonly masterTagPaths = new Map>() async importFromDirectory (directoryPath: string): Promise { const result: UnifiedDocProcessResult = { @@ -45,30 +47,41 @@ export class UnifiedDocProcessor { const yamlPath = path.join(currentPath, entry.name) const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record - if (yamlConfig?.class === card.class.MasterTag) { - const masterTag = await this.createMasterTag(yamlConfig, parentMasterTagId) - const masterTagId = masterTag.props._id as Ref - const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) - - // Add master tag and its attributes - const docs = result.docs.get(yamlPath) ?? [] - docs.push( - masterTag, - ...Array.from(masterTagAttrs.values()) - ) - result.docs.set(yamlPath, docs) - - // Recursively process the master tag directory - const masterTagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) - if (fs.existsSync(masterTagDir) && fs.statSync(masterTagDir).isDirectory()) { - await this.processDirectory(masterTagDir, result, masterTagId, masterTagAttrs) - } - } else if (yamlConfig?.class === card.class.Tag) { - if (parentMasterTagId === undefined) { - throw new Error('Tag should be inside master tag folder: ' + currentPath) + switch (yamlConfig?.class) { + case card.class.MasterTag: { + const masterTagId = this.masterTagPaths.get(yamlPath) ?? generateId() + this.masterTagPaths.set(yamlPath, masterTagId) + const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) + const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) + + const docs = result.docs.get(yamlPath) ?? [] + docs.push( + masterTag, + ...Array.from(masterTagAttrs.values()) + ) + result.docs.set(yamlPath, docs) + + const masterTagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) + if (fs.existsSync(masterTagDir) && fs.statSync(masterTagDir).isDirectory()) { + await this.processDirectory(masterTagDir, result, masterTagId, masterTagAttrs) + } + break } + case card.class.Tag: { + if (parentMasterTagId === undefined) { + throw new Error('Tag should be inside master tag folder: ' + currentPath) + } - await this.processTag(yamlPath, yamlConfig, result, parentMasterTagId) + await this.processTag(yamlPath, yamlConfig, result, parentMasterTagId) + break + } + case core.class.Association: { + const association = await this.createAssociation(currentPath, yamlConfig) + result.docs.set(yamlPath, [association]) + break + } + default: + throw new Error('Unsupported class: ' + yamlConfig?.class) // todo: handle default case just convert to UnifiedDoc } } @@ -143,6 +156,7 @@ export class UnifiedDocProcessor { private async createMasterTag ( data: Record, + masterTagId: Ref, parentMasterTagId?: Ref ): Promise> { const { class: _class, title } = data @@ -153,7 +167,7 @@ export class UnifiedDocProcessor { return { _class: card.class.MasterTag, props: { - _id: generateId(), + _id: masterTagId, space: core.space.Model, extends: parentMasterTagId ?? card.class.Card, label: 'embedded:embedded:' + title as IntlString, // todo: check if it's correct @@ -325,4 +339,36 @@ export class UnifiedDocProcessor { result.mixins.set(cardPath, mixins) } } + + private async createAssociation ( + currentPath: string, + yamlConfig: Record + ): Promise> { + const { class: _class, typeA, typeB, ...otherProps } = yamlConfig + + const typeAPath = path.resolve(currentPath, typeA) + let typeAId = this.masterTagPaths.get(typeAPath) + if (typeAId === undefined) { + typeAId = generateId() + this.masterTagPaths.set(typeAPath, typeAId) + } + + const typeBPath = path.resolve(currentPath, typeB) + let typeBId = this.masterTagPaths.get(typeBPath) + if (typeBId === undefined) { + typeBId = generateId() + this.masterTagPaths.set(typeBPath, typeBId) + } + + return { + _class: core.class.Association, + props: { + _id: generateId(), + space: core.space.Model, + classA: typeAId, + classB: typeBId, + ...otherProps + } as unknown as Props + } + } } diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 8a901c4cfb9..ab01d815ac3 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -14,7 +14,7 @@ // import card, { Card, MasterTag, Tag } from '@hcengineering/card' import documents, { ControlledDocument, DocumentState } from '@hcengineering/controlled-documents' -import core, { Attribute, Doc, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' +import core, { Association, Attribute, Doc, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' import document from '@hcengineering/document' import tracker, { IssuePriority, type IssueStatus } from '@hcengineering/tracker' import { @@ -62,6 +62,7 @@ export class ImportWorkspaceBuilder { private readonly masterTagAttributes = new Map>>() private readonly tags = new Map>() private readonly cards = new Map>() + private readonly associations = new Map>() private readonly mixins = new Map>() private readonly projectTypes = new Map() @@ -252,6 +253,11 @@ export class ImportWorkspaceBuilder { return this } + addAssociation (path: string, association: UnifiedDoc): this { + this.validateAndAdd('association', path, association, (a) => this.validateAssociation(a), this.associations, path) + return this + } + addTagMixin (path: string, mixin: UnifiedMixin): this { this.validateAndAdd('tagMixin', path, mixin, (m) => this.validateTagMixin(m), this.mixins, path) return this @@ -331,7 +337,8 @@ export class ImportWorkspaceBuilder { ...Array.from(this.masterTags.values()), ...Array.from(this.masterTagAttributes.values()), ...Array.from(this.tags.values()), - ...Array.from(this.cards.values()) + ...Array.from(this.cards.values()), + ...Array.from(this.associations.values()) ], mixins: Array.from(this.mixins.values()), attachments: [] @@ -884,6 +891,14 @@ export class ImportWorkspaceBuilder { return errors } + private validateAssociation (association: UnifiedDoc): string[] { + const errors: string[] = [] + + // todo: validate association + + return errors + } + private validateCard (card: UnifiedDoc): string[] { const errors: string[] = [] From 52fb46499fc5115228c4bff6b6857f6e05a7fbd2 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Mon, 7 Apr 2025 14:52:12 +0700 Subject: [PATCH 29/50] association supported bw tags Signed-off-by: Anna Khismatullina --- .../huly/example-workspace/FamiliarHelpers.yaml | 6 ++++++ .../example-workspace/SlaveCard/FamiliarTag.yaml | 5 +++++ .../huly/example-workspace/SlaveCard/MinionTag.yaml | 5 +++++ .../docs/huly/example-workspace/SlaveMinion.yaml | 6 ++++++ packages/importer/src/huly/unified.ts | 13 ++++++------- packages/importer/src/importer/unified.ts | 0 6 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/FamiliarHelpers.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveCard/FamiliarTag.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveCard/MinionTag.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveMinion.yaml delete mode 100644 packages/importer/src/importer/unified.ts diff --git a/dev/import-tool/docs/huly/example-workspace/FamiliarHelpers.yaml b/dev/import-tool/docs/huly/example-workspace/FamiliarHelpers.yaml new file mode 100644 index 00000000000..5ebdc291d4c --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/FamiliarHelpers.yaml @@ -0,0 +1,6 @@ +class: core:class:Association +typeA: ./SlaveCard/FamiliarTag.yaml +typeB: ./SlaveCard/MinionTag.yaml +nameA: Familiar +nameB: Helpers +type: 1:N diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/FamiliarTag.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard/FamiliarTag.yaml new file mode 100644 index 00000000000..83945544978 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/FamiliarTag.yaml @@ -0,0 +1,5 @@ +class: card:class:Tag +title: Familiar +properties: + - label: x + type: TypeString diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/MinionTag.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard/MinionTag.yaml new file mode 100644 index 00000000000..d5104ca8c7d --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/MinionTag.yaml @@ -0,0 +1,5 @@ +class: card:class:Tag +title: Minion +properties: + - label: x + type: TypeString diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveMinion.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveMinion.yaml new file mode 100644 index 00000000000..9db133db8ca --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/SlaveMinion.yaml @@ -0,0 +1,6 @@ +class: core:class:Association +typeA: ./SlaveCard.yaml +typeB: ./SlaveCard/MinionTag.yaml +nameA: Mother +nameB: Children +type: 1:N diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 68162c1968a..ab651dba3e4 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -21,7 +21,6 @@ export interface UnifiedDocProcessResult { export class UnifiedDocProcessor { private readonly tagPaths = new Map>() - private readonly masterTagPaths = new Map>() async importFromDirectory (directoryPath: string): Promise { const result: UnifiedDocProcessResult = { @@ -49,8 +48,8 @@ export class UnifiedDocProcessor { switch (yamlConfig?.class) { case card.class.MasterTag: { - const masterTagId = this.masterTagPaths.get(yamlPath) ?? generateId() - this.masterTagPaths.set(yamlPath, masterTagId) + const masterTagId = this.tagPaths.get(yamlPath) ?? generateId() + this.tagPaths.set(yamlPath, masterTagId) const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) @@ -347,17 +346,17 @@ export class UnifiedDocProcessor { const { class: _class, typeA, typeB, ...otherProps } = yamlConfig const typeAPath = path.resolve(currentPath, typeA) - let typeAId = this.masterTagPaths.get(typeAPath) + let typeAId = this.tagPaths.get(typeAPath) if (typeAId === undefined) { typeAId = generateId() - this.masterTagPaths.set(typeAPath, typeAId) + this.tagPaths.set(typeAPath, typeAId) } const typeBPath = path.resolve(currentPath, typeB) - let typeBId = this.masterTagPaths.get(typeBPath) + let typeBId = this.tagPaths.get(typeBPath) if (typeBId === undefined) { typeBId = generateId() - this.masterTagPaths.set(typeBPath, typeBId) + this.tagPaths.set(typeBPath, typeBId) } return { diff --git a/packages/importer/src/importer/unified.ts b/packages/importer/src/importer/unified.ts deleted file mode 100644 index e69de29bb2d..00000000000 From 3fa4cdfa6f1bd5a3049b335a47a08ce71a07e164 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Mon, 7 Apr 2025 17:17:11 +0700 Subject: [PATCH 30/50] path resolver Signed-off-by: Anna Khismatullina --- .../huly/example-workspace/MasterCard.yaml | 2 +- .../huly/example-workspace/SlaveCard.yaml | 4 +- .../huly/example-workspace/SlaveCard/Frodo.md | 6 +++ .../example-workspace/SlaveCard/Gendalf.md | 6 +++ .../huly/example-workspace/SlaveCard/Queen.md | 10 ++++ .../example-workspace/SlaveCard/Worker1.md | 6 +++ .../example-workspace/SlaveCard/Worker2.md | 6 +++ packages/importer/src/huly/resolver.ts | 20 ++++++++ packages/importer/src/huly/unified.ts | 49 +++++++------------ 9 files changed, 75 insertions(+), 34 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md create mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md create mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveCard/Queen.md create mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker1.md create mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker2.md create mode 100644 packages/importer/src/huly/resolver.ts diff --git a/dev/import-tool/docs/huly/example-workspace/MasterCard.yaml b/dev/import-tool/docs/huly/example-workspace/MasterCard.yaml index 05b41e1e8e8..b097d7b0a86 100644 --- a/dev/import-tool/docs/huly/example-workspace/MasterCard.yaml +++ b/dev/import-tool/docs/huly/example-workspace/MasterCard.yaml @@ -1,5 +1,5 @@ class: card:class:MasterTag -title: Master Card 4 +title: Master Card 5 properties: - label: aaa type: TypeString diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml index 38c6ab55797..5a44be768d4 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml @@ -1,9 +1,9 @@ class: card:class:MasterTag -title: Slave Card 4 +title: Slave Card 6 properties: - label: Sex type: TypeBoolean defaultValue: false - label: color type: TypeString - defaultValue: "black" # todo: is default value supported? \ No newline at end of file + defaultValue: "black" # todo: is default value supported? diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md new file mode 100644 index 00000000000..212ec98039e --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md @@ -0,0 +1,6 @@ +--- +title: "Frodo Baggins" +familiar: "./Minion.yaml" +--- + +A brave hobbit guided by Gandalf. diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md new file mode 100644 index 00000000000..85445df225a --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md @@ -0,0 +1,6 @@ +--- +title: "Gandalf the Grey" +familiar: "./Familiar.yaml" +--- + +A wise and powerful wizard who guides others. \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Queen.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Queen.md new file mode 100644 index 00000000000..b0a2863d800 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Queen.md @@ -0,0 +1,10 @@ +--- +title: "Queen Ant" +children: [ + "./Worker1.md", + "./Worker2.md", + "./Worker3.md" +] +--- + +The colony's queen responsible for reproduction. \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker1.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker1.md new file mode 100644 index 00000000000..214414b72b8 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker1.md @@ -0,0 +1,6 @@ +--- +title: "Worker Ant 1" +mother: "./Mother.md" +--- + +A diligent worker ant. \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker2.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker2.md new file mode 100644 index 00000000000..437b564fe72 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker2.md @@ -0,0 +1,6 @@ +--- +title: "Worker Ant 2" +mother: "./Mother.md" +--- + +Another hard-working ant. \ No newline at end of file diff --git a/packages/importer/src/huly/resolver.ts b/packages/importer/src/huly/resolver.ts new file mode 100644 index 00000000000..baac9346dc4 --- /dev/null +++ b/packages/importer/src/huly/resolver.ts @@ -0,0 +1,20 @@ +import { Doc, generateId, Ref } from '@hcengineering/core' +import path from 'path' + +export class PathToRefResolver { + private readonly pathToRef = new Map>() + + public getIdByFullPath (path: string): Ref { + let id = this.pathToRef.get(path) + if (id === undefined) { + id = generateId() + this.pathToRef.set(path, id) + } + return id + } + + public getIdByRelativePath (currentPath: string, relativePath: string): Ref { + const fullPath = path.resolve(currentPath, relativePath) + return this.getIdByFullPath(fullPath) + } +} diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index ab651dba3e4..b2c00304743 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -13,6 +13,7 @@ import * as path from 'path' import { IntlString } from '../../../platform/types' import { Props, UnifiedDoc, UnifiedMixin } from '../types' import { readMarkdownContent, readYamlHeader } from './parsing' +import { PathToRefResolver } from './resolver' export interface UnifiedDocProcessResult { docs: Map>> @@ -20,7 +21,7 @@ export interface UnifiedDocProcessResult { } export class UnifiedDocProcessor { - private readonly tagPaths = new Map>() + private readonly pathToRefResolver = new PathToRefResolver() async importFromDirectory (directoryPath: string): Promise { const result: UnifiedDocProcessResult = { @@ -43,13 +44,12 @@ export class UnifiedDocProcessor { for (const entry of entries) { if (!entry.isFile() || !entry.name.endsWith('.yaml')) continue // todo: filter entries by extension - const yamlPath = path.join(currentPath, entry.name) + const yamlPath = path.resolve(currentPath, entry.name) const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record switch (yamlConfig?.class) { case card.class.MasterTag: { - const masterTagId = this.tagPaths.get(yamlPath) ?? generateId() - this.tagPaths.set(yamlPath, masterTagId) + const masterTagId = this.pathToRefResolver.getIdByFullPath(yamlPath) as Ref const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) @@ -75,7 +75,7 @@ export class UnifiedDocProcessor { break } case core.class.Association: { - const association = await this.createAssociation(currentPath, yamlConfig) + const association = await this.createAssociation(yamlPath, yamlConfig) result.docs.set(yamlPath, [association]) break } @@ -183,9 +183,8 @@ export class UnifiedDocProcessor { masterTagId: Ref, parentTagId?: Ref ): Promise { - const tagId = this.tagPaths.get(tagPath) ?? generateId() + const tagId = this.pathToRefResolver.getIdByFullPath(tagPath) as Ref const tag = await this.createTag(tagConfig, tagId, masterTagId, parentTagId) - this.tagPaths.set(tagPath, tagId) const attributes = await this.createAttributes(tagConfig, tagId) @@ -281,8 +280,9 @@ export class UnifiedDocProcessor { ): Promise> { const { _class, title, tags, ...customProperties } = cardHeader + const cardId = this.pathToRefResolver.getIdByFullPath(cardPath) as Ref const props: Record = { - _id: generateId(), + _id: cardId, space: core.space.Workspace, title, parent: parentCardId @@ -290,9 +290,9 @@ export class UnifiedDocProcessor { for (const [key, value] of Object.entries(customProperties)) { const attrName = masterTagAttrs.get(key)?.props.name // todo: handle tag attributes separately - if (attrName === undefined) { - throw new Error(`Attribute not found: ${key}`) // todo: keep the error till builder validation - } + // if (attrName === undefined) { + // throw new Error(`Attribute not found: ${key}`) // todo: keep the error till builder validation + // } props[attrName] = value } @@ -316,11 +316,7 @@ export class UnifiedDocProcessor { for (const tagPath of cardHeader.tags) { const cardDir = path.dirname(cardPath) const fullTagPath = path.resolve(cardDir, tagPath) - let tagId = this.tagPaths.get(fullTagPath) - if (tagId === undefined) { - tagId = generateId() - this.tagPaths.set(fullTagPath, tagId) - } + const tagId = this.pathToRefResolver.getIdByFullPath(fullTagPath) as Ref const mixin: UnifiedMixin = { _class: card._class, @@ -340,29 +336,20 @@ export class UnifiedDocProcessor { } private async createAssociation ( - currentPath: string, + yamlPath: string, yamlConfig: Record ): Promise> { const { class: _class, typeA, typeB, ...otherProps } = yamlConfig - const typeAPath = path.resolve(currentPath, typeA) - let typeAId = this.tagPaths.get(typeAPath) - if (typeAId === undefined) { - typeAId = generateId() - this.tagPaths.set(typeAPath, typeAId) - } - - const typeBPath = path.resolve(currentPath, typeB) - let typeBId = this.tagPaths.get(typeBPath) - if (typeBId === undefined) { - typeBId = generateId() - this.tagPaths.set(typeBPath, typeBId) - } + const currentPath = path.dirname(yamlPath) + const typeAId = this.pathToRefResolver.getIdByRelativePath(currentPath, typeA) as Ref + const typeBId = this.pathToRefResolver.getIdByRelativePath(currentPath, typeB) as Ref + const associationId = this.pathToRefResolver.getIdByFullPath(yamlPath) as Ref return { _class: core.class.Association, props: { - _id: generateId(), + _id: associationId, space: core.space.Model, classA: typeAId, classB: typeBId, From 7e00613a6e2aa56d55aed1f1ef65caeb50b4a5ae Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Mon, 7 Apr 2025 22:19:06 +0700 Subject: [PATCH 31/50] init support for relations Signed-off-by: Anna Khismatullina --- .../example-workspace/MasterCard/master.md | 5 + .../huly/example-workspace/SlaveCard/slave.md | 6 + packages/importer/src/huly/huly.ts | 3 + packages/importer/src/huly/metadata.ts | 68 ++++++++++ packages/importer/src/huly/resolver.ts | 20 --- packages/importer/src/huly/unified.ts | 118 ++++++++++++------ packages/importer/src/importer/builder.ts | 19 ++- 7 files changed, 182 insertions(+), 57 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/MasterCard/master.md create mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md create mode 100644 packages/importer/src/huly/metadata.ts delete mode 100644 packages/importer/src/huly/resolver.ts diff --git a/dev/import-tool/docs/huly/example-workspace/MasterCard/master.md b/dev/import-tool/docs/huly/example-workspace/MasterCard/master.md new file mode 100644 index 00000000000..4d1940b884a --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/MasterCard/master.md @@ -0,0 +1,5 @@ +--- +title : simple master +mainSlave: ../SlaveCard/slave.md +--- +ljhhdclkfjsdwd \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md new file mode 100644 index 00000000000..6a845474cd0 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md @@ -0,0 +1,6 @@ +--- +title: simple slave +sex: true +mainMaster: ../MasterCard/master.md +--- +nksdnflksj diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 2114caabec0..19c4bd42f8e 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -506,6 +506,9 @@ export class HulyFormatImporter { case core.class.Association: builder.addAssociation(path, doc as UnifiedDoc) break + case core.class.Relation: + builder.addRelation(path, doc as UnifiedDoc) + break default: if (isId(doc._class) || (doc._class as string).startsWith('card:types:')) { // todo: fix system cards validation builder.addCard(path, doc as UnifiedDoc) diff --git a/packages/importer/src/huly/metadata.ts b/packages/importer/src/huly/metadata.ts new file mode 100644 index 00000000000..641a4b30ac0 --- /dev/null +++ b/packages/importer/src/huly/metadata.ts @@ -0,0 +1,68 @@ +import { Tag } from '@hcengineering/card' +import { Association, Attribute, Doc, generateId, Ref } from '@hcengineering/core' +import path from 'path' +import { UnifiedDoc } from '../types' + +export interface RelationMetadata { + association: Ref + field: 'docA' | 'docB' + type: '1:1' | '1:N' | 'N:N' +} +export type MapAttributeToUnifiedDoc = Map>> +export type MapNameToIsMany = Map // todo: rename +export interface TagMetadata { + _id: string + attributes: MapAttributeToUnifiedDoc // title -> attribute id + associations: MapNameToIsMany // nameB -> isMany +} + +export class MetadataStorage { + private readonly pathToRef = new Map>() + private readonly pathToMetadata = new Map() + + public getIdByFullPath (path: string): Ref { + let id = this.pathToRef.get(path) + if (id === undefined) { + id = generateId() + this.pathToRef.set(path, id) + } + return id + } + + public getIdByRelativePath (currentPath: string, relativePath: string): Ref { + const fullPath = path.resolve(currentPath, relativePath) + return this.getIdByFullPath(fullPath) + } + + public hasMetadata (path: string): boolean { + return this.pathToMetadata.has(path) + } + + public getAttributes (path: string): MapAttributeToUnifiedDoc { + return this.pathToMetadata.get(path)?.attributes ?? new Map() + } + + public getAssociations (path: string): MapNameToIsMany { + return this.pathToMetadata.get(path)?.associations ?? new Map() + } + + public setAttributes (path: string, attributes: MapAttributeToUnifiedDoc): void { + const metadata = this.pathToMetadata.get(path) ?? { + _id: this.getIdByFullPath(path), + attributes: new Map(), + associations: new Map() + } + metadata.attributes = attributes + this.pathToMetadata.set(path, metadata) + } + + public addAssociation (tagPath: string, propName: string, relationMetadata: RelationMetadata): void { + const metadata = this.pathToMetadata.get(tagPath) ?? { + _id: this.getIdByFullPath(tagPath), + attributes: new Map(), + associations: new Map() + } + metadata.associations.set(propName, relationMetadata) + this.pathToMetadata.set(tagPath, metadata) + } +} diff --git a/packages/importer/src/huly/resolver.ts b/packages/importer/src/huly/resolver.ts deleted file mode 100644 index baac9346dc4..00000000000 --- a/packages/importer/src/huly/resolver.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Doc, generateId, Ref } from '@hcengineering/core' -import path from 'path' - -export class PathToRefResolver { - private readonly pathToRef = new Map>() - - public getIdByFullPath (path: string): Ref { - let id = this.pathToRef.get(path) - if (id === undefined) { - id = generateId() - this.pathToRef.set(path, id) - } - return id - } - - public getIdByRelativePath (currentPath: string, relativePath: string): Ref { - const fullPath = path.resolve(currentPath, relativePath) - return this.getIdByFullPath(fullPath) - } -} diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index b2c00304743..162d8a13c7f 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -5,7 +5,8 @@ import core, { Attribute, Doc, generateId, - Ref + Ref, + Relation } from '@hcengineering/core' import * as fs from 'fs' import * as yaml from 'js-yaml' @@ -13,7 +14,7 @@ import * as path from 'path' import { IntlString } from '../../../platform/types' import { Props, UnifiedDoc, UnifiedMixin } from '../types' import { readMarkdownContent, readYamlHeader } from './parsing' -import { PathToRefResolver } from './resolver' +import { MetadataStorage } from './metadata' export interface UnifiedDocProcessResult { docs: Map>> @@ -21,7 +22,7 @@ export interface UnifiedDocProcessResult { } export class UnifiedDocProcessor { - private readonly pathToRefResolver = new PathToRefResolver() + private readonly metadataStorage = new MetadataStorage() async importFromDirectory (directoryPath: string): Promise { const result: UnifiedDocProcessResult = { @@ -49,9 +50,11 @@ export class UnifiedDocProcessor { switch (yamlConfig?.class) { case card.class.MasterTag: { - const masterTagId = this.pathToRefResolver.getIdByFullPath(yamlPath) as Ref + const masterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) + const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) + this.metadataStorage.setAttributes(yamlPath, masterTagAttrs) const docs = result.docs.get(yamlPath) ?? [] docs.push( @@ -87,8 +90,8 @@ export class UnifiedDocProcessor { if (parentMasterTagId === undefined || parentMasterTagAttrs === undefined) { await this.processSystemCards(currentPath, result) } else { - // Then process markdown files (cards) - await this.processCardDirectory(result, currentPath, parentMasterTagId, parentMasterTagAttrs) + // await this.processCardDirectory(result, currentPath, parentMasterTagId, parentMasterTagAttrs) // todo: handle parent master tag attrs + await this.processCardDirectory(result, currentPath, parentMasterTagId) } } @@ -107,7 +110,7 @@ export class UnifiedDocProcessor { throw new Error('Unsupported card type: ' + cardType) } - await this.processCard(result, cardPath, cardProps, cardType, new Map()) // todo: get right master tag attributes + await this.processCard(result, cardPath, cardProps, cardType) // todo: get right master tag attributes } } @@ -116,22 +119,22 @@ export class UnifiedDocProcessor { cardPath: string, cardProps: Record, masterTagId: Ref, - masterTagAttrs: Map>>, parentCardId?: Ref ): Promise { - const card = await this.createCard(cardProps, cardPath, masterTagId, masterTagAttrs, parentCardId) + const cardWithRelations = await this.createCard(cardProps, cardPath, masterTagId, parentCardId) - if (card != null) { + if (cardWithRelations.length > 0) { const docs = result.docs.get(cardPath) ?? [] - docs.push(card) + docs.push(...cardWithRelations) result.docs.set(cardPath, docs) + const card = cardWithRelations[0] as UnifiedDoc await this.applyTags(card, cardProps, cardPath, result) // Проверяем наличие дочерних карточек const cardDir = path.join(path.dirname(cardPath), path.basename(cardPath, '.md')) if (fs.existsSync(cardDir) && fs.statSync(cardDir).isDirectory()) { - await this.processCardDirectory(result, cardDir, masterTagId, masterTagAttrs, card.props._id as Ref) + await this.processCardDirectory(result, cardDir, masterTagId, card.props._id as Ref) } } } @@ -140,16 +143,15 @@ export class UnifiedDocProcessor { result: UnifiedDocProcessResult, cardDir: string, masterTagId: Ref, - masterTagAttrs: Map>>, parentCardId?: Ref ): Promise { const entries = fs.readdirSync(cardDir, { withFileTypes: true }) + .filter(entry => entry.isFile() && entry.name.endsWith('.md')) for (const entry of entries) { - if (!entry.isFile() || !entry.name.endsWith('.md')) continue const childCardPath = path.join(cardDir, entry.name) const { class: cardClass, ...cardProps } = await readYamlHeader(childCardPath) - await this.processCard(result, childCardPath, cardProps, masterTagId, masterTagAttrs, parentCardId) + await this.processCard(result, childCardPath, cardProps, masterTagId, parentCardId) } } @@ -183,7 +185,7 @@ export class UnifiedDocProcessor { masterTagId: Ref, parentTagId?: Ref ): Promise { - const tagId = this.pathToRefResolver.getIdByFullPath(tagPath) as Ref + const tagId = this.metadataStorage.getIdByFullPath(tagPath) as Ref const tag = await this.createTag(tagConfig, tagId, masterTagId, parentTagId) const attributes = await this.createAttributes(tagConfig, tagId) @@ -275,12 +277,11 @@ export class UnifiedDocProcessor { cardHeader: Record, cardPath: string, masterTagId: Ref, - masterTagAttrs: Map>>, parentCardId?: Ref - ): Promise> { + ): Promise[]> { const { _class, title, tags, ...customProperties } = cardHeader - const cardId = this.pathToRefResolver.getIdByFullPath(cardPath) as Ref + const cardId = this.metadataStorage.getIdByFullPath(cardPath) as Ref const props: Record = { _id: cardId, space: core.space.Workspace, @@ -288,20 +289,49 @@ export class UnifiedDocProcessor { parent: parentCardId } + const masterTagPath = path.dirname(cardPath) + '.yaml' // todo: fix master tag path + const masterTagAttrs = this.metadataStorage.getAttributes(masterTagPath) + const masterTagRelations = this.metadataStorage.getAssociations(masterTagPath) + // todo: handle tag attributes separately + + const relations: UnifiedDoc[] = [] for (const [key, value] of Object.entries(customProperties)) { - const attrName = masterTagAttrs.get(key)?.props.name // todo: handle tag attributes separately - // if (attrName === undefined) { + const propName = masterTagAttrs.get(key)?.props.name // todo: handle tag attributes separately + // if (propName === undefined) { // throw new Error(`Attribute not found: ${key}`) // todo: keep the error till builder validation // } - props[attrName] = value + if (masterTagRelations.has(key)) { + const metadata = masterTagRelations.get(key) + if (metadata === undefined) { + throw new Error(`Association not found: ${key}, ${cardPath}`) // todo: keep the error till builder validation + } + const otherCardField = metadata.field === 'docA' ? 'docB' : 'docA' + const otherCardId = this.metadataStorage.getIdByFullPath(path.resolve(path.dirname(cardPath), value)) as Ref + const relation: UnifiedDoc = { + _class: core.class.Relation, + props: { + _id: generateId(), + space: core.space.Model, + [metadata.field]: cardId, + [otherCardField]: otherCardId, + association: metadata.association + } as unknown as Props + } + relations.push(relation) + } else { + props[propName] = value + } } - return { - _class: masterTagId, - collabField: 'content', - contentProvider: () => readMarkdownContent(cardPath), - props: props as Props // todo: what is the correct props type? - } + return [ + { + _class: masterTagId, + collabField: 'content', + contentProvider: () => readMarkdownContent(cardPath), + props: props as Props // todo: what is the correct props type? + }, + ...relations + ] } private async applyTags ( @@ -316,7 +346,7 @@ export class UnifiedDocProcessor { for (const tagPath of cardHeader.tags) { const cardDir = path.dirname(cardPath) const fullTagPath = path.resolve(cardDir, tagPath) - const tagId = this.pathToRefResolver.getIdByFullPath(fullTagPath) as Ref + const tagId = this.metadataStorage.getIdByFullPath(fullTagPath) as Ref const mixin: UnifiedMixin = { _class: card._class, @@ -339,21 +369,39 @@ export class UnifiedDocProcessor { yamlPath: string, yamlConfig: Record ): Promise> { - const { class: _class, typeA, typeB, ...otherProps } = yamlConfig + console.log('createAssociation', yamlPath) + const { class: _class, typeA, typeB, type, nameA, nameB } = yamlConfig const currentPath = path.dirname(yamlPath) - const typeAId = this.pathToRefResolver.getIdByRelativePath(currentPath, typeA) as Ref - const typeBId = this.pathToRefResolver.getIdByRelativePath(currentPath, typeB) as Ref + const associationId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + + const typeAPath = path.resolve(currentPath, typeA) + this.metadataStorage.addAssociation(typeAPath, nameB, { + association: associationId, + field: 'docA', + type + }) + + const typeBPath = path.resolve(currentPath, typeB) + this.metadataStorage.addAssociation(typeBPath, nameA, { + association: associationId, + field: 'docB', + type + }) + + const typeAId = this.metadataStorage.getIdByFullPath(typeAPath) as Ref + const typeBId = this.metadataStorage.getIdByFullPath(typeBPath) as Ref - const associationId = this.pathToRefResolver.getIdByFullPath(yamlPath) as Ref return { - _class: core.class.Association, + _class, props: { _id: associationId, space: core.space.Model, classA: typeAId, classB: typeBId, - ...otherProps + nameA, + nameB, + type } as unknown as Props } } diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index ab01d815ac3..85421930d60 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -14,7 +14,7 @@ // import card, { Card, MasterTag, Tag } from '@hcengineering/card' import documents, { ControlledDocument, DocumentState } from '@hcengineering/controlled-documents' -import core, { Association, Attribute, Doc, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' +import core, { Association, Attribute, Doc, Relation, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' import document from '@hcengineering/document' import tracker, { IssuePriority, type IssueStatus } from '@hcengineering/tracker' import { @@ -63,6 +63,7 @@ export class ImportWorkspaceBuilder { private readonly tags = new Map>() private readonly cards = new Map>() private readonly associations = new Map>() + private readonly relations = new Map>() private readonly mixins = new Map>() private readonly projectTypes = new Map() @@ -258,6 +259,11 @@ export class ImportWorkspaceBuilder { return this } + addRelation (path: string, relation: UnifiedDoc): this { + this.validateAndAdd('relation', path, relation, (r) => this.validateRelation(r), this.relations, path) + return this + } + addTagMixin (path: string, mixin: UnifiedMixin): this { this.validateAndAdd('tagMixin', path, mixin, (m) => this.validateTagMixin(m), this.mixins, path) return this @@ -338,7 +344,8 @@ export class ImportWorkspaceBuilder { ...Array.from(this.masterTagAttributes.values()), ...Array.from(this.tags.values()), ...Array.from(this.cards.values()), - ...Array.from(this.associations.values()) + ...Array.from(this.associations.values()), + ...Array.from(this.relations.values()) ], mixins: Array.from(this.mixins.values()), attachments: [] @@ -899,6 +906,14 @@ export class ImportWorkspaceBuilder { return errors } + private validateRelation (relation: UnifiedDoc): string[] { + const errors: string[] = [] + + // todo: validate relation + + return errors + } + private validateCard (card: UnifiedDoc): string[] { const errors: string[] = [] From f090d352d57cece45d5ad2beeeecc7e43905b5f3 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Mon, 7 Apr 2025 22:40:29 +0700 Subject: [PATCH 32/50] init support for relations - two runs Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/huly.ts | 1 + packages/importer/src/huly/unified.ts | 98 ++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 19c4bd42f8e..0fd43a992da 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -32,6 +32,7 @@ import core, { isId, PersonId, type Ref, + Relation, SocialIdType, type Space, type TxOperations diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 162d8a13c7f..5e3b472c403 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -29,10 +29,106 @@ export class UnifiedDocProcessor { docs: new Map(), mixins: new Map() } - await this.processDirectory(directoryPath, result) + // Первый проход - собираем метаданные + await this.processMetadata(directoryPath, result) + + // Второй проход - обрабатываем карточки + await this.processCards(directoryPath, result) + return result } + private async processMetadata ( + currentPath: string, + result: UnifiedDocProcessResult, + parentMasterTagId?: Ref + ): Promise { + const entries = fs.readdirSync(currentPath, { withFileTypes: true }) + + // Обрабатываем только YAML файлы + for (const entry of entries) { + if (!entry.isFile() || !entry.name.endsWith('.yaml')) continue + + const yamlPath = path.resolve(currentPath, entry.name) + const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record + + switch (yamlConfig?.class) { + case card.class.MasterTag: { + const masterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) + const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) + + this.metadataStorage.setAttributes(yamlPath, masterTagAttrs) + result.docs.set(yamlPath, [masterTag, ...Array.from(masterTagAttrs.values())]) + + // Рекурсивно обрабатываем поддиректорию + const masterTagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) + if (fs.existsSync(masterTagDir) && fs.statSync(masterTagDir).isDirectory()) { + await this.processMetadata(masterTagDir, result, masterTagId) + } + break + } + case card.class.Tag: { + if (parentMasterTagId === undefined) { + throw new Error('Tag should be inside master tag folder: ' + currentPath) + } + await this.processTag(yamlPath, yamlConfig, result, parentMasterTagId) + break + } + case core.class.Association: { + const association = await this.createAssociation(yamlPath, yamlConfig) + result.docs.set(yamlPath, [association]) + break + } + default: + throw new Error('Unsupported class: ' + yamlConfig?.class) // todo: handle default case just convert to UnifiedDoc + } + } + + // Рекурсивно обрабатываем поддиректории + for (const entry of entries) { + if (!entry.isDirectory()) continue + const dirPath = path.join(currentPath, entry.name) + await this.processMetadata(dirPath, result, parentMasterTagId) + } + } + + private async processCards ( + currentPath: string, + result: UnifiedDocProcessResult, + currentMasterTagId?: Ref + ): Promise { + const entries = fs.readdirSync(currentPath, { withFileTypes: true }) + + // Проверяем, есть ли YAML файл MasterTag'а для текущей директории + const yamlPath = currentPath + '.yaml' + if (fs.existsSync(yamlPath)) { + const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record + if (yamlConfig?.class === card.class.MasterTag) { + currentMasterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + } + } + + // Обрабатываем MD файлы с учетом текущего MasterTag'а + for (const entry of entries) { + if (entry.isFile() && entry.name.endsWith('.md')) { + const cardPath = path.join(currentPath, entry.name) + const { class: cardType, ...cardProps } = await readYamlHeader(cardPath) + + if (currentMasterTagId !== undefined) { + await this.processCard(result, cardPath, cardProps, currentMasterTagId) + } + } + } + + // Рекурсивно обрабатываем поддиректории с передачей текущего MasterTag'а + for (const entry of entries) { + if (!entry.isDirectory()) continue + const dirPath = path.join(currentPath, entry.name) + await this.processCards(dirPath, result, currentMasterTagId) + } + } + private async processDirectory ( currentPath: string, result: UnifiedDocProcessResult, From cfcad5f0f25fd16cee1f4fc1ac123953e21979b9 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Mon, 7 Apr 2025 23:21:16 +0700 Subject: [PATCH 33/50] init support for relations recursivelly Signed-off-by: Anna Khismatullina --- .../MasterCard/SubMasterCard.yaml | 6 + .../MasterCard/SubMasterCard/submaster.md | 5 + .../huly/example-workspace/MasterSlave11.yaml | 4 +- .../huly/example-workspace/SlaveCard/slave.md | 2 +- packages/importer/src/huly/unified.ts | 104 +++++------------- 5 files changed, 39 insertions(+), 82 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/MasterCard/SubMasterCard.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/MasterCard/SubMasterCard/submaster.md diff --git a/dev/import-tool/docs/huly/example-workspace/MasterCard/SubMasterCard.yaml b/dev/import-tool/docs/huly/example-workspace/MasterCard/SubMasterCard.yaml new file mode 100644 index 00000000000..19e70a6174e --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/MasterCard/SubMasterCard.yaml @@ -0,0 +1,6 @@ +class: card:class:MasterTag +title: Sub Master Card 5 +properties: + - label: bbb + type: TypeString + defaultValue: "" \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/MasterCard/SubMasterCard/submaster.md b/dev/import-tool/docs/huly/example-workspace/MasterCard/SubMasterCard/submaster.md new file mode 100644 index 00000000000..40797cfa852 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/MasterCard/SubMasterCard/submaster.md @@ -0,0 +1,5 @@ +--- +title : simple sub master +mainSlave: ../../SlaveCard/slave.md +--- +jhjsdjshghfjh diff --git a/dev/import-tool/docs/huly/example-workspace/MasterSlave11.yaml b/dev/import-tool/docs/huly/example-workspace/MasterSlave11.yaml index c1ab4b98295..e63d14470f9 100644 --- a/dev/import-tool/docs/huly/example-workspace/MasterSlave11.yaml +++ b/dev/import-tool/docs/huly/example-workspace/MasterSlave11.yaml @@ -1,6 +1,6 @@ class: core:class:Association typeA: MasterCard.yaml typeB: SlaveCard.yaml -nameA: Main Master -nameB: Main Slave +nameA: mainMaster +nameB: mainSlave type: 1:1 \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md index 6a845474cd0..d811a523cbf 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md @@ -1,6 +1,6 @@ --- title: simple slave sex: true -mainMaster: ../MasterCard/master.md +mainMaster: ../MasterCard/SubMasterCard/submaster.md --- nksdnflksj diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 5e3b472c403..cbd1348dc35 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -13,8 +13,8 @@ import * as yaml from 'js-yaml' import * as path from 'path' import { IntlString } from '../../../platform/types' import { Props, UnifiedDoc, UnifiedMixin } from '../types' +import { MetadataStorage, RelationMetadata } from './metadata' import { readMarkdownContent, readYamlHeader } from './parsing' -import { MetadataStorage } from './metadata' export interface UnifiedDocProcessResult { docs: Map>> @@ -32,8 +32,9 @@ export class UnifiedDocProcessor { // Первый проход - собираем метаданные await this.processMetadata(directoryPath, result) + await this.processSystemCards(directoryPath, result, new Map()) // todo: get master tag relations // Второй проход - обрабатываем карточки - await this.processCards(directoryPath, result) + await this.processCards(directoryPath, result, new Map()) return result } @@ -96,7 +97,8 @@ export class UnifiedDocProcessor { private async processCards ( currentPath: string, result: UnifiedDocProcessResult, - currentMasterTagId?: Ref + masterTagRelations: Map, + masterTagId?: Ref ): Promise { const entries = fs.readdirSync(currentPath, { withFileTypes: true }) @@ -105,7 +107,10 @@ export class UnifiedDocProcessor { if (fs.existsSync(yamlPath)) { const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record if (yamlConfig?.class === card.class.MasterTag) { - currentMasterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + masterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + this.metadataStorage.getAssociations(yamlPath).forEach((relationMetadata, propName) => { + masterTagRelations.set(propName, relationMetadata) + }) } } @@ -115,8 +120,8 @@ export class UnifiedDocProcessor { const cardPath = path.join(currentPath, entry.name) const { class: cardType, ...cardProps } = await readYamlHeader(cardPath) - if (currentMasterTagId !== undefined) { - await this.processCard(result, cardPath, cardProps, currentMasterTagId) + if (masterTagId !== undefined) { + await this.processCard(result, cardPath, cardProps, masterTagId, masterTagRelations) } } } @@ -125,75 +130,14 @@ export class UnifiedDocProcessor { for (const entry of entries) { if (!entry.isDirectory()) continue const dirPath = path.join(currentPath, entry.name) - await this.processCards(dirPath, result, currentMasterTagId) - } - } - - private async processDirectory ( - currentPath: string, - result: UnifiedDocProcessResult, - parentMasterTagId?: Ref, - parentMasterTagAttrs?: Map>> - ): Promise { - const entries = fs.readdirSync(currentPath, { withFileTypes: true }) - - // Сначала обрабатываем YAML файлы (потенциальные мастер-теги) - for (const entry of entries) { - if (!entry.isFile() || !entry.name.endsWith('.yaml')) continue // todo: filter entries by extension - - const yamlPath = path.resolve(currentPath, entry.name) - const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record - - switch (yamlConfig?.class) { - case card.class.MasterTag: { - const masterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref - const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) - - const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) - this.metadataStorage.setAttributes(yamlPath, masterTagAttrs) - - const docs = result.docs.get(yamlPath) ?? [] - docs.push( - masterTag, - ...Array.from(masterTagAttrs.values()) - ) - result.docs.set(yamlPath, docs) - - const masterTagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) - if (fs.existsSync(masterTagDir) && fs.statSync(masterTagDir).isDirectory()) { - await this.processDirectory(masterTagDir, result, masterTagId, masterTagAttrs) - } - break - } - case card.class.Tag: { - if (parentMasterTagId === undefined) { - throw new Error('Tag should be inside master tag folder: ' + currentPath) - } - - await this.processTag(yamlPath, yamlConfig, result, parentMasterTagId) - break - } - case core.class.Association: { - const association = await this.createAssociation(yamlPath, yamlConfig) - result.docs.set(yamlPath, [association]) - break - } - default: - throw new Error('Unsupported class: ' + yamlConfig?.class) // todo: handle default case just convert to UnifiedDoc - } - } - - if (parentMasterTagId === undefined || parentMasterTagAttrs === undefined) { - await this.processSystemCards(currentPath, result) - } else { - // await this.processCardDirectory(result, currentPath, parentMasterTagId, parentMasterTagAttrs) // todo: handle parent master tag attrs - await this.processCardDirectory(result, currentPath, parentMasterTagId) + await this.processCards(dirPath, result, masterTagRelations, masterTagId) } } private async processSystemCards ( currentDir: string, - result: UnifiedDocProcessResult + result: UnifiedDocProcessResult, + masterTagRelations: Map ): Promise { const entries = fs.readdirSync(currentDir, { withFileTypes: true }) @@ -206,7 +150,7 @@ export class UnifiedDocProcessor { throw new Error('Unsupported card type: ' + cardType) } - await this.processCard(result, cardPath, cardProps, cardType) // todo: get right master tag attributes + await this.processCard(result, cardPath, cardProps, cardType, masterTagRelations) // todo: get right master tag attributes } } @@ -215,9 +159,10 @@ export class UnifiedDocProcessor { cardPath: string, cardProps: Record, masterTagId: Ref, + masterTagRelations: Map, parentCardId?: Ref ): Promise { - const cardWithRelations = await this.createCard(cardProps, cardPath, masterTagId, parentCardId) + const cardWithRelations = await this.createCard(cardProps, cardPath, masterTagId, masterTagRelations, parentCardId) if (cardWithRelations.length > 0) { const docs = result.docs.get(cardPath) ?? [] @@ -230,7 +175,7 @@ export class UnifiedDocProcessor { // Проверяем наличие дочерних карточек const cardDir = path.join(path.dirname(cardPath), path.basename(cardPath, '.md')) if (fs.existsSync(cardDir) && fs.statSync(cardDir).isDirectory()) { - await this.processCardDirectory(result, cardDir, masterTagId, card.props._id as Ref) + await this.processCardDirectory(result, cardDir, masterTagId, masterTagRelations, card.props._id as Ref) } } } @@ -239,6 +184,7 @@ export class UnifiedDocProcessor { result: UnifiedDocProcessResult, cardDir: string, masterTagId: Ref, + masterTagRelations: Map, parentCardId?: Ref ): Promise { const entries = fs.readdirSync(cardDir, { withFileTypes: true }) @@ -247,7 +193,7 @@ export class UnifiedDocProcessor { for (const entry of entries) { const childCardPath = path.join(cardDir, entry.name) const { class: cardClass, ...cardProps } = await readYamlHeader(childCardPath) - await this.processCard(result, childCardPath, cardProps, masterTagId, parentCardId) + await this.processCard(result, childCardPath, cardProps, masterTagId, masterTagRelations, parentCardId) } } @@ -373,12 +319,13 @@ export class UnifiedDocProcessor { cardHeader: Record, cardPath: string, masterTagId: Ref, + masterTagRelations: Map, parentCardId?: Ref ): Promise[]> { const { _class, title, tags, ...customProperties } = cardHeader const cardId = this.metadataStorage.getIdByFullPath(cardPath) as Ref - const props: Record = { + const cardProps: Record = { _id: cardId, space: core.space.Workspace, title, @@ -387,9 +334,8 @@ export class UnifiedDocProcessor { const masterTagPath = path.dirname(cardPath) + '.yaml' // todo: fix master tag path const masterTagAttrs = this.metadataStorage.getAttributes(masterTagPath) - const masterTagRelations = this.metadataStorage.getAssociations(masterTagPath) + // const masterTagRelations = this.metadataStorage.getAssociations(masterTagPath) // todo: handle tag attributes separately - const relations: UnifiedDoc[] = [] for (const [key, value] of Object.entries(customProperties)) { const propName = masterTagAttrs.get(key)?.props.name // todo: handle tag attributes separately @@ -415,7 +361,7 @@ export class UnifiedDocProcessor { } relations.push(relation) } else { - props[propName] = value + cardProps[propName] = value } } @@ -424,7 +370,7 @@ export class UnifiedDocProcessor { _class: masterTagId, collabField: 'content', contentProvider: () => readMarkdownContent(cardPath), - props: props as Props // todo: what is the correct props type? + props: cardProps as Props // todo: what is the correct props type? }, ...relations ] From 685459528ceabe5640478f45137d382b7a8f8536 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Mon, 7 Apr 2025 23:37:45 +0700 Subject: [PATCH 34/50] Handle tags releations Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/unified.ts | 47 ++++++++++++++++++--------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index cbd1348dc35..da1b2be945a 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -319,7 +319,7 @@ export class UnifiedDocProcessor { cardHeader: Record, cardPath: string, masterTagId: Ref, - masterTagRelations: Map, + masterTagRelations: Map, // todo: rename to masterTagsAssociations parentCardId?: Ref ): Promise[]> { const { _class, title, tags, ...customProperties } = cardHeader @@ -333,32 +333,32 @@ export class UnifiedDocProcessor { } const masterTagPath = path.dirname(cardPath) + '.yaml' // todo: fix master tag path - const masterTagAttrs = this.metadataStorage.getAttributes(masterTagPath) + const masterTagAttrs = this.metadataStorage.getAttributes(masterTagPath) // todo: handle master tag attributes recursively // const masterTagRelations = this.metadataStorage.getAssociations(masterTagPath) // todo: handle tag attributes separately + + const tagAssociations = new Map() + for (const tag of tags) { + const tagPath = path.resolve(path.dirname(cardPath), tag) + this.metadataStorage.getAssociations(tagPath).forEach((relationMetadata, propName) => { + tagAssociations.set(propName, relationMetadata) + }) + } + const relations: UnifiedDoc[] = [] for (const [key, value] of Object.entries(customProperties)) { const propName = masterTagAttrs.get(key)?.props.name // todo: handle tag attributes separately // if (propName === undefined) { // throw new Error(`Attribute not found: ${key}`) // todo: keep the error till builder validation // } - if (masterTagRelations.has(key)) { - const metadata = masterTagRelations.get(key) + if (masterTagRelations.has(key) || tagAssociations.has(key)) { + const metadata = masterTagRelations.get(key) ?? tagAssociations.get(key) if (metadata === undefined) { throw new Error(`Association not found: ${key}, ${cardPath}`) // todo: keep the error till builder validation } - const otherCardField = metadata.field === 'docA' ? 'docB' : 'docA' - const otherCardId = this.metadataStorage.getIdByFullPath(path.resolve(path.dirname(cardPath), value)) as Ref - const relation: UnifiedDoc = { - _class: core.class.Relation, - props: { - _id: generateId(), - space: core.space.Model, - [metadata.field]: cardId, - [otherCardField]: otherCardId, - association: metadata.association - } as unknown as Props - } + const otherCardPath = path.resolve(path.dirname(cardPath), value) + const otherCardId = this.metadataStorage.getIdByFullPath(otherCardPath) as Ref + const relation: UnifiedDoc = this.createRelation(metadata, cardId, otherCardId) relations.push(relation) } else { cardProps[propName] = value @@ -376,6 +376,21 @@ export class UnifiedDocProcessor { ] } + private createRelation (metadata: RelationMetadata, cardId: Ref, otherCardId: Ref): UnifiedDoc { + const otherCardField = metadata.field === 'docA' ? 'docB' : 'docA' + const relation: UnifiedDoc = { + _class: core.class.Relation, + props: { + _id: generateId(), + space: core.space.Model, + [metadata.field]: cardId, + [otherCardField]: otherCardId, + association: metadata.association + } as unknown as Props + } + return relation + } + private async applyTags ( card: UnifiedDoc, cardHeader: Record, From 025dde92c043dec7fde1f3c96861a124ef1af3a2 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Mon, 7 Apr 2025 23:38:03 +0700 Subject: [PATCH 35/50] Handle tags releations Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/unified.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index da1b2be945a..cfc1525aed1 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -322,7 +322,10 @@ export class UnifiedDocProcessor { masterTagRelations: Map, // todo: rename to masterTagsAssociations parentCardId?: Ref ): Promise[]> { - const { _class, title, tags, ...customProperties } = cardHeader + const { _class, title, tags: rawTags, ...customProperties } = cardHeader + + // Приводим tags к массиву + const tags = rawTags !== undefined ? (Array.isArray(rawTags) ? rawTags : [rawTags]) : [] const cardId = this.metadataStorage.getIdByFullPath(cardPath) as Ref const cardProps: Record = { From ee80ddba34a1aa1d2c5f9ff24cc95ac656c8a60d Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 00:16:26 +0700 Subject: [PATCH 36/50] working tags relations + examples Signed-off-by: Anna Khismatullina --- .vscode/launch.json | 2 +- dev/import-tool/docs/huly/README.md | 211 ------------------ .../huly/example-workspace/Documentation.yaml | 10 - .../Documentation/Getting Started.md | 19 -- .../Documentation/User Guide.md | 16 -- .../Documentation/User Guide/Installation.md | 19 -- .../Documentation/files/architecture.png | Bin 40292 -> 0 bytes .../example-workspace/FamiliarHelpers.yaml | 4 +- .../docs/huly/example-workspace/File1.md | 9 - .../huly/example-workspace/MasterCard.yaml | 6 - .../MasterCard/SubMasterCard.yaml | 6 - .../MasterCard/SubMasterCard/submaster.md | 5 - .../example-workspace/MasterCard/master.md | 5 - .../huly/example-workspace/MasterSlave11.yaml | 6 - .../huly/example-workspace/MasterSlave1N.yaml | 6 - .../huly/example-workspace/MasterSlaveNN.yaml | 6 - .../huly/example-workspace/MasterTag.yaml | 36 --- .../huly/example-workspace/MasterTag/Card1.md | 28 --- .../MasterTag/Card1/SubCard1.md | 28 --- .../MasterTag/Child Type.yaml | 7 - .../MasterTag/Child Type/Card of Child 1.md | 27 --- .../Child Type/Child Child Type.yaml | 7 - .../Card of Child of Child.md | 27 --- .../huly/example-workspace/MasterTag/Tag.yaml | 5 - .../MasterTag/Tag/SubTag1.yaml | 5 - .../huly/example-workspace/Project Alpha.yaml | 12 - .../Project Alpha/1.Project Setup.md | 30 --- .../1.Project Setup/2.Configure CI.md | 13 -- .../Project Alpha/4.Update Docs.md | 11 - .../Project Alpha/files/config.yaml | 18 -- .../Project Alpha/files/screenshot.png | Bin 39710 -> 0 bytes .../files/screenshot/drawing1.json | 55 ----- .../huly/example-workspace/QMS Documents.yaml | 11 - .../[SOP-001] Document Control.md | 32 --- .../[SOP-002] Document Review.md | 42 ---- .../[WI-001] Document Template Usage.md | 37 --- .../docs/huly/example-workspace/Recipes.yaml | 15 -- .../Recipes/Chocolate Lava Cake.md | 35 --- .../Recipes/Classic Margherita Pizza.md | 34 --- .../Recipes/Vegan/Mushroom Risotto.md | 40 ---- .../Recipes/Vegan/Vegan Recipe.yaml | 9 - .../huly/example-workspace/SlaveCard.yaml | 2 +- .../huly/example-workspace/SlaveCard/Frodo.md | 3 +- .../example-workspace/SlaveCard/Gendalf.md | 3 +- .../huly/example-workspace/SlaveCard/Queen.md | 8 +- .../example-workspace/SlaveCard/Worker1.md | 3 +- .../example-workspace/SlaveCard/Worker2.md | 3 +- .../huly/example-workspace/SlaveCard/slave.md | 6 - .../huly/example-workspace/SlaveMinion.yaml | 4 +- packages/importer/src/huly/unified.ts | 74 +++++- 50 files changed, 81 insertions(+), 919 deletions(-) delete mode 100644 dev/import-tool/docs/huly/README.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png delete mode 100644 dev/import-tool/docs/huly/example-workspace/File1.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterCard.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterCard/SubMasterCard.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterCard/SubMasterCard/submaster.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterCard/master.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterSlave11.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterSlave1N.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterSlaveNN.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag/Card1.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag/Card1/SubCard1.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type/Card of Child 1.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type/Child Child Type.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag/Child Type/Child Child Type/Card of Child of Child.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag/Tag.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/MasterTag/Tag/SubTag1.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/1.Project Setup.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/1.Project Setup/2.Configure CI.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/4.Update Docs.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/config.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot.png delete mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json delete mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml delete mode 100644 dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md diff --git a/.vscode/launch.json b/.vscode/launch.json index 6586d5bfd89..4540a70bc5c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -839,7 +839,7 @@ "-pw", "1234", "-ws", - "ws2" + "ws4" ], "env": { "FRONT_URL": "http://localhost:8087" diff --git a/dev/import-tool/docs/huly/README.md b/dev/import-tool/docs/huly/README.md deleted file mode 100644 index e5a4539924d..00000000000 --- a/dev/import-tool/docs/huly/README.md +++ /dev/null @@ -1,211 +0,0 @@ -## Import from Unified Format Guide - -### Overview -The unified format represents workspace data in a hierarchical folder structure where: -* Root directory contains space configurations (*.yaml) and their corresponding folders -* Each space folder contains documents/issues (*.md) and their subdocuments/subissues -* Documents/issues can have child items in similarly-named folders -* File named `settings.yaml` is reserved and should not be used for spaces configuration -* Files without required `class` property in frontmatter will be skipped - -See the complete working example in the [example workspace](./example-workspace). - -### File Structure Example -``` -workspace/ -├── Documentation/ -│ ├── Getting Started.md # Standalone document -│ ├── User Guide.md # Document with children -│ ├── User Guide/ # Child documents folder -│ │ ├── Installation.md -│ │ └── Configuration.md -│ └── files/ # Attachments -│ └── architecture.png -├── Documentation.yaml # Space configuration -├── Project Alpha/ -│ ├── 1.Project Setup.md # Issue with subtasks -│ ├── 1.Project Setup/ # Subtasks folder -│ │ ├── 2.Configure CI.md -│ │ └── 3.Setup Tests.md -│ ├── 4.Update Docs.md # Standalone issue -│ └── files/ -│ └── diagram.png # Can be referenced in markdown content -└── Project Alpha.yaml # Project configuration -├── QMS Documents/ # QMS documentation -│ ├── [SOP-001] Document Control.md # Document template -│ ├── [SOP-001] Document Control/ # Template implementations -│ │ └── [SOP-002] Document Review.md # Controlled document -│ └── [WI-001] Document Template Usage.md # Standalone controlled document -└── QMS Documents.yaml # QMS space configuration -``` - -### File Format Requirements -* All spaces files must be in YAML format -* All document/issue files must include YAML frontmatter followed by Markdown content -* Children documents/issues are located in the folder with the same name as the parent document/issue - - -#### Tracker Issues - -##### 1. Project Configuration (*.yaml) -Example: `Project Alpha.yaml`: -```yaml -class: tracker:class:Project # Required -title: Project Alpha # Required -identifier: ALPHA # Required, max 5 uppercase letters/numbers, must start with a letter -private: false # Optional, default: false -autoJoin: true # Optional, default: true -owners: # Optional, list of email addresses - - john.doe@example.com -members: # Optional, list of email addresses - - joe.shmoe@example.com -description: string # Optional -defaultIssueStatus: Todo # Optional -``` - -##### 2. Issue (*.md) -Example: `1.Project Setup.md`: -```yaml ---- -class: tracker:class:Issue # Required -title: Project Setup # Required -status: In Progress # Required -priority: High # Optional -assignee: John Smith # Optional -estimation: 8 # Optional, in hours -remainingTime: 4 # Optional, in hours ---- -Task description in Markdown... -``` - -##### Issue Identification -* Human-readable issue ID is formed by combining project's identifier and issue number from filename -* Example: For project with identifier `ALPHA` and issue `1.Setup Project.md`, the issue ID will be `ALPHA-1` - -##### Allowed Values - -Issue status values: -* `Backlog` -* `Todo` -* `In Progress` -* `Done` -* `Canceled` - -Issue priority values: -* `Low` -* `Medium` -* `High` -* `Urgent` - -#### Documents - -##### 1. Teamspace Configuration (*.yaml) -Example: `Documentation.yaml`: -```yaml -class: document:class:Teamspace # Required -title: Documentation # Required -private: false # Optional, default: false -autoJoin: true # Optional, default: true -owners: # Optional, list of email addresses - - john.doe@example.com -members: # Optional, list of email addresses - - joe.shmoe@example.com -description: string # Optional -``` - -##### 2. Document (*.md) -Example: `Getting Started.md`: -```yaml ---- -class: document:class:Document # Required -title: Getting Started Guide # Required ---- -# Content in Markdown format -``` - -#### Controlled Documents -##### 1. Space Configuration (*.yaml) -QMS Document Space: `QMS Documents.yaml`: -```yaml -class: documents:class:OrgSpace # Required -title: QMS Documents # Required -private: false # Optional, default: false -owners: # Optional, list of email addresses - - john.doe@example.com -members: # Optional, list of email addresses - - joe.shmoe@example.com -description: string # Optional -qualified: john.doe@example.com # Optional, qualified user -manager: jane.doe@example.com # Optional, QMS manager -qara: bob.smith@example.com # Optional, QA/RA specialist -``` - -##### 2. Document Template (*.md) -Example: `[SOP-001] Document Control.md`: -```yaml ---- -class: documents:mixin:DocumentTemplate # Required -title: SOP Template # Required -docPrefix: SOP # Required, document code prefix -category: documents:category:Procedures # Required -author: John Smith # Required -owner: Jane Wilson # Required -abstract: Template description # Optional -reviewers: # Optional - - alice.cooper@example.com -approvers: # Optional - - david.brown@example.com -coAuthors: # Optional - - bob.dylan@example.com ---- -Template content in Markdown... -``` - -##### 3. Controlled Document (*.md) -Example: `[SOP-002] Document Review.md`: -```yaml ---- -class: documents:class:ControlledDocument # Required -title: Document Review Procedure # Required -template: [SOP-001] Document Control.md # Required, path to template -author: John Smith # Required -owner: Jane Wilson # Required -abstract: Document description # Optional -reviewers: # Optional - - alice.cooper@example.com -approvers: # Optional - - david.brown@example.com -coAuthors: # Optional - - bob.dylan@example.com -changeControl: # Optional - description: Initial document creation - reason: Need for standardized process - impact: Improved document control ---- -Document content in Markdown... -``` -##### Controlled Document Code Format -* Document code must be specified in file name: `[CODE] Any File Name.md` -* If code is not specified for controlled document, it will be generated automatically using template's docPrefix and sequential number (e.g. `SOP-99`) -* If code is not specified for template, it will be generated automatically as `TMPL-seqNumber`, where `seqNumber` is the sequence number of the template in the space - - -### Run Import Tool -```bash -docker run \ - -e FRONT_URL="https://huly.app" \ - -v /path/to/workspace:/data \ - hardcoreeng/import-tool:latest \ - -- bundle.js import /data \ - --user your.email@company.com \ - --password yourpassword \ - --workspace workspace-id -``` - -### Limitations -* All users must exist in the system before import -* Assignees are mapped by full name -* Files in space directories can be used as attachments when referenced in markdown content -* Document codes (in square brackets) must be unique across all document spaces -* Controlled documents must be created in the same space as their templates -* Controlled documents can be imported only with `Draft` status diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation.yaml b/dev/import-tool/docs/huly/example-workspace/Documentation.yaml deleted file mode 100644 index 781c2b6f123..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Documentation.yaml +++ /dev/null @@ -1,10 +0,0 @@ -class: document:class:Teamspace -title: Documentation -emoji: 📖 -private: false -autoJoin: true -owners: - - john.doe@example.com -members: - - joe.shmoe@example.com -description: Technical documentation and guides diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md b/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md deleted file mode 100644 index 4b0f5dab76d..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -class: document:class:Document -title: Getting Started Guide ---- -# Getting Started - -Welcome to our project! This guide will help you get started with development. - -## Setup Steps - -1. Clone the repository -2. Install dependencies -3. Set up your environment - -## Project Communication -We use Huly for all project communication: -- Team discussions in Virtual Office -- Technical discussions in issue comments -- Documentation in Huly Documents \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md deleted file mode 100644 index 5cbe18d0340..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -class: document:class:Document -title: User Guide ---- -# User Guide - -Our platform architecture and key components. - -## System Overview - - - -## Development Workflow -- Code reviews via GitHub integration -- CI/CD status in Huly Activity Feed -- Team sync-ups in Huly Virtual Office diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md deleted file mode 100644 index 0c39873c056..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -class: document:class:Document -title: Installation Guide ---- -# Installation - -## System Requirements -- Node.js 18 or higher -- Docker Desktop -- Git - -## Setup Steps - -1. Clone the repository -2. Install dependencies -3. Configure your environment - -## Need Help? -Contact @Joe Shmoe for technical support \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png b/dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png deleted file mode 100644 index 131b4072aa97c69e9075cc05b4a91b95eea3a54e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40292 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 VNc#M!*-5xl(=2CMoE3X)`#vi%=pn*%YOW2pLJp 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%0At-fNf1Ku}>_1&Gj;m?&OmPh`mH_Kl%VOaUq z&XJj(PQ$!jeY!vP{C35TV&g?yv|5(+Te->h#}(O+btcE&x*yfxm>+ygbyq^>wV^9+ z&&e=4#v;ssDHT&x-0`?K=}q$x6V1d^k#nbN_t)d3C@V>P4bI4>N83@h`a|c(g*yAe z8pO?0l~(s>=Y0WfFr=3Z9h}*NeO+@M>(ghnHM|hK?&^khOzA6vQdeJ#1^a&Vi)$@a zcdoY6>-OkvQ}59uV;bHs9Y1tPTKI6E@|*{rlVY_~&!69>J8|JL=kxn}y9b3*Wq?R&T!AIA1xbup4HLm5ux_3uE{l`|E{;%IxAN6VeN_W6du!-!>TPn|T8zjG^dRJfT z3S0fy+b)LNojQGtko_2ODtL~vp79sQpI$7}i*?)BF~}v!dH1JoJ?j44JYmw7E(SR{ zt^EcLEZ(6Rwr0GYy+Ks=fCGO9p?J0Tf`^CE_6K zT(adj5VP=jR`llO!;g-fu}Qu>Axw4G+gz{IuxmfcAFeGe&eJoj-rHAddeda9!ps@3 z-VCfa>(WWPebvKLp0?9$-Yj#8Ja)ju@%@`t#}5k+JxXyf`mL!Xdd@X9nP=;RX0-W9 z=8W^Xo@+X#z)G`8BY*bo`1+)gWBqz|-!*i?==HW*O&f>aI@n-pwb40dhHi(v;%M`g z7f;G;*3O>hzv4}3TweKs7u{;MrIal(?ACLOopNiCr1k#d%wnr)_Lf(DJa_xuiLmxp zbkmf%d%O7k=IORt8Kv)+hwRkqSFDq@*6Hs1ij|4`^Ig#my_gKbw&L-qumEXi1CE4# z{rWvc1}2VnF$XYNX@zX0f(t_k&JJ-5!Rq@Q-U|sIc1wF#RS_Kt1W->Vlzb{Hw?VHa zlF4<5DJ|TJ%(bNXyfTT%7TY7T!Z~n+kb5k0r)5QTqRU8RK2+DjVj}FnI4yL|_yH0K z5nWO!Xv+$r#}W{;!1~V_6pg%Np`%nyqocGR|gl`!`|5VjIEZ*h6$rFL0&f;^`JYy(Z=njq}07hlTV$ zTw6OBcarDEV5b2Q*_HR+vT(eiqtRlO1HTpk)XkSKJx-k(V(3scRmf>{r$Q%0Pcv(- zM2{0LMsgi^?8G5F5`>}+NR?P1OT$Ho)mYzO4p|K2GMbv1aTZYH)lxCsB8nDRX$?vb zBRA#&EWxsi9A=uT7tOZjk(ub&U$XPKNPs zixW;vd*VCJdJz5B_9Y%YO&{vGR%%$7RC&yaRB`a=Uz?WKDMtO4%Fsc-*-g;!yPvc@ zt#a_=Xt!}IP2EC_P8m%Pvvc>cbRKX1@R83#I~D)rA9~N9Fg-uxnxkT|o~f;ot%7gQ z%MaaquP*XtwlwBNTTCgwv!1L$DM6{5z!8WOZM<1{eFqL)fcXiyaRyy;&GoXDOpC~R z7LIVA@eow#-M|A020}dzW{}bQ_;De)2v8_LL%?FK){i=3WGOr%B5z-ku(xf@loy>h zect62=l&RHr?LAjO*0eLnY2@!2&+h+vU7UJY9prvXy$BkBan;N!|RlkmHFk!pdS8& z8-@E40U;o7U(!doN5Gxg+1ZXCAIOOEM?S*GG}X+HOv_kP^cFWx5Qm{@Kn5_uBj&ql z0m2}BQ=2O_$ZfgEQzFcFsCMG?86Z~P6S4|V^LZjTKuR!?yw+6YK1pJwd0GtpKs1WU z1$w>Yw6yWwBW~et5ez^!ux$01x+GKYX6VGgoIz>HcN=E3d>`~Gg$aXE7qHv@c$sAUo;@T{RZu#vb!X4~0K zgDwzux8S|ahhQ@$;wrOa3(gzkq412bphd1Lu90<7IiOm<(Ak0XPApn~#_YtvcHerV4rAw$`u3yzuGKuc(PPt(g%8>y?N?5Cy0F-)chJ2Tp8eM@ zmbue)-ZHhyi6v1JU4QQyz-D)7d%2tE#bY;m_4m1)xoL`WNXyd4cRreb4jYlE`0nXA zE%)2gubDm3dYkAsX!dA}%jL8EI_k|ctv@%P%(`vmZB#xrFYm9weG$EW%liCyTbJb# zuG1~TZg%AFT0K3>d8^rbBdNO@e=Yf>J9Ix4Kv2xw58o#S?`o%S?qRP+ErKej*`jib@HSxCmU#JXy6jBX9+w9rC~m>$47?Q z`NG;ZN+lT#r%-llnP7@Q@Qf^m7!tvz5=vqJ1CI*twK$38Cb|`2;H5a*z5nyTLX;iM zpnRRMK{Eg}hjcNh`B9jcgkFE#sfSlUz#=Rh^c+R%0ThIH^0?%+Np|AY1gK zqWtLOj7dW)?Hmkm?uxf+&u~2S=+HB}L#?s}ub1!ou;1?P{edg8m$d84Y^>AmcwMzj zdi;*eKkxWVOG%#CG2GaE{e16OYlDg)=YigP z3LGUxREAkDOW0O;yNfx2{cceu?EnoH>9LWaW^L z&_Z)AuHU0VVwrPyrfr(?%k2+Mr-RbWBXaCH5O0o!Md(_mfxS0Kb?*G+gzNMC6$r#R zO%l-@HrD{wZtiW{6CNH;*(`bY&ag4B9viVAlkp4%F2foT_371GgE6{(6-8TNFGDU} zf-m|$>snEQ8Nmj#VVQn@Q^s4fFryQ@Og0)`!?Wb)#fMnVrFZG*)m2*E3#Tt!HJ%wI z+qCi>80-&cMsAm*H0OwEcc)~ey8I7qIW)0gNuLzaC}yDsaJwr%=?h!Aci`?MbS55?}pA8RiQ_Rabeav8v`LZTe1;1YyKqR~@U`F$} z+Po_~wHOU|wJc+fo6+{3^s`;&p9@QCjN;{XowBx3^Uoc_)aBEprND6*7hj~{d}Jy! zET?jjwRNpomeHzJbMd9VXXaSs?E$s6y}8v*!Y*_1;>DZ{?b50r^nNaAzvh9Lq_DKK zRBiEU!vNcI17XX-;`+L(%(=JT)t5f+@a<*EvzGiX2JJZvNvr}?73VqOnvTa$DAGAV z$WppB>o6huGaG>6Ie|t}WFjutYAP`cPWqBRQ|r3k)vd-xHw&T{(ZOh zq+zN_vo9uwY}KfaOF!V$F>$nYv`^X)85z~em~geg<)7PQXQcz-7brMk=DQ$5+DMq7 zB^!o27lOD${iWHIL1V^_wVBYm9gZCnY|NJ;cVX-1RG0Sl&vA5sk`o!ec~lF4?|v4# zupUoP@ za{vDQ7E(2~hIWNr=||=w&anI8W4ZUdH~K}B3x~@3phO9c0r^a1iURUvbhFlqeU(pZh3yx5GOfp?m{|~F`e zt)ej|=e9w4#r=KjDwM~z3=U}gvh*j%u zE|QY)g|bk?k@;{^HakS?!%PlyTY2cL&FN|0YuiufyUD2;HVyNexb%Lxo%{YL?I)G4 ze&-_vG){RfKpL|gy> diff --git a/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json b/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json deleted file mode 100644 index 4848657f095..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "class": "drawing:class:Drawing", - "content": [ - { - "lineWidth": 4, - "erasing": false, - "penColor": "#0000ff", - "points": [ - { "x": 213, "y": 181.5 }, - { "x": 203, "y": 181.5 }, - { "x": 193, "y": 181.5 }, - { "x": 168, "y": 181.5 }, - { "x": 136, "y": 181.5 }, - { "x": 108, "y": 181.5 }, - { "x": 84, "y": 188.5 }, - { "x": 59, "y": 196.5 }, - { "x": 42, "y": 205.5 }, - { "x": 35, "y": 220.5 }, - { "x": 32, "y": 227.5 }, - { "x": 31, "y": 233.5 }, - { "x": 31, "y": 241.5 }, - { "x": 31, "y": 248.5 }, - { "x": 34, "y": 257.5 }, - { "x": 41, "y": 269.5 }, - { "x": 53, "y": 280.5 }, - { "x": 69, "y": 290.5 }, - { "x": 84, "y": 297.5 }, - { "x": 112, "y": 305.5 }, - { "x": 190, "y": 319.5 }, - { "x": 250, "y": 323.5 }, - { "x": 314, "y": 323.5 }, - { "x": 372, "y": 317.5 }, - { "x": 410, "y": 303.5 }, - { "x": 424, "y": 292.5 }, - { "x": 427, "y": 279.5 }, - { "x": 427, "y": 265.5 }, - { "x": 425, "y": 250.5 }, - { "x": 413, "y": 225.5 }, - { "x": 407, "y": 214.5 }, - { "x": 398, "y": 203.5 }, - { "x": 380, "y": 189.5 }, - { "x": 361, "y": 181.5 }, - { "x": 317, "y": 168.5 }, - { "x": 261, "y": 154.5 }, - { "x": 207, "y": 148.5 }, - { "x": 168, "y": 148.5 }, - { "x": 153, "y": 148.5 }, - { "x": 147, "y": 151.5 }, - { "x": 143, "y": 157.5 }, - { "x": 142, "y": 165.5 }, - { "x": 142, "y": 165.5 } - ] - } - ] -} diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml b/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml deleted file mode 100644 index 3074c615960..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml +++ /dev/null @@ -1,11 +0,0 @@ -class: documents:class:OrgSpace -title: QMS Documents -description: Quality Management System Documentation -private: false -owners: - - user1 -members: - - user1 -qualified: user1 -manager: user1 -qara: user1 diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md deleted file mode 100644 index 8daf8101ade..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -class: documents:mixin:DocumentTemplate -title: 'Standard Operating Procedure Template' -docPrefix: SOP -category: DOC -author: John Appleseed -owner: John Appleseed -abstract: Template for Standard Operating Procedures -reviewers: - - John Appleseed -approvers: - - John Appleseed ---- -# Standard Operating Procedure - -## 1. Purpose -[Describe the purpose of the procedure] - -## 2. Scope -[Define the scope and applicability] - -## 3. Responsibilities -[List key roles and responsibilities] - -## 4. Procedure -[Detail the step-by-step procedure] - -## 5. References -[List related documents] - -## 6. Revision History -[Document revision history] diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md deleted file mode 100644 index 3741298f42c..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -class: documents:class:ControlledDocument -title: Document Review Procedure -template: '../[SOP-001] Document Control.md' -author: John Appleseed -owner: John Appleseed -abstract: Procedure for document review and approval process -reviewers: - - John Appleseed -approvers: - - John Appleseed -changeControl: - description: Initial document creation - reason: Need for standardized review process - impact: Improved document quality control ---- -# Document Review Procedure - -## 1. Purpose -This procedure defines the process for reviewing quality management system documents. - -## 2. Scope -Applies to all controlled documents within the QMS. - -## 3. Responsibilities -- Document Owner: Responsible for content -- Reviewers: Technical review -- QA Manager: Final approval - -## 4. Procedure -1. Author prepares document -2. Technical review -3. QA review -4. Final approval -5. Document release - -## 5. References -- Quality Manual -- Document Control Procedure - -## 6. Revision History -Rev 0.1 - Initial draft diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md deleted file mode 100644 index b8963023acd..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -class: documents:class:ControlledDocument -title: Document Template Usage Guide -template: '[SOP-001] Document Control.md' -author: John Appleseed -owner: John Appleseed -abstract: Work instruction for using document templates -reviewers: - - John Appleseed -approvers: - - John Appleseed ---- -# Document Template Usage Guide - -## 1. Purpose -Guide users in proper usage of QMS document templates. - -## 2. Scope -All personnel creating QMS documentation. - -## 3. Procedure -1. Select appropriate template -2. Fill in required sections -3. Submit for review - -## 4. Document Review Changes - -| Step | Current Text | Updated Text | Comments | -| --- | --- | --- | --- | -| Initial Review | Select a template from library | Select a template that matches your document type | Clarified selection criteria | -| Metadata | Fill in required fields | Complete all required metadata and content fields | Added metadata specification | -| Review Process | Submit for review | Submit document for review according to procedure | Added reference to procedure | -| Approval | Wait for approval | Submit for approval after receiving all reviews | Process detail added | - -## 5. References -- [Document Control SOP](./[SOP-001]%20Document%20Control.md) -- [Document Review Procedure](./[SOP-001]%20Document%20Control/[SOP-002]%20Document%20Review.md) diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes.yaml b/dev/import-tool/docs/huly/example-workspace/Recipes.yaml deleted file mode 100644 index 5e231728696..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Recipes.yaml +++ /dev/null @@ -1,15 +0,0 @@ -class: card:class:MasterTag -title: Recipe -properties: - - label: cookingTime - type: TypeString - - label: servings - type: TypeNumber - - label: difficulty - type: TypeString - - label: category - type: TypeString - - label: calories - type: TypeNumber - - label: chef - type: TypeString diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md b/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md deleted file mode 100644 index 6f34d8b02fa..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Chocolate Lava Cake -cookingTime: 25 minutes -servings: 4 -difficulty: Medium -category: Dessert -calories: 450 -chef: Anna Smith ---- - -# Chocolate Lava Cake - -## Ingredients -- 200g dark chocolate (70% cocoa) -- 200g butter -- 4 eggs -- 200g sugar -- 120g flour -- 1 tsp vanilla extract -- Pinch of salt -- Butter for ramekins -- Cocoa powder for dusting - -## Instructions -1. Melt chocolate and butter together -2. Whisk eggs and sugar until pale -3. Fold in chocolate mixture -4. Add flour and vanilla -5. Pour into buttered ramekins -6. Bake at 200°C (400°F) for 12 minutes - -## Notes -- Serve immediately while warm -- Can be prepared ahead and refrigerated -- Perfect with vanilla ice cream \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md b/dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md deleted file mode 100644 index d842bd2c611..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Classic Margherita Pizza -cookingTime: 30 minutes -servings: 4 -difficulty: Medium -category: Italian -calories: 850 -chef: Mario Rossi ---- - -# Classic Margherita Pizza - -## Ingredients -- 2 1/2 cups (300g) all-purpose flour -- 1 tsp salt -- 1 tsp active dry yeast -- 1 cup warm water -- 2 tbsp olive oil -- 1 cup tomato sauce -- 2 cups mozzarella cheese -- Fresh basil leaves -- Extra virgin olive oil - -## Instructions -1. Mix flour, salt, and yeast in a large bowl -2. Add warm water and olive oil, knead for 10 minutes -3. Let rise for 1 hour -4. Roll out dough and add toppings -5. Bake at 450°F (230°C) for 15-20 minutes - -## Notes -- For best results, use San Marzano tomatoes for the sauce -- Fresh mozzarella is preferred over pre-shredded -- Add basil leaves after baking \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md b/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md deleted file mode 100644 index 4d38d8f0a75..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: Vegan Mushroom Risotto -cookingTime: 45 minutes -servings: 4 -difficulty: Medium -category: Italian -calories: 380 -chef: Maria Green -proteinSource: Mushrooms -isGlutenFree: true -allergens: None ---- - -# Vegan Mushroom Risotto - -## Ingredients -- 300g Arborio rice -- 500g mixed mushrooms -- 1 onion, finely chopped -- 2 cloves garlic, minced -- 1 cup white wine -- 6 cups vegetable stock -- 2 tbsp nutritional yeast -- 2 tbsp olive oil -- Salt and pepper to taste -- Fresh parsley - -## Instructions -1. Sauté mushrooms until golden -2. Add onion and garlic, cook until soft -3. Add rice and toast for 2 minutes -4. Gradually add wine and stock -5. Cook until rice is creamy -6. Finish with nutritional yeast - -## Notes -- Use a variety of mushrooms for better flavor -- Keep stock warm while adding -- Stir constantly for creamy texture -- Nutritional yeast adds cheesy flavor \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml b/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml deleted file mode 100644 index a3e275d172b..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml +++ /dev/null @@ -1,9 +0,0 @@ -class: card:class:MasterTag -title: Vegan Recipe -properties: - - label: proteinSource - type: TypeString - - label: isGlutenFree - type: TypeBoolean - - label: allergens - type: TypeString \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml index 5a44be768d4..b949ef23499 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml @@ -1,5 +1,5 @@ class: card:class:MasterTag -title: Slave Card 6 +title: Slave Card 2 properties: - label: Sex type: TypeBoolean diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md index 212ec98039e..3c4d2ad3baa 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md @@ -1,6 +1,7 @@ --- title: "Frodo Baggins" -familiar: "./Minion.yaml" +tags: ./MinionTag.yaml +familiar: "./Gendalf.md" --- A brave hobbit guided by Gandalf. diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md index 85445df225a..5bb7595cb46 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md @@ -1,6 +1,7 @@ --- title: "Gandalf the Grey" -familiar: "./Familiar.yaml" +tags: ./FamiliarTag.yaml +helpers: "./Frodo.md" --- A wise and powerful wizard who guides others. \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Queen.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Queen.md index b0a2863d800..57ca9869b89 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Queen.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Queen.md @@ -1,10 +1,6 @@ --- title: "Queen Ant" -children: [ - "./Worker1.md", - "./Worker2.md", - "./Worker3.md" -] +children: "./Worker1.md" --- -The colony's queen responsible for reproduction. \ No newline at end of file +The colony's queen responsible for reproduction. diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker1.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker1.md index 214414b72b8..9fbec116697 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker1.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker1.md @@ -1,6 +1,7 @@ --- title: "Worker Ant 1" -mother: "./Mother.md" +tags: ./MinionTag.yaml +mother: "./Queen.md" --- A diligent worker ant. \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker2.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker2.md index 437b564fe72..70111bebfdc 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker2.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Worker2.md @@ -1,6 +1,7 @@ --- title: "Worker Ant 2" -mother: "./Mother.md" +tags: ./MinionTag.yaml +# mother: "./Queen.md" --- Another hard-working ant. \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md deleted file mode 100644 index d811a523cbf..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/slave.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: simple slave -sex: true -mainMaster: ../MasterCard/SubMasterCard/submaster.md ---- -nksdnflksj diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveMinion.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveMinion.yaml index 9db133db8ca..6af526758da 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveMinion.yaml +++ b/dev/import-tool/docs/huly/example-workspace/SlaveMinion.yaml @@ -1,6 +1,6 @@ class: core:class:Association typeA: ./SlaveCard.yaml typeB: ./SlaveCard/MinionTag.yaml -nameA: Mother -nameB: Children +nameA: mother +nameB: children type: 1:N diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index cfc1525aed1..1777e578bc3 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -85,13 +85,6 @@ export class UnifiedDocProcessor { throw new Error('Unsupported class: ' + yamlConfig?.class) // todo: handle default case just convert to UnifiedDoc } } - - // Рекурсивно обрабатываем поддиректории - for (const entry of entries) { - if (!entry.isDirectory()) continue - const dirPath = path.join(currentPath, entry.name) - await this.processMetadata(dirPath, result, parentMasterTagId) - } } private async processCards ( @@ -134,6 +127,68 @@ export class UnifiedDocProcessor { } } + // private async processDirectory ( + // currentPath: string, + // result: UnifiedDocProcessResult, + // parentMasterTagId?: Ref, + // parentMasterTagAttrs?: Map>> + // ): Promise { + // const entries = fs.readdirSync(currentPath, { withFileTypes: true }) + + // // Сначала обрабатываем YAML файлы (потенциальные мастер-теги) + // for (const entry of entries) { + // if (!entry.isFile() || !entry.name.endsWith('.yaml')) continue // todo: filter entries by extension + + // const yamlPath = path.resolve(currentPath, entry.name) + // const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record + + // switch (yamlConfig?.class) { + // case card.class.MasterTag: { + // const masterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + // const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) + + // const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) + // this.metadataStorage.setAttributes(yamlPath, masterTagAttrs) + + // const docs = result.docs.get(yamlPath) ?? [] + // docs.push( + // masterTag, + // ...Array.from(masterTagAttrs.values()) + // ) + // result.docs.set(yamlPath, docs) + + // const masterTagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) + // if (fs.existsSync(masterTagDir) && fs.statSync(masterTagDir).isDirectory()) { + // await this.processDirectory(masterTagDir, result, masterTagId, masterTagAttrs) + // } + // break + // } + // case card.class.Tag: { + // if (parentMasterTagId === undefined) { + // throw new Error('Tag should be inside master tag folder: ' + currentPath) + // } + + // await this.processTag(yamlPath, yamlConfig, result, parentMasterTagId) + // break + // } + // case core.class.Association: { + // const association = await this.createAssociation(yamlPath, yamlConfig) + // result.docs.set(yamlPath, [association]) + // break + // } + // default: + // throw new Error('Unsupported class: ' + yamlConfig?.class) // todo: handle default case just convert to UnifiedDoc + // } + // } + + // if (parentMasterTagId === undefined || parentMasterTagAttrs === undefined) { + // await this.processSystemCards(currentPath, result) + // } else { + // // await this.processCardDirectory(result, currentPath, parentMasterTagId, parentMasterTagAttrs) // todo: handle parent master tag attrs + // await this.processCardDirectory(result, currentPath, parentMasterTagId) + // } + // } + private async processSystemCards ( currentDir: string, result: UnifiedDocProcessResult, @@ -359,7 +414,7 @@ export class UnifiedDocProcessor { if (metadata === undefined) { throw new Error(`Association not found: ${key}, ${cardPath}`) // todo: keep the error till builder validation } - const otherCardPath = path.resolve(path.dirname(cardPath), value) + const otherCardPath = path.resolve(path.dirname(cardPath), value) // todo: value can be array of paths const otherCardId = this.metadataStorage.getIdByFullPath(otherCardPath) as Ref const relation: UnifiedDoc = this.createRelation(metadata, cardId, otherCardId) relations.push(relation) @@ -403,7 +458,8 @@ export class UnifiedDocProcessor { if (cardHeader.tags === undefined) return const mixins: UnifiedMixin[] = [] - for (const tagPath of cardHeader.tags) { + const tags = cardHeader.tags !== undefined ? (Array.isArray(cardHeader.tags) ? cardHeader.tags : [cardHeader.tags]) : [] + for (const tagPath of tags) { const cardDir = path.dirname(cardPath) const fullTagPath = path.resolve(cardDir, tagPath) const tagId = this.metadataStorage.getIdByFullPath(fullTagPath) as Ref From 15f728731bc5104673a715ab81f4c8637614e72b Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 11:34:43 +0700 Subject: [PATCH 37/50] fix tags attributes Signed-off-by: Anna Khismatullina --- .../huly/example-workspace/SlaveCard.yaml | 4 +- .../huly/example-workspace/SlaveCard/Frodo.md | 8 ++- .../example-workspace/SlaveCard/Gendalf.md | 3 + .../SlaveCard/MinionTag.yaml | 2 +- packages/importer/src/huly/unified.ts | 58 ++++++++++--------- packages/importer/src/importer/builder.ts | 2 +- 6 files changed, 46 insertions(+), 31 deletions(-) diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml index b949ef23499..b1e1a108c07 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml @@ -1,7 +1,7 @@ class: card:class:MasterTag -title: Slave Card 2 +title: Slave Card 3 properties: - - label: Sex + - label: sex type: TypeBoolean defaultValue: false - label: color diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md index 3c4d2ad3baa..c1af614b9fe 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md @@ -1,7 +1,13 @@ --- title: "Frodo Baggins" -tags: ./MinionTag.yaml +tags: + - ./FamiliarTag.yaml + - ./MinionTag.yaml familiar: "./Gendalf.md" +sex: true +color: pink +x: from familiar +y: from minion --- A brave hobbit guided by Gandalf. diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md index 5bb7595cb46..1730f655c78 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md @@ -2,6 +2,9 @@ title: "Gandalf the Grey" tags: ./FamiliarTag.yaml helpers: "./Frodo.md" +sex: true +color: gray +x: from familiar --- A wise and powerful wizard who guides others. \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/MinionTag.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard/MinionTag.yaml index d5104ca8c7d..92ac7bf43ff 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/MinionTag.yaml +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/MinionTag.yaml @@ -1,5 +1,5 @@ class: card:class:Tag title: Minion properties: - - label: x + - label: y type: TypeString diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 1777e578bc3..c9b173a27bd 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -32,9 +32,9 @@ export class UnifiedDocProcessor { // Первый проход - собираем метаданные await this.processMetadata(directoryPath, result) - await this.processSystemCards(directoryPath, result, new Map()) // todo: get master tag relations + await this.processSystemCards(directoryPath, result, new Map(), new Map()) // todo: get master tag relations // Второй проход - обрабатываем карточки - await this.processCards(directoryPath, result, new Map()) + await this.processCards(directoryPath, result, new Map(), new Map()) return result } @@ -91,6 +91,7 @@ export class UnifiedDocProcessor { currentPath: string, result: UnifiedDocProcessResult, masterTagRelations: Map, + masterTagAttrs: Map>>, masterTagId?: Ref ): Promise { const entries = fs.readdirSync(currentPath, { withFileTypes: true }) @@ -104,6 +105,9 @@ export class UnifiedDocProcessor { this.metadataStorage.getAssociations(yamlPath).forEach((relationMetadata, propName) => { masterTagRelations.set(propName, relationMetadata) }) + this.metadataStorage.getAttributes(yamlPath).forEach((attr, propName) => { + masterTagAttrs.set(propName, attr) + }) } } @@ -114,7 +118,7 @@ export class UnifiedDocProcessor { const { class: cardType, ...cardProps } = await readYamlHeader(cardPath) if (masterTagId !== undefined) { - await this.processCard(result, cardPath, cardProps, masterTagId, masterTagRelations) + await this.processCard(result, cardPath, cardProps, masterTagId, masterTagRelations, masterTagAttrs) } } } @@ -123,7 +127,7 @@ export class UnifiedDocProcessor { for (const entry of entries) { if (!entry.isDirectory()) continue const dirPath = path.join(currentPath, entry.name) - await this.processCards(dirPath, result, masterTagRelations, masterTagId) + await this.processCards(dirPath, result, masterTagRelations, masterTagAttrs, masterTagId) } } @@ -192,7 +196,8 @@ export class UnifiedDocProcessor { private async processSystemCards ( currentDir: string, result: UnifiedDocProcessResult, - masterTagRelations: Map + masterTagRelations: Map, + masterTagAttrs: Map>> ): Promise { const entries = fs.readdirSync(currentDir, { withFileTypes: true }) @@ -205,7 +210,7 @@ export class UnifiedDocProcessor { throw new Error('Unsupported card type: ' + cardType) } - await this.processCard(result, cardPath, cardProps, cardType, masterTagRelations) // todo: get right master tag attributes + await this.processCard(result, cardPath, cardProps, cardType, masterTagRelations, masterTagAttrs) // todo: get right master tag attributes } } @@ -215,9 +220,10 @@ export class UnifiedDocProcessor { cardProps: Record, masterTagId: Ref, masterTagRelations: Map, + masterTagAttrs: Map>>, parentCardId?: Ref ): Promise { - const cardWithRelations = await this.createCard(cardProps, cardPath, masterTagId, masterTagRelations, parentCardId) + const cardWithRelations = await this.createCard(cardProps, cardPath, masterTagId, masterTagRelations, masterTagAttrs, parentCardId) if (cardWithRelations.length > 0) { const docs = result.docs.get(cardPath) ?? [] @@ -230,7 +236,7 @@ export class UnifiedDocProcessor { // Проверяем наличие дочерних карточек const cardDir = path.join(path.dirname(cardPath), path.basename(cardPath, '.md')) if (fs.existsSync(cardDir) && fs.statSync(cardDir).isDirectory()) { - await this.processCardDirectory(result, cardDir, masterTagId, masterTagRelations, card.props._id as Ref) + await this.processCardDirectory(result, cardDir, masterTagId, masterTagRelations, masterTagAttrs, card.props._id as Ref) } } } @@ -240,6 +246,7 @@ export class UnifiedDocProcessor { cardDir: string, masterTagId: Ref, masterTagRelations: Map, + masterTagAttrs: Map>>, parentCardId?: Ref ): Promise { const entries = fs.readdirSync(cardDir, { withFileTypes: true }) @@ -248,7 +255,7 @@ export class UnifiedDocProcessor { for (const entry of entries) { const childCardPath = path.join(cardDir, entry.name) const { class: cardClass, ...cardProps } = await readYamlHeader(childCardPath) - await this.processCard(result, childCardPath, cardProps, masterTagId, masterTagRelations, parentCardId) + await this.processCard(result, childCardPath, cardProps, masterTagId, masterTagRelations, masterTagAttrs, parentCardId) } } @@ -286,6 +293,7 @@ export class UnifiedDocProcessor { const tag = await this.createTag(tagConfig, tagId, masterTagId, parentTagId) const attributes = await this.createAttributes(tagConfig, tagId) + this.metadataStorage.setAttributes(tagPath, attributes) const docs = result.docs.get(tagPath) ?? [] docs.push(tag, ...Array.from(attributes.values())) @@ -375,11 +383,10 @@ export class UnifiedDocProcessor { cardPath: string, masterTagId: Ref, masterTagRelations: Map, // todo: rename to masterTagsAssociations + masterTagAttrs: Map>>, parentCardId?: Ref ): Promise[]> { const { _class, title, tags: rawTags, ...customProperties } = cardHeader - - // Приводим tags к массиву const tags = rawTags !== undefined ? (Array.isArray(rawTags) ? rawTags : [rawTags]) : [] const cardId = this.metadataStorage.getIdByFullPath(cardPath) as Ref @@ -390,11 +397,6 @@ export class UnifiedDocProcessor { parent: parentCardId } - const masterTagPath = path.dirname(cardPath) + '.yaml' // todo: fix master tag path - const masterTagAttrs = this.metadataStorage.getAttributes(masterTagPath) // todo: handle master tag attributes recursively - // const masterTagRelations = this.metadataStorage.getAssociations(masterTagPath) - // todo: handle tag attributes separately - const tagAssociations = new Map() for (const tag of tags) { const tagPath = path.resolve(path.dirname(cardPath), tag) @@ -405,11 +407,10 @@ export class UnifiedDocProcessor { const relations: UnifiedDoc[] = [] for (const [key, value] of Object.entries(customProperties)) { - const propName = masterTagAttrs.get(key)?.props.name // todo: handle tag attributes separately - // if (propName === undefined) { - // throw new Error(`Attribute not found: ${key}`) // todo: keep the error till builder validation - // } - if (masterTagRelations.has(key) || tagAssociations.has(key)) { + if (masterTagAttrs.has(key)) { + const propName = masterTagAttrs.get(key)?.props.name + cardProps[propName] = value + } else if (masterTagRelations.has(key) || tagAssociations.has(key)) { const metadata = masterTagRelations.get(key) ?? tagAssociations.get(key) if (metadata === undefined) { throw new Error(`Association not found: ${key}, ${cardPath}`) // todo: keep the error till builder validation @@ -418,8 +419,6 @@ export class UnifiedDocProcessor { const otherCardId = this.metadataStorage.getIdByFullPath(otherCardPath) as Ref const relation: UnifiedDoc = this.createRelation(metadata, cardId, otherCardId) relations.push(relation) - } else { - cardProps[propName] = value } } @@ -455,22 +454,29 @@ export class UnifiedDocProcessor { cardPath: string, result: UnifiedDocProcessResult ): Promise { - if (cardHeader.tags === undefined) return + const tags = cardHeader.tags !== undefined ? (Array.isArray(cardHeader.tags) ? cardHeader.tags : [cardHeader.tags]) : [] + if (tags.length === 0) return + console.log(cardHeader.title, cardHeader.tags) const mixins: UnifiedMixin[] = [] - const tags = cardHeader.tags !== undefined ? (Array.isArray(cardHeader.tags) ? cardHeader.tags : [cardHeader.tags]) : [] for (const tagPath of tags) { const cardDir = path.dirname(cardPath) const fullTagPath = path.resolve(cardDir, tagPath) const tagId = this.metadataStorage.getIdByFullPath(fullTagPath) as Ref + const tagProps: Record = {} + this.metadataStorage.getAttributes(fullTagPath).forEach((attr, label) => { + tagProps[attr.props.name] = cardHeader[label] + }) + const mixin: UnifiedMixin = { _class: card._class, mixin: tagId, props: { _id: card.props._id as Ref, space: core.space.Workspace, - __mixin: 'true' + __mixin: 'true', + ...tagProps } as unknown as Props // todo: what is the correct props type? } mixins.push(mixin) diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 85421930d60..484258b7509 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -265,7 +265,7 @@ export class ImportWorkspaceBuilder { } addTagMixin (path: string, mixin: UnifiedMixin): this { - this.validateAndAdd('tagMixin', path, mixin, (m) => this.validateTagMixin(m), this.mixins, path) + this.validateAndAdd('tagMixin', path, mixin, (m) => this.validateTagMixin(m), this.mixins, path + '/' + mixin.mixin) // todo: fix mixin key return this } From 33d061c1739e8724428735cb4a014d8c0d3e86b4 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 12:13:47 +0700 Subject: [PATCH 38/50] wip: support refto type Signed-off-by: Anna Khismatullina --- .../huly/example-workspace/SlaveCard.yaml | 2 ++ .../example-workspace/SlaveCard/Gendalf.md | 1 + packages/importer/src/huly/unified.ts | 19 ++++++++++++++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml index b1e1a108c07..8f30778d59e 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml @@ -7,3 +7,5 @@ properties: - label: color type: TypeString defaultValue: "black" # todo: is default value supported? + - label: ref + type: RefTo \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md index 1730f655c78..f0f93cd8895 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md @@ -5,6 +5,7 @@ helpers: "./Frodo.md" sex: true color: gray x: from familiar +ref: ./Queen.md --- A wise and powerful wizard who guides others. \ No newline at end of file diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index c9b173a27bd..bad2a29979a 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -368,7 +368,8 @@ export class UnifiedDocProcessor { label: 'embedded:embedded:' + property.label as IntlString, isCustom: true, type: { - _class: 'core:class:' + property.type + _class: 'core:class:' + property.type, + to: property.type === 'RefTo' ? this.metadataStorage.getIdByFullPath('/home/anna/huly/platform/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml') : undefined // todo: provide correct prop 'to' }, defaultValue: property.defaultValue ?? null } @@ -408,8 +409,20 @@ export class UnifiedDocProcessor { const relations: UnifiedDoc[] = [] for (const [key, value] of Object.entries(customProperties)) { if (masterTagAttrs.has(key)) { - const propName = masterTagAttrs.get(key)?.props.name - cardProps[propName] = value + const attr = masterTagAttrs.get(key) + if (attr === undefined) { + throw new Error(`Attribute not found: ${key}, ${cardPath}`) // todo: keep the error till builder validation + } + + const attrProps = attr.props + console.log(key, attrProps.name, value) + if (attrProps.type._class === core.class.RefTo) { + const refPath = path.resolve(path.dirname(cardPath), value) + const ref = this.metadataStorage.getIdByFullPath(refPath) as Ref + cardProps[attrProps.name] = ref + } else { + cardProps[attrProps.name] = value + } } else if (masterTagRelations.has(key) || tagAssociations.has(key)) { const metadata = masterTagRelations.get(key) ?? tagAssociations.get(key) if (metadata === undefined) { From f36c01fb8f623b2b7e2c240aef2dc5217b343803 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 12:24:21 +0700 Subject: [PATCH 39/50] support refto attributes Signed-off-by: Anna Khismatullina --- .../docs/huly/example-workspace/SlaveCard.yaml | 2 +- .../huly/example-workspace/SlaveCard/Gendalf.md | 2 +- packages/importer/src/huly/unified.ts | 17 ++++++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml index 8f30778d59e..bbf18c8e688 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml @@ -8,4 +8,4 @@ properties: type: TypeString defaultValue: "black" # todo: is default value supported? - label: ref - type: RefTo \ No newline at end of file + refTo: ./SlaveCard.yaml \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md index f0f93cd8895..3ed986b68c2 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md @@ -5,7 +5,7 @@ helpers: "./Frodo.md" sex: true color: gray x: from familiar -ref: ./Queen.md +ref: ./Worker2.md --- A wise and powerful wizard who guides others. \ No newline at end of file diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index bad2a29979a..333e905cc4a 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -57,7 +57,7 @@ export class UnifiedDocProcessor { case card.class.MasterTag: { const masterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) - const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) + const masterTagAttrs = await this.createAttributes(yamlPath, yamlConfig, masterTagId) this.metadataStorage.setAttributes(yamlPath, masterTagAttrs) result.docs.set(yamlPath, [masterTag, ...Array.from(masterTagAttrs.values())]) @@ -350,6 +350,7 @@ export class UnifiedDocProcessor { } private async createAttributes ( + currentPath: string, data: Record, masterTagId: Ref ): Promise>>> { @@ -359,6 +360,15 @@ export class UnifiedDocProcessor { const attributesByLabel = new Map>>() for (const property of data.properties) { + const type: Record = {} + if (property.refTo !== undefined) { + type._class = core.class.RefTo + const refPath = path.resolve(path.dirname(currentPath), property.refTo) + type.to = this.metadataStorage.getIdByFullPath(refPath) + } else { + type._class = 'core:class:' + property.type + } + const attr: UnifiedDoc> = { _class: core.class.Attribute, props: { @@ -367,10 +377,7 @@ export class UnifiedDocProcessor { name: generateId>(), label: 'embedded:embedded:' + property.label as IntlString, isCustom: true, - type: { - _class: 'core:class:' + property.type, - to: property.type === 'RefTo' ? this.metadataStorage.getIdByFullPath('/home/anna/huly/platform/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml') : undefined // todo: provide correct prop 'to' - }, + type, defaultValue: property.defaultValue ?? null } } From e704d085231c5abaacd33a39cf0d34f8d3ffe9f0 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 13:14:42 +0700 Subject: [PATCH 40/50] fix for tags refto attributes - not tested Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/unified.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 333e905cc4a..c693178ae40 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -292,7 +292,7 @@ export class UnifiedDocProcessor { const tagId = this.metadataStorage.getIdByFullPath(tagPath) as Ref const tag = await this.createTag(tagConfig, tagId, masterTagId, parentTagId) - const attributes = await this.createAttributes(tagConfig, tagId) + const attributes = await this.createAttributes(tagPath, tagConfig, tagId) this.metadataStorage.setAttributes(tagPath, attributes) const docs = result.docs.get(tagPath) ?? [] From 4d5e77d220d84ccca3ef9032843f8558e7780b8f Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 13:30:40 +0700 Subject: [PATCH 41/50] support importing new enums Signed-off-by: Anna Khismatullina --- .../docs/huly/example-workspace/Enum.yaml | 5 ++++ packages/importer/src/huly/huly.ts | 5 ++++ packages/importer/src/huly/unified.ts | 23 +++++++++++++++++++ packages/importer/src/importer/builder.ts | 19 +++++++++++++-- 4 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/Enum.yaml diff --git a/dev/import-tool/docs/huly/example-workspace/Enum.yaml b/dev/import-tool/docs/huly/example-workspace/Enum.yaml new file mode 100644 index 00000000000..7f431c07054 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Enum.yaml @@ -0,0 +1,5 @@ +class: core:class:Enum +title: AlphaBeta +values: + - Alpha + - Beta diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 0fd43a992da..3e06ac5d5d4 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -28,6 +28,7 @@ import core, { Attribute, type Class, type Doc, + Enum, generateId, isId, PersonId, @@ -510,6 +511,9 @@ export class HulyFormatImporter { case core.class.Relation: builder.addRelation(path, doc as UnifiedDoc) break + case core.class.Enum: + builder.addEnum(path, doc as UnifiedDoc) + break default: if (isId(doc._class) || (doc._class as string).startsWith('card:types:')) { // todo: fix system cards validation builder.addCard(path, doc as UnifiedDoc) @@ -573,6 +577,7 @@ export class HulyFormatImporter { break } + case core.class.Enum: case core.class.Association: case card.class.MasterTag: { this.logger.log(`Skipping ${spaceName}: master tag already processed`) diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index c693178ae40..84f2a23abdc 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -4,6 +4,7 @@ import core, { Association, Attribute, Doc, + Enum, generateId, Ref, Relation @@ -81,6 +82,11 @@ export class UnifiedDocProcessor { result.docs.set(yamlPath, [association]) break } + case core.class.Enum: { + const enumDoc = await this.createEnum(yamlPath, yamlConfig) + result.docs.set(yamlPath, [enumDoc]) + break + } default: throw new Error('Unsupported class: ' + yamlConfig?.class) // todo: handle default case just convert to UnifiedDoc } @@ -547,4 +553,21 @@ export class UnifiedDocProcessor { } as unknown as Props } } + + private async createEnum ( + yamlPath: string, + yamlConfig: Record + ): Promise> { + const { title, values } = yamlConfig + const enumId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + return { + _class: core.class.Enum, + props: { + _id: enumId, + space: core.space.Model, + name: title, + enumValues: values + } + } + } } diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 484258b7509..1078f4e5631 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -14,7 +14,7 @@ // import card, { Card, MasterTag, Tag } from '@hcengineering/card' import documents, { ControlledDocument, DocumentState } from '@hcengineering/controlled-documents' -import core, { Association, Attribute, Doc, Relation, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' +import core, { Association, Attribute, Doc, Enum, Relation, type DocumentQuery, type Ref, type Status, type TxOperations } from '@hcengineering/core' import document from '@hcengineering/document' import tracker, { IssuePriority, type IssueStatus } from '@hcengineering/tracker' import { @@ -64,6 +64,7 @@ export class ImportWorkspaceBuilder { private readonly cards = new Map>() private readonly associations = new Map>() private readonly relations = new Map>() + private readonly enums = new Map>() private readonly mixins = new Map>() private readonly projectTypes = new Map() @@ -264,6 +265,11 @@ export class ImportWorkspaceBuilder { return this } + addEnum (path: string, enumDoc: UnifiedDoc): this { + this.validateAndAdd('enum', path, enumDoc, (e) => this.validateEnum(e), this.enums, path) + return this + } + addTagMixin (path: string, mixin: UnifiedMixin): this { this.validateAndAdd('tagMixin', path, mixin, (m) => this.validateTagMixin(m), this.mixins, path + '/' + mixin.mixin) // todo: fix mixin key return this @@ -345,7 +351,8 @@ export class ImportWorkspaceBuilder { ...Array.from(this.tags.values()), ...Array.from(this.cards.values()), ...Array.from(this.associations.values()), - ...Array.from(this.relations.values()) + ...Array.from(this.relations.values()), + ...Array.from(this.enums.values()) ], mixins: Array.from(this.mixins.values()), attachments: [] @@ -914,6 +921,14 @@ export class ImportWorkspaceBuilder { return errors } + private validateEnum (enumDoc: UnifiedDoc): string[] { + const errors: string[] = [] + + // todo: validate enum + + return errors + } + private validateCard (card: UnifiedDoc): string[] { const errors: string[] = [] From 46ec4227db01b40c0cc4f9a32c19c117de9a6aae Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 16:43:45 +0700 Subject: [PATCH 42/50] support enumof attributes Signed-off-by: Anna Khismatullina --- .../{Enum.yaml => AlphaBetaEnum.yaml} | 0 .../huly/example-workspace/SlaveCard.yaml | 22 +++++++--- .../example-workspace/SlaveCard/Gendalf.md | 1 + packages/importer/src/huly/unified.ts | 42 +++++++++++++++---- 4 files changed, 52 insertions(+), 13 deletions(-) rename dev/import-tool/docs/huly/example-workspace/{Enum.yaml => AlphaBetaEnum.yaml} (100%) diff --git a/dev/import-tool/docs/huly/example-workspace/Enum.yaml b/dev/import-tool/docs/huly/example-workspace/AlphaBetaEnum.yaml similarity index 100% rename from dev/import-tool/docs/huly/example-workspace/Enum.yaml rename to dev/import-tool/docs/huly/example-workspace/AlphaBetaEnum.yaml diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml index bbf18c8e688..4bee33385ed 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml @@ -1,11 +1,23 @@ class: card:class:MasterTag -title: Slave Card 3 +title: Slave Card 8 properties: - label: sex type: TypeBoolean - defaultValue: false + # defaultValue: false - label: color type: TypeString - defaultValue: "black" # todo: is default value supported? - - label: ref - refTo: ./SlaveCard.yaml \ No newline at end of file + # defaultValue: "black" # todo: is default value supported? + # - label: ref + # refTo: ./SlaveCard.yaml + # - label: url + # type: TypeHyperlink + # - label: date + # type: TypeDate + - label: select-enum + enumOf: ./AlphaBetaEnum.yaml + # - label: multy-enum + # enumOf: ./AlphaBeta.yaml + # isArray: true + # - label: multy-ref + # refTo: ./SlaveCard.yaml + # isArray: true diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md index 3ed986b68c2..2e2f544f8b1 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md @@ -6,6 +6,7 @@ sex: true color: gray x: from familiar ref: ./Worker2.md +select-enum: Alpha --- A wise and powerful wizard who guides others. \ No newline at end of file diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 84f2a23abdc..353a8a13d70 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -366,14 +366,7 @@ export class UnifiedDocProcessor { const attributesByLabel = new Map>>() for (const property of data.properties) { - const type: Record = {} - if (property.refTo !== undefined) { - type._class = core.class.RefTo - const refPath = path.resolve(path.dirname(currentPath), property.refTo) - type.to = this.metadataStorage.getIdByFullPath(refPath) - } else { - type._class = 'core:class:' + property.type - } + const type = await this.convertPropertyType(property, currentPath) const attr: UnifiedDoc> = { _class: core.class.Attribute, @@ -392,6 +385,39 @@ export class UnifiedDocProcessor { return attributesByLabel } + private async convertPropertyType (property: Record, currentPath: string): Promise> { + const type: Record = {} + if (property.refTo !== undefined) { + type._class = core.class.RefTo + const refPath = path.resolve(path.dirname(currentPath), property.refTo) + type.to = this.metadataStorage.getIdByFullPath(refPath) + type.label = 'core:string:Ref' + } else if (property.enumOf !== undefined) { + type._class = core.class.EnumOf + const enumPath = path.resolve(path.dirname(currentPath), property.enumOf) + type.of = this.metadataStorage.getIdByFullPath(enumPath) + type.label = 'core:string:Enum' + } else { + switch (property.type) { + case 'TypeString': + type._class = core.class.TypeString + type.label = 'core:string:String' + break + case 'TypeNumber': + type._class = core.class.TypeNumber + type.label = 'core:number:Number' + break + case 'TypeBoolean': + type._class = core.class.TypeBoolean + type.label = 'core:boolean:Boolean' + break + default: + throw new Error('Unsupported type: ' + property.type + ' ' + currentPath) + } + } + return type + } + private async createCard ( cardHeader: Record, cardPath: string, From 772b82c11038a4d177b633e5c592bd8c37db6786 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 17:23:19 +0700 Subject: [PATCH 43/50] support multy-select - multy-ref not tested Signed-off-by: Anna Khismatullina --- .../huly/example-workspace/SlaveCard.yaml | 14 ++--- .../huly/example-workspace/SlaveCard/Frodo.md | 1 + .../example-workspace/SlaveCard/Gendalf.md | 6 +- packages/importer/src/huly/unified.ts | 56 +++++++++++++------ 4 files changed, 53 insertions(+), 24 deletions(-) diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml index 4bee33385ed..208d94645c0 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml @@ -1,5 +1,5 @@ class: card:class:MasterTag -title: Slave Card 8 +title: Slave Card 11 properties: - label: sex type: TypeBoolean @@ -15,9 +15,9 @@ properties: # type: TypeDate - label: select-enum enumOf: ./AlphaBetaEnum.yaml - # - label: multy-enum - # enumOf: ./AlphaBeta.yaml - # isArray: true - # - label: multy-ref - # refTo: ./SlaveCard.yaml - # isArray: true + - label: multy-enum + enumOf: ./AlphaBetaEnum.yaml + isArray: true + - label: multy-ref + refTo: ./SlaveCard.yaml + isArray: true diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md index c1af614b9fe..e1c86d10dcd 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md @@ -8,6 +8,7 @@ sex: true color: pink x: from familiar y: from minion +multy-enum: [Beta] --- A brave hobbit guided by Gandalf. diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md index 2e2f544f8b1..0a311753af3 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Gendalf.md @@ -6,7 +6,11 @@ sex: true color: gray x: from familiar ref: ./Worker2.md -select-enum: Alpha +# select-enum: Alpha +# multy-enum: [Alpha, Beta] +multy-ref: + - ./Queen.md + - ./Worker1.md --- A wise and powerful wizard who guides others. \ No newline at end of file diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 353a8a13d70..1d372a6b7e5 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -386,30 +386,46 @@ export class UnifiedDocProcessor { } private async convertPropertyType (property: Record, currentPath: string): Promise> { - const type: Record = {} + let type: Record = {} if (property.refTo !== undefined) { - type._class = core.class.RefTo + const baseType: Record = {} + baseType._class = core.class.RefTo const refPath = path.resolve(path.dirname(currentPath), property.refTo) - type.to = this.metadataStorage.getIdByFullPath(refPath) - type.label = 'core:string:Ref' + baseType.to = this.metadataStorage.getIdByFullPath(refPath) + baseType.label = core.string.Ref + type = property.isArray === true + ? { + _class: core.class.ArrOf, + label: core.string.Array, + of: baseType + } + : baseType } else if (property.enumOf !== undefined) { - type._class = core.class.EnumOf + const baseType: Record = {} + baseType._class = core.class.EnumOf const enumPath = path.resolve(path.dirname(currentPath), property.enumOf) - type.of = this.metadataStorage.getIdByFullPath(enumPath) - type.label = 'core:string:Enum' + baseType.of = this.metadataStorage.getIdByFullPath(enumPath) + baseType.label = 'core:string:Enum' + type = property.isArray === true + ? { + _class: core.class.ArrOf, + label: core.string.Array, + of: baseType + } + : baseType } else { switch (property.type) { case 'TypeString': type._class = core.class.TypeString - type.label = 'core:string:String' + type.label = core.string.String break case 'TypeNumber': type._class = core.class.TypeNumber - type.label = 'core:number:Number' + type.label = core.string.Number break case 'TypeBoolean': type._class = core.class.TypeBoolean - type.label = 'core:boolean:Boolean' + type.label = core.string.Boolean break default: throw new Error('Unsupported type: ' + property.type + ' ' + currentPath) @@ -455,13 +471,21 @@ export class UnifiedDocProcessor { const attrProps = attr.props console.log(key, attrProps.name, value) - if (attrProps.type._class === core.class.RefTo) { - const refPath = path.resolve(path.dirname(cardPath), value) - const ref = this.metadataStorage.getIdByFullPath(refPath) as Ref - cardProps[attrProps.name] = ref - } else { - cardProps[attrProps.name] = value + + const attrType = attrProps.type + const attrBaseType = attrType._class === core.class.ArrOf ? attrType.of : attrType + const values = attrType._class === core.class.ArrOf ? value : [value] + const propValues = [] + for (const val of values) { + if (attrBaseType._class === core.class.RefTo) { + const refPath = path.resolve(path.dirname(cardPath), val) + const ref = this.metadataStorage.getIdByFullPath(refPath) as Ref + propValues.push(ref) + } else { + propValues.push(val) + } } + cardProps[attrProps.name] = attrType._class === core.class.ArrOf ? propValues : propValues[0] } else if (masterTagRelations.has(key) || tagAssociations.has(key)) { const metadata = masterTagRelations.get(key) ?? tagAssociations.get(key) if (metadata === undefined) { From 07b3a7d35d695e6fcff9479a22a3369c8f34dcfc Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 19:35:31 +0700 Subject: [PATCH 44/50] support attachments Signed-off-by: Anna Khismatullina --- .vscode/launch.json | 2 +- .../huly/example-workspace/SlaveCard.yaml | 2 +- .../huly/example-workspace/SlaveCard/Frodo.md | 5 + packages/importer/src/huly/huly.ts | 10 +- packages/importer/src/huly/metadata.ts | 2 +- packages/importer/src/huly/unified.ts | 119 ++++++++---------- packages/importer/src/importer/builder.ts | 35 +++++- packages/importer/src/importer/importer.ts | 44 ++++++- packages/importer/src/types.ts | 10 +- 9 files changed, 155 insertions(+), 74 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 4540a70bc5c..164f4d12d1d 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -839,7 +839,7 @@ "-pw", "1234", "-ws", - "ws4" + "ws1" ], "env": { "FRONT_URL": "http://localhost:8087" diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml index 208d94645c0..dda314ba429 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard.yaml @@ -1,5 +1,5 @@ class: card:class:MasterTag -title: Slave Card 11 +title: Slave Card 1 properties: - label: sex type: TypeBoolean diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md index e1c86d10dcd..ddf394bead9 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md @@ -9,6 +9,11 @@ color: pink x: from familiar y: from minion multy-enum: [Beta] +attachments: + - ../../CARDS_INSTRUCTIONS.md + - ../files/screenshot.png +# blobs: +# - ../README.md --- A brave hobbit guided by Gandalf. diff --git a/packages/importer/src/huly/huly.ts b/packages/importer/src/huly/huly.ts index 3e06ac5d5d4..7369ad0c3f6 100644 --- a/packages/importer/src/huly/huly.ts +++ b/packages/importer/src/huly/huly.ts @@ -68,6 +68,7 @@ import { type FileUploader } from '../importer/uploader' import { UnifiedDoc } from '../types' import { readMarkdownContent, readYamlHeader } from './parsing' import { UnifiedDocProcessor } from './unified' +import attachment from '@hcengineering/model-attachment' export interface HulyComment { author: string @@ -490,7 +491,7 @@ export class HulyFormatImporter { } // Импортируем UnifiedDoc сущности - const { docs: unifiedDocs, mixins: unifiedMixins } = await this.unifiedDocImporter.importFromDirectory(folderPath) + const { docs: unifiedDocs, mixins: unifiedMixins, files } = await this.unifiedDocImporter.importFromDirectory(folderPath) // Разбираем и добавляем в билдер по классу for (const [path, docs] of unifiedDocs.entries()) { @@ -514,6 +515,9 @@ export class HulyFormatImporter { case core.class.Enum: builder.addEnum(path, doc as UnifiedDoc) break + case attachment.class.Attachment: + builder.addAttachment(path, doc as UnifiedDoc) + break default: if (isId(doc._class) || (doc._class as string).startsWith('card:types:')) { // todo: fix system cards validation builder.addCard(path, doc as UnifiedDoc) @@ -532,6 +536,10 @@ export class HulyFormatImporter { } } + for (const [path, file] of files.entries()) { + builder.addFile(path, file) + } + // Process all yaml files first const yamlFiles = fs.readdirSync(folderPath).filter((f) => f.endsWith('.yaml') && f !== 'settings.yaml') diff --git a/packages/importer/src/huly/metadata.ts b/packages/importer/src/huly/metadata.ts index 641a4b30ac0..f61f3c75b3c 100644 --- a/packages/importer/src/huly/metadata.ts +++ b/packages/importer/src/huly/metadata.ts @@ -17,7 +17,7 @@ export interface TagMetadata { } export class MetadataStorage { - private readonly pathToRef = new Map>() + private readonly pathToRef = new Map>() // todo: attachments to a separate map? private readonly pathToMetadata = new Map() public getIdByFullPath (path: string): Ref { diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 1d372a6b7e5..f0e4a062f5b 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -1,8 +1,11 @@ // unified.ts +import { Attachment } from '@hcengineering/attachment' import card, { Card, MasterTag, Tag } from '@hcengineering/card' import core, { Association, Attribute, + Blob as PlatformBlob, + Class, Doc, Enum, generateId, @@ -11,15 +14,17 @@ import core, { } from '@hcengineering/core' import * as fs from 'fs' import * as yaml from 'js-yaml' +import { contentType } from 'mime-types' import * as path from 'path' import { IntlString } from '../../../platform/types' -import { Props, UnifiedDoc, UnifiedMixin } from '../types' +import { Props, UnifiedDoc, UnifiedFile, UnifiedMixin } from '../types' import { MetadataStorage, RelationMetadata } from './metadata' import { readMarkdownContent, readYamlHeader } from './parsing' export interface UnifiedDocProcessResult { docs: Map>> mixins: Map>> + files: Map } export class UnifiedDocProcessor { @@ -28,7 +33,8 @@ export class UnifiedDocProcessor { async importFromDirectory (directoryPath: string): Promise { const result: UnifiedDocProcessResult = { docs: new Map(), - mixins: new Map() + mixins: new Map(), + files: new Map() } // Первый проход - собираем метаданные await this.processMetadata(directoryPath, result) @@ -137,68 +143,6 @@ export class UnifiedDocProcessor { } } - // private async processDirectory ( - // currentPath: string, - // result: UnifiedDocProcessResult, - // parentMasterTagId?: Ref, - // parentMasterTagAttrs?: Map>> - // ): Promise { - // const entries = fs.readdirSync(currentPath, { withFileTypes: true }) - - // // Сначала обрабатываем YAML файлы (потенциальные мастер-теги) - // for (const entry of entries) { - // if (!entry.isFile() || !entry.name.endsWith('.yaml')) continue // todo: filter entries by extension - - // const yamlPath = path.resolve(currentPath, entry.name) - // const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record - - // switch (yamlConfig?.class) { - // case card.class.MasterTag: { - // const masterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref - // const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) - - // const masterTagAttrs = await this.createAttributes(yamlConfig, masterTagId) - // this.metadataStorage.setAttributes(yamlPath, masterTagAttrs) - - // const docs = result.docs.get(yamlPath) ?? [] - // docs.push( - // masterTag, - // ...Array.from(masterTagAttrs.values()) - // ) - // result.docs.set(yamlPath, docs) - - // const masterTagDir = path.join(currentPath, path.basename(yamlPath, '.yaml')) - // if (fs.existsSync(masterTagDir) && fs.statSync(masterTagDir).isDirectory()) { - // await this.processDirectory(masterTagDir, result, masterTagId, masterTagAttrs) - // } - // break - // } - // case card.class.Tag: { - // if (parentMasterTagId === undefined) { - // throw new Error('Tag should be inside master tag folder: ' + currentPath) - // } - - // await this.processTag(yamlPath, yamlConfig, result, parentMasterTagId) - // break - // } - // case core.class.Association: { - // const association = await this.createAssociation(yamlPath, yamlConfig) - // result.docs.set(yamlPath, [association]) - // break - // } - // default: - // throw new Error('Unsupported class: ' + yamlConfig?.class) // todo: handle default case just convert to UnifiedDoc - // } - // } - - // if (parentMasterTagId === undefined || parentMasterTagAttrs === undefined) { - // await this.processSystemCards(currentPath, result) - // } else { - // // await this.processCardDirectory(result, currentPath, parentMasterTagId, parentMasterTagAttrs) // todo: handle parent master tag attrs - // await this.processCardDirectory(result, currentPath, parentMasterTagId) - // } - // } - private async processSystemCards ( currentDir: string, result: UnifiedDocProcessResult, @@ -239,6 +183,9 @@ export class UnifiedDocProcessor { const card = cardWithRelations[0] as UnifiedDoc await this.applyTags(card, cardProps, cardPath, result) + const attachments = cardProps.attachments ?? [] + await this.processAttachments(attachments, cardPath, card, result) + // Проверяем наличие дочерних карточек const cardDir = path.join(path.dirname(cardPath), path.basename(cardPath, '.md')) if (fs.existsSync(cardDir) && fs.statSync(cardDir).isDirectory()) { @@ -563,6 +510,50 @@ export class UnifiedDocProcessor { } } + private async processAttachments ( + attachments: string[], + cardPath: string, + card: UnifiedDoc, + result: UnifiedDocProcessResult + ): Promise { + for (const attachment of attachments) { + const attachmentPath = path.resolve(path.dirname(cardPath), attachment) + const attachmentName = path.basename(attachmentPath) + const fileId = this.metadataStorage.getIdByFullPath(attachmentPath) as Ref + const type = contentType(attachmentPath) + const size = fs.statSync(attachmentPath).size + + const file: UnifiedFile = { + _id: fileId, // id for datastore + name: attachmentName, + blobProvider: async () => { + const data = fs.readFileSync(attachmentPath) + const props = type !== false ? { type } : undefined + return new Blob([data], props) + } + } + result.files.set(attachmentPath, file) + + const attachmentId = this.metadataStorage.getIdByFullPath(attachmentPath) as Ref + const attachmentDoc: UnifiedDoc = { + _class: 'attachment:class:Attachment' as Ref>, + props: { + _id: attachmentId, // id for attachment doc + space: core.space.Workspace, + attachedTo: card.props._id as Ref, + attachedToClass: card._class, + file: fileId, + name: attachmentName, + collection: 'attachments', + lastModified: Date.now(), + type: type !== false ? type : 'application/octet-stream', + size + } + } + result.docs.set(attachmentPath, [attachmentDoc]) + } + } + private async createAssociation ( yamlPath: string, yamlConfig: Record diff --git a/packages/importer/src/importer/builder.ts b/packages/importer/src/importer/builder.ts index 1078f4e5631..396608ae65a 100644 --- a/packages/importer/src/importer/builder.ts +++ b/packages/importer/src/importer/builder.ts @@ -29,7 +29,8 @@ import { type ImportTeamspace, type ImportWorkspace } from './importer' -import { UnifiedDoc, UnifiedMixin } from '../types' +import { UnifiedDoc, UnifiedFile, UnifiedMixin } from '../types' +import { Attachment } from '@hcengineering/attachment' export interface ValidationError { path: string @@ -65,7 +66,9 @@ export class ImportWorkspaceBuilder { private readonly associations = new Map>() private readonly relations = new Map>() private readonly enums = new Map>() + private readonly attachments = new Map>() private readonly mixins = new Map>() + private readonly files = new Map() private readonly projectTypes = new Map() private readonly issueStatusCache = new Map>() @@ -270,11 +273,21 @@ export class ImportWorkspaceBuilder { return this } + addAttachment (path: string, attachment: UnifiedDoc): this { + this.validateAndAdd('attachment', path, attachment, (a) => this.validateAttachment(a), this.attachments, path) + return this + } + addTagMixin (path: string, mixin: UnifiedMixin): this { this.validateAndAdd('tagMixin', path, mixin, (m) => this.validateTagMixin(m), this.mixins, path + '/' + mixin.mixin) // todo: fix mixin key return this } + addFile (path: string, file: UnifiedFile): this { + this.validateAndAdd('file', path, file, (f) => this.validateFile(f), this.files, path) + return this + } + validate (): ValidationResult { // Perform cross-entity validation this.validateSpacesReferences() @@ -352,9 +365,11 @@ export class ImportWorkspaceBuilder { ...Array.from(this.cards.values()), ...Array.from(this.associations.values()), ...Array.from(this.relations.values()), - ...Array.from(this.enums.values()) + ...Array.from(this.enums.values()), + ...Array.from(this.attachments.values()) ], mixins: Array.from(this.mixins.values()), + files: Array.from(this.files.values()), attachments: [] } } @@ -929,6 +944,14 @@ export class ImportWorkspaceBuilder { return errors } + private validateAttachment (attachment: UnifiedDoc): string[] { + const errors: string[] = [] + + // todo: validate attachment + + return errors + } + private validateCard (card: UnifiedDoc): string[] { const errors: string[] = [] @@ -1001,6 +1024,14 @@ export class ImportWorkspaceBuilder { return errors } + private validateFile (file: UnifiedFile): string[] { + const errors: string[] = [] + + // todo: validate file + + return errors + } + private validateOrgSpace (space: ImportOrgSpace): string[] { const errors: string[] = [] diff --git a/packages/importer/src/importer/importer.ts b/packages/importer/src/importer/importer.ts index 9cdfb70bfba..634539844bb 100644 --- a/packages/importer/src/importer/importer.ts +++ b/packages/importer/src/importer/importer.ts @@ -32,6 +32,7 @@ import documents, { import core, { type AccountUuid, type AttachedData, + AttachedDoc, type Class, type CollaborativeDoc, type Data, @@ -69,7 +70,7 @@ import tracker, { TimeReportDayType } from '@hcengineering/tracker' import view from '@hcengineering/view' -import { UnifiedDoc, UnifiedMixin } from '../types' +import { Props, UnifiedDoc, UnifiedFile, UnifiedMixin } from '../types' import { Logger } from './logger' import { type MarkdownPreprocessor, NoopMarkdownPreprocessor } from './preprocessor' import { type FileUploader } from './uploader' @@ -77,8 +78,10 @@ export interface ImportWorkspace { projectTypes?: ImportProjectType[] spaces?: ImportSpace[] attachments?: ImportAttachment[] + unifiedDocs?: UnifiedDoc>[] mixins?: UnifiedMixin, Doc>[] + files?: UnifiedFile[] } export interface ImportProjectType { @@ -243,8 +246,10 @@ export class WorkspaceImporter { await this.importProjectTypes() await this.importSpaces() await this.importAttachments() + await this.importUnifiedDocs() await this.importUnifiedMixins() + await this.uploadFiles() } private async importProjectTypes (): Promise { @@ -1151,7 +1156,30 @@ export class WorkspaceImporter { const res = await this.createCollaborativeContent(_id, collabId, collabContent, props.space) ;(props as any)[unifiedDoc.collabField] = res } - await this.client.createDoc(_class, props.space, props as Data>, _id) + + const hierarchy = this.client.getHierarchy() + if (hierarchy.isDerived(_class, core.class.AttachedDoc)) { + const { space, attachedTo, attachedToClass, collection, ...data } = props as unknown as Props + if ( + attachedTo === undefined || + space === undefined || + attachedToClass === undefined || + collection === undefined + ) { + throw new Error('Add collection step must have attachedTo, attachedToClass, collection and space') + } + await this.client.addCollection( + _class, + space, + attachedTo, + attachedToClass, + collection, + data, + _id as Ref | undefined + ) + } else { + await this.client.createDoc(_class, props.space, props as Data>, _id) + } } private async importUnifiedMixins (): Promise { @@ -1167,4 +1195,16 @@ export class WorkspaceImporter { const { _id, space, ...data } = props await this.client.createMixin(_id ?? generateId>(), _class, space, mixinClass, data as Data>) } + + private async uploadFiles (): Promise { + if (this.workspaceData.files === undefined) return + + for (const file of this.workspaceData.files) { + const id = file._id ?? generateId() + const uploadResult = await this.fileUploader.uploadFile(id, await file.blobProvider()) + if (!uploadResult.success) { + throw new Error('Failed to upload attachment file: ' + file.name) + } + } + } } diff --git a/packages/importer/src/types.ts b/packages/importer/src/types.ts index e595ac05ef0..3680670f8a8 100644 --- a/packages/importer/src/types.ts +++ b/packages/importer/src/types.ts @@ -1,5 +1,4 @@ -import { Class, Data, Doc, Mixin, Ref, Space } from '@hcengineering/core' - +import { Class, Data, Doc, Mixin, Ref, Space, Blob as PlatformBlob } from '@hcengineering/core' export type Props = Data & Partial & { space: Ref } export interface UnifiedDoc { @@ -15,4 +14,11 @@ export interface UnifiedMixin { // todo: extends T props: Props } +export interface UnifiedFile { + _id: Ref + name: string + blobProvider: blobProvider +} + export type contentProvider = () => Promise +export type blobProvider = () => Promise From 4558068fd9a3fdd095166e7a50f211a8216d4ab7 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 20:07:59 +0700 Subject: [PATCH 45/50] renamings Signed-off-by: Anna Khismatullina --- packages/importer/src/huly/metadata.ts | 10 +++--- packages/importer/src/huly/unified.ts | 46 +++++++++++++------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/importer/src/huly/metadata.ts b/packages/importer/src/huly/metadata.ts index f61f3c75b3c..8c182040b36 100644 --- a/packages/importer/src/huly/metadata.ts +++ b/packages/importer/src/huly/metadata.ts @@ -3,7 +3,7 @@ import { Association, Attribute, Doc, generateId, Ref } from '@hcengineering/cor import path from 'path' import { UnifiedDoc } from '../types' -export interface RelationMetadata { +export interface RelationMetadata { // todo: rename association: Ref field: 'docA' | 'docB' type: '1:1' | '1:N' | 'N:N' @@ -20,7 +20,7 @@ export class MetadataStorage { private readonly pathToRef = new Map>() // todo: attachments to a separate map? private readonly pathToMetadata = new Map() - public getIdByFullPath (path: string): Ref { + public getIdByAbsolutePath (path: string): Ref { let id = this.pathToRef.get(path) if (id === undefined) { id = generateId() @@ -31,7 +31,7 @@ export class MetadataStorage { public getIdByRelativePath (currentPath: string, relativePath: string): Ref { const fullPath = path.resolve(currentPath, relativePath) - return this.getIdByFullPath(fullPath) + return this.getIdByAbsolutePath(fullPath) } public hasMetadata (path: string): boolean { @@ -48,7 +48,7 @@ export class MetadataStorage { public setAttributes (path: string, attributes: MapAttributeToUnifiedDoc): void { const metadata = this.pathToMetadata.get(path) ?? { - _id: this.getIdByFullPath(path), + _id: this.getIdByAbsolutePath(path), attributes: new Map(), associations: new Map() } @@ -58,7 +58,7 @@ export class MetadataStorage { public addAssociation (tagPath: string, propName: string, relationMetadata: RelationMetadata): void { const metadata = this.pathToMetadata.get(tagPath) ?? { - _id: this.getIdByFullPath(tagPath), + _id: this.getIdByAbsolutePath(tagPath), attributes: new Map(), associations: new Map() } diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index f0e4a062f5b..b2ef2644ad6 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -4,11 +4,11 @@ import card, { Card, MasterTag, Tag } from '@hcengineering/card' import core, { Association, Attribute, - Blob as PlatformBlob, Class, Doc, Enum, generateId, + Blob as PlatformBlob, Ref, Relation } from '@hcengineering/core' @@ -62,7 +62,7 @@ export class UnifiedDocProcessor { switch (yamlConfig?.class) { case card.class.MasterTag: { - const masterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + const masterTagId = this.metadataStorage.getIdByAbsolutePath(yamlPath) as Ref const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) const masterTagAttrs = await this.createAttributes(yamlPath, yamlConfig, masterTagId) @@ -113,7 +113,7 @@ export class UnifiedDocProcessor { if (fs.existsSync(yamlPath)) { const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record if (yamlConfig?.class === card.class.MasterTag) { - masterTagId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + masterTagId = this.metadataStorage.getIdByAbsolutePath(yamlPath) as Ref this.metadataStorage.getAssociations(yamlPath).forEach((relationMetadata, propName) => { masterTagRelations.set(propName, relationMetadata) }) @@ -173,7 +173,7 @@ export class UnifiedDocProcessor { masterTagAttrs: Map>>, parentCardId?: Ref ): Promise { - const cardWithRelations = await this.createCard(cardProps, cardPath, masterTagId, masterTagRelations, masterTagAttrs, parentCardId) + const cardWithRelations = await this.createCardWithRelations(cardProps, cardPath, masterTagId, masterTagRelations, masterTagAttrs, parentCardId, blobs) if (cardWithRelations.length > 0) { const docs = result.docs.get(cardPath) ?? [] @@ -183,10 +183,10 @@ export class UnifiedDocProcessor { const card = cardWithRelations[0] as UnifiedDoc await this.applyTags(card, cardProps, cardPath, result) - const attachments = cardProps.attachments ?? [] - await this.processAttachments(attachments, cardPath, card, result) + if (cardProps.attachments !== undefined) { + await this.processAttachments(cardProps.attachments, cardPath, card, result) + } - // Проверяем наличие дочерних карточек const cardDir = path.join(path.dirname(cardPath), path.basename(cardPath, '.md')) if (fs.existsSync(cardDir) && fs.statSync(cardDir).isDirectory()) { await this.processCardDirectory(result, cardDir, masterTagId, masterTagRelations, masterTagAttrs, card.props._id as Ref) @@ -242,7 +242,7 @@ export class UnifiedDocProcessor { masterTagId: Ref, parentTagId?: Ref ): Promise { - const tagId = this.metadataStorage.getIdByFullPath(tagPath) as Ref + const tagId = this.metadataStorage.getIdByAbsolutePath(tagPath) as Ref const tag = await this.createTag(tagConfig, tagId, masterTagId, parentTagId) const attributes = await this.createAttributes(tagPath, tagConfig, tagId) @@ -338,7 +338,7 @@ export class UnifiedDocProcessor { const baseType: Record = {} baseType._class = core.class.RefTo const refPath = path.resolve(path.dirname(currentPath), property.refTo) - baseType.to = this.metadataStorage.getIdByFullPath(refPath) + baseType.to = this.metadataStorage.getIdByAbsolutePath(refPath) baseType.label = core.string.Ref type = property.isArray === true ? { @@ -351,7 +351,7 @@ export class UnifiedDocProcessor { const baseType: Record = {} baseType._class = core.class.EnumOf const enumPath = path.resolve(path.dirname(currentPath), property.enumOf) - baseType.of = this.metadataStorage.getIdByFullPath(enumPath) + baseType.of = this.metadataStorage.getIdByAbsolutePath(enumPath) baseType.label = 'core:string:Enum' type = property.isArray === true ? { @@ -381,7 +381,7 @@ export class UnifiedDocProcessor { return type } - private async createCard ( + private async createCardWithRelations ( cardHeader: Record, cardPath: string, masterTagId: Ref, @@ -392,7 +392,7 @@ export class UnifiedDocProcessor { const { _class, title, tags: rawTags, ...customProperties } = cardHeader const tags = rawTags !== undefined ? (Array.isArray(rawTags) ? rawTags : [rawTags]) : [] - const cardId = this.metadataStorage.getIdByFullPath(cardPath) as Ref + const cardId = this.metadataStorage.getIdByAbsolutePath(cardPath) as Ref const cardProps: Record = { _id: cardId, space: core.space.Workspace, @@ -426,7 +426,7 @@ export class UnifiedDocProcessor { for (const val of values) { if (attrBaseType._class === core.class.RefTo) { const refPath = path.resolve(path.dirname(cardPath), val) - const ref = this.metadataStorage.getIdByFullPath(refPath) as Ref + const ref = this.metadataStorage.getIdByAbsolutePath(refPath) as Ref propValues.push(ref) } else { propValues.push(val) @@ -439,7 +439,7 @@ export class UnifiedDocProcessor { throw new Error(`Association not found: ${key}, ${cardPath}`) // todo: keep the error till builder validation } const otherCardPath = path.resolve(path.dirname(cardPath), value) // todo: value can be array of paths - const otherCardId = this.metadataStorage.getIdByFullPath(otherCardPath) as Ref + const otherCardId = this.metadataStorage.getIdByAbsolutePath(otherCardPath) as Ref const relation: UnifiedDoc = this.createRelation(metadata, cardId, otherCardId) relations.push(relation) } @@ -484,11 +484,11 @@ export class UnifiedDocProcessor { const mixins: UnifiedMixin[] = [] for (const tagPath of tags) { const cardDir = path.dirname(cardPath) - const fullTagPath = path.resolve(cardDir, tagPath) - const tagId = this.metadataStorage.getIdByFullPath(fullTagPath) as Ref + const tagAbsPath = path.resolve(cardDir, tagPath) + const tagId = this.metadataStorage.getIdByAbsolutePath(tagAbsPath) as Ref const tagProps: Record = {} - this.metadataStorage.getAttributes(fullTagPath).forEach((attr, label) => { + this.metadataStorage.getAttributes(tagAbsPath).forEach((attr, label) => { tagProps[attr.props.name] = cardHeader[label] }) @@ -519,7 +519,7 @@ export class UnifiedDocProcessor { for (const attachment of attachments) { const attachmentPath = path.resolve(path.dirname(cardPath), attachment) const attachmentName = path.basename(attachmentPath) - const fileId = this.metadataStorage.getIdByFullPath(attachmentPath) as Ref + const fileId = this.metadataStorage.getIdByAbsolutePath(attachmentPath) as Ref const type = contentType(attachmentPath) const size = fs.statSync(attachmentPath).size @@ -534,7 +534,7 @@ export class UnifiedDocProcessor { } result.files.set(attachmentPath, file) - const attachmentId = this.metadataStorage.getIdByFullPath(attachmentPath) as Ref + const attachmentId = this.metadataStorage.getIdByAbsolutePath(attachmentPath) as Ref const attachmentDoc: UnifiedDoc = { _class: 'attachment:class:Attachment' as Ref>, props: { @@ -562,7 +562,7 @@ export class UnifiedDocProcessor { const { class: _class, typeA, typeB, type, nameA, nameB } = yamlConfig const currentPath = path.dirname(yamlPath) - const associationId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + const associationId = this.metadataStorage.getIdByAbsolutePath(yamlPath) as Ref const typeAPath = path.resolve(currentPath, typeA) this.metadataStorage.addAssociation(typeAPath, nameB, { @@ -578,8 +578,8 @@ export class UnifiedDocProcessor { type }) - const typeAId = this.metadataStorage.getIdByFullPath(typeAPath) as Ref - const typeBId = this.metadataStorage.getIdByFullPath(typeBPath) as Ref + const typeAId = this.metadataStorage.getIdByAbsolutePath(typeAPath) as Ref + const typeBId = this.metadataStorage.getIdByAbsolutePath(typeBPath) as Ref return { _class, @@ -600,7 +600,7 @@ export class UnifiedDocProcessor { yamlConfig: Record ): Promise> { const { title, values } = yamlConfig - const enumId = this.metadataStorage.getIdByFullPath(yamlPath) as Ref + const enumId = this.metadataStorage.getIdByAbsolutePath(yamlPath) as Ref return { _class: core.class.Enum, props: { From 1e0e6bf03398ba5961269eea88099955d5991c30 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 20:54:08 +0700 Subject: [PATCH 46/50] support blobs Signed-off-by: Anna Khismatullina --- .../docs/huly/example-workspace/FileImport.md | 9 ++ .../huly/example-workspace/SlaveCard/Frodo.md | 6 +- packages/importer/src/huly/metadata.ts | 11 +-- packages/importer/src/huly/unified.ts | 92 ++++++++++++++----- packages/importer/src/types.ts | 2 + 5 files changed, 87 insertions(+), 33 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/FileImport.md diff --git a/dev/import-tool/docs/huly/example-workspace/FileImport.md b/dev/import-tool/docs/huly/example-workspace/FileImport.md new file mode 100644 index 00000000000..62c0d2b7671 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/FileImport.md @@ -0,0 +1,9 @@ +--- +class: card:types:File +title: File with blob +blobs: + - ./files/screenshot.png +--- + +*DESCR)* +[](./files/screenshot.png) \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md index ddf394bead9..74cd2f3a0a3 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md @@ -9,10 +9,10 @@ color: pink x: from familiar y: from minion multy-enum: [Beta] -attachments: - - ../../CARDS_INSTRUCTIONS.md +# attachments: +# - ../../CARDS_INSTRUCTIONS.md +blobs: - ../files/screenshot.png -# blobs: # - ../README.md --- diff --git a/packages/importer/src/huly/metadata.ts b/packages/importer/src/huly/metadata.ts index 8c182040b36..27373b9dce6 100644 --- a/packages/importer/src/huly/metadata.ts +++ b/packages/importer/src/huly/metadata.ts @@ -1,7 +1,7 @@ import { Tag } from '@hcengineering/card' -import { Association, Attribute, Doc, generateId, Ref } from '@hcengineering/core' +import { Association, Attribute, Doc, generateId, Ref, Blob as PlatformBlob } from '@hcengineering/core' import path from 'path' -import { UnifiedDoc } from '../types' +import { UnifiedDoc, UnifiedFile } from '../types' export interface RelationMetadata { // todo: rename association: Ref @@ -10,6 +10,7 @@ export interface RelationMetadata { // todo: rename } export type MapAttributeToUnifiedDoc = Map>> export type MapNameToIsMany = Map // todo: rename + export interface TagMetadata { _id: string attributes: MapAttributeToUnifiedDoc // title -> attribute id @@ -19,6 +20,7 @@ export interface TagMetadata { export class MetadataStorage { private readonly pathToRef = new Map>() // todo: attachments to a separate map? private readonly pathToMetadata = new Map() + private readonly pathToBlobUuid = new Map>() // todo: blobs to a separate map? public getIdByAbsolutePath (path: string): Ref { let id = this.pathToRef.get(path) @@ -29,11 +31,6 @@ export class MetadataStorage { return id } - public getIdByRelativePath (currentPath: string, relativePath: string): Ref { - const fullPath = path.resolve(currentPath, relativePath) - return this.getIdByAbsolutePath(fullPath) - } - public hasMetadata (path: string): boolean { return this.pathToMetadata.has(path) } diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index b2ef2644ad6..9215cd20835 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -4,6 +4,7 @@ import card, { Card, MasterTag, Tag } from '@hcengineering/card' import core, { Association, Attribute, + BlobType, Class, Doc, Enum, @@ -29,7 +30,6 @@ export interface UnifiedDocProcessResult { export class UnifiedDocProcessor { private readonly metadataStorage = new MetadataStorage() - async importFromDirectory (directoryPath: string): Promise { const result: UnifiedDocProcessResult = { docs: new Map(), @@ -173,7 +173,11 @@ export class UnifiedDocProcessor { masterTagAttrs: Map>>, parentCardId?: Ref ): Promise { - const cardWithRelations = await this.createCardWithRelations(cardProps, cardPath, masterTagId, masterTagRelations, masterTagAttrs, parentCardId, blobs) + if (cardProps.blobs !== undefined) { + await this.createBlobs(cardProps.blobs, cardPath, result) + } + + const cardWithRelations = await this.createCardWithRelations(cardProps, cardPath, masterTagId, masterTagRelations, masterTagAttrs, result.files, parentCardId) if (cardWithRelations.length > 0) { const docs = result.docs.get(cardPath) ?? [] @@ -184,7 +188,7 @@ export class UnifiedDocProcessor { await this.applyTags(card, cardProps, cardPath, result) if (cardProps.attachments !== undefined) { - await this.processAttachments(cardProps.attachments, cardPath, card, result) + await this.createAttachments(cardProps.attachments, cardPath, card, result) } const cardDir = path.join(path.dirname(cardPath), path.basename(cardPath, '.md')) @@ -387,10 +391,12 @@ export class UnifiedDocProcessor { masterTagId: Ref, masterTagRelations: Map, // todo: rename to masterTagsAssociations masterTagAttrs: Map>>, + blobFiles: Map, parentCardId?: Ref ): Promise[]> { - const { _class, title, tags: rawTags, ...customProperties } = cardHeader + const { _class, title, blobs: rawBlobs, tags: rawTags, ...customProperties } = cardHeader const tags = rawTags !== undefined ? (Array.isArray(rawTags) ? rawTags : [rawTags]) : [] + const blobs = rawBlobs !== undefined ? (Array.isArray(rawBlobs) ? rawBlobs : [rawBlobs]) : [] const cardId = this.metadataStorage.getIdByAbsolutePath(cardPath) as Ref const cardProps: Record = { @@ -400,6 +406,24 @@ export class UnifiedDocProcessor { parent: parentCardId } + if (blobs.length > 0) { + const blobProps: Record = {} + for (const blob of blobs) { + const blobPath = path.resolve(path.dirname(cardPath), blob) + const blobFile = blobFiles.get(blobPath) + if (blobFile === undefined) { + throw new Error('Blob file not found: ' + blobPath + ' from:' + cardPath) + } + blobProps[blobFile._id] = { + file: blobFile._id, + type: blobFile.type, + name: blobFile.name, + metadata: {} // todo: blobFile.metadata + } + } + cardProps.blobs = blobProps + } + const tagAssociations = new Map() for (const tag of tags) { const tagPath = path.resolve(path.dirname(cardPath), tag) @@ -510,7 +534,7 @@ export class UnifiedDocProcessor { } } - private async processAttachments ( + private async createAttachments ( attachments: string[], cardPath: string, card: UnifiedDoc, @@ -518,20 +542,7 @@ export class UnifiedDocProcessor { ): Promise { for (const attachment of attachments) { const attachmentPath = path.resolve(path.dirname(cardPath), attachment) - const attachmentName = path.basename(attachmentPath) - const fileId = this.metadataStorage.getIdByAbsolutePath(attachmentPath) as Ref - const type = contentType(attachmentPath) - const size = fs.statSync(attachmentPath).size - - const file: UnifiedFile = { - _id: fileId, // id for datastore - name: attachmentName, - blobProvider: async () => { - const data = fs.readFileSync(attachmentPath) - const props = type !== false ? { type } : undefined - return new Blob([data], props) - } - } + const file = await this.createFile(attachmentPath) result.files.set(attachmentPath, file) const attachmentId = this.metadataStorage.getIdByAbsolutePath(attachmentPath) as Ref @@ -542,18 +553,53 @@ export class UnifiedDocProcessor { space: core.space.Workspace, attachedTo: card.props._id as Ref, attachedToClass: card._class, - file: fileId, - name: attachmentName, + file: file._id, + name: file.name, collection: 'attachments', lastModified: Date.now(), - type: type !== false ? type : 'application/octet-stream', - size + type: file.type, + size: file.size } } result.docs.set(attachmentPath, [attachmentDoc]) } } + private async createBlobs ( + blobs: string[], + cardPath: string, + result: UnifiedDocProcessResult + ): Promise { + for (const blob of blobs) { + const blobPath = path.resolve(path.dirname(cardPath), blob) + const file = await this.createFile(blobPath) + result.files.set(blobPath, file) + } + } + + private async createFile ( + fileAbsPath: string + ): Promise { + // const fileAbsPath = path.resolve(path.dirname(currentPath), filePath) + const fileName = path.basename(fileAbsPath) + const fileId = this.metadataStorage.getIdByAbsolutePath(fileAbsPath) as Ref + const type = contentType(fileName) + const size = fs.statSync(fileAbsPath).size + + const file: UnifiedFile = { + _id: fileId, // id for datastore + name: fileName, + type: type !== false ? type : 'application/octet-stream', + size, // todo: make sure this one is needed + blobProvider: async () => { + const data = fs.readFileSync(fileAbsPath) + const props = type !== false ? { type } : undefined + return new Blob([data], props) + } + } + return file + } + private async createAssociation ( yamlPath: string, yamlConfig: Record diff --git a/packages/importer/src/types.ts b/packages/importer/src/types.ts index 3680670f8a8..cc230793179 100644 --- a/packages/importer/src/types.ts +++ b/packages/importer/src/types.ts @@ -17,6 +17,8 @@ export interface UnifiedMixin { // todo: extends T export interface UnifiedFile { _id: Ref name: string + type: string + size: number blobProvider: blobProvider } From 0cb3f04c9846384f26bca6b4872ce3cb07fe41bb Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 21:11:30 +0700 Subject: [PATCH 47/50] support blobs - uuids Signed-off-by: Anna Khismatullina --- .../huly/example-workspace/SlaveCard/Frodo.md | 6 +-- packages/importer/src/huly/metadata.ts | 32 ++++++++++------ packages/importer/src/huly/unified.ts | 37 +++++++++---------- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md index 74cd2f3a0a3..a6566f9319b 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md @@ -9,11 +9,11 @@ color: pink x: from familiar y: from minion multy-enum: [Beta] -# attachments: -# - ../../CARDS_INSTRUCTIONS.md +attachments: + - ../../CARDS_INSTRUCTIONS.md blobs: - ../files/screenshot.png -# - ../README.md + - ../../CARDS_INSTRUCTIONS.md --- A brave hobbit guided by Gandalf. diff --git a/packages/importer/src/huly/metadata.ts b/packages/importer/src/huly/metadata.ts index 27373b9dce6..d125d2c24df 100644 --- a/packages/importer/src/huly/metadata.ts +++ b/packages/importer/src/huly/metadata.ts @@ -1,7 +1,6 @@ import { Tag } from '@hcengineering/card' -import { Association, Attribute, Doc, generateId, Ref, Blob as PlatformBlob } from '@hcengineering/core' -import path from 'path' -import { UnifiedDoc, UnifiedFile } from '../types' +import { Association, Attribute, Doc, generateId, generateUuid, Blob as PlatformBlob, Ref } from '@hcengineering/core' +import { UnifiedDoc } from '../types' export interface RelationMetadata { // todo: rename association: Ref @@ -20,15 +19,24 @@ export interface TagMetadata { export class MetadataStorage { private readonly pathToRef = new Map>() // todo: attachments to a separate map? private readonly pathToMetadata = new Map() - private readonly pathToBlobUuid = new Map>() // todo: blobs to a separate map? + private readonly pathToBlobUuid = new Map>() - public getIdByAbsolutePath (path: string): Ref { - let id = this.pathToRef.get(path) - if (id === undefined) { - id = generateId() - this.pathToRef.set(path, id) + public getRefByPath (path: string): Ref { + let ref = this.pathToRef.get(path) + if (ref === undefined) { + ref = generateId() + this.pathToRef.set(path, ref) } - return id + return ref + } + + public getUuidByPath (path: string): Ref { + let uuid = this.pathToBlobUuid.get(path) + if (uuid === undefined) { + uuid = generateUuid() as Ref + this.pathToBlobUuid.set(path, uuid) + } + return uuid } public hasMetadata (path: string): boolean { @@ -45,7 +53,7 @@ export class MetadataStorage { public setAttributes (path: string, attributes: MapAttributeToUnifiedDoc): void { const metadata = this.pathToMetadata.get(path) ?? { - _id: this.getIdByAbsolutePath(path), + _id: this.getRefByPath(path), attributes: new Map(), associations: new Map() } @@ -55,7 +63,7 @@ export class MetadataStorage { public addAssociation (tagPath: string, propName: string, relationMetadata: RelationMetadata): void { const metadata = this.pathToMetadata.get(tagPath) ?? { - _id: this.getIdByAbsolutePath(tagPath), + _id: this.getRefByPath(tagPath), attributes: new Map(), associations: new Map() } diff --git a/packages/importer/src/huly/unified.ts b/packages/importer/src/huly/unified.ts index 9215cd20835..e3e3b03b665 100644 --- a/packages/importer/src/huly/unified.ts +++ b/packages/importer/src/huly/unified.ts @@ -9,7 +9,6 @@ import core, { Doc, Enum, generateId, - Blob as PlatformBlob, Ref, Relation } from '@hcengineering/core' @@ -62,7 +61,7 @@ export class UnifiedDocProcessor { switch (yamlConfig?.class) { case card.class.MasterTag: { - const masterTagId = this.metadataStorage.getIdByAbsolutePath(yamlPath) as Ref + const masterTagId = this.metadataStorage.getRefByPath(yamlPath) as Ref const masterTag = await this.createMasterTag(yamlConfig, masterTagId, parentMasterTagId) const masterTagAttrs = await this.createAttributes(yamlPath, yamlConfig, masterTagId) @@ -113,7 +112,7 @@ export class UnifiedDocProcessor { if (fs.existsSync(yamlPath)) { const yamlConfig = yaml.load(fs.readFileSync(yamlPath, 'utf8')) as Record if (yamlConfig?.class === card.class.MasterTag) { - masterTagId = this.metadataStorage.getIdByAbsolutePath(yamlPath) as Ref + masterTagId = this.metadataStorage.getRefByPath(yamlPath) as Ref this.metadataStorage.getAssociations(yamlPath).forEach((relationMetadata, propName) => { masterTagRelations.set(propName, relationMetadata) }) @@ -246,7 +245,7 @@ export class UnifiedDocProcessor { masterTagId: Ref, parentTagId?: Ref ): Promise { - const tagId = this.metadataStorage.getIdByAbsolutePath(tagPath) as Ref + const tagId = this.metadataStorage.getRefByPath(tagPath) as Ref const tag = await this.createTag(tagConfig, tagId, masterTagId, parentTagId) const attributes = await this.createAttributes(tagPath, tagConfig, tagId) @@ -342,7 +341,7 @@ export class UnifiedDocProcessor { const baseType: Record = {} baseType._class = core.class.RefTo const refPath = path.resolve(path.dirname(currentPath), property.refTo) - baseType.to = this.metadataStorage.getIdByAbsolutePath(refPath) + baseType.to = this.metadataStorage.getRefByPath(refPath) baseType.label = core.string.Ref type = property.isArray === true ? { @@ -355,7 +354,7 @@ export class UnifiedDocProcessor { const baseType: Record = {} baseType._class = core.class.EnumOf const enumPath = path.resolve(path.dirname(currentPath), property.enumOf) - baseType.of = this.metadataStorage.getIdByAbsolutePath(enumPath) + baseType.of = this.metadataStorage.getRefByPath(enumPath) baseType.label = 'core:string:Enum' type = property.isArray === true ? { @@ -398,7 +397,7 @@ export class UnifiedDocProcessor { const tags = rawTags !== undefined ? (Array.isArray(rawTags) ? rawTags : [rawTags]) : [] const blobs = rawBlobs !== undefined ? (Array.isArray(rawBlobs) ? rawBlobs : [rawBlobs]) : [] - const cardId = this.metadataStorage.getIdByAbsolutePath(cardPath) as Ref + const cardId = this.metadataStorage.getRefByPath(cardPath) as Ref const cardProps: Record = { _id: cardId, space: core.space.Workspace, @@ -450,7 +449,7 @@ export class UnifiedDocProcessor { for (const val of values) { if (attrBaseType._class === core.class.RefTo) { const refPath = path.resolve(path.dirname(cardPath), val) - const ref = this.metadataStorage.getIdByAbsolutePath(refPath) as Ref + const ref = this.metadataStorage.getRefByPath(refPath) as Ref propValues.push(ref) } else { propValues.push(val) @@ -463,7 +462,7 @@ export class UnifiedDocProcessor { throw new Error(`Association not found: ${key}, ${cardPath}`) // todo: keep the error till builder validation } const otherCardPath = path.resolve(path.dirname(cardPath), value) // todo: value can be array of paths - const otherCardId = this.metadataStorage.getIdByAbsolutePath(otherCardPath) as Ref + const otherCardId = this.metadataStorage.getRefByPath(otherCardPath) as Ref const relation: UnifiedDoc = this.createRelation(metadata, cardId, otherCardId) relations.push(relation) } @@ -509,7 +508,7 @@ export class UnifiedDocProcessor { for (const tagPath of tags) { const cardDir = path.dirname(cardPath) const tagAbsPath = path.resolve(cardDir, tagPath) - const tagId = this.metadataStorage.getIdByAbsolutePath(tagAbsPath) as Ref + const tagId = this.metadataStorage.getRefByPath(tagAbsPath) as Ref const tagProps: Record = {} this.metadataStorage.getAttributes(tagAbsPath).forEach((attr, label) => { @@ -545,11 +544,11 @@ export class UnifiedDocProcessor { const file = await this.createFile(attachmentPath) result.files.set(attachmentPath, file) - const attachmentId = this.metadataStorage.getIdByAbsolutePath(attachmentPath) as Ref + const attachmentId = this.metadataStorage.getRefByPath(attachmentPath) as Ref const attachmentDoc: UnifiedDoc = { _class: 'attachment:class:Attachment' as Ref>, props: { - _id: attachmentId, // id for attachment doc + _id: attachmentId, space: core.space.Workspace, attachedTo: card.props._id as Ref, attachedToClass: card._class, @@ -582,15 +581,15 @@ export class UnifiedDocProcessor { ): Promise { // const fileAbsPath = path.resolve(path.dirname(currentPath), filePath) const fileName = path.basename(fileAbsPath) - const fileId = this.metadataStorage.getIdByAbsolutePath(fileAbsPath) as Ref + const fileUuid = this.metadataStorage.getUuidByPath(fileAbsPath) const type = contentType(fileName) const size = fs.statSync(fileAbsPath).size const file: UnifiedFile = { - _id: fileId, // id for datastore + _id: fileUuid, // id for datastore name: fileName, type: type !== false ? type : 'application/octet-stream', - size, // todo: make sure this one is needed + size, blobProvider: async () => { const data = fs.readFileSync(fileAbsPath) const props = type !== false ? { type } : undefined @@ -608,7 +607,7 @@ export class UnifiedDocProcessor { const { class: _class, typeA, typeB, type, nameA, nameB } = yamlConfig const currentPath = path.dirname(yamlPath) - const associationId = this.metadataStorage.getIdByAbsolutePath(yamlPath) as Ref + const associationId = this.metadataStorage.getRefByPath(yamlPath) as Ref const typeAPath = path.resolve(currentPath, typeA) this.metadataStorage.addAssociation(typeAPath, nameB, { @@ -624,8 +623,8 @@ export class UnifiedDocProcessor { type }) - const typeAId = this.metadataStorage.getIdByAbsolutePath(typeAPath) as Ref - const typeBId = this.metadataStorage.getIdByAbsolutePath(typeBPath) as Ref + const typeAId = this.metadataStorage.getRefByPath(typeAPath) as Ref + const typeBId = this.metadataStorage.getRefByPath(typeBPath) as Ref return { _class, @@ -646,7 +645,7 @@ export class UnifiedDocProcessor { yamlConfig: Record ): Promise> { const { title, values } = yamlConfig - const enumId = this.metadataStorage.getIdByAbsolutePath(yamlPath) as Ref + const enumId = this.metadataStorage.getRefByPath(yamlPath) as Ref return { _class: core.class.Enum, props: { From 0e9e9ae3d99b72814235d9975568710ca2306400 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 23:00:16 +0700 Subject: [PATCH 48/50] logs and fixess Signed-off-by: Anna Khismatullina --- .../huly/example-workspace/Difficulty.yaml | 7 ++ .../huly/example-workspace/DocumentCard.yaml | 6 ++ .../docs/huly/example-workspace/FileCard.md | 10 +++ .../docs/huly/example-workspace/FileImport.md | 9 --- .../example-workspace/RecipeAssociations.yaml | 7 ++ .../docs/huly/example-workspace/Recipes.yaml | 19 ++++++ .../Recipes/Chocolate Lava Cake.md | 40 +++++++++++ .../Chocolate Lava Cake/Chocolate Sauce.md | 39 +++++++++++ .../Recipes/Classic Margherita Pizza.md | 63 ++++++++++++++++++ .../Recipes/DietaryType.yaml | 7 ++ .../Recipes/Vegan/Mushroom Risotto.md | 43 ++++++++++++ .../Recipes/Vegan/Vegan Recipe.yaml | 9 +++ .../example-workspace/Recipes/files/cake.png | Bin 0 -> 115511 bytes .../example-workspace/files/screenshot.png | Bin 0 -> 78997 bytes packages/importer/src/huly/unified.ts | 16 +++-- 15 files changed, 261 insertions(+), 14 deletions(-) create mode 100644 dev/import-tool/docs/huly/example-workspace/Difficulty.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/DocumentCard.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/FileCard.md delete mode 100644 dev/import-tool/docs/huly/example-workspace/FileImport.md create mode 100644 dev/import-tool/docs/huly/example-workspace/RecipeAssociations.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake/Chocolate Sauce.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/DietaryType.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Recipes/files/cake.png create mode 100644 dev/import-tool/docs/huly/example-workspace/files/screenshot.png diff --git a/dev/import-tool/docs/huly/example-workspace/Difficulty.yaml b/dev/import-tool/docs/huly/example-workspace/Difficulty.yaml new file mode 100644 index 00000000000..6d7cb4ab054 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Difficulty.yaml @@ -0,0 +1,7 @@ +class: core:class:Enum +title: Difficulty +values: + - Easy + - Medium + - Hard + - Expert \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/DocumentCard.yaml b/dev/import-tool/docs/huly/example-workspace/DocumentCard.yaml new file mode 100644 index 00000000000..28bcd717627 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/DocumentCard.yaml @@ -0,0 +1,6 @@ +--- +class: card:class:DocumentCard +title: Document Example +--- +CONTENT +bla bla bla diff --git a/dev/import-tool/docs/huly/example-workspace/FileCard.md b/dev/import-tool/docs/huly/example-workspace/FileCard.md new file mode 100644 index 00000000000..dc7173b519d --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/FileCard.md @@ -0,0 +1,10 @@ +--- +class: card:types:File +title: File Example +blobs: + - ./files/cake.png +attachments: + - ./files/cake.png +--- + +*DESCRiption* \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/FileImport.md b/dev/import-tool/docs/huly/example-workspace/FileImport.md deleted file mode 100644 index 62c0d2b7671..00000000000 --- a/dev/import-tool/docs/huly/example-workspace/FileImport.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -class: card:types:File -title: File with blob -blobs: - - ./files/screenshot.png ---- - -*DESCR)* -[](./files/screenshot.png) \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/RecipeAssociations.yaml b/dev/import-tool/docs/huly/example-workspace/RecipeAssociations.yaml new file mode 100644 index 00000000000..3b82b3e0e89 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/RecipeAssociations.yaml @@ -0,0 +1,7 @@ +# RecipeRelations.yaml +class: core:class:Association +typeA: "./Recipes.yaml" +typeB: "./Recipes.yaml" +nameA: recommendedDesserts +nameB: recommendedMainDishes +type: "N:N" # 1:1, 1:N, N:N \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes.yaml b/dev/import-tool/docs/huly/example-workspace/Recipes.yaml new file mode 100644 index 00000000000..c57b0ca48f3 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes.yaml @@ -0,0 +1,19 @@ +class: card:class:MasterTag +title: Recipe +properties: + - label: cookingTime + type: TypeString + - label: servings + type: TypeNumber + - label: difficulty + enumOf: "./Difficulty.yaml" + # isArray: true # for multiple values + - label: category + type: TypeString + - label: calories + type: TypeNumber + - label: chef + type: TypeString + - label: relatedRecipes + refTo: "./Recipes.yaml" + isArray: true \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md b/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md new file mode 100644 index 00000000000..1208904b0e0 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake.md @@ -0,0 +1,40 @@ +--- +title: Chocolate Lava Cake +cookingTime: 25 minutes +servings: 4 +difficulty: Medium +category: Dessert +calories: 450 +chef: Anna Smith +blobs: + - ./files/cake.png +recommendedMainDishes: + - ./Classic Margherita Pizza.md + - ./Vegan/Mushroom Risotto.md +--- + +# Chocolate Lava Cake + +## Ingredients +- 200g dark chocolate (70% cocoa) +- 200g butter +- 4 eggs +- 200g sugar +- 120g flour +- 1 tsp vanilla extract +- Pinch of salt +- Butter for ramekins +- Cocoa powder for dusting + +## Instructions +1. Melt chocolate and butter together +2. Whisk eggs and sugar until pale +3. Fold in chocolate mixture +4. Add flour and vanilla +5. Pour into buttered ramekins +6. Bake at 200°C (400°F) for 12 minutes + +## Notes +- Serve immediately while warm +- Can be prepared ahead and refrigerated +- Perfect with vanilla ice cream diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake/Chocolate Sauce.md b/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake/Chocolate Sauce.md new file mode 100644 index 00000000000..7c42f5cbde7 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes/Chocolate Lava Cake/Chocolate Sauce.md @@ -0,0 +1,39 @@ +--- +title: Rich Chocolate Sauce +tags: + - ../DietaryType.yaml +cookingTime: 10 minutes +servings: 4 +difficulty: Easy +category: Dessert Components +calories: 200 +chef: Maria Green +restrictions: Vegetarian +allergens: Dairy +relatedRecipes: + - '../Chocolate Lava Cake.md' +--- + +# Rich Chocolate Sauce for Lava Cake + +## Ingredients +- 100g dark chocolate (70% cocoa) +- 100ml heavy cream +- 30g unsalted butter +- 1 tsp vanilla extract +- Pinch of sea salt + +## Instructions +1. Chop chocolate into small pieces +2. Heat cream until just simmering +3. Pour hot cream over chocolate +4. Let stand for 1 minute +5. Stir until smooth +6. Add butter and vanilla +7. Mix until glossy + +## Notes +- Use high-quality chocolate for best results +- Can be made ahead and reheated +- Store in refrigerator for up to 3 days +- Warm slightly before serving diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md b/dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md new file mode 100644 index 00000000000..81e72e7e093 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes/Classic Margherita Pizza.md @@ -0,0 +1,63 @@ +--- +title: Classic Margherita Pizza +tags: + - ./DietaryType.yaml +cookingTime: 30 minutes +servings: 4 +difficulty: Medium +category: Italian +calories: 850 +chef: Mario Rossi +restrictions: Vegetarian +allergens: Gluten, Dairy +recommendedDesserts: + - ./Chocolate Lava Cake.md + +--- + +# Classic Margherita Pizza + +## Ingredients +- 2 1/2 cups (300g) all-purpose flour +- 1 tsp salt +- 1 tsp active dry yeast +- 1 cup warm water +- 2 tbsp olive oil +- 1 cup tomato sauce +- 2 cups mozzarella cheese +- Fresh basil leaves +- Extra virgin olive oil + +## Instructions +1. Mix flour, salt, and yeast in a large bowl +2. Add warm water and olive oil, knead for 10 minutes +3. Let rise for 1 hour +4. Roll out dough and add toppings +5. Bake at 450°F (230°C) for 15-20 minutes + +## Notes +- For best results, use San Marzano tomatoes for the sauce +- Fresh mozzarella is preferred over pre-shredded +- Add basil leaves after baking + +# Classic Margherita Pizza + +## Ingredients +- Pizza dough +- San Marzano tomatoes +- Fresh mozzarella +- Fresh basil +- Extra virgin olive oil +- Salt + +## Instructions +1. Preheat oven to 450°F (230°C) +2. Roll out the pizza dough +3. Add tomato sauce +4. Add fresh mozzarella +5. Bake for 12-15 minutes +6. Add fresh basil and olive oil + +## Notes +- Best served immediately +- Use high-quality ingredients for authentic taste \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/DietaryType.yaml b/dev/import-tool/docs/huly/example-workspace/Recipes/DietaryType.yaml new file mode 100644 index 00000000000..0c98fe40e82 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes/DietaryType.yaml @@ -0,0 +1,7 @@ +class: card:class:Tag +title: DietaryType +properties: + - label: restrictions + type: TypeString + - label: allergens + type: TypeString \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md b/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md new file mode 100644 index 00000000000..06e62f03100 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Mushroom Risotto.md @@ -0,0 +1,43 @@ +--- +title: Vegan Mushroom Risotto +cookingTime: 45 minutes +servings: 4 +difficulty: Medium +category: Italian +calories: 380 +chef: Maria Green +proteinSource: Mushrooms +isGlutenFree: true +allergens: None +recommendedDesserts: + - ./Chocolate Lava Cake.md + +--- + +# Vegan Mushroom Risotto + +## Ingredients +- 300g Arborio rice +- 500g mixed mushrooms +- 1 onion, finely chopped +- 2 cloves garlic, minced +- 1 cup white wine +- 6 cups vegetable stock +- 2 tbsp nutritional yeast +- 2 tbsp olive oil +- Salt and pepper to taste +- Fresh parsley + +## Instructions +1. Sauté mushrooms until golden +2. Add onion and garlic, cook until soft +3. Add rice and toast for 2 minutes +4. Gradually add wine and stock +5. Cook until rice is creamy +6. Finish with nutritional yeast + +## Notes +- Use a variety of mushrooms for better flavor +- Keep stock warm while adding +- Stir constantly for creamy texture +- Nutritional yeast adds cheesy flavor \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml b/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml new file mode 100644 index 00000000000..a3e275d172b --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Recipes/Vegan/Vegan Recipe.yaml @@ -0,0 +1,9 @@ +class: card:class:MasterTag +title: Vegan Recipe +properties: + - label: proteinSource + type: TypeString + - label: isGlutenFree + type: TypeBoolean + - label: allergens + type: TypeString \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Recipes/files/cake.png b/dev/import-tool/docs/huly/example-workspace/Recipes/files/cake.png new file mode 100644 index 0000000000000000000000000000000000000000..589aa737e8cb142eddb7687e1e47272d4aae8ddb GIT binary patch literal 115511 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 (U7Dr3U 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 Hu0mjfKJZwL literal 0 HcmV?d00001 diff --git a/dev/import-tool/docs/huly/example-workspace/files/screenshot.png b/dev/import-tool/docs/huly/example-workspace/files/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..fff3974097624f786d5350a267715ad2fad5facc GIT binary patch literal 78997 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̥GO~=G4#&|xvE=oZr|^1Grj#@ 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 switch (yamlConfig?.class) { @@ -156,7 +157,7 @@ export class UnifiedDocProcessor { const { class: cardType, ...cardProps } = await readYamlHeader(cardPath) if (cardType.startsWith('card:types:') === false) { - throw new Error('Unsupported card type: ' + cardType) + throw new Error('Unsupported card type: ' + cardType + ' in ' + cardPath) } await this.processCard(result, cardPath, cardProps, cardType, masterTagRelations, masterTagAttrs) // todo: get right master tag attributes @@ -172,6 +173,8 @@ export class UnifiedDocProcessor { masterTagAttrs: Map>>, parentCardId?: Ref ): Promise { + console.log('Processing card:', cardPath) + if (cardProps.blobs !== undefined) { await this.createBlobs(cardProps.blobs, cardPath, result) } @@ -461,10 +464,13 @@ export class UnifiedDocProcessor { if (metadata === undefined) { throw new Error(`Association not found: ${key}, ${cardPath}`) // todo: keep the error till builder validation } - const otherCardPath = path.resolve(path.dirname(cardPath), value) // todo: value can be array of paths - const otherCardId = this.metadataStorage.getRefByPath(otherCardPath) as Ref - const relation: UnifiedDoc = this.createRelation(metadata, cardId, otherCardId) - relations.push(relation) + const values = Array.isArray(value) ? value : [value] + for (const val of values) { + const otherCardPath = path.resolve(path.dirname(cardPath), val) + const otherCardId = this.metadataStorage.getRefByPath(otherCardPath) as Ref + const relation: UnifiedDoc = this.createRelation(metadata, cardId, otherCardId) + relations.push(relation) + } } } From 88e8ec6fe5d3dd5d06b1288a245240d5db3d0fb9 Mon Sep 17 00:00:00 2001 From: Anna Khismatullina Date: Tue, 8 Apr 2025 23:00:35 +0700 Subject: [PATCH 49/50] fix examples Signed-off-by: Anna Khismatullina --- .../docs/huly/example-workspace/Difficulty.yaml | 4 +++- .../{DocumentCard.yaml => DocumentCard.md} | 2 +- .../docs/huly/example-workspace/FileCard.md | 4 ++-- .../huly/example-workspace/SlaveCard/Frodo.md | 2 +- .../huly/example-workspace/files/screenshot.png | Bin 78997 -> 0 bytes 5 files changed, 7 insertions(+), 5 deletions(-) rename dev/import-tool/docs/huly/example-workspace/{DocumentCard.yaml => DocumentCard.md} (62%) delete mode 100644 dev/import-tool/docs/huly/example-workspace/files/screenshot.png diff --git a/dev/import-tool/docs/huly/example-workspace/Difficulty.yaml b/dev/import-tool/docs/huly/example-workspace/Difficulty.yaml index 6d7cb4ab054..b025431660a 100644 --- a/dev/import-tool/docs/huly/example-workspace/Difficulty.yaml +++ b/dev/import-tool/docs/huly/example-workspace/Difficulty.yaml @@ -4,4 +4,6 @@ values: - Easy - Medium - Hard - - Expert \ No newline at end of file + - Expert + - Impossible + \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/DocumentCard.yaml b/dev/import-tool/docs/huly/example-workspace/DocumentCard.md similarity index 62% rename from dev/import-tool/docs/huly/example-workspace/DocumentCard.yaml rename to dev/import-tool/docs/huly/example-workspace/DocumentCard.md index 28bcd717627..3a573e3c900 100644 --- a/dev/import-tool/docs/huly/example-workspace/DocumentCard.yaml +++ b/dev/import-tool/docs/huly/example-workspace/DocumentCard.md @@ -1,5 +1,5 @@ --- -class: card:class:DocumentCard +class: card:types:Document title: Document Example --- CONTENT diff --git a/dev/import-tool/docs/huly/example-workspace/FileCard.md b/dev/import-tool/docs/huly/example-workspace/FileCard.md index dc7173b519d..3dbadc01691 100644 --- a/dev/import-tool/docs/huly/example-workspace/FileCard.md +++ b/dev/import-tool/docs/huly/example-workspace/FileCard.md @@ -2,9 +2,9 @@ class: card:types:File title: File Example blobs: - - ./files/cake.png + - ./Recipes/files/cake.png attachments: - - ./files/cake.png + - ./Recipes/files/cake.png --- *DESCRiption* \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md index a6566f9319b..77694d0e36e 100644 --- a/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md +++ b/dev/import-tool/docs/huly/example-workspace/SlaveCard/Frodo.md @@ -12,7 +12,7 @@ multy-enum: [Beta] attachments: - ../../CARDS_INSTRUCTIONS.md blobs: - - ../files/screenshot.png + - ../Recipes/files/cake.png - ../../CARDS_INSTRUCTIONS.md --- diff --git a/dev/import-tool/docs/huly/example-workspace/files/screenshot.png b/dev/import-tool/docs/huly/example-workspace/files/screenshot.png deleted file mode 100644 index fff3974097624f786d5350a267715ad2fad5facc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 78997 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̥GO~=G4#&|xvE=oZr|^1Grj#@ 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 Date: Tue, 8 Apr 2025 23:14:54 +0700 Subject: [PATCH 50/50] revert redundant changes Signed-off-by: Anna Khismatullina --- dev/import-tool/docs/huly/README.md | 211 ++++++++++++++++++ .../huly/example-workspace/Documentation.yaml | 10 + .../Documentation/Getting Started.md | 19 ++ .../Documentation/User Guide.md | 16 ++ .../Documentation/User Guide/Installation.md | 19 ++ .../Documentation/files/architecture.png | Bin 0 -> 40292 bytes .../huly/example-workspace/Project Alpha.yaml | 12 + .../Project Alpha/1.Project Setup.md | 30 +++ .../1.Project Setup/2.Configure CI.md | 13 ++ .../Project Alpha/4.Update Docs.md | 11 + .../Project Alpha/files/config.yaml | 18 ++ .../Project Alpha/files/screenshot.png | Bin 0 -> 39710 bytes .../files/screenshot/drawing1.json | 55 +++++ .../huly/example-workspace/QMS Documents.yaml | 11 + .../[SOP-001] Document Control.md | 32 +++ .../[SOP-002] Document Review.md | 42 ++++ .../[WI-001] Document Template Usage.md | 37 +++ plugins/export-assets/lang/zh.json | 167 ++------------ 18 files changed, 560 insertions(+), 143 deletions(-) create mode 100644 dev/import-tool/docs/huly/README.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/1.Project Setup.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/1.Project Setup/2.Configure CI.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/4.Update Docs.md create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/config.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot.png create mode 100644 dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json create mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml create mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md create mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md create mode 100644 dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md diff --git a/dev/import-tool/docs/huly/README.md b/dev/import-tool/docs/huly/README.md new file mode 100644 index 00000000000..e5a4539924d --- /dev/null +++ b/dev/import-tool/docs/huly/README.md @@ -0,0 +1,211 @@ +## Import from Unified Format Guide + +### Overview +The unified format represents workspace data in a hierarchical folder structure where: +* Root directory contains space configurations (*.yaml) and their corresponding folders +* Each space folder contains documents/issues (*.md) and their subdocuments/subissues +* Documents/issues can have child items in similarly-named folders +* File named `settings.yaml` is reserved and should not be used for spaces configuration +* Files without required `class` property in frontmatter will be skipped + +See the complete working example in the [example workspace](./example-workspace). + +### File Structure Example +``` +workspace/ +├── Documentation/ +│ ├── Getting Started.md # Standalone document +│ ├── User Guide.md # Document with children +│ ├── User Guide/ # Child documents folder +│ │ ├── Installation.md +│ │ └── Configuration.md +│ └── files/ # Attachments +│ └── architecture.png +├── Documentation.yaml # Space configuration +├── Project Alpha/ +│ ├── 1.Project Setup.md # Issue with subtasks +│ ├── 1.Project Setup/ # Subtasks folder +│ │ ├── 2.Configure CI.md +│ │ └── 3.Setup Tests.md +│ ├── 4.Update Docs.md # Standalone issue +│ └── files/ +│ └── diagram.png # Can be referenced in markdown content +└── Project Alpha.yaml # Project configuration +├── QMS Documents/ # QMS documentation +│ ├── [SOP-001] Document Control.md # Document template +│ ├── [SOP-001] Document Control/ # Template implementations +│ │ └── [SOP-002] Document Review.md # Controlled document +│ └── [WI-001] Document Template Usage.md # Standalone controlled document +└── QMS Documents.yaml # QMS space configuration +``` + +### File Format Requirements +* All spaces files must be in YAML format +* All document/issue files must include YAML frontmatter followed by Markdown content +* Children documents/issues are located in the folder with the same name as the parent document/issue + + +#### Tracker Issues + +##### 1. Project Configuration (*.yaml) +Example: `Project Alpha.yaml`: +```yaml +class: tracker:class:Project # Required +title: Project Alpha # Required +identifier: ALPHA # Required, max 5 uppercase letters/numbers, must start with a letter +private: false # Optional, default: false +autoJoin: true # Optional, default: true +owners: # Optional, list of email addresses + - john.doe@example.com +members: # Optional, list of email addresses + - joe.shmoe@example.com +description: string # Optional +defaultIssueStatus: Todo # Optional +``` + +##### 2. Issue (*.md) +Example: `1.Project Setup.md`: +```yaml +--- +class: tracker:class:Issue # Required +title: Project Setup # Required +status: In Progress # Required +priority: High # Optional +assignee: John Smith # Optional +estimation: 8 # Optional, in hours +remainingTime: 4 # Optional, in hours +--- +Task description in Markdown... +``` + +##### Issue Identification +* Human-readable issue ID is formed by combining project's identifier and issue number from filename +* Example: For project with identifier `ALPHA` and issue `1.Setup Project.md`, the issue ID will be `ALPHA-1` + +##### Allowed Values + +Issue status values: +* `Backlog` +* `Todo` +* `In Progress` +* `Done` +* `Canceled` + +Issue priority values: +* `Low` +* `Medium` +* `High` +* `Urgent` + +#### Documents + +##### 1. Teamspace Configuration (*.yaml) +Example: `Documentation.yaml`: +```yaml +class: document:class:Teamspace # Required +title: Documentation # Required +private: false # Optional, default: false +autoJoin: true # Optional, default: true +owners: # Optional, list of email addresses + - john.doe@example.com +members: # Optional, list of email addresses + - joe.shmoe@example.com +description: string # Optional +``` + +##### 2. Document (*.md) +Example: `Getting Started.md`: +```yaml +--- +class: document:class:Document # Required +title: Getting Started Guide # Required +--- +# Content in Markdown format +``` + +#### Controlled Documents +##### 1. Space Configuration (*.yaml) +QMS Document Space: `QMS Documents.yaml`: +```yaml +class: documents:class:OrgSpace # Required +title: QMS Documents # Required +private: false # Optional, default: false +owners: # Optional, list of email addresses + - john.doe@example.com +members: # Optional, list of email addresses + - joe.shmoe@example.com +description: string # Optional +qualified: john.doe@example.com # Optional, qualified user +manager: jane.doe@example.com # Optional, QMS manager +qara: bob.smith@example.com # Optional, QA/RA specialist +``` + +##### 2. Document Template (*.md) +Example: `[SOP-001] Document Control.md`: +```yaml +--- +class: documents:mixin:DocumentTemplate # Required +title: SOP Template # Required +docPrefix: SOP # Required, document code prefix +category: documents:category:Procedures # Required +author: John Smith # Required +owner: Jane Wilson # Required +abstract: Template description # Optional +reviewers: # Optional + - alice.cooper@example.com +approvers: # Optional + - david.brown@example.com +coAuthors: # Optional + - bob.dylan@example.com +--- +Template content in Markdown... +``` + +##### 3. Controlled Document (*.md) +Example: `[SOP-002] Document Review.md`: +```yaml +--- +class: documents:class:ControlledDocument # Required +title: Document Review Procedure # Required +template: [SOP-001] Document Control.md # Required, path to template +author: John Smith # Required +owner: Jane Wilson # Required +abstract: Document description # Optional +reviewers: # Optional + - alice.cooper@example.com +approvers: # Optional + - david.brown@example.com +coAuthors: # Optional + - bob.dylan@example.com +changeControl: # Optional + description: Initial document creation + reason: Need for standardized process + impact: Improved document control +--- +Document content in Markdown... +``` +##### Controlled Document Code Format +* Document code must be specified in file name: `[CODE] Any File Name.md` +* If code is not specified for controlled document, it will be generated automatically using template's docPrefix and sequential number (e.g. `SOP-99`) +* If code is not specified for template, it will be generated automatically as `TMPL-seqNumber`, where `seqNumber` is the sequence number of the template in the space + + +### Run Import Tool +```bash +docker run \ + -e FRONT_URL="https://huly.app" \ + -v /path/to/workspace:/data \ + hardcoreeng/import-tool:latest \ + -- bundle.js import /data \ + --user your.email@company.com \ + --password yourpassword \ + --workspace workspace-id +``` + +### Limitations +* All users must exist in the system before import +* Assignees are mapped by full name +* Files in space directories can be used as attachments when referenced in markdown content +* Document codes (in square brackets) must be unique across all document spaces +* Controlled documents must be created in the same space as their templates +* Controlled documents can be imported only with `Draft` status diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation.yaml b/dev/import-tool/docs/huly/example-workspace/Documentation.yaml new file mode 100644 index 00000000000..781c2b6f123 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Documentation.yaml @@ -0,0 +1,10 @@ +class: document:class:Teamspace +title: Documentation +emoji: 📖 +private: false +autoJoin: true +owners: + - john.doe@example.com +members: + - joe.shmoe@example.com +description: Technical documentation and guides diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md b/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md new file mode 100644 index 00000000000..4b0f5dab76d --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Documentation/Getting Started.md @@ -0,0 +1,19 @@ +--- +class: document:class:Document +title: Getting Started Guide +--- +# Getting Started + +Welcome to our project! This guide will help you get started with development. + +## Setup Steps + +1. Clone the repository +2. Install dependencies +3. Set up your environment + +## Project Communication +We use Huly for all project communication: +- Team discussions in Virtual Office +- Technical discussions in issue comments +- Documentation in Huly Documents \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md new file mode 100644 index 00000000000..5cbe18d0340 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide.md @@ -0,0 +1,16 @@ +--- +class: document:class:Document +title: User Guide +--- +# User Guide + +Our platform architecture and key components. + +## System Overview + + + +## Development Workflow +- Code reviews via GitHub integration +- CI/CD status in Huly Activity Feed +- Team sync-ups in Huly Virtual Office diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md new file mode 100644 index 00000000000..0c39873c056 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Documentation/User Guide/Installation.md @@ -0,0 +1,19 @@ +--- +class: document:class:Document +title: Installation Guide +--- +# Installation + +## System Requirements +- Node.js 18 or higher +- Docker Desktop +- Git + +## Setup Steps + +1. Clone the repository +2. Install dependencies +3. Configure your environment + +## Need Help? +Contact @Joe Shmoe for technical support \ No newline at end of file diff --git a/dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png b/dev/import-tool/docs/huly/example-workspace/Documentation/files/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..131b4072aa97c69e9075cc05b4a91b95eea3a54e GIT binary patch literal 40292 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 VNc#M!*-5xl(=2CMoE3X)`#vi%=pn*%YOW2pLJp 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%0At-fNf1Ku}>_1&Gj;m?&OmPh`mH_Kl%VOaUq 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 literal 0 HcmV?d00001 diff --git a/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json b/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json new file mode 100644 index 00000000000..4848657f095 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/Project Alpha/files/screenshot/drawing1.json @@ -0,0 +1,55 @@ +{ + "class": "drawing:class:Drawing", + "content": [ + { + "lineWidth": 4, + "erasing": false, + "penColor": "#0000ff", + "points": [ + { "x": 213, "y": 181.5 }, + { "x": 203, "y": 181.5 }, + { "x": 193, "y": 181.5 }, + { "x": 168, "y": 181.5 }, + { "x": 136, "y": 181.5 }, + { "x": 108, "y": 181.5 }, + { "x": 84, "y": 188.5 }, + { "x": 59, "y": 196.5 }, + { "x": 42, "y": 205.5 }, + { "x": 35, "y": 220.5 }, + { "x": 32, "y": 227.5 }, + { "x": 31, "y": 233.5 }, + { "x": 31, "y": 241.5 }, + { "x": 31, "y": 248.5 }, + { "x": 34, "y": 257.5 }, + { "x": 41, "y": 269.5 }, + { "x": 53, "y": 280.5 }, + { "x": 69, "y": 290.5 }, + { "x": 84, "y": 297.5 }, + { "x": 112, "y": 305.5 }, + { "x": 190, "y": 319.5 }, + { "x": 250, "y": 323.5 }, + { "x": 314, "y": 323.5 }, + { "x": 372, "y": 317.5 }, + { "x": 410, "y": 303.5 }, + { "x": 424, "y": 292.5 }, + { "x": 427, "y": 279.5 }, + { "x": 427, "y": 265.5 }, + { "x": 425, "y": 250.5 }, + { "x": 413, "y": 225.5 }, + { "x": 407, "y": 214.5 }, + { "x": 398, "y": 203.5 }, + { "x": 380, "y": 189.5 }, + { "x": 361, "y": 181.5 }, + { "x": 317, "y": 168.5 }, + { "x": 261, "y": 154.5 }, + { "x": 207, "y": 148.5 }, + { "x": 168, "y": 148.5 }, + { "x": 153, "y": 148.5 }, + { "x": 147, "y": 151.5 }, + { "x": 143, "y": 157.5 }, + { "x": 142, "y": 165.5 }, + { "x": 142, "y": 165.5 } + ] + } + ] +} diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml b/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml new file mode 100644 index 00000000000..3074c615960 --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/QMS Documents.yaml @@ -0,0 +1,11 @@ +class: documents:class:OrgSpace +title: QMS Documents +description: Quality Management System Documentation +private: false +owners: + - user1 +members: + - user1 +qualified: user1 +manager: user1 +qara: user1 diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md new file mode 100644 index 00000000000..8daf8101ade --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control.md @@ -0,0 +1,32 @@ +--- +class: documents:mixin:DocumentTemplate +title: 'Standard Operating Procedure Template' +docPrefix: SOP +category: DOC +author: John Appleseed +owner: John Appleseed +abstract: Template for Standard Operating Procedures +reviewers: + - John Appleseed +approvers: + - John Appleseed +--- +# Standard Operating Procedure + +## 1. Purpose +[Describe the purpose of the procedure] + +## 2. Scope +[Define the scope and applicability] + +## 3. Responsibilities +[List key roles and responsibilities] + +## 4. Procedure +[Detail the step-by-step procedure] + +## 5. References +[List related documents] + +## 6. Revision History +[Document revision history] diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md new file mode 100644 index 00000000000..3741298f42c --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[SOP-001] Document Control/[SOP-002] Document Review.md @@ -0,0 +1,42 @@ +--- +class: documents:class:ControlledDocument +title: Document Review Procedure +template: '../[SOP-001] Document Control.md' +author: John Appleseed +owner: John Appleseed +abstract: Procedure for document review and approval process +reviewers: + - John Appleseed +approvers: + - John Appleseed +changeControl: + description: Initial document creation + reason: Need for standardized review process + impact: Improved document quality control +--- +# Document Review Procedure + +## 1. Purpose +This procedure defines the process for reviewing quality management system documents. + +## 2. Scope +Applies to all controlled documents within the QMS. + +## 3. Responsibilities +- Document Owner: Responsible for content +- Reviewers: Technical review +- QA Manager: Final approval + +## 4. Procedure +1. Author prepares document +2. Technical review +3. QA review +4. Final approval +5. Document release + +## 5. References +- Quality Manual +- Document Control Procedure + +## 6. Revision History +Rev 0.1 - Initial draft diff --git a/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md new file mode 100644 index 00000000000..b8963023acd --- /dev/null +++ b/dev/import-tool/docs/huly/example-workspace/QMS Documents/[WI-001] Document Template Usage.md @@ -0,0 +1,37 @@ +--- +class: documents:class:ControlledDocument +title: Document Template Usage Guide +template: '[SOP-001] Document Control.md' +author: John Appleseed +owner: John Appleseed +abstract: Work instruction for using document templates +reviewers: + - John Appleseed +approvers: + - John Appleseed +--- +# Document Template Usage Guide + +## 1. Purpose +Guide users in proper usage of QMS document templates. + +## 2. Scope +All personnel creating QMS documentation. + +## 3. Procedure +1. Select appropriate template +2. Fill in required sections +3. Submit for review + +## 4. Document Review Changes + +| Step | Current Text | Updated Text | Comments | +| --- | --- | --- | --- | +| Initial Review | Select a template from library | Select a template that matches your document type | Clarified selection criteria | +| Metadata | Fill in required fields | Complete all required metadata and content fields | Added metadata specification | +| Review Process | Submit for review | Submit document for review according to procedure | Added reference to procedure | +| Approval | Wait for approval | Submit for approval after receiving all reviews | Process detail added | + +## 5. References +- [Document Control SOP](./[SOP-001]%20Document%20Control.md) +- [Document Review Procedure](./[SOP-001]%20Document%20Control/[SOP-002]%20Document%20Review.md) diff --git a/plugins/export-assets/lang/zh.json b/plugins/export-assets/lang/zh.json index a93e89558f0..08e383d4dc9 100644 --- a/plugins/export-assets/lang/zh.json +++ b/plugins/export-assets/lang/zh.json @@ -1,145 +1,26 @@ { - "projectTypes": [], - "spaces": [], - "unifiedDocs": [ - { - "_class": "card:class:MasterTag", - "props": { - "_id": "67ef664b667b87c74d9a3fc5", - "space": "core:space:Model", - "extends": "card:class:Card", - "label": "embedded:embedded:Master Tag 4", - "kind": 0, - "icon": "card:icon:MasterTag" - } - }, - { - "_class": "card:class:MasterTag", - "props": { - "_id": "67ef664b667b87c74d9a3fc8", - "space": "core:space:Model", - "extends": "67ef664b667b87c74d9a3fc5", - "label": "embedded:embedded:Child Type 2", - "kind": 0, - "icon": "card:icon:MasterTag" - } - }, - { - "_class": "card:class:MasterTag", - "props": { - "_id": "67ef664b667b87c74d9a3fcb", - "space": "core:space:Model", - "extends": "67ef664b667b87c74d9a3fc8", - "label": "embedded:embedded:Child Child Type 2", - "kind": 0, - "icon": "card:icon:MasterTag" - } - }, - { - "_class": "core:class:Attribute", - "props": { - "space": "core:space:Model", - "attributeOf": "67ef664b667b87c74d9a3fc5", - "name": "67ef664b667b87c74d9a3fc6", - "label": "embedded:embedded:aaa", - "isCustom": true, - "type": { "_class": "core:class:TypeString" }, - "defaultValue": null - } - }, - { - "_class": "core:class:Attribute", - "props": { - "space": "core:space:Model", - "attributeOf": "67ef664b667b87c74d9a3fc5", - "name": "67ef664b667b87c74d9a3fc7", - "label": "embedded:embedded:bbb", - "isCustom": true, - "type": { "_class": "core:class:TypeBoolean" }, - "defaultValue": null - } - }, - { - "_class": "core:class:Attribute", - "props": { - "space": "core:space:Model", - "attributeOf": "67ef664b667b87c74d9a3fc8", - "name": "67ef664b667b87c74d9a3fc9", - "label": "embedded:embedded:ccc", - "isCustom": true, - "type": { "_class": "core:class:TypeString" }, - "defaultValue": null - } - }, - { - "_class": "core:class:Attribute", - "props": { - "space": "core:space:Model", - "attributeOf": "67ef664b667b87c74d9a3fc8", - "name": "67ef664b667b87c74d9a3fca", - "label": "embedded:embedded:ddd", - "isCustom": true, - "type": { "_class": "core:class:TypeString" }, - "defaultValue": null - } - }, - { - "_class": "core:class:Attribute", - "props": { - "space": "core:space:Model", - "attributeOf": "67ef664b667b87c74d9a3fcb", - "name": "67ef664b667b87c74d9a3fcc", - "label": "embedded:embedded:ccc", - "isCustom": true, - "type": { "_class": "core:class:TypeString" }, - "defaultValue": null - } - }, - { - "_class": "core:class:Attribute", - "props": { - "space": "core:space:Model", - "attributeOf": "67ef664b667b87c74d9a3fcb", - "name": "67ef664b667b87c74d9a3fcd", - "label": "embedded:embedded:ddd", - "isCustom": true, - "type": { "_class": "core:class:TypeString" }, - "defaultValue": null - } - }, - { - "_class": "67ef664b667b87c74d9a3fcb", - "collabField": "content", - "props": { - "_id": "67ef664b667b87c74d9a3fce", - "space": "core:space:Workspace", - "title": "Card from Child 2", - "67ef664b667b87c74d9a3fcc": "ccc", - "67ef664b667b87c74d9a3fcd": "ddd" - } - }, - { - "_class": "67ef664b667b87c74d9a3fc8", - "collabField": "content", - "props": { - "_id": "67ef664b667b87c74d9a3fcf", - "space": "core:space:Workspace", - "title": "Card from Child 2", - "67ef664b667b87c74d9a3fc9": "ccc", - "67ef664b667b87c74d9a3fca": "ddd" - } - }, - { - "_class": "67ef664b667b87c74d9a3fc5", - "collabField": "content", - "props": { - "_id": "67ef664b667b87c74d9a3fd0", - "space": "core:space:Workspace", - "title": "Card of MT 1", - "67ef664b667b87c74d9a3fc6": "some text", - "67ef664b667b87c74d9a3fc7": true - } - } - ], - "attachments": [] + "string": { + "WorkspaceNamePattern": "名称必须为 40 个字符或更少,不能为空,不能包含特殊字符(<、>、/)", + "Export": "导出", + "DataToExport": "要导出的数据", + "ExportDocuments": "文档", + "ExportMilestones": "里程碑", + "ExportIssues": "问题", + "ExportTestCases": "测试用例", + "ExportTestRuns": "测试运行", + "ExportTestPlans": "测试计划", + "ExportFormat": "导出格式", + "ExportJSON": "JSON", + "ExportCSV": "CSV", + "ExportUnifiedFormat": "Huly 统一格式", + "ExportIncludeContent": "包含内容", + "ExportEverything": "所有内容", + "ExportAttributesOnly": "仅属性", + "ExportRequestSuccess": "导出已开始。", + "ExportRequestSuccessMessage": "导出已开始。完成后您将在收件箱中收到通知。", + "ExportRequestFailed": "提交导出失败。", + "ExportRequestFailedMessage": "提交导出时发生错误。请稍后重试。", + "ExportCompleted": "导出完成。您的文件已准备就绪。您可以从驱动器下载它们。", + "ExportFailed": "导出数据失败。请稍后重试。" + } }