Move skill definitions for recruit from tags to recruit (#1043)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2022-02-25 16:02:38 +07:00 committed by GitHub
parent 1b9a5e0395
commit 7ab4e59b7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 150 additions and 87 deletions

View File

@ -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

View File

@ -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"
}
}

View File

@ -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, {

View File

@ -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"
}
}

View File

@ -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 {

View File

@ -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 })
}
}
}
}

View File

@ -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[]>>

View File

@ -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)

View File

@ -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))
}

View File

@ -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[] = []

View File

@ -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 })

View File

@ -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>
}
})

View File

@ -29,6 +29,7 @@
"CategoryTargetClass": "Category",
"CategoryTagsLabel": "Category Items",
"OtherCategoryLabel": "Other",
"AllCategories": "All Categories"
"AllCategories": "All Categories",
"DefaultLabel": "Default category"
}
}

View File

@ -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
},

View File

@ -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)

View File

@ -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({

View File

@ -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
}
/**