From 496e0cff3815c207b5cf496c7e38d86597ccb756 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Wed, 1 Mar 2023 16:22:39 +0700 Subject: [PATCH] TSK-736, TSK-759, TSK-733 (#2691) Signed-off-by: Andrey Sobolev --- dev/tool/package.json | 3 +- dev/tool/src/clean.ts | 127 ++++++++ dev/tool/src/index.ts | 21 ++ models/recruit/src/index.ts | 34 +++ models/recruit/src/plugin.ts | 4 +- .../src/components/AttachmentPresenter.svelte | 2 +- .../src/components/AttributeMapper.svelte | 9 +- .../components/CreateMappingAttribute.svelte | 3 + .../components/FieldMappingPresenter.svelte | 3 + .../components/mappings/CopyMapping.svelte | 2 +- .../mappings/FindReferenceMapping.svelte | 101 +++++++ .../mappings/FindReferencePresenter.svelte | 36 +++ plugins/bitrix/package.json | 3 +- plugins/bitrix/src/sync.ts | 50 ++- plugins/bitrix/src/types.ts | 25 +- plugins/bitrix/src/utils.ts | 97 ++++-- .../components/schedule/MonthTableView.svelte | 2 +- .../src/components/schedule/YearView.svelte | 2 +- plugins/hr-resources/src/utils.ts | 14 +- plugins/recruit-assets/lang/en.json | 3 +- plugins/recruit-assets/lang/ru.json | 3 +- .../components/ApplicationPresenter.svelte | 2 +- .../src/components/CreateVacancy.svelte | 2 +- .../src/components/Organizations.svelte | 285 ++++++++++++++++++ .../VacancyListApplicationsPopup.svelte | 52 ++++ .../VacancyListCountPresenter.svelte | 50 +++ .../organizations/VacancyPopup.svelte | 45 +++ .../components/review/ReviewPresenter.svelte | 2 +- plugins/recruit-resources/src/index.ts | 2 + plugins/recruit-resources/src/plugin.ts | 1 + server/core/src/storage.ts | 90 ++---- 31 files changed, 966 insertions(+), 109 deletions(-) create mode 100644 dev/tool/src/clean.ts create mode 100644 plugins/bitrix-resources/src/components/mappings/FindReferenceMapping.svelte create mode 100644 plugins/bitrix-resources/src/components/mappings/FindReferencePresenter.svelte create mode 100644 plugins/recruit-resources/src/components/Organizations.svelte create mode 100644 plugins/recruit-resources/src/components/organizations/VacancyListApplicationsPopup.svelte create mode 100644 plugins/recruit-resources/src/components/organizations/VacancyListCountPresenter.svelte create mode 100644 plugins/recruit-resources/src/components/organizations/VacancyPopup.svelte diff --git a/dev/tool/package.json b/dev/tool/package.json index 80811f050e..b5f0406c6d 100644 --- a/dev/tool/package.json +++ b/dev/tool/package.json @@ -117,6 +117,7 @@ "libphonenumber-js": "^1.9.46", "@hcengineering/setting": "^0.6.2", "@hcengineering/minio": "^0.6.0", - "@hcengineering/openai": "^0.6.0" + "@hcengineering/openai": "^0.6.0", + "@hcengineering/tracker": "^0.6.1" } } diff --git a/dev/tool/src/clean.ts b/dev/tool/src/clean.ts new file mode 100644 index 0000000000..4d335f3639 --- /dev/null +++ b/dev/tool/src/clean.ts @@ -0,0 +1,127 @@ +// +// Copyright © 2023 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import attachment from '@hcengineering/attachment' +import contact from '@hcengineering/contact' +import core, { BackupClient, Client as CoreClient, DOMAIN_TX, TxOperations, WorkspaceId } from '@hcengineering/core' +import { MinioService } from '@hcengineering/minio' +import { getWorkspaceDB } from '@hcengineering/mongo' +import recruit from '@hcengineering/recruit' +import { connect } from '@hcengineering/server-tool' +import tracker from '@hcengineering/tracker' +import { MongoClient } from 'mongodb' + +export async function cleanWorkspace ( + mongoUrl: string, + workspaceId: WorkspaceId, + minio: MinioService, + elasticUrl: string, + transactorUrl: string, + opt: { recruit: boolean, tracker: boolean, removeTx: boolean } +): Promise { + const connection = (await connect(transactorUrl, workspaceId, undefined, { + mode: 'backup', + model: 'upgrade' + })) as unknown as CoreClient & BackupClient + try { + const ops = new TxOperations(connection, core.account.System) + + const hierarchy = ops.getHierarchy() + + const attachments = await ops.findAll(attachment.class.Attachment, {}) + + const contacts = await ops.findAll(contact.class.Contact, {}) + + const files = new Set( + attachments.map((it) => it.file).concat(contacts.map((it) => it.avatar).filter((it) => it) as string[]) + ) + + const minioList = await minio.list(workspaceId) + const toClean: string[] = [] + for (const mv of minioList) { + if (!files.has(mv.name)) { + toClean.push(mv.name) + } + } + await minio.remove(workspaceId, toClean) + // connection.loadChunk(DOMAIN_BLOB, idx = ) + + if (opt.recruit) { + const contacts = await ops.findAll(recruit.mixin.Candidate, {}) + console.log('removing Talents', contacts.length) + const filter = contacts.filter((it) => !hierarchy.isDerived(it._class, contact.class.Employee)) + + while (filter.length > 0) { + const part = filter.splice(0, 100) + const op = ops.apply('') + for (const c of part) { + await op.remove(c) + } + const t = Date.now() + console.log('remove:', part.map((it) => it.name).join(', ')) + await op.commit() + const t2 = Date.now() + console.log('remove time:', t2 - t, filter.length) + } + + // const vacancies = await ops.findAll(recruit.class.Vacancy, {}) + // console.log('removing vacancies', vacancies.length) + // for (const c of vacancies) { + // console.log('Remove', c.name) + // await ops.remove(c) + // } + } + + if (opt.tracker) { + const issues = await ops.findAll(tracker.class.Issue, {}) + console.log('removing Issues', issues.length) + + while (issues.length > 0) { + const part = issues.splice(0, 5) + const op = ops.apply('') + for (const c of part) { + await op.remove(c) + } + const t = Date.now() + await op.commit() + const t2 = Date.now() + console.log('remove time:', t2 - t, issues.length) + } + } + + const client = new MongoClient(mongoUrl) + try { + await client.connect() + const db = getWorkspaceDB(client, workspaceId) + + if (opt.removeTx) { + const txes = await db.collection(DOMAIN_TX).find({}).toArray() + + for (const tx of txes) { + if (tx._class === core.class.TxRemoveDoc) { + // We need to remove all update and create operations for document + await db.collection(DOMAIN_TX).deleteMany({ objectId: tx.objectId }) + } + } + } + } finally { + await client.close() + } + } catch (err: any) { + console.trace(err) + } finally { + await connection.close() + } +} diff --git a/dev/tool/src/index.ts b/dev/tool/src/index.ts index fd88e67738..bb385a9d81 100644 --- a/dev/tool/src/index.ts +++ b/dev/tool/src/index.ts @@ -51,6 +51,7 @@ import { MigrateOperation } from '@hcengineering/model' import { openAIConfigDefaults } from '@hcengineering/openai' import { rebuildElastic } from './elastic' import { openAIConfig } from './openai' +import { cleanWorkspace } from './clean' /** * @public @@ -428,5 +429,25 @@ export function devTool ( console.log(decodeToken(token)) }) + program + .command('clean-workspace ') + .description('set user role') + .option('--recruit', 'Clean recruit', false) + .option('--tracker', 'Clean tracker', false) + .option('--removedTx', 'Clean removed transactions', false) + .action(async (workspace: string, cmd: { recruit: boolean, tracker: boolean, removeTx: boolean }) => { + const { mongodbUri, minio } = prepareTools() + return await withDatabase(mongodbUri, async (db) => { + await cleanWorkspace( + mongodbUri, + getWorkspaceId(workspace, productId), + minio, + getElasticUrl(), + transactorUrl, + cmd + ) + }) + }) + program.parse(process.argv) } diff --git a/models/recruit/src/index.ts b/models/recruit/src/index.ts index 68a0dcd44c..845ee7da51 100644 --- a/models/recruit/src/index.ts +++ b/models/recruit/src/index.ts @@ -226,6 +226,7 @@ export function createModel (builder: Builder): void { const candidatesId = 'candidates' const archiveId = 'archive' const assignedId = 'assigned' + const organizationsId = 'organizations' builder.createDoc( workbench.class.Application, @@ -246,6 +247,13 @@ export function createModel (builder: Builder): void { createItemLabel: recruit.string.VacancyCreateLabel, position: 'vacancy' }, + { + id: organizationsId, + component: recruit.component.Organizations, + icon: contact.icon.Company, + label: recruit.string.Organizations, + position: 'vacancy' + }, { id: candidatesId, component: workbench.component.SpecialView, @@ -403,6 +411,32 @@ export function createModel (builder: Builder): void { }, recruit.viewlet.TableVacancy ) + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: recruit.mixin.VacancyList, + descriptor: view.viewlet.Table, + config: [ + '', + { + key: '@vacancies', + label: recruit.string.Vacancies + }, + { + key: '@applications', + label: recruit.string.Applications + }, + '$lookup.channels', + { + key: '@applications.modifiedOn', + label: core.string.Modified + } + ], + hiddenKeys: ['name', 'space', 'modifiedOn'] + }, + recruit.viewlet.TableVacancyList + ) builder.createDoc( view.class.Viewlet, diff --git a/models/recruit/src/plugin.ts b/models/recruit/src/plugin.ts index f4f96bd6f7..16c6cab36a 100644 --- a/models/recruit/src/plugin.ts +++ b/models/recruit/src/plugin.ts @@ -77,6 +77,7 @@ export default mergeIds(recruitId, recruit, { Applications: '' as AnyComponent, SkillsView: '' as AnyComponent, Vacancies: '' as AnyComponent, + Organizations: '' as AnyComponent, CreateReview: '' as AnyComponent, Reviews: '' as AnyComponent, @@ -104,6 +105,7 @@ export default mergeIds(recruitId, recruit, { TableApplicant: '' as Ref, TableApplicantMatch: '' as Ref, CalendarReview: '' as Ref, - TableReview: '' as Ref + TableReview: '' as Ref, + TableVacancyList: '' as Ref } }) diff --git a/plugins/attachment-resources/src/components/AttachmentPresenter.svelte b/plugins/attachment-resources/src/components/AttachmentPresenter.svelte index acab376228..1176058d98 100644 --- a/plugins/attachment-resources/src/components/AttachmentPresenter.svelte +++ b/plugins/attachment-resources/src/components/AttachmentPresenter.svelte @@ -30,7 +30,7 @@ fname.length > maxLenght ? fname.substr(0, (maxLenght - 1) / 2) + '...' + fname.substr(-(maxLenght - 1) / 2) : fname function iconLabel (name: string): string { - const parts = name.split('.') + const parts = `${name}`.split('.') const ext = parts[parts.length - 1] return ext.substring(0, 4).toUpperCase() } diff --git a/plugins/bitrix-resources/src/components/AttributeMapper.svelte b/plugins/bitrix-resources/src/components/AttributeMapper.svelte index 6d36f6c969..844eefa3fe 100644 --- a/plugins/bitrix-resources/src/components/AttributeMapper.svelte +++ b/plugins/bitrix-resources/src/components/AttributeMapper.svelte @@ -23,7 +23,8 @@ core.class.TypeNumber, core.class.EnumOf, core.class.Collection, - core.class.ArrOf + core.class.ArrOf, + core.class.RefTo ]) function addMapping (evt: MouseEvent, kind: MappingOperation): void { @@ -63,6 +64,12 @@ action: (_: any, evt: MouseEvent) => { addMapping(evt, MappingOperation.DownloadAttachment) } + }, + { + label: getEmbeddedLabel('Add Field reference mapping'), + action: (_: any, evt: MouseEvent) => { + addMapping(evt, MappingOperation.FindReference) + } } ] as Action[] diff --git a/plugins/bitrix-resources/src/components/CreateMappingAttribute.svelte b/plugins/bitrix-resources/src/components/CreateMappingAttribute.svelte index 932d2c6cb4..61b9e059c7 100644 --- a/plugins/bitrix-resources/src/components/CreateMappingAttribute.svelte +++ b/plugins/bitrix-resources/src/components/CreateMappingAttribute.svelte @@ -8,6 +8,7 @@ import CreateChannelMapping from './mappings/CreateChannelMapping.svelte' import CreateTagMapping from './mappings/CreateTagMapping.svelte' import DownloadAttachmentMapping from './mappings/DownloadAttachmentMapping.svelte' + import FindReferenceMapping from './mappings/FindReferenceMapping.svelte' export let mapping: BitrixEntityMapping export let fields: Fields = {} @@ -42,5 +43,7 @@ {:else if _kind === MappingOperation.DownloadAttachment} + {:else if _kind === MappingOperation.FindReference} + {/if} diff --git a/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte b/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte index b3a3936579..ae361ac3e9 100644 --- a/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte +++ b/plugins/bitrix-resources/src/components/FieldMappingPresenter.svelte @@ -7,6 +7,7 @@ import CreateChannelMappingPresenter from './mappings/CreateChannelMappingPresenter.svelte' import CreateTagMappingPresenter from './mappings/CreateTagMappingPresenter.svelte' import DownloadAttachmentPresenter from './mappings/DownloadAttachmentPresenter.svelte' + import FindReferencePresenter from './mappings/FindReferencePresenter.svelte' export let mapping: BitrixEntityMapping export let value: BitrixFieldMapping @@ -37,6 +38,8 @@ {:else if kind === MappingOperation.DownloadAttachment} + {:else if kind === MappingOperation.FindReference} + {/if}