DFiP_Budget_planing/perf/analysis/svod2_search_oracle.mjs
2026-10-28 21:09:29 +04:00

150 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Оракул экрана «до» свода 2 (Svod2Page на коммите 399234a + material-react-table). Копии дословные.
// matchesSearch: Svod2Page.jsx:283-294; equals: material-react-table/dist/index.esm.js:663-667.
import { mkdirSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const matchesSearch = (row, query) => {
const searchableData = {
...row.data?.header,
...row.data?.contract_summary,
...row.data?.contract_detail,
};
return Object.values(searchableData).some((value) =>
String(value ?? '')
.toLowerCase()
.includes(query),
);
};
const equals = (row, id, filterValue) => {
var _a;
return ((_a = row.getValue(id)) === null || _a === void 0 ? void 0 : _a.toString().toLowerCase().trim()) ===
filterValue.toString().toLowerCase().trim();
};
equals.autoRemove = (val) => !val;
export { matchesSearch, equals };
export const FILTER_COLUMNS = ['smeta_type', 'smeta_direction', 'org_name'];
// accessorFn колонок: valueAt(row, ['header', id])
const asTableRow = (row, id) => ({ getValue: () => row.data?.header?.[id] });
const cellValue = (row, id) => row.data?.header?.[id];
const activeFilters = (filters, skipId) =>
FILTER_COLUMNS.filter((id) => id !== skipId && filters[id] && !equals.autoRemove(filters[id]));
const bySearch = (rows, search) => {
const normalizedSearch = String(search ?? '').trim().toLowerCase();
return normalizedSearch ? rows.filter((row) => matchesSearch(row, normalizedSearch)) : rows;
};
const byFilters = (rows, filters, skipId) => {
const ids = activeFilters(filters, skipId);
return rows.filter((row) => ids.every((id) => equals({ getValue: () => cellValue(row, id) }, id, filters[id])));
};
export function screenRows(rows, { search = '', filters = {} } = {}) {
return byFilters(bySearch(rows, search), filters, null);
}
export function screenFound(rows, search) {
return { found: bySearch(rows, search).length, total: rows.length };
}
export function screenFacets(rows, { search = '', filters = {} } = {}) {
const searched = bySearch(rows, search);
const result = { total: rows.length, search_total: searched.length };
for (const id of FILTER_COLUMNS) {
const counts = new Map();
for (const row of byFilters(searched, filters, id)) {
const value = asTableRow(row, id).getValue(id);
counts.set(value, (counts.get(value) ?? 0) + 1);
}
result[id] = Array.from(counts.keys())
.filter((value) => value !== null && value !== undefined)
.sort((a, b) => a.localeCompare(b))
.map((value) => [value, counts.get(value)]);
}
return result;
}
export const NUMBER_VECTOR_INPUTS = [
'0', '-0', '1', '-1', '100', '100.00', '1234.50', '12.34', '0.5', '0.30000000000000004',
'0.000001', '1e-7', '-1.5e-10', '123e-20', '1e15', '1e20', '1e21', '123456789012345680000',
'9007199254740993', '5e-324', '1.7976931348623157e308',
];
// Числа в строках берём через JSON.parse, чтобы запись была как у JSON-ответа API.
const J = (text) => JSON.parse(text);
export const SYNTHETIC_ROWS = [
{ data: { line_id: 1, header: { name: 'Аренда помещения', smeta_type: 'КВ', smeta_direction: 'Развитие', org_name: 'Филиал Ёлки' },
contract_summary: { counterparty: 'ООО Ромашка' }, contract_detail: { counterparty: null, ceiling_amount: J('1234.50') } } },
{ data: { line_id: 2, header: { name: 'АРЕНДА склада', smeta_type: 'АХР', smeta_direction: 'Поддержка', org_name: 'Филиал Ёлки' },
contract_summary: {}, contract_detail: { counterparty: 'ООО Ромашка Плюс', ceiling_amount: J('100') } } },
{ data: { line_id: 3, header: { name: 'Ёлка новогодняя', smeta_type: 'КВ', smeta_direction: null, org_name: 'филиал ёлки ' },
contract_summary: { counterparty: 'ИП Ёж' }, contract_detail: { ceiling_amount: J('0') } } },
{ data: { line_id: 4, header: { name: 'ёжик', smeta_type: 'кв ', smeta_direction: 'Развитие', org_name: 'Центр' },
contract_summary: {}, contract_detail: { ceiling_amount: J('1e-7') } } },
{ data: { line_id: 5, header: { name: 'Услуги связи', smeta_type: 'КВ', smeta_direction: 'Поддержка', org_name: 'Центр' },
contract_summary: {}, contract_detail: { note: 'a b' } } },
{ data: { line_id: 6, header: { name: 'Без маркера', smeta_type: 'АХР', smeta_direction: 'Развитие', org_name: 'Север' },
contract_summary: {}, contract_detail: { note: 'xy' } } },
{ data: { line_id: 7, header: { name: 'Флаг', smeta_type: 'КВ', smeta_direction: 'Развитие', org_name: 'Север', is_active: true },
contract_summary: { flag: true }, contract_detail: { note: null } } },
{ data: { line_id: 8, header: { name: null, smeta_type: null, smeta_direction: null, org_name: null },
contract_summary: {}, contract_detail: {} } },
{ data: { line_id: 9, header: { name: 'Итого 1', smeta_type: 'АХР', smeta_direction: 'Поддержка', org_name: 'Центр' },
contract_summary: { counterparty: 'ООО Ромашка' }, contract_detail: { counterparty: 'ООО Ромашка', ceiling_amount: J('1234.5') } } },
{ data: { line_id: 10, header: { name: 'Прочее', smeta_type: 'ПР', smeta_direction: 'Развитие', org_name: 'Филиал Ёлки' },
contract_summary: {}, contract_detail: { ceiling_amount: J('1e-7'), note: 'Договор №1' } } },
{ data: { line_id: 11, header: { name: 'Лицензии', smeta_type: 'КВ', smeta_direction: 'Поддержка', org_name: 'Юг' },
contract_summary: {}, contract_detail: { ceiling_amount: J('100.00') } } },
];
export const SCREEN_CASES = [
{ search: '', filters: {} },
{ search: 'аРЕНДА', filters: {} },
{ search: 'ё', filters: {} },
{ search: 'ромаш', filters: {} },
{ search: '1234.5', filters: {} },
{ search: '1234.50', filters: {} },
{ search: ' аренда ', filters: {} },
{ search: ' ', filters: {} },
{ search: '1', filters: {} },
{ search: '', filters: { smeta_type: 'кв ' } },
{ search: '', filters: { org_name: 'Филиал Ёлки' } },
{ search: '', filters: { smeta_type: 'КВ', smeta_direction: 'Развитие' } },
{ search: 'ё', filters: { smeta_type: 'КВ' } },
{ search: '', filters: { org_name: 'филиал ёлки' } },
];
export function emitNumberVectors() {
return NUMBER_VECTOR_INPUTS.map((input) => ({ input, js: String(JSON.parse(input)) }));
}
export function emitScreenVectors() {
return {
rows: SYNTHETIC_ROWS,
cases: SCREEN_CASES.map(({ search, filters }) => ({
search,
filters,
line_ids: screenRows(SYNTHETIC_ROWS, { search, filters }).map((r) => r.data.line_id),
found: screenFound(SYNTHETIC_ROWS, search),
facets: screenFacets(SYNTHETIC_ROWS, { search, filters }),
})),
};
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const i = process.argv.indexOf('--emit-vectors');
if (i > 0 && process.argv[i + 1]) {
const dir = process.argv[i + 1];
mkdirSync(dir, { recursive: true });
writeFileSync(path.join(dir, 'js_number_text_vectors.json'), `${JSON.stringify(emitNumberVectors(), null, 2)}\n`);
writeFileSync(path.join(dir, 'svod2_screen_vectors.json'), `${JSON.stringify(emitScreenVectors(), null, 2)}\n`);
}
}