@whiteslove/parsing-lexicon 0.6.9 → 0.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/hiring-professions.js +1 -1
- package/src/hiring-semantics.d.ts +1 -1
- package/src/hiring-semantics.js +1 -0
- package/src/hiring-skills.js +1 -1
- package/src/hiring-source-semantics.js +8 -0
- package/src/housing-address.js +7 -4
- package/src/housing-poi-extensions.js +15 -0
- package/src/ua-city-location-expansions.js +8 -0
package/package.json
CHANGED
|
@@ -188,7 +188,7 @@ const rows = [
|
|
|
188
188
|
['chat_operator','customer_support',['chat operator','оператор чата','чат оператор']],
|
|
189
189
|
['customer_success_manager','customer_support',['customer success manager','csm','менеджер по успеху клиентов']],
|
|
190
190
|
['office_manager','administration',['office manager','офис менеджер','офіс менеджер']],
|
|
191
|
-
['administrator','administration',['administrator','администратор','адміністратор','
|
|
191
|
+
['administrator','administration',['administrator','администратор','администратором','адміністратор','адміністратором']],
|
|
192
192
|
['receptionist','administration',['receptionist','ресепшионист','ресепшн','рецепционист']],
|
|
193
193
|
['executive_assistant','administration',['executive assistant','personal assistant','ассистент руководителя','личный помощник']],
|
|
194
194
|
['secretary','administration',['secretary','секретарь','секретар']],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DegreeLevel } from './hiring-requirements.js';
|
|
2
2
|
|
|
3
|
-
export type CandidateFeatureCode = 'student' | 'parentalLeave' | 'noExperience' | 'partTime' | 'nightShift' | 'openToRelocation';
|
|
3
|
+
export type CandidateFeatureCode = 'student' | 'parentalLeave' | 'noExperience' | 'partTime' | 'nightShift' | 'openToRelocation' | 'withPets';
|
|
4
4
|
export type HiringScopeCode = 'architecture' | 'leadership' | 'mentoring' | 'scale' | 'ownership';
|
|
5
5
|
|
|
6
6
|
export function isCandidateNonTargetContext(value: unknown): boolean;
|
package/src/hiring-semantics.js
CHANGED
|
@@ -31,6 +31,7 @@ const FEATURE_RULES = Object.freeze([
|
|
|
31
31
|
['partTime', /подработк|підробіт|part[-\s]?time|неполный\s+день|неповн(?:ий|а)\s+день|yarim\s+stavka/iu],
|
|
32
32
|
['nightShift', /ночн(?:ая|ую|ой)\s+смен|нічн(?:а|у|ої)\s+змін|night\s+shift|tungi\s+smena/iu],
|
|
33
33
|
['openToRelocation', /готов\p{L}*\s+к\s+переезду|готов\p{L}*\s+переехать|готов\p{L}*\s+до\s+переїзду|relocat(?:e|ion)|ko(?:'|’)chib\s+o(?:'|’)tish/iu],
|
|
34
|
+
['withPets', /тварин\p{L}*|животн\p{L}*|жануар\p{L}*|\bwith\s+(?:animals|pets)\b|\banimals?\b|\bpets?\b|hayvon(?:lar)?(?:\s+bilan)?/iu],
|
|
34
35
|
]);
|
|
35
36
|
|
|
36
37
|
const RELOCATION_NEGATIVE_RE = /не\s+готов\p{L}*\s+к\s+переезду|не\s+розгляда\p{L}*\s+переїзд|not\s+(?:open|ready)\s+to\s+relocat/iu;
|
package/src/hiring-skills.js
CHANGED
|
@@ -149,7 +149,7 @@ export const SKILL_CATALOG = [
|
|
|
149
149
|
...group('Marketing', 'Digital Marketing', [
|
|
150
150
|
['Digital Marketing'], ['Content Marketing'], ['Social Media Marketing', ['smm']], ['Email Marketing'],
|
|
151
151
|
['Google Ads', ['google adwords']], ['Meta Ads', ['facebook ads']], ['Google Analytics', ['ga4']],
|
|
152
|
-
['Google Tag Manager'
|
|
152
|
+
['Google Tag Manager'], ['Marketing Automation'], ['Copywriting', ['копирайтинг']],
|
|
153
153
|
['Market Research'], ['A/B Testing', ['ab testing']], ['Conversion Rate Optimization', ['cro']],
|
|
154
154
|
]),
|
|
155
155
|
...group('HR', 'Recruiting & People', [
|
|
@@ -24,6 +24,10 @@ const FIELD_EXTRA_ALIASES = Object.freeze({
|
|
|
24
24
|
const APPLICATION_MODAL_RE = /(?:отправить\s*резюме\s*)+(?:\[telegram\]\s*)?пол\s*мужской\s*женский\s*образование[\s\S]{0,500}?выберите\s*вакансию[\s\S]*/iu;
|
|
25
25
|
const LEAKED_STYLESHEET_START_RE = /:root\s*\{\s*--[\w-]+\s*:/iu;
|
|
26
26
|
const LEAKED_CSS_RULE_RE = /\s*(?:[.#][\w\\-]+|:root)(?:[^{}\n]{0,300})\{\s*(?:(?:--)?[\w-]+\s*:\s*[^;{}]+;?\s*)+\}\s*/giu;
|
|
27
|
+
// Button/chrome labels copied along with a resume from Ukrainian job boards
|
|
28
|
+
// (novarobota.ua, robota.ua, work.ua and similar). Matched as whole lines so
|
|
29
|
+
// genuine resume prose is never touched.
|
|
30
|
+
const JOB_BOARD_UI_NOISE_RE = /^(?:подробнее|детальніше|предпросмотр|попередній\s+перегляд|добавить\s+в\s+отклики\/?\s*избранное|додати\s+до\s+відгуків\/?\s*обраного|предложить\s+вакансию|запропонувати\s+вакансію|для\s+того\s+чтобы\s+предложить\s+вакансию\s+нужно\s+авторизоваться\s+как\s+работодатель|щоб\s+запропонувати\s+вакансію[^\n]*|пожаловаться\s+на\s+резюме|поскаржитися\s+на\s+резюме|отправить)$/iu;
|
|
27
31
|
|
|
28
32
|
/** Remove source-page controls and stylesheet fragments copied into hiring text. */
|
|
29
33
|
export function cleanHiringSourceText(value) {
|
|
@@ -32,7 +36,11 @@ export function cleanHiringSourceText(value) {
|
|
|
32
36
|
return (stylesheetStart >= 0 ? text.slice(0, stylesheetStart) : text)
|
|
33
37
|
.replace(APPLICATION_MODAL_RE, '')
|
|
34
38
|
.replace(LEAKED_CSS_RULE_RE, ' ')
|
|
39
|
+
.split('\n')
|
|
40
|
+
.filter((line) => !JOB_BOARD_UI_NOISE_RE.test(line.trim()))
|
|
41
|
+
.join('\n')
|
|
35
42
|
.replace(/[ \t]+(?=\n)/g, '')
|
|
43
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
36
44
|
.trim();
|
|
37
45
|
}
|
|
38
46
|
|
package/src/housing-address.js
CHANGED
|
@@ -12,10 +12,10 @@ const HOUSE_MARKER = String.raw`(?:дом|д\.|будинок|буд\.|house|h\.
|
|
|
12
12
|
const BUILDING_MARKER = String.raw`(?:корп(?:ус)?\.?|к\.|строен(?:ие)?|стр\.|будова|секц(?:ия|ія)?|bloc|corp|building|bldg\.?|korpus)`;
|
|
13
13
|
const NUMBER_TOKEN = String.raw`\d{1,5}(?:[-\/]?[\p{L}])?(?:[\/-]\d{1,4}(?:[-\/]?[\p{L}])?)?`;
|
|
14
14
|
const STREET_WORD = String.raw`[\p{L}'’.-]{2,48}`;
|
|
15
|
-
const ADDRESS_FIELD_STOP_RE = /\s+(?:цена|ціна|нарх(?:и)?|narx|price|стоимост[ьи]|этаж(?:ность)?|поверх|qavat|қабат|кават|қават|комнат(?:ы|а)?|кімнат(?:и|а)?|xona|хона|площадь|площа|maydon|тел(?:ефон)?|phone|комисси\p{L}*|депозит|deposit)(?=$|[\s:№#-])/iu;
|
|
15
|
+
const ADDRESS_FIELD_STOP_RE = /\s+(?:цена|ціна|нарх(?:и)?|narx|price|стоимост[ьи]|этаж(?:ность)?|поверх|qavat|қабат|кават|қават|комнат(?:ы|а)?|кімнат(?:и|а)?|xona|хона|площадь|площа|maydon|тел(?:ефон)?|phone|комисси\p{L}*|депозит|deposit|ориентир\p{L}*|ор[-–—]?р\.?)(?=$|[\s:№#-])/iu;
|
|
16
16
|
const PROPERTY_AREA_LINE_RE = /(?:^|[^\p{L}\p{N}_])(?:(?:общая|жилая|полезная|кухонная)\s+площадь|площадь\s+(?:квартиры|дома|комнаты))(?=$|[^\p{L}\p{N}_])/iu;
|
|
17
17
|
const NON_ADDRESS_BARE_RE = /^(?:(?:(?:перш(?:ий|ому)|перв(?:ый|ом)|друг(?:ий|ому)|втор(?:ой|ом)|трет(?:ій|ьем|ий)|\d{1,3}(?:-?й)?)\s+(?:поверх|этаж|floor|qavat|қабат))|(?:поверх|этаж|floor|qavat|қабат)(?:\s|$)|(?:район|р-н|рн|мікрорайон|микрорайон|мкр\.?|жк|ж\.к\.|жилой\s+комплекс|житловий\s+комплекс|residential\s+complex)(?:\s|$)|(?:недалеко|поруч|рядом|біля|около|возле)(?=$|[^\p{L}\p{N}_])|(?:зупинка|остановка|станція|станция)(?:\s|$))/iu;
|
|
18
|
-
const DELIMITED_STREET_REJECT_RE = /(?:^|\s)(?:город|місто|city|район|р-н|рн|мікрорайон|микрорайон|мкр|жк|метро|поверх|этаж|floor|qavat|кімнат\p{L}*|комнат\p{L}*|квартира|квартири|квартиры|оренда|аренда|продаж\p{L}*|цена|ціна|площад\p{L}
|
|
18
|
+
const DELIMITED_STREET_REJECT_RE = /(?:^|\s)(?:город|місто|city|район|р-н|рн|мікрорайон|микрорайон|мкр|жк|метро|поверх|этаж|floor|qavat|кімнат\p{L}*|комнат\p{L}*|квартира|квартири|квартиры|оренда|аренда|продаж\p{L}*|цена|ціна|площад\p{L}*|площа|зупинка|остановка|ориентир\p{L}*|ор[-–—]?р\.?)(?:\s|$)/iu;
|
|
19
19
|
|
|
20
20
|
function clean(value) {
|
|
21
21
|
return String(value ?? '')
|
|
@@ -265,11 +265,14 @@ function delimitedBareAddress(text) {
|
|
|
265
265
|
function bareAddress(text) {
|
|
266
266
|
const cleaned = clean(text);
|
|
267
267
|
if (!cleaned || PROPERTY_AREA_LINE_RE.test(cleaned) || NON_ADDRESS_BARE_RE.test(cleaned)) return null;
|
|
268
|
-
const
|
|
268
|
+
const stopMatch = cleaned.match(ADDRESS_FIELD_STOP_RE);
|
|
269
|
+
const truncated = stopMatch ? clean(cleaned.slice(0, stopMatch.index)) : cleaned;
|
|
270
|
+
if (!truncated) return null;
|
|
271
|
+
const tail = splitAddressTail(truncated);
|
|
269
272
|
if (!tail) return null;
|
|
270
273
|
if (/^(?:центр|centre|center)$/iu.test(compactStreet(tail.street) || '')) return null;
|
|
271
274
|
const confidence = tail.houseNumber ? 0.85 : 0.55;
|
|
272
|
-
return result(
|
|
275
|
+
return result(truncated, tail.street, tail.houseNumber, tail.building, confidence);
|
|
273
276
|
}
|
|
274
277
|
|
|
275
278
|
/**
|
|
@@ -137,4 +137,19 @@ export const HOUSING_POI_EXTENSIONS = Object.freeze([
|
|
|
137
137
|
}, { category: 'park', display: { ru: 'Парк Kitoblar Dunyosi', en: 'Kitoblar Dunyosi Park' } }),
|
|
138
138
|
group('Interpol', { ru: ['Интерпол'], en: ['Interpol'], uzLatn: ['Interpol'] }, { category: 'landmark', display: { ru: 'Интерпол', en: 'Interpol' } }),
|
|
139
139
|
group('Jemchug', { ru: ['Жемчуг'], en: ['Jemchug'], uzLatn: ['jemchug'] }, { category: 'landmark', display: { ru: 'Жемчуг', en: 'Jemchug' } }),
|
|
140
|
+
group('Майдан Незалежності', {
|
|
141
|
+
uk: ['Площа Незалежності'],
|
|
142
|
+
}, { category: 'square', country: 'UA', city: 'Sumy', display: { uk: 'Майдан Незалежності' } }),
|
|
143
|
+
group('Eurobazar', {
|
|
144
|
+
uk: ['Євробазар', 'ТЦ Євробазар'],
|
|
145
|
+
en: ['Eurobazar'],
|
|
146
|
+
}, { category: 'shopping_mall', country: 'UA', city: 'Sumy', display: { uk: 'ТЦ «Євробазар»', en: 'Eurobazar' } }),
|
|
147
|
+
group('Sumy Railway Station', {
|
|
148
|
+
uk: ['Суми (станція)', 'залізничний вокзал Суми', 'вокзал станції Суми'],
|
|
149
|
+
en: ['Sumy railway station'],
|
|
150
|
+
}, { category: 'transport', country: 'UA', city: 'Sumy', display: { uk: 'Залізничний вокзал Суми', en: 'Sumy Railway Station' } }),
|
|
151
|
+
group('Sumy State University', {
|
|
152
|
+
uk: ['Сумський державний університет', 'СумДУ'],
|
|
153
|
+
en: ['SumDU'],
|
|
154
|
+
}, { category: 'university', country: 'UA', city: 'Sumy', display: { uk: 'Сумський державний університет', en: 'Sumy State University' } }),
|
|
140
155
|
]);
|
|
@@ -156,6 +156,14 @@ export const UA_CITY_LOCATION_EXPANSIONS = Object.freeze({
|
|
|
156
156
|
['Свято-Миколаївський кафедральний собор', 'Миколаївський кафедральний собор (Ніжин)', 'Миколаївський собор', 'Собор Святого Миколая Чудотворця'],
|
|
157
157
|
]),
|
|
158
158
|
|
|
159
|
+
Konotop: landmarks([
|
|
160
|
+
['Konotop Railway Station'],
|
|
161
|
+
['Конотопський міський краєзнавчий музей ім. О. М. Лазаревського', 'Конотопський міський краєзнавчий музей ім.О.М.Лазаревського', 'Конотопський краєзнавчий музей ім. О.М.Лазаревського'],
|
|
162
|
+
['Музей-садиба генерала М. І. Драгомирова', 'Музей-садиба генерала М.І.Драгомирова', 'Музей-садиба М.І.Драгомирова'],
|
|
163
|
+
['Конотопський музей авіації', 'Музей авіації'],
|
|
164
|
+
['Площа Миру'],
|
|
165
|
+
]),
|
|
166
|
+
|
|
159
167
|
Pryluky: landmarks([
|
|
160
168
|
['Pryluky Railway Station'],
|
|
161
169
|
['Прилуцький краєзнавчий музей імені В. І. Маслова', 'Прилуцький краєзнавчий музей ім. В. І. Маслова', 'Прилуцький краєзнавчий музей ім. В.І. Маслова'],
|