mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-14 12:25:17 +00:00
Move skill definitions for recruit from tags to recruit (#1043)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
1b9a5e0395
commit
7ab4e59b7a
@ -12518,11 +12518,12 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-recruit.tgz_typescript@4.5.4:
|
||||
resolution: {integrity: sha512-8jDIXUx3KFtzeCO7AChSlND5+RBTLNG83eAO8syMdJVAmOA5UpEhFQvpPZCRLhYEzj3VG2zgJg7USd+CZKEPhA==, tarball: file:projects/model-recruit.tgz}
|
||||
resolution: {integrity: sha512-e8rfK0JjHSG0Zy0OzesYS38vL7VPzaKOUx5FC9PjeckdklAGYFbPUc1PZnLkeisogVcd3tDR7UkjIL0GvohJ+w==, tarball: file:projects/model-recruit.tgz}
|
||||
id: file:projects/model-recruit.tgz
|
||||
name: '@rush-temp/model-recruit'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
'@anticrm/skillset': 0.6.0
|
||||
'@rushstack/heft': 0.41.8
|
||||
'@types/heft-jest': 1.0.2
|
||||
'@typescript-eslint/eslint-plugin': 5.7.0_c25e8c1f4f4f7aaed27aa6f9ce042237
|
||||
@ -12768,7 +12769,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/model-tags.tgz_typescript@4.5.4:
|
||||
resolution: {integrity: sha512-M9oDcJc4rUtXqhC4FcPW+Z62RTGfstzLFi6DnorfVhtEANY/2VTUPiyjwLM+ml6sCHVdG49kaAx1gLHyEbAF7Q==, tarball: file:projects/model-tags.tgz}
|
||||
resolution: {integrity: sha512-M+xHdfGituHUoH7YgkHc5lw2TXsvV3jvf3g73EpjJgGl4NFclNd1VkBtyEQ9Czsjy3N/jff1B2FePFu7u2p6FA==, tarball: file:projects/model-tags.tgz}
|
||||
id: file:projects/model-tags.tgz
|
||||
name: '@rush-temp/model-tags'
|
||||
version: 0.0.0
|
||||
|
@ -44,6 +44,7 @@
|
||||
"@anticrm/model-task": "~0.6.0",
|
||||
"@anticrm/workbench": "~0.6.1",
|
||||
"@anticrm/model-presentation": "~0.6.0",
|
||||
"@anticrm/model-tags": "~0.6.0"
|
||||
"@anticrm/model-tags": "~0.6.0",
|
||||
"@anticrm/skillset": "^0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -14,13 +14,16 @@
|
||||
//
|
||||
|
||||
import { Person } from '@anticrm/contact'
|
||||
import core, { AttachedDoc, Class, Doc, DOMAIN_TX, MixinData, Ref, TxCollectionCUD, TxCreateDoc, TxMixin, TxUpdateDoc } from '@anticrm/core'
|
||||
import { MigrateOperation, MigrationClient, MigrationResult, MigrationUpgradeClient } from '@anticrm/model'
|
||||
import core, { AttachedDoc, Class, Doc, DocumentQuery, DOMAIN_TX, MixinData, Ref, TxCollectionCUD, TxCreateDoc, TxMixin, TxOperations, TxUpdateDoc } from '@anticrm/core'
|
||||
import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
||||
import { DOMAIN_ATTACHMENT } from '@anticrm/model-attachment'
|
||||
import { DOMAIN_COMMENT } from '@anticrm/model-chunter'
|
||||
import contact, { DOMAIN_CONTACT } from '@anticrm/model-contact'
|
||||
import tags, { DOMAIN_TAGS, TagCategory, TagElement } from '@anticrm/model-tags'
|
||||
import { DOMAIN_TASK } from '@anticrm/model-task'
|
||||
import recruit, { Candidate } from '@anticrm/recruit'
|
||||
import { Candidate } from '@anticrm/recruit'
|
||||
import recruit from './plugin'
|
||||
import { getCategories } from '@anticrm/skillset'
|
||||
|
||||
function toCandidateData (c: Pick<Candidate, 'onsite'|'title'|'remote'|'source'> | undefined): MixinData<Person, Candidate> {
|
||||
if (c === undefined) {
|
||||
@ -37,12 +40,15 @@ function toCandidateData (c: Pick<Candidate, 'onsite'|'title'|'remote'|'source'>
|
||||
return result
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function logInfo (msg: string, result: MigrationResult): void {
|
||||
if (result.updated > 0) {
|
||||
console.log(`Recruit: Migrate ${msg} ${result.updated}`)
|
||||
function findTagCategory (title: string, categories: TagCategory[]): Ref<TagCategory> {
|
||||
for (const c of categories) {
|
||||
if (c.tags.findIndex((it) => it.toLowerCase() === title.toLowerCase()) !== -1) {
|
||||
return c._id
|
||||
}
|
||||
}
|
||||
return recruit.category.Other
|
||||
}
|
||||
|
||||
export const recruitOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {
|
||||
// Move all candidates to mixins.
|
||||
@ -128,8 +134,88 @@ export const recruitOperation: MigrateOperation = {
|
||||
}, {
|
||||
objectClass: contact.class.Person
|
||||
})
|
||||
|
||||
// Rename other
|
||||
const categories = await client.find(DOMAIN_TAGS, { _class: tags.class.TagCategory })
|
||||
let prefix = 'tags:category:Category'
|
||||
for (const c of categories) {
|
||||
if (c._id.startsWith(prefix) || c._id === 'tags:category:Other') {
|
||||
let newCID = c._id.replace(prefix, recruit.category.Category + '.') as Ref<TagCategory>
|
||||
if (c._id === 'tags:category:Other') {
|
||||
newCID = recruit.category.Other
|
||||
}
|
||||
await client.delete(DOMAIN_TAGS, c._id)
|
||||
await client.create(DOMAIN_TAGS, { ...c, _id: newCID, targetClass: recruit.mixin.Candidate })
|
||||
await client.update(DOMAIN_TAGS, { _class: tags.class.TagElement, category: c._id }, {
|
||||
category: newCID,
|
||||
targetClass: recruit.mixin.Candidate
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
prefix = 'recruit:category:Category'
|
||||
for (const c of categories) {
|
||||
if ((c._id.startsWith(prefix) && !c._id.startsWith(prefix + '.')) || c._id === 'tags:category:Other') {
|
||||
let newCID = c._id.replace(prefix, recruit.category.Category + '.') as Ref<TagCategory>
|
||||
if (c._id === 'tags:category:Other') {
|
||||
newCID = recruit.category.Other
|
||||
}
|
||||
await client.delete(DOMAIN_TAGS, c._id)
|
||||
try {
|
||||
await client.create(DOMAIN_TAGS, { ...c, _id: newCID, targetClass: recruit.mixin.Candidate })
|
||||
} catch (err: any) {
|
||||
// Ignore
|
||||
}
|
||||
await client.update(DOMAIN_TAGS, { _class: tags.class.TagElement, category: c._id }, {
|
||||
category: newCID,
|
||||
targetClass: recruit.mixin.Candidate
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {}
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
const tx = new TxOperations(client, core.account.System)
|
||||
|
||||
await createOrUpdate(
|
||||
tx,
|
||||
tags.class.TagCategory,
|
||||
tags.space.Tags,
|
||||
{
|
||||
icon: tags.icon.Tags,
|
||||
label: 'Other',
|
||||
targetClass: recruit.mixin.Candidate,
|
||||
tags: [],
|
||||
default: true
|
||||
},
|
||||
recruit.category.Other
|
||||
)
|
||||
|
||||
for (const c of getCategories()) {
|
||||
await createOrUpdate(
|
||||
tx,
|
||||
tags.class.TagCategory,
|
||||
tags.space.Tags,
|
||||
{
|
||||
icon: tags.icon.Tags,
|
||||
label: c.label,
|
||||
targetClass: recruit.mixin.Candidate,
|
||||
tags: c.skills,
|
||||
default: false
|
||||
},
|
||||
(recruit.category.Category + '.' + c.id) as Ref<TagCategory>
|
||||
)
|
||||
}
|
||||
|
||||
const categories = await tx.findAll(tags.class.TagCategory, { targetClass: recruit.mixin.Candidate })
|
||||
// Find all existing TagElement and update category based on skillset
|
||||
const tagElements = await tx.findAll(tags.class.TagElement, { category: null } as unknown as DocumentQuery<TagElement>)
|
||||
for (const t of tagElements) {
|
||||
if (t.category == null) {
|
||||
const category = findTagCategory(t.title, categories)
|
||||
await tx.update(t, { category: category })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async function migrateUpdateCandidateToPersonAndMixin (client: MigrationClient): Promise<void> {
|
||||
const updateCandidates = await client.find(DOMAIN_TX, {
|
||||
|
@ -33,7 +33,6 @@
|
||||
"@anticrm/tags-resources": "~0.6.0",
|
||||
"@anticrm/ui": "~0.6.0",
|
||||
"@anticrm/model-core": "~0.6.0",
|
||||
"@anticrm/model-view": "~0.6.0",
|
||||
"@anticrm/skillset": "^0.6.0"
|
||||
"@anticrm/model-view": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import tags from './plugin'
|
||||
|
||||
export { tagsOperation } from './migration'
|
||||
export { tags as default }
|
||||
export { TagElement, TagReference, TagCategory } from '@anticrm/tags'
|
||||
|
||||
export const DOMAIN_TAGS = 'tags' as Domain
|
||||
|
||||
@ -75,6 +76,9 @@ export class TTagCategory extends TDoc implements TagCategory {
|
||||
|
||||
@Prop(Collection(core.class.TypeString), tags.string.CategoryTagsLabel)
|
||||
tags!: string[]
|
||||
|
||||
@Prop(TypeString(), tags.string.DefaultLabel)
|
||||
default!: boolean
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
|
@ -1,50 +1,7 @@
|
||||
import core, { DocumentQuery, Ref, TxOperations } from '@anticrm/core'
|
||||
import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
||||
import { getCategories } from '@anticrm/skillset'
|
||||
import { findTagCategory, TagCategory, TagElement } from '@anticrm/tags'
|
||||
import tags from './plugin'
|
||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
||||
|
||||
export const tagsOperation: MigrateOperation = {
|
||||
async migrate (client: MigrationClient): Promise<void> {},
|
||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||
const tx = new TxOperations(client, core.account.System)
|
||||
|
||||
await createOrUpdate(
|
||||
tx,
|
||||
tags.class.TagCategory,
|
||||
tags.space.Tags,
|
||||
{
|
||||
icon: tags.icon.Tags,
|
||||
label: 'Other',
|
||||
targetClass: core.class.Doc,
|
||||
tags: ['']
|
||||
},
|
||||
tags.category.Other
|
||||
)
|
||||
|
||||
for (const c of getCategories()) {
|
||||
await createOrUpdate(
|
||||
tx,
|
||||
tags.class.TagCategory,
|
||||
tags.space.Tags,
|
||||
{
|
||||
icon: tags.icon.Tags,
|
||||
label: c.label,
|
||||
targetClass: core.class.Doc,
|
||||
tags: c.skills
|
||||
},
|
||||
(tags.category.Category + c.id) as Ref<TagCategory>
|
||||
)
|
||||
}
|
||||
|
||||
const categories = await tx.findAll(tags.class.TagCategory, {})
|
||||
// Find all existing TagElement and update category based on skillset
|
||||
const tagElements = await tx.findAll(tags.class.TagElement, { category: null } as unknown as DocumentQuery<TagElement>)
|
||||
for (const t of tagElements) {
|
||||
if (t.category == null) {
|
||||
const category = findTagCategory(t.title, categories)
|
||||
await tx.update(t, { category: category })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,8 @@ export default mergeIds(tagsId, tags, {
|
||||
TagReference: '' as IntlString,
|
||||
AssetLabel: '' as IntlString,
|
||||
CategoryTargetClass: '' as IntlString,
|
||||
CategoryTagsLabel: '' as IntlString
|
||||
CategoryTagsLabel: '' as IntlString,
|
||||
DefaultLabel: '' as IntlString
|
||||
},
|
||||
dd: {
|
||||
DeleteTagElement: '' as Resource<(doc: Doc, client: TxOperations) => Promise<Doc[]>>
|
||||
|
@ -28,15 +28,18 @@ function diffAttributes (doc: Data<Doc>, newDoc: Data<Doc>): DocumentUpdate<Doc>
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update document if modified only by system account.
|
||||
* @public
|
||||
*/
|
||||
export async function createOrUpdate<T extends Doc> (client: TxOperations, _class: Ref<Class<T>>, space: Ref<Space>, data: Data<T>, _id: Ref<T>): Promise<void> {
|
||||
const existingDoc = await client.findOne<Doc>(_class, { _id })
|
||||
if (existingDoc !== undefined) {
|
||||
const { _class: _oldClass, _id, space: _oldSpace, modifiedBy, modifiedOn, ...oldData } = existingDoc
|
||||
const updateOp = diffAttributes(oldData, data)
|
||||
if (Object.keys(updateOp).length > 0) {
|
||||
await client.update(existingDoc, updateOp)
|
||||
if (modifiedBy === client.txFactory.account) {
|
||||
const updateOp = diffAttributes(oldData, data)
|
||||
if (Object.keys(updateOp).length > 0) {
|
||||
await client.update(existingDoc, updateOp)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await client.createDoc<T>(_class, space, data, _id)
|
||||
|
@ -55,7 +55,6 @@
|
||||
$: updateResultQuery(search, documentIds)
|
||||
|
||||
function updateCategory (detail: {category: Ref<TagCategory> | null, elements: TagElement[] }) {
|
||||
console.log(detail)
|
||||
category = detail.category ?? undefined
|
||||
selectedTagElements.set(Array.from(detail.elements ?? []).map(it => it._id))
|
||||
}
|
||||
|
@ -154,7 +154,7 @@
|
||||
)
|
||||
}
|
||||
|
||||
const categories = await client.findAll(tags.class.TagCategory, {})
|
||||
const categories = await client.findAll(tags.class.TagCategory, { targetClass: recruit.mixin.Candidate })
|
||||
// Tag elements
|
||||
const skillTagElements = new Map((await client.findAll(tags.class.TagElement, { _id: { $in: skills.map(it => it.tag) } })).map(it => ([it._id, it])))
|
||||
for (const skill of skills) {
|
||||
@ -246,7 +246,7 @@
|
||||
// Create skills
|
||||
await elementsPromise
|
||||
|
||||
const categories = await client.findAll(tags.class.TagCategory, {})
|
||||
const categories = await client.findAll(tags.class.TagCategory, { targetClass: recruit.mixin.Candidate })
|
||||
const categoriesMap = new Map(Array.from(categories.map(it => ([it._id, it]))))
|
||||
|
||||
const newSkills:TagReference[] = []
|
||||
|
@ -14,9 +14,12 @@
|
||||
//
|
||||
|
||||
import type { Client, Doc } from '@anticrm/core'
|
||||
import { getMetadata, IntlString, OK, Resources, Severity, Status, translate } from '@anticrm/platform'
|
||||
import { Applicant, Vacancy } from '@anticrm/recruit'
|
||||
import { IntlString, OK, Resources, Severity, Status, translate } from '@anticrm/platform'
|
||||
import { ObjectSearchResult } from '@anticrm/presentation'
|
||||
import { Applicant } from '@anticrm/recruit'
|
||||
import task from '@anticrm/task'
|
||||
import { showPopup } from '@anticrm/ui'
|
||||
import ApplicationItem from './components/ApplicationItem.svelte'
|
||||
import ApplicationPresenter from './components/ApplicationPresenter.svelte'
|
||||
import Applications from './components/Applications.svelte'
|
||||
import ApplicationsPresenter from './components/ApplicationsPresenter.svelte'
|
||||
@ -27,16 +30,10 @@ import CreateVacancy from './components/CreateVacancy.svelte'
|
||||
import EditApplication from './components/EditApplication.svelte'
|
||||
import EditVacancy from './components/EditVacancy.svelte'
|
||||
import KanbanCard from './components/KanbanCard.svelte'
|
||||
import TemplatesIcon from './components/TemplatesIcon.svelte'
|
||||
import recruit from './plugin'
|
||||
import { ObjectSearchResult } from '@anticrm/presentation'
|
||||
import task from '@anticrm/task'
|
||||
import ApplicationItem from './components/ApplicationItem.svelte'
|
||||
import VacancyPresenter from './components/VacancyPresenter.svelte'
|
||||
import SkillsView from './components/SkillsView.svelte'
|
||||
import login from '@anticrm/login'
|
||||
import workbench from '@anticrm/workbench'
|
||||
import view from '@anticrm/view'
|
||||
import TemplatesIcon from './components/TemplatesIcon.svelte'
|
||||
import VacancyPresenter from './components/VacancyPresenter.svelte'
|
||||
import recruit from './plugin'
|
||||
|
||||
async function createApplication (object: Doc): Promise<void> {
|
||||
showPopup(CreateApplication, { candidate: object._id, preserveCandidate: true })
|
||||
|
@ -17,6 +17,7 @@ import { Ref, Space } from '@anticrm/core'
|
||||
import type { IntlString, StatusCode } from '@anticrm/platform'
|
||||
import { mergeIds } from '@anticrm/platform'
|
||||
import recruit, { recruitId } from '@anticrm/recruit'
|
||||
import { TagCategory } from '@anticrm/tags'
|
||||
|
||||
export default mergeIds(recruitId, recruit, {
|
||||
status: {
|
||||
@ -69,5 +70,9 @@ export default mergeIds(recruitId, recruit, {
|
||||
},
|
||||
space: {
|
||||
CandidatesPublic: '' as Ref<Space>
|
||||
},
|
||||
category: {
|
||||
Other: '' as Ref<TagCategory>,
|
||||
Category: '' as Ref<TagCategory>
|
||||
}
|
||||
})
|
||||
|
@ -29,6 +29,7 @@
|
||||
"CategoryTargetClass": "Category",
|
||||
"CategoryTagsLabel": "Category Items",
|
||||
"OtherCategoryLabel": "Other",
|
||||
"AllCategories": "All Categories"
|
||||
"AllCategories": "All Categories",
|
||||
"DefaultLabel": "Default category"
|
||||
}
|
||||
}
|
@ -14,11 +14,11 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, Ref, SortingOrder } from '@anticrm/core'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { TagCategory, TagElement } from '@anticrm/tags'
|
||||
import tags from '../plugin'
|
||||
import { getPlatformColorForText, Label } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import tags from '../plugin'
|
||||
import { getTagStyle } from '../utils'
|
||||
|
||||
export let targetClass: Ref<Class<Doc>>
|
||||
@ -31,7 +31,6 @@
|
||||
const stepStyle = gap === 'small' ? 'gap-1' : 'gap-2'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
let elements: TagElement[] = []
|
||||
|
||||
@ -41,7 +40,7 @@
|
||||
const elementsQuery = createQuery()
|
||||
$: elementsQuery.query(
|
||||
tags.class.TagElement,
|
||||
{ targetClass: { $in: [targetClass, ...client.getHierarchy().getAncestors(targetClass)] } },
|
||||
{ targetClass },
|
||||
(res) => {
|
||||
elements = res
|
||||
},
|
||||
|
@ -31,7 +31,7 @@
|
||||
let color: number = 0
|
||||
|
||||
let categoryWasSet = false
|
||||
let category: Ref<TagCategory> = tags.category.Other
|
||||
let category: Ref<TagCategory> | undefined
|
||||
|
||||
let categories: TagCategory[] = []
|
||||
let categoryItems: DropdownTextItem[] = []
|
||||
@ -42,7 +42,7 @@
|
||||
color = getColorNumberByText(title)
|
||||
}
|
||||
|
||||
$: if (!categoryWasSet) {
|
||||
$: if (!categoryWasSet && categories.length > 0) {
|
||||
category = findTagCategory(title, categories)
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
query.query(tags.class.TagCategory, {}, async (result) => {
|
||||
query.query(tags.class.TagCategory, { targetClass }, async (result) => {
|
||||
const newItems: DropdownTextItem[] = []
|
||||
for (const r of result) {
|
||||
newItems.push({
|
||||
@ -74,7 +74,7 @@
|
||||
description,
|
||||
targetClass,
|
||||
color,
|
||||
category: category
|
||||
category: category ?? tags.category.NoCategory
|
||||
}
|
||||
|
||||
await client.createDoc(tags.class.TagElement, tags.space.Tags, tagElement, tagElementId)
|
||||
|
@ -77,7 +77,7 @@
|
||||
}
|
||||
|
||||
const query = createQuery()
|
||||
query.query(tags.class.TagCategory, {}, async (result) => {
|
||||
query.query(tags.class.TagCategory, { targetClass: value.targetClass }, async (result) => {
|
||||
const newItems: DropdownTextItem[] = []
|
||||
for (const r of result) {
|
||||
newItems.push({
|
||||
|
@ -51,6 +51,9 @@ export interface TagCategory extends Doc {
|
||||
targetClass: Ref<Class<Doc>>
|
||||
// A list of possible variants.
|
||||
tags: string[]
|
||||
|
||||
// If defined, will be used for unidentified tags
|
||||
default: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +82,7 @@ const tagsPlugin = plugin(tagsId, {
|
||||
TagsCategoryBar: '' as AnyComponent
|
||||
},
|
||||
category: {
|
||||
Other: '' as Ref<TagCategory>
|
||||
NoCategory: '' as Ref<TagCategory>
|
||||
}
|
||||
})
|
||||
|
||||
@ -92,12 +95,19 @@ export default tagsPlugin
|
||||
* @public
|
||||
*/
|
||||
export function findTagCategory (title: string, categories: TagCategory[]): Ref<TagCategory> {
|
||||
let defaultCategory: TagCategory | undefined
|
||||
for (const c of categories) {
|
||||
if (c.default) {
|
||||
defaultCategory = c
|
||||
}
|
||||
if (c.tags.findIndex((it) => it.toLowerCase() === title.toLowerCase()) !== -1) {
|
||||
return c._id
|
||||
}
|
||||
}
|
||||
return tagsPlugin.category.Other
|
||||
if (defaultCategory === undefined) {
|
||||
throw new Error('Tag category not found')
|
||||
}
|
||||
return defaultCategory._id
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user