Saving a quick snapshot after Gemini changes to Dexie function and processing functions.

This commit is contained in:
Scott Idem
2025-11-13 12:12:58 -05:00
parent 848a3e9a4b
commit dfaa27384b
5 changed files with 261 additions and 311 deletions

View File

@@ -11,10 +11,10 @@ Core Aether modules
Additional Aether modules Additional Aether modules
* events * events
* presentation management - import the program data (events, session, presentations, presenters, evnet files, locations/rooms, devices) * presentation management - import the program data (events, session, presentations, presenters, event files, locations/rooms, devices)
* launcher - Technically this is used with presentation management. It is part of the native app that uses Electron. One of the libraries is for functions that only work when the site is opened in an Electron app. For example the regular browser can not move files around on the local computer or run local commands. * launcher - Technically this is used with presentation management. It is part of the native app that uses Electron. One of the libraries is for functions that only work when the site is opened in an Electron app. For example the regular browser can not move files around on the local computer or run local commands.
* badge printing * badge printing
* lead retreavl - attendee tracking; QR codes * lead retrieval - attendee tracking; QR codes
* journals - journal, documentation, notes, diary, blog, etc * journals - journal, documentation, notes, diary, blog, etc
* idaa - One of my clients * idaa - One of my clients

View File

@@ -1,248 +1,174 @@
import type { key_val } from '$lib/ae_stores'; import type { key_val } from '$lib/ae_stores';
import { get_object } from './api_get_object'; import { get_object } from './api_get_object';
// The lookup "obj_type" should broken out into a separate function. - 2024-08-07 // Refactored 2025-11-13 to use a lookup map for endpoints.
// Updated 2023-11-15 const objTypeToEndpointMap: Record<string, string> = {
export async function get_ae_obj_li_for_obj_id_crud_v2( account: '/crud/account/list',
{ address: '/crud/address/list',
api_cfg, archive: '/crud/archive/list',
obj_type, archive_content: '/crud/archive/content/list',
for_obj_type, contact: '/crud/contact/list',
for_obj_id, // NOTE: Changed 2023-12-06 to no longer required data_store: '/crud/data_store/list',
use_alt_tbl = false, // Alternate table or view name event: '/crud/event/list',
use_alt_mdl = false, // Alternate model name event_abstract: '/crud/event/abstract/list',
use_alt_exp = false, // Alternate export table or view name event_badge: '/crud/event/badge/list',
inc = {}, event_badge_template: '/crud/event/badge/template/list',
enabled = 'enabled', event_device: '/crud/event/device/list',
hidden = 'not_hidden', event_exhibit: '/crud/event/exhibit/list',
order_by_li = null, event_exhibit_tracking: '/crud/event/exhibit/tracking/list',
limit = 999999, event_file: '/crud/event/file/list',
offset = 0, event_location: '/crud/event/location/list',
// key, event_person: '/crud/event/person/list',
// jwt = null, event_presentation: '/crud/event/presentation/list',
headers = {}, event_presenter: '/crud/event/presenter/list',
params_json = null, // NOTE: This is a JSON object that needs to be safely converted to a string for the params. This is used for the API endpoint. Example: { "fulltext_search": { "default_qry_str": "Search string for default", "address_default_qry_str": "Search string for address", "contact_1_default_qry_str": "Search string for contact_1" } } event_session: '/crud/event/session/list',
// json_obj = null, // NOTE: This is a JSON object that needs to be safely converted to a string for the params. This is used for the search endpoint. event_track: '/crud/event/track/list',
params = {}, grant: '/crud/grant/list',
// return_meta = false, hosted_file: '/crud/hosted_file/list',
log_lvl = 0 journal: '/crud/journal/list',
}: { journal_entry: '/crud/journal/entry/list',
api_cfg: any, order: '/crud/order/list',
obj_type: string, order_line: '/crud/order/line/list',
for_obj_type: string, page: '/crud/page/list',
for_obj_id?: string, person: '/crud/person/list',
use_alt_tbl?: boolean|string, post: '/crud/post/list',
use_alt_mdl?: boolean|string, post_comment: '/crud/post/comment/list',
use_alt_exp?: boolean|string, site: '/crud/site/list',
inc?: key_val sponsorship_cfg: '/crud/sponsorship/cfg/list',
enabled?: string, sponsorship: '/crud/sponsorship/list',
hidden?: string, // user: '/crud/user/list',
order_by_li?: any, 'lu-country_subdivision': '/crud/lu/country_subdivision/list',
limit?: number, 'lu-country': '/crud/lu/country/list',
offset?: number, 'lu-time_zone': '/crud/lu/time_zone/list'
// key: string, };
// jwt?: string,
headers?: any,
params_json?: any,
// json_obj?: any,
params?: key_val,
// return_meta?: boolean,
log_lvl?: number
}
) {
if (log_lvl) {
console.log('*** get_ae_obj_li_for_obj_id_crud() ***');
}
// data = {}; function getEndpointForObjType(obj_type: string, for_obj_type?: string): string {
// data['super_key'] = key; if (obj_type === 'lu' && for_obj_type) {
// data['jwt'] = jwt; const key = `lu-${for_obj_type}`;
// NOTE: The key and or JWT should be in the header of the DELETE, GET, PATCH, POST const endpoint = objTypeToEndpointMap[key];
if (endpoint) return endpoint;
}
// const endpoint = `/crud/${obj_type}/list`; const endpoint = objTypeToEndpointMap[obj_type];
if (endpoint) return endpoint;
let endpoint = ''; throw new Error(`Unknown object type: ${obj_type}`);
if (obj_type == 'account') { }
endpoint = `/crud/account/list`;
} else if (obj_type == 'address') { type OrderBy = { [key: string]: 'ASC' | 'DESC' };
endpoint = `/crud/address/list`;
} else if (obj_type == 'archive') { interface GetAeObjLiForObjIdCrudV2Params {
endpoint = `/crud/archive/list`; api_cfg: any; // Consider defining a specific type for api_cfg
} else if (obj_type == 'archive_content') { obj_type: string;
endpoint = `/crud/archive/content/list`; for_obj_type: string;
} else if (obj_type == 'contact') { for_obj_id?: string;
endpoint = `/crud/contact/list`; use_alt_tbl?: boolean | string;
} else if (obj_type == 'data_store') { use_alt_mdl?: boolean | string;
endpoint = `/crud/data_store/list`; use_alt_exp?: boolean | string;
} else if (obj_type == 'event') { inc?: key_val;
endpoint = `/crud/event/list`; enabled?: 'all' | 'enabled' | 'not_enabled';
} else if (obj_type == 'event_abstract') { hidden?: 'all' | 'hidden' | 'not_hidden';
endpoint = `/crud/event/abstract/list`; order_by_li?: OrderBy[] | null;
} else if (obj_type == 'event_badge') { limit?: number;
endpoint = `/crud/event/badge/list`; offset?: number;
} else if (obj_type == 'event_badge_template') { headers?: Record<string, string>;
endpoint = `/crud/event/badge/template/list`; params_json?: any;
} else if (obj_type == 'event_device') { params?: key_val;
endpoint = `/crud/event/device/list`; log_lvl?: number;
} else if (obj_type == 'event_exhibit') { }
endpoint = `/crud/event/exhibit/list`;
} else if (obj_type == 'event_exhibit_tracking') { export async function get_ae_obj_li_for_obj_id_crud_v2({
endpoint = `/crud/event/exhibit/tracking/list`; api_cfg,
} else if (obj_type == 'event_file') { obj_type,
endpoint = `/crud/event/file/list`; for_obj_type,
} else if (obj_type == 'event_location') { for_obj_id,
endpoint = `/crud/event/location/list`; use_alt_tbl = false,
} else if (obj_type == 'event_person') { use_alt_mdl = false,
endpoint = `/crud/event/person/list`; use_alt_exp = false,
} else if (obj_type == 'event_presentation') { enabled = 'enabled',
endpoint = `/crud/event/presentation/list`; hidden = 'not_hidden',
} else if (obj_type == 'event_presenter') { order_by_li = null,
endpoint = `/crud/event/presenter/list`; limit = 999999,
} else if (obj_type == 'event_session') { offset = 0,
endpoint = `/crud/event/session/list`; headers = {},
} else if (obj_type == 'event_track') { params_json = null,
endpoint = `/crud/event/track/list`; params = {},
} else if (obj_type == 'grant') { log_lvl = 0
endpoint = `/crud/grant/list`; }: GetAeObjLiForObjIdCrudV2Params) {
} else if (obj_type == 'hosted_file') { if (log_lvl) {
endpoint = `/crud/hosted_file/list`; console.log('*** get_ae_obj_li_for_obj_id_crud_v2() ***');
} else if (obj_type == 'journal') { }
endpoint = `/crud/journal/list`;
} else if (obj_type == 'journal_entry') { try {
endpoint = `/crud/journal/entry/list`; const endpoint = `/v2${getEndpointForObjType(obj_type, for_obj_type)}`;
} else if (obj_type == 'order') { if (log_lvl) {
endpoint = `/crud/order/list`; console.log('Endpoint:', endpoint);
} else if (obj_type == 'order_line') { }
endpoint = `/crud/order/line/list`;
} else if (obj_type == 'page') { // We need to remove a few parameters from the params object that are not allowed.
endpoint = `/crud/page/list`; delete params['qry__enabled'];
} else if (obj_type == 'person') { delete params['qry__hidden'];
endpoint = `/crud/person/list`; delete params['qry__limit'];
} else if (obj_type == 'post') { delete params['qry__offset'];
endpoint = `/crud/post/list`;
} else if (obj_type == 'post_comment') { if (for_obj_type) params['for_obj_type'] = for_obj_type;
endpoint = `/crud/post/comment/list`; if (for_obj_id) params['for_obj_id'] = for_obj_id;
} else if (obj_type == 'site') {
endpoint = `/crud/site/list`; if (use_alt_tbl === true) params['tbl_alt'] = 'alt';
} else if (obj_type == 'sponsorship_cfg') { if (use_alt_mdl === true) params['mdl_alt'] = 'alt';
endpoint = `/crud/sponsorship/cfg/list`; if (use_alt_exp === true) params['exp_alt'] = 'alt';
} else if (obj_type == 'sponsorship') {
endpoint = `/crud/sponsorship/list`; const allowed_enabled_list = ['all', 'enabled', 'not_enabled'];
// } else if (obj_type == 'user') { if (allowed_enabled_list.includes(enabled)) {
// endpoint = `/crud/user/list`; params['enabled'] = enabled;
} else if (obj_type == 'lu' && for_obj_type == 'country_subdivision') { }
endpoint = `/crud/lu/country_subdivision/list`;
// for_obj_type = null; const allowed_hidden_list = ['all', 'hidden', 'not_hidden'];
} else if (obj_type == 'lu' && for_obj_type == 'country') { if (allowed_hidden_list.includes(hidden)) {
endpoint = `/crud/lu/country/list`; params['hidden'] = hidden;
// for_obj_type = null; }
} else if (obj_type == 'lu' && for_obj_type == 'time_zone') {
endpoint = `/crud/lu/time_zone/list`; // NOTE: The order_by_li variable is in the "headers" because URL GET params do not handle complex objects very well.
// for_obj_type = null; if (order_by_li) {
} else { headers['order_by_li'] = JSON.stringify(order_by_li);
console.log(`Unknown object type: ${obj_type}`); }
return false;
} if (limit > 0) params['limit'] = limit;
endpoint = `/v2${endpoint}`; if (offset > 0) params['offset'] = offset;
if (log_lvl) {
console.log('Endpoint:', endpoint); if (params_json) {
} // NOTE: "jp" stands for "JSON Params". This is a JSON object that needs to be safely converted to a string for the params.
// Max characters for a GET request is ~2000. This is a limitation of the browser.
// We need to remove a few parameters from the params object that are not allowed. const json_params_str = encodeURIComponent(JSON.stringify(params_json));
delete params['qry__enabled']; if (json_params_str.length > 2083) {
delete params['qry__hidden']; // Using console.error instead of throwing an error to avoid crashing the app for a known limitation.
delete params['qry__limit']; console.error(
delete params['qry__offset']; `The JSON object is too large to be used as a GET parameter. Max length is 2083 characters. Length = ${json_params_str.length}`
);
return false;
if (for_obj_type) { }
params['for_obj_type'] = for_obj_type; params['jp'] = json_params_str;
} }
if (for_obj_id) {
params['for_obj_id'] = for_obj_id; if (log_lvl) {
} console.log('Params:', params);
}
if (use_alt_tbl === true) {
params['tbl_alt'] = 'alt'; // Use alternate table or view name const object_li_get_promise = await get_object({
} api_cfg: api_cfg,
if (use_alt_mdl === true) { endpoint: endpoint,
params['mdl_alt'] = 'alt'; // Use alternate model name headers: headers,
} params: params,
if (use_alt_exp === true) { log_lvl: log_lvl
params['exp_alt'] = 'alt'; // Use alternate export table or view name });
}
if (log_lvl > 1) {
console.log(object_li_get_promise);
// Need to deal with inc params here??? }
return object_li_get_promise;
let allowed_enabled_list = ['all', 'enabled', 'not_enabled'] } catch (error) {
if (allowed_enabled_list.includes(enabled) ) { console.error('Error in get_ae_obj_li_for_obj_id_crud_v2:', error);
params['enabled'] = enabled; return false; // Or handle the error as appropriate
} }
let allowed_hidden_list = ['all', 'hidden', 'not_hidden'];
if (allowed_hidden_list.includes(hidden) ) {
params['hidden'] = hidden;
}
// NOTE: The order_by_li variable is in the "headers" because if is a the URL GET params do not handle multiple values very well. Maybe base64 encore in the future or something? Reminder that GET requests should not have a body (no JSON).
// NOTE: The order_by_li should be a key value pair of the property/DB field to sort and how to sort (ASC or DESC)
if (order_by_li) {
// headers['order_by_li'] = order_by_li;
headers['order_by_li'] = JSON.stringify(order_by_li);
}
if (limit > 0) {
params['limit'] = limit;
}
if (offset > 0) {
params['offset'] = offset;
}
if (params_json) {
// NOTE: This is a JSON object that needs to be safely converted to a string for the params. This is used for the search endpoint.
// Max characters for a GET request is 2083. This is a limitation of the browser (Microsoft IE and Edge).
if (log_lvl) {
console.log('JSON Object:', params_json);
console.log(JSON.stringify(params_json));
}
// NOTE: "jp" stands for "JSON Params"
params['jp'] = encodeURIComponent(JSON.stringify(params_json));
if (params['jp'].length > 2083) {
console.log(`The JSON object is too large to be used as a GET parameter. The overall max URL length is 2083 characters. Please use the POST endpoint instead. Length = ${params['jp'].length} [THIS DOES NOT EXIST YET]`);
return false;
}
}
// if (json_obj) {
// // NOTE: This is a JSON object that needs to be safely converted to a string for the params. This is used for the search endpoint.
// // Max characters for a GET request is 2083. This is a limitation of the browser (Microsoft IE and Edge).
// console.log('JSON Object:', json_obj);
// params['json_str'] = encodeURIComponent(JSON.stringify(json_obj));
// if (params['json_str'].length > 2083) {
// console.log(`The JSON object is too large to be used as a GET parameter. The overall max URL length is 2083 characters. Please use the POST endpoint instead. Length = ${params['json_str'].length} [THIS DOES NOT EXIST YET]`);
// return false;
// }
// }
if (log_lvl) {
console.log('Params:', params);
}
let object_li_get_promise = await get_object({
api_cfg: api_cfg,
endpoint: endpoint,
headers: headers,
params: params,
// return_meta: return_meta,
log_lvl: log_lvl
});
if (log_lvl > 1) {
console.log(object_li_get_promise);
}
return object_li_get_promise;
} }

View File

@@ -1,91 +1,115 @@
import type { Dexie, Table } from 'dexie';
// This function will save an array of objects to a Dexie database table. /**
// It will first attempt to update existing records using bulkUpdate. * Extracts the primary key from an object using a prioritized list of possible key names.
// If that fails, it will fall back to bulkPut. * @param obj The object to extract the ID from.
// The function takes a database instance, table name, array of objects, and properties to save. * @param table_name The name of the table, used to construct legacy key names.
// It also accepts a log level for debugging purposes. * @param log_lvl The logging level.
// Updated 2025-05-09 * @returns The found ID, or undefined if no ID could be found.
export async function db_save_ae_obj_li__ae_obj({ */
db_instance, function findObjectId(obj: any, table_name: string, log_lvl: number): string | number | undefined {
table_name, const potentialKeys = ['id', `${table_name}_id`, `${table_name}_id_random`];
obj_li,
properties_to_save,
log_lvl = 0,
}: {
db_instance: any; // The Dexie database instance
table_name: string; // The name of the table in the database
obj_li: any[];
properties_to_save: string[];
log_lvl?: number;
}) {
// log_lvl = 1;
if (log_lvl) {
console.log(`*** db_save_ae_obj_li__ae_obj() *** table_name=${table_name}`, obj_li);
}
if (!obj_li || obj_li.length === 0) { for (const key of potentialKeys) {
if (log_lvl) { if (obj[key]) {
console.log('No objects to save.'); if (key !== 'id' && log_lvl > 0) {
} console.warn(
return []; `Found legacy ID key "${key}" for table "${table_name}". Consider standardizing to "id".`,
} obj
);
}
return obj[key];
}
}
const db_table = db_instance[table_name]; console.error(`Object is missing a valid ID for table "${table_name}". It will be skipped.`, obj);
if (!db_table) { return undefined;
console.error(`Table not found in ${db_instance}: ${table_name}`); }
return [];
} /**
* Saves an array of objects to a Dexie database table using bulkPut.
const bulkUpdateData = []; * This function handles both creating new records and updating existing ones.
const bulkPutData = []; *
* @param db_instance The Dexie database instance.
for (const obj of obj_li) { * @param table_name The name of the table in the database.
const obj_record: Record<string, any> = {}; * @param obj_li The array of objects to save.
* @param properties_to_save The list of property names to include in the saved object.
// Extract only the specified properties to save * @param log_lvl The logging level for debugging.
for (const prop of properties_to_save) { * @returns A promise that resolves with the keys of the saved objects.
obj_record[prop] = obj[prop]; * @throws An error if the database operation fails.
} *
* @version 2.0.0
// Ensure the `id` field is included * @since 2025-11-13
obj_record.id = obj_record.id || obj.id || obj[`${table_name}_id`] || obj[`${table_name}_id_random`]; */
export async function db_save_ae_obj_li__ae_obj<T extends Record<string, any>>({
if (!obj_record.id) { db_instance,
console.error(`Object is missing an ID:`, obj); table_name,
continue; obj_li,
} properties_to_save,
log_lvl = 0
// Prepare data for bulkUpdate or bulkPut }: {
bulkUpdateData.push({ db_instance: Dexie;
key: obj_record.id, table_name: string;
changes: obj_record, obj_li: T[];
}); properties_to_save: (keyof T)[];
bulkPutData.push(obj_record); log_lvl?: number;
} }) {
if (log_lvl > 0) {
// Attempt bulkUpdate first console.log(
try { `*** db_save_ae_obj_li__ae_obj: Attempting to save ${obj_li.length} objects to table "${table_name}" ***`
const updatedKeys = await db_table.bulkUpdate(bulkUpdateData); );
if (log_lvl) { }
console.log(`Bulk update completed. Updated keys:`, updatedKeys);
} if (!obj_li || obj_li.length === 0) {
} catch (error) { if (log_lvl > 0) console.log('No objects to save.');
// This is fairly common and normal if the object is new return [];
if (log_lvl) { }
console.log(`Bulk update failed. Falling back to bulkPut. This is normal.`, error);
} const db_table: Table<T> = db_instance.table(table_name);
} if (!db_table) {
const errorMsg = `Table not found in Dexie instance: ${table_name}`;
// Use bulkPut for any records that couldn't be updated console.error(errorMsg);
try { throw new Error(errorMsg);
const putKeys = await db_table.bulkPut(bulkPutData); }
if (log_lvl) {
console.log(`Bulk put completed. Put keys:`, putKeys); const dataToSave = obj_li
} .map((obj) => {
return putKeys; const record: Partial<T> = {};
} catch (error) {
// This should not happen if the object is new // Extract only the specified properties to save.
console.error(`Bulk put failed. Something likely went wrong!`, error); for (const prop of properties_to_save) {
return []; record[prop] = obj[prop];
} }
// Ensure the primary key is included, attempting to find it from various legacy keys.
const id = findObjectId(obj, table_name, log_lvl);
if (id === undefined) {
return null; // Skip objects without a valid ID.
}
(record as any).id = id;
return record;
})
.filter(Boolean) as T[];
if (dataToSave.length === 0) {
if (log_lvl > 0) {
console.warn('All objects were skipped, likely due to missing IDs.');
}
return [];
}
try {
// bulkPut efficiently handles both inserts and updates.
const keys = await db_table.bulkPut(dataToSave);
if (log_lvl > 0) {
console.log(`Successfully saved ${keys.length} objects to "${table_name}".`);
}
return keys;
} catch (error) {
console.error(`Failed to save objects to "${table_name}":`, error);
// Re-throw the error to let the caller handle it.
throw error;
}
} }

View File

@@ -1,13 +1,13 @@
import type { key_val } from '$lib/ae_stores'; import type { key_val } from '$lib/ae_stores';
import { api } from '$lib/api'; import { api } from '$lib/api';
import { db_save_ae_obj_li__ae_obj } from "$lib/ae_core/core__idb_dexie"; import { db_save_ae_obj_li__ae_obj } from '$lib/ae_core/core__idb_dexie';
import { db_events } from "$lib/ae_events/db_events"; import { db_events } from '$lib/ae_events/db_events';
import { load_ae_obj_li__event_device } from './ae_events__event_device'; import { load_ae_obj_li__event_device } from './ae_events__event_device';
import { load_ae_obj_li__event_location } from './ae_events__event_location'; import { load_ae_obj_li__event_location } from './ae_events__event_location';
import { load_ae_obj_li__event_session } from './ae_events__event_session'; import { load_ae_obj_li__event_session } from './ae_events__event_session';
import { load_ae_obj_li__event_badge_template } from "$lib/ae_events/ae_events__event_badge_template"; import { load_ae_obj_li__event_badge_template } from '$lib/ae_events/ae_events__event_badge_template';
let ae_promises: key_val = {}; let ae_promises: key_val = {};