All files / lib/schemas sessions.js

80% Statements 12/15
0% Branches 0/2
83.33% Functions 5/6
80% Lines 12/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138                      16x             16x             16x                                                                           16x                             16x           16x           40x           16x                       40x                 40x                 40x                 40x    
import { scope, type } from 'arktype';
 
// schemas are imported by scripts that are run by Bun directly, so dont use $lib here
import { keys } from '../utils.js';
import { ID } from './common.js';
import { MetadataValues } from './metadata.js';
 
/**
 * @import * as DB from '$lib/database.js';
 */
 
export const SORT_FIELDS = /** @type {const} */ ({
	metadataValue: { label: 'Métadonnée…', needsMetadata: true },
	metadataConfidence: { label: 'Confiance en…', needsMetadata: true },
	id: { label: 'ID', needsMetadata: false },
	name: { label: 'Nom', needsMetadata: false }
});
 
export const GROUP_FIELDS = /** @type {const} */ ({
	metadataValue: { label: 'Métadonnée…', needsMetadata: true, needsTolerance: true },
	metadataPresence: { label: 'Présence de…', needsMetadata: true, needsTolerance: false },
	metadataConfidence: { label: 'Confiance en…', needsMetadata: true, needsTolerance: false },
	none: { label: 'Aucun regroupement', needsMetadata: false, needsTolerance: false }
});
 
export const GROUPING_TOLERANCES = /** @type {const} */ ({
	dates: {
		label: 'Dates',
		help: 'Précision des dates',
		affectedTypes: /** @type {DB.MetadataType[]} */ (['date']),
		options: {
			year: { scientific: '', casual: 'Par année' },
			month: { scientific: '', casual: 'Par mois' },
			day: { scientific: '', casual: 'Par jour' },
			hour: { scientific: '', casual: 'Par heure' },
			minute: { scientific: '', casual: 'Par minute' }
		}
	},
	decimal: {
		label: 'Nombres',
		help: 'Précision des nombres',
		affectedTypes: /** @type {DB.MetadataType[]} */ ([
			'integer',
			'float',
			'boundingbox',
			'location'
		]),
		options: {
			giga: { scientific: 'G', casual: 'Au milliard' },
			mega: { scientific: 'M', casual: 'Au million' },
			kilo: { scientific: 'k', casual: 'Au millier' },
			hecto: { scientific: '100', casual: 'À la centaine' },
			deca: { scientific: '10', casual: 'À la dizaine' },
			unit: { scientific: '1', casual: "À l'entier" },
			deci: { scientific: '0.1', casual: 'Au dixième' },
			centi: { scientific: '0.01', casual: 'Au centième' },
			milli: { scientific: 'm', casual: 'Au millième' },
			micro: { scientific: 'µ', casual: 'Au millionième' },
			nano: { scientific: 'n', casual: 'Au milliardième' }
		}
	}
});
 
export const FULLSCREEN_CLASSIFY_LAYOUTS = /** @type {const} */ (['top-bottom', 'left-right']);
 
/**
 * @param {['sort', keyof typeof SORT_FIELDS] | ['group', keyof typeof GROUP_FIELDS]} param0 [task, field]
 * @returns {boolean}
 */
export function sortOrGroupFieldNeedsMetadata(...[task, field]) {
	switch (task) {
		case 'sort':
			return SORT_FIELDS[field].needsMetadata;
		case 'group':
			return GROUP_FIELDS[field].needsMetadata;
	}
}
 
export const SortSettings = type({
	field: type.enumerated(...keys(SORT_FIELDS)),
	'metadata?': ID,
	direction: type.enumerated('asc', 'desc')
});
 
export const GroupSettings = type({
	field: type.enumerated(...keys(GROUP_FIELDS)),
	'metadata?': ID,
	tolerances: type({
		dates: type.enumerated(...keys(GROUPING_TOLERANCES.dates.options)),
		decimal: type.enumerated(...keys(GROUPING_TOLERANCES.decimal.options))
	}).default(() => ({
		dates: 'day',
		decimal: 'unit'
	}))
});
 
export const Session = type({
	id: ID,
	name: 'string',
	// Date is not compatible with JSON Schemas, use a datestring instead
	createdAt: 'string.date.iso',
	openedAt: 'string.date.iso',
	description: 'string',
	protocol: ID,
	metadata: MetadataValues,
	fullscreenClassifier: type({
		layout: type.enumerated(...FULLSCREEN_CLASSIFY_LAYOUTS),
		'focusedMetadata?': ID
	}).default(() => ({
		layout: 'top-bottom'
	})),
	sort: type({
		global: SortSettings,
		// Per-tab
		'import?': SortSettings,
		'crop?': SortSettings,
		'classify?': SortSettings
	}).default(() => ({
		global: { field: 'id', direction: 'asc' }
	})),
	group: type({
		global: GroupSettings,
		// Per-tab
		'import?': GroupSettings,
		'crop?': GroupSettings,
		'classify?': GroupSettings
	}).default(() => ({
		global: { field: 'none' }
	})),
	inferenceModels: scope({ ID })
		.type({
			// -1 is for none selected
			'[ID]': 'number.integer >= -1'
		})
		.describe('Maps metadata IDs to selected model indices')
		.default(() => ({}))
});