АРЧ АП11 - Архитектура после регресса: Архитектура: сохранить свежую дату при inventory root restore и очистить data-scope ответы от грязных лейблов
This commit is contained in:
@@ -47,7 +47,9 @@ const ORGANIZATION_SCOPE_STOPWORDS = new Set([
|
||||
|
||||
function normalizeScopeLabel(value: unknown): string {
|
||||
return String(value ?? "")
|
||||
.replace(/\\/g, " ")
|
||||
.replace(/[“”«»]/g, '"')
|
||||
.replace(/([\p{L}])"(?=[\p{L}])/gu, "$1в")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
@@ -109,6 +111,54 @@ function organizationTokenVariants(token: string): string[] {
|
||||
return Array.from(variants);
|
||||
}
|
||||
|
||||
function isSingleInsertionOrDeletionAway(left: string, right: string): boolean {
|
||||
const longer = left.length >= right.length ? left : right;
|
||||
const shorter = left.length >= right.length ? right : left;
|
||||
if (longer.length - shorter.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
let longIndex = 0;
|
||||
let shortIndex = 0;
|
||||
let mismatchUsed = false;
|
||||
while (longIndex < longer.length && shortIndex < shorter.length) {
|
||||
if (longer[longIndex] === shorter[shortIndex]) {
|
||||
longIndex += 1;
|
||||
shortIndex += 1;
|
||||
continue;
|
||||
}
|
||||
if (mismatchUsed) {
|
||||
return false;
|
||||
}
|
||||
mismatchUsed = true;
|
||||
longIndex += 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function organizationTokensLookEquivalent(left: string, right: string): boolean {
|
||||
if (!left || !right) {
|
||||
return false;
|
||||
}
|
||||
if (left === right) {
|
||||
return true;
|
||||
}
|
||||
if (left.length >= 5 && right.length >= 5 && (left.startsWith(right) || right.startsWith(left))) {
|
||||
return true;
|
||||
}
|
||||
const leftCompact = left.replace(/\s+/g, "");
|
||||
const rightCompact = right.replace(/\s+/g, "");
|
||||
if (!leftCompact || !rightCompact) {
|
||||
return false;
|
||||
}
|
||||
if (leftCompact === rightCompact) {
|
||||
return true;
|
||||
}
|
||||
if (leftCompact.length >= 6 && rightCompact.length >= 6 && isSingleInsertionOrDeletionAway(leftCompact, rightCompact)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function scoreOrganizationMentionInMessage(message: unknown, organization: unknown): number {
|
||||
const messageNorm = normalizeOrganizationScopeSearchText(message);
|
||||
const organizationNorm = normalizeOrganizationScopeSearchText(organization);
|
||||
@@ -170,20 +220,73 @@ export function scoreOrganizationMentionInMessage(message: unknown, organization
|
||||
return score;
|
||||
}
|
||||
|
||||
export function organizationsLikelySameEntity(left: unknown, right: unknown): boolean {
|
||||
const leftNorm = normalizeOrganizationScopeSearchText(left);
|
||||
const rightNorm = normalizeOrganizationScopeSearchText(right);
|
||||
if (!leftNorm || !rightNorm) {
|
||||
return false;
|
||||
}
|
||||
if (leftNorm === rightNorm) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const leftTokens = tokenizeOrganizationScope(leftNorm);
|
||||
const rightTokens = tokenizeOrganizationScope(rightNorm);
|
||||
if (leftTokens.length === 0 || rightTokens.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const leftCompact = leftTokens.join("");
|
||||
const rightCompact = rightTokens.join("");
|
||||
if (leftCompact && rightCompact) {
|
||||
if (leftCompact === rightCompact) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
leftCompact.length >= 8 &&
|
||||
rightCompact.length >= 8 &&
|
||||
isSingleInsertionOrDeletionAway(leftCompact, rightCompact)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const leftCovered = leftTokens.every((leftToken) =>
|
||||
rightTokens.some((rightToken) => organizationTokensLookEquivalent(leftToken, rightToken))
|
||||
);
|
||||
if (!leftCovered) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const rightCovered = rightTokens.every((rightToken) =>
|
||||
leftTokens.some((leftToken) => organizationTokensLookEquivalent(leftToken, rightToken))
|
||||
);
|
||||
return rightCovered;
|
||||
}
|
||||
|
||||
export function mergeKnownOrganizations(values: unknown[], limit = 50): string[] {
|
||||
const dedup = new Map<string, string>();
|
||||
const dedup: string[] = [];
|
||||
for (const raw of Array.isArray(values) ? values : []) {
|
||||
const normalized = normalizeOrganizationScopeValue(raw);
|
||||
if (!normalized) {
|
||||
continue;
|
||||
}
|
||||
const key = normalizeOrganizationScopeSearchText(normalized);
|
||||
if (!key || dedup.has(key)) {
|
||||
if (!key) {
|
||||
continue;
|
||||
}
|
||||
dedup.set(key, normalized);
|
||||
const existingIndex = dedup.findIndex((item) => organizationsLikelySameEntity(item, normalized));
|
||||
if (existingIndex >= 0) {
|
||||
const existing = dedup[existingIndex];
|
||||
const existingKey = normalizeOrganizationScopeSearchText(existing);
|
||||
if (key.length > existingKey.length || normalized.length > existing.length) {
|
||||
dedup[existingIndex] = normalized;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
dedup.push(normalized);
|
||||
}
|
||||
return Array.from(dedup.values()).slice(0, limit);
|
||||
return dedup.slice(0, limit);
|
||||
}
|
||||
|
||||
export function resolveOrganizationSelectionFromMessage(
|
||||
|
||||
Reference in New Issue
Block a user