150 lines
7.2 KiB
JavaScript
150 lines
7.2 KiB
JavaScript
// Оракул экрана «до» свода 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`);
|
||
}
|
||
}
|