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,149 +1,106 @@
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',
archive: '/crud/archive/list',
archive_content: '/crud/archive/content/list',
contact: '/crud/contact/list',
data_store: '/crud/data_store/list',
event: '/crud/event/list',
event_abstract: '/crud/event/abstract/list',
event_badge: '/crud/event/badge/list',
event_badge_template: '/crud/event/badge/template/list',
event_device: '/crud/event/device/list',
event_exhibit: '/crud/event/exhibit/list',
event_exhibit_tracking: '/crud/event/exhibit/tracking/list',
event_file: '/crud/event/file/list',
event_location: '/crud/event/location/list',
event_person: '/crud/event/person/list',
event_presentation: '/crud/event/presentation/list',
event_presenter: '/crud/event/presenter/list',
event_session: '/crud/event/session/list',
event_track: '/crud/event/track/list',
grant: '/crud/grant/list',
hosted_file: '/crud/hosted_file/list',
journal: '/crud/journal/list',
journal_entry: '/crud/journal/entry/list',
order: '/crud/order/list',
order_line: '/crud/order/line/list',
page: '/crud/page/list',
person: '/crud/person/list',
post: '/crud/post/list',
post_comment: '/crud/post/comment/list',
site: '/crud/site/list',
sponsorship_cfg: '/crud/sponsorship/cfg/list',
sponsorship: '/crud/sponsorship/list',
// user: '/crud/user/list',
'lu-country_subdivision': '/crud/lu/country_subdivision/list',
'lu-country': '/crud/lu/country/list',
'lu-time_zone': '/crud/lu/time_zone/list'
};
function getEndpointForObjType(obj_type: string, for_obj_type?: string): string {
if (obj_type === 'lu' && for_obj_type) {
const key = `lu-${for_obj_type}`;
const endpoint = objTypeToEndpointMap[key];
if (endpoint) return endpoint;
}
const endpoint = objTypeToEndpointMap[obj_type];
if (endpoint) return endpoint;
throw new Error(`Unknown object type: ${obj_type}`);
}
type OrderBy = { [key: string]: 'ASC' | 'DESC' };
interface GetAeObjLiForObjIdCrudV2Params {
api_cfg: any; // Consider defining a specific type for api_cfg
obj_type: string;
for_obj_type: string;
for_obj_id?: string;
use_alt_tbl?: boolean | string;
use_alt_mdl?: boolean | string;
use_alt_exp?: boolean | string;
inc?: key_val;
enabled?: 'all' | 'enabled' | 'not_enabled';
hidden?: 'all' | 'hidden' | 'not_hidden';
order_by_li?: OrderBy[] | null;
limit?: number;
offset?: number;
headers?: Record<string, string>;
params_json?: any;
params?: key_val;
log_lvl?: number;
}
export async function get_ae_obj_li_for_obj_id_crud_v2({
api_cfg, api_cfg,
obj_type, obj_type,
for_obj_type, for_obj_type,
for_obj_id, // NOTE: Changed 2023-12-06 to no longer required for_obj_id,
use_alt_tbl = false, // Alternate table or view name use_alt_tbl = false,
use_alt_mdl = false, // Alternate model name use_alt_mdl = false,
use_alt_exp = false, // Alternate export table or view name use_alt_exp = false,
inc = {},
enabled = 'enabled', enabled = 'enabled',
hidden = 'not_hidden', hidden = 'not_hidden',
order_by_li = null, order_by_li = null,
limit = 999999, limit = 999999,
offset = 0, offset = 0,
// key,
// jwt = null,
headers = {}, headers = {},
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" } } params_json = null,
// 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.
params = {}, params = {},
// return_meta = false,
log_lvl = 0 log_lvl = 0
}: { }: GetAeObjLiForObjIdCrudV2Params) {
api_cfg: any,
obj_type: string,
for_obj_type: string,
for_obj_id?: string,
use_alt_tbl?: boolean|string,
use_alt_mdl?: boolean|string,
use_alt_exp?: boolean|string,
inc?: key_val
enabled?: string,
hidden?: string,
order_by_li?: any,
limit?: number,
offset?: number,
// key: string,
// jwt?: string,
headers?: any,
params_json?: any,
// json_obj?: any,
params?: key_val,
// return_meta?: boolean,
log_lvl?: number
}
) {
if (log_lvl) { if (log_lvl) {
console.log('*** get_ae_obj_li_for_obj_id_crud() ***'); console.log('*** get_ae_obj_li_for_obj_id_crud_v2() ***');
} }
// data = {}; try {
// data['super_key'] = key; const endpoint = `/v2${getEndpointForObjType(obj_type, for_obj_type)}`;
// data['jwt'] = jwt;
// NOTE: The key and or JWT should be in the header of the DELETE, GET, PATCH, POST
// const endpoint = `/crud/${obj_type}/list`;
let endpoint = '';
if (obj_type == 'account') {
endpoint = `/crud/account/list`;
} else if (obj_type == 'address') {
endpoint = `/crud/address/list`;
} else if (obj_type == 'archive') {
endpoint = `/crud/archive/list`;
} else if (obj_type == 'archive_content') {
endpoint = `/crud/archive/content/list`;
} else if (obj_type == 'contact') {
endpoint = `/crud/contact/list`;
} else if (obj_type == 'data_store') {
endpoint = `/crud/data_store/list`;
} else if (obj_type == 'event') {
endpoint = `/crud/event/list`;
} else if (obj_type == 'event_abstract') {
endpoint = `/crud/event/abstract/list`;
} else if (obj_type == 'event_badge') {
endpoint = `/crud/event/badge/list`;
} else if (obj_type == 'event_badge_template') {
endpoint = `/crud/event/badge/template/list`;
} else if (obj_type == 'event_device') {
endpoint = `/crud/event/device/list`;
} else if (obj_type == 'event_exhibit') {
endpoint = `/crud/event/exhibit/list`;
} else if (obj_type == 'event_exhibit_tracking') {
endpoint = `/crud/event/exhibit/tracking/list`;
} else if (obj_type == 'event_file') {
endpoint = `/crud/event/file/list`;
} else if (obj_type == 'event_location') {
endpoint = `/crud/event/location/list`;
} else if (obj_type == 'event_person') {
endpoint = `/crud/event/person/list`;
} else if (obj_type == 'event_presentation') {
endpoint = `/crud/event/presentation/list`;
} else if (obj_type == 'event_presenter') {
endpoint = `/crud/event/presenter/list`;
} else if (obj_type == 'event_session') {
endpoint = `/crud/event/session/list`;
} else if (obj_type == 'event_track') {
endpoint = `/crud/event/track/list`;
} else if (obj_type == 'grant') {
endpoint = `/crud/grant/list`;
} else if (obj_type == 'hosted_file') {
endpoint = `/crud/hosted_file/list`;
} else if (obj_type == 'journal') {
endpoint = `/crud/journal/list`;
} else if (obj_type == 'journal_entry') {
endpoint = `/crud/journal/entry/list`;
} else if (obj_type == 'order') {
endpoint = `/crud/order/list`;
} else if (obj_type == 'order_line') {
endpoint = `/crud/order/line/list`;
} else if (obj_type == 'page') {
endpoint = `/crud/page/list`;
} else if (obj_type == 'person') {
endpoint = `/crud/person/list`;
} else if (obj_type == 'post') {
endpoint = `/crud/post/list`;
} else if (obj_type == 'post_comment') {
endpoint = `/crud/post/comment/list`;
} else if (obj_type == 'site') {
endpoint = `/crud/site/list`;
} else if (obj_type == 'sponsorship_cfg') {
endpoint = `/crud/sponsorship/cfg/list`;
} else if (obj_type == 'sponsorship') {
endpoint = `/crud/sponsorship/list`;
// } else if (obj_type == 'user') {
// endpoint = `/crud/user/list`;
} else if (obj_type == 'lu' && for_obj_type == 'country_subdivision') {
endpoint = `/crud/lu/country_subdivision/list`;
// for_obj_type = null;
} else if (obj_type == 'lu' && for_obj_type == 'country') {
endpoint = `/crud/lu/country/list`;
// for_obj_type = null;
} else if (obj_type == 'lu' && for_obj_type == 'time_zone') {
endpoint = `/crud/lu/time_zone/list`;
// for_obj_type = null;
} else {
console.log(`Unknown object type: ${obj_type}`);
return false;
}
endpoint = `/v2${endpoint}`;
if (log_lvl) { if (log_lvl) {
console.log('Endpoint:', endpoint); console.log('Endpoint:', endpoint);
} }
@@ -154,89 +111,54 @@ export async function get_ae_obj_li_for_obj_id_crud_v2(
delete params['qry__limit']; delete params['qry__limit'];
delete params['qry__offset']; delete params['qry__offset'];
if (for_obj_type) params['for_obj_type'] = for_obj_type;
if (for_obj_id) params['for_obj_id'] = for_obj_id;
if (for_obj_type) { if (use_alt_tbl === true) params['tbl_alt'] = 'alt';
params['for_obj_type'] = for_obj_type; if (use_alt_mdl === true) params['mdl_alt'] = 'alt';
} if (use_alt_exp === true) params['exp_alt'] = 'alt';
if (for_obj_id) {
params['for_obj_id'] = for_obj_id;
}
if (use_alt_tbl === true) { const allowed_enabled_list = ['all', 'enabled', 'not_enabled'];
params['tbl_alt'] = 'alt'; // Use alternate table or view name
}
if (use_alt_mdl === true) {
params['mdl_alt'] = 'alt'; // Use alternate model name
}
if (use_alt_exp === true) {
params['exp_alt'] = 'alt'; // Use alternate export table or view name
}
// Need to deal with inc params here???
let allowed_enabled_list = ['all', 'enabled', 'not_enabled']
if (allowed_enabled_list.includes(enabled)) { if (allowed_enabled_list.includes(enabled)) {
params['enabled'] = enabled; params['enabled'] = enabled;
} }
let allowed_hidden_list = ['all', 'hidden', 'not_hidden']; const allowed_hidden_list = ['all', 'hidden', 'not_hidden'];
if (allowed_hidden_list.includes(hidden)) { if (allowed_hidden_list.includes(hidden)) {
params['hidden'] = 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 variable is in the "headers" because URL GET params do not handle complex objects very well.
// 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) { if (order_by_li) {
// headers['order_by_li'] = order_by_li;
headers['order_by_li'] = JSON.stringify(order_by_li); headers['order_by_li'] = JSON.stringify(order_by_li);
} }
if (limit > 0) { if (limit > 0) params['limit'] = limit;
params['limit'] = limit; if (offset > 0) params['offset'] = offset;
}
if (offset > 0) {
params['offset'] = offset;
}
if (params_json) { 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. // 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 2083. This is a limitation of the browser (Microsoft IE and Edge). // Max characters for a GET request is ~2000. This is a limitation of the browser.
if (log_lvl) { const json_params_str = encodeURIComponent(JSON.stringify(params_json));
console.log('JSON Object:', params_json); if (json_params_str.length > 2083) {
console.log(JSON.stringify(params_json)); // Using console.error instead of throwing an error to avoid crashing the app for a known limitation.
} console.error(
// NOTE: "jp" stands for "JSON Params" `The JSON object is too large to be used as a GET parameter. Max length is 2083 characters. Length = ${json_params_str.length}`
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; return false;
} }
params['jp'] = json_params_str;
} }
// 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) { if (log_lvl) {
console.log('Params:', params); console.log('Params:', params);
} }
let object_li_get_promise = await get_object({ const object_li_get_promise = await get_object({
api_cfg: api_cfg, api_cfg: api_cfg,
endpoint: endpoint, endpoint: endpoint,
headers: headers, headers: headers,
params: params, params: params,
// return_meta: return_meta,
log_lvl: log_lvl log_lvl: log_lvl
}); });
@@ -245,4 +167,8 @@ export async function get_ae_obj_li_for_obj_id_crud_v2(
} }
return object_li_get_promise; return object_li_get_promise;
} catch (error) {
console.error('Error in get_ae_obj_li_for_obj_id_crud_v2:', error);
return false; // Or handle the error as appropriate
}
} }

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