@aleonnet/healthcare-scheduler 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +264 -0
- package/dist/core/EventBus.d.ts +14 -0
- package/dist/core/EventBus.d.ts.map +1 -0
- package/dist/core/EventBus.js +47 -0
- package/dist/core/EventBus.js.map +1 -0
- package/dist/core/HealthcareScheduler.d.ts +63 -0
- package/dist/core/HealthcareScheduler.d.ts.map +1 -0
- package/dist/core/HealthcareScheduler.js +269 -0
- package/dist/core/HealthcareScheduler.js.map +1 -0
- package/dist/core/types.d.ts +234 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/database/adapters/MockAdapter.d.ts +37 -0
- package/dist/database/adapters/MockAdapter.d.ts.map +1 -0
- package/dist/database/adapters/MockAdapter.js +518 -0
- package/dist/database/adapters/MockAdapter.js.map +1 -0
- package/dist/database/adapters/SQLiteAdapter.d.ts +40 -0
- package/dist/database/adapters/SQLiteAdapter.d.ts.map +1 -0
- package/dist/database/adapters/SQLiteAdapter.js +147 -0
- package/dist/database/adapters/SQLiteAdapter.js.map +1 -0
- package/dist/database/adapters/SupabaseAdapter.d.ts +20 -0
- package/dist/database/adapters/SupabaseAdapter.d.ts.map +1 -0
- package/dist/database/adapters/SupabaseAdapter.js +310 -0
- package/dist/database/adapters/SupabaseAdapter.js.map +1 -0
- package/dist/database/interfaces.d.ts +20 -0
- package/dist/database/interfaces.d.ts.map +1 -0
- package/dist/database/interfaces.js +3 -0
- package/dist/database/interfaces.js.map +1 -0
- package/dist/database/repositories/EventsRepository.d.ts +26 -0
- package/dist/database/repositories/EventsRepository.d.ts.map +1 -0
- package/dist/database/repositories/EventsRepository.js +91 -0
- package/dist/database/repositories/EventsRepository.js.map +1 -0
- package/dist/database/repositories/ItemsRepository.d.ts +11 -0
- package/dist/database/repositories/ItemsRepository.d.ts.map +1 -0
- package/dist/database/repositories/ItemsRepository.js +44 -0
- package/dist/database/repositories/ItemsRepository.js.map +1 -0
- package/dist/database/repositories/MedicationsRepository.d.ts +40 -0
- package/dist/database/repositories/MedicationsRepository.d.ts.map +1 -0
- package/dist/database/repositories/MedicationsRepository.js +136 -0
- package/dist/database/repositories/MedicationsRepository.js.map +1 -0
- package/dist/database/repositories/OccurrencesRepository.d.ts +25 -0
- package/dist/database/repositories/OccurrencesRepository.d.ts.map +1 -0
- package/dist/database/repositories/OccurrencesRepository.js +150 -0
- package/dist/database/repositories/OccurrencesRepository.js.map +1 -0
- package/dist/database/repositories/PlansRepository.d.ts +13 -0
- package/dist/database/repositories/PlansRepository.d.ts.map +1 -0
- package/dist/database/repositories/PlansRepository.js +76 -0
- package/dist/database/repositories/PlansRepository.js.map +1 -0
- package/dist/database/repositories/index.d.ts +6 -0
- package/dist/database/repositories/index.d.ts.map +1 -0
- package/dist/database/repositories/index.js +14 -0
- package/dist/database/repositories/index.js.map +1 -0
- package/dist/database/schema.base.d.ts +3 -0
- package/dist/database/schema.base.d.ts.map +1 -0
- package/dist/database/schema.base.js +110 -0
- package/dist/database/schema.base.js.map +1 -0
- package/dist/database/sync/interfaces.d.ts +20 -0
- package/dist/database/sync/interfaces.d.ts.map +1 -0
- package/dist/database/sync/interfaces.js +3 -0
- package/dist/database/sync/interfaces.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications/adapters/MockNotificationAdapter.d.ts +18 -0
- package/dist/notifications/adapters/MockNotificationAdapter.d.ts.map +1 -0
- package/dist/notifications/adapters/MockNotificationAdapter.js +109 -0
- package/dist/notifications/adapters/MockNotificationAdapter.js.map +1 -0
- package/dist/notifications/adapters/NotifeeAdapter.d.ts +46 -0
- package/dist/notifications/adapters/NotifeeAdapter.d.ts.map +1 -0
- package/dist/notifications/adapters/NotifeeAdapter.js +479 -0
- package/dist/notifications/adapters/NotifeeAdapter.js.map +1 -0
- package/dist/notifications/interfaces.d.ts +8 -0
- package/dist/notifications/interfaces.d.ts.map +1 -0
- package/dist/notifications/interfaces.js +3 -0
- package/dist/notifications/interfaces.js.map +1 -0
- package/dist/planning/buildAgenda.d.ts +22 -0
- package/dist/planning/buildAgenda.d.ts.map +1 -0
- package/dist/planning/buildAgenda.js +148 -0
- package/dist/planning/buildAgenda.js.map +1 -0
- package/dist/planning/calculateTotalDoses.d.ts +3 -0
- package/dist/planning/calculateTotalDoses.d.ts.map +1 -0
- package/dist/planning/calculateTotalDoses.js +19 -0
- package/dist/planning/calculateTotalDoses.js.map +1 -0
- package/dist/planning/expandPlan.d.ts +3 -0
- package/dist/planning/expandPlan.d.ts.map +1 -0
- package/dist/planning/expandPlan.js +200 -0
- package/dist/planning/expandPlan.js.map +1 -0
- package/dist/planning/utils.d.ts +15 -0
- package/dist/planning/utils.d.ts.map +1 -0
- package/dist/planning/utils.js +40 -0
- package/dist/planning/utils.js.map +1 -0
- package/dist/planning/windowPlanner.d.ts +6 -0
- package/dist/planning/windowPlanner.d.ts.map +1 -0
- package/dist/planning/windowPlanner.js +18 -0
- package/dist/planning/windowPlanner.js.map +1 -0
- package/dist/plugins/InventoryPlugin.d.ts +26 -0
- package/dist/plugins/InventoryPlugin.d.ts.map +1 -0
- package/dist/plugins/InventoryPlugin.js +166 -0
- package/dist/plugins/InventoryPlugin.js.map +1 -0
- package/dist/plugins/PluginRegistry.d.ts +13 -0
- package/dist/plugins/PluginRegistry.d.ts.map +1 -0
- package/dist/plugins/PluginRegistry.js +43 -0
- package/dist/plugins/PluginRegistry.js.map +1 -0
- package/dist/plugins/SyncPluginEventDriven.d.ts +32 -0
- package/dist/plugins/SyncPluginEventDriven.d.ts.map +1 -0
- package/dist/plugins/SyncPluginEventDriven.js +609 -0
- package/dist/plugins/SyncPluginEventDriven.js.map +1 -0
- package/dist/plugins/SyncPluginPolling.d.ts +24 -0
- package/dist/plugins/SyncPluginPolling.d.ts.map +1 -0
- package/dist/plugins/SyncPluginPolling.js +266 -0
- package/dist/plugins/SyncPluginPolling.js.map +1 -0
- package/dist/plugins/gamification/GamificationPlugin.d.ts +26 -0
- package/dist/plugins/gamification/GamificationPlugin.d.ts.map +1 -0
- package/dist/plugins/gamification/GamificationPlugin.js +346 -0
- package/dist/plugins/gamification/GamificationPlugin.js.map +1 -0
- package/dist/plugins/gamification/__tests__/hysteresis.spec.d.ts +2 -0
- package/dist/plugins/gamification/__tests__/hysteresis.spec.d.ts.map +1 -0
- package/dist/plugins/gamification/__tests__/hysteresis.spec.js +22 -0
- package/dist/plugins/gamification/__tests__/hysteresis.spec.js.map +1 -0
- package/dist/plugins/gamification/index.d.ts +6 -0
- package/dist/plugins/gamification/index.d.ts.map +1 -0
- package/dist/plugins/gamification/index.js +14 -0
- package/dist/plugins/gamification/index.js.map +1 -0
- package/dist/plugins/gamification/levelsConfig.example.d.ts +17 -0
- package/dist/plugins/gamification/levelsConfig.example.d.ts.map +1 -0
- package/dist/plugins/gamification/levelsConfig.example.js +18 -0
- package/dist/plugins/gamification/levelsConfig.example.js.map +1 -0
- package/dist/plugins/gamification/levelsEngine.d.ts +19 -0
- package/dist/plugins/gamification/levelsEngine.d.ts.map +1 -0
- package/dist/plugins/gamification/levelsEngine.js +50 -0
- package/dist/plugins/gamification/levelsEngine.js.map +1 -0
- package/dist/plugins/gamification/streak.d.ts +7 -0
- package/dist/plugins/gamification/streak.d.ts.map +1 -0
- package/dist/plugins/gamification/streak.js +39 -0
- package/dist/plugins/gamification/streak.js.map +1 -0
- package/dist/plugins/index.d.ts +8 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +28 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/plugins/interfaces.d.ts +56 -0
- package/dist/plugins/interfaces.d.ts.map +1 -0
- package/dist/plugins/interfaces.js +3 -0
- package/dist/plugins/interfaces.js.map +1 -0
- package/dist/reconciler/WindowScheduler.d.ts +29 -0
- package/dist/reconciler/WindowScheduler.d.ts.map +1 -0
- package/dist/reconciler/WindowScheduler.js +396 -0
- package/dist/reconciler/WindowScheduler.js.map +1 -0
- package/dist/services/MedicationsServiceCompat.d.ts +24 -0
- package/dist/services/MedicationsServiceCompat.d.ts.map +1 -0
- package/dist/services/MedicationsServiceCompat.js +98 -0
- package/dist/services/MedicationsServiceCompat.js.map +1 -0
- package/dist/utils/queue.d.ts +8 -0
- package/dist/utils/queue.d.ts.map +1 -0
- package/dist/utils/queue.js +28 -0
- package/dist/utils/queue.js.map +1 -0
- package/dist/utils/timeOffset.d.ts +4 -0
- package/dist/utils/timeOffset.d.ts.map +1 -0
- package/dist/utils/timeOffset.js +61 -0
- package/dist/utils/timeOffset.js.map +1 -0
- package/dist/utils/timestamp.d.ts +6 -0
- package/dist/utils/timestamp.d.ts.map +1 -0
- package/dist/utils/timestamp.js +29 -0
- package/dist/utils/timestamp.js.map +1 -0
- package/dist/utils/timezone.d.ts +4 -0
- package/dist/utils/timezone.d.ts.map +1 -0
- package/dist/utils/timezone.js +30 -0
- package/dist/utils/timezone.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventsRepository = void 0;
|
|
4
|
+
const queue_1 = require("../../utils/queue");
|
|
5
|
+
class EventsRepository {
|
|
6
|
+
constructor(db, eventBus) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
this.eventBus = eventBus;
|
|
9
|
+
}
|
|
10
|
+
async recordEvent(occurrenceId, action, meta = {}) {
|
|
11
|
+
const occExists = await this.db.getFirstAsync('SELECT 1 FROM occurrences WHERE id = ?', [occurrenceId]);
|
|
12
|
+
if (!occExists) {
|
|
13
|
+
console.warn(`⚠️ Orphan notification detected: ${occurrenceId} (occurrence deleted or invalid)`);
|
|
14
|
+
return { id: '', occurrenceId, action };
|
|
15
|
+
}
|
|
16
|
+
const id = `evt:${occurrenceId}:${action}:${Date.now()}`;
|
|
17
|
+
const payload = meta.payload ? JSON.stringify(meta.payload) : null;
|
|
18
|
+
await (0, queue_1.enqueueWrite)(async () => {
|
|
19
|
+
await this.db.transaction(async () => {
|
|
20
|
+
await this.db.runAsync("INSERT INTO events (id, occurrence_id, action, source, note, payload, updated_at, version) VALUES (?, ?, ?, ?, ?, ?, datetime('now'), 1)", [id, occurrenceId, action, meta.source || 'screen', meta.note || null, payload]);
|
|
21
|
+
let newStatus = 'PENDING';
|
|
22
|
+
if (action === 'TAKEN')
|
|
23
|
+
newStatus = 'DONE';
|
|
24
|
+
else if (action === 'SKIPPED')
|
|
25
|
+
newStatus = 'SKIPPED';
|
|
26
|
+
else if (action === 'SNOOZED')
|
|
27
|
+
newStatus = 'SNOOZED';
|
|
28
|
+
if (newStatus !== 'PENDING') {
|
|
29
|
+
await this.db.runAsync("UPDATE occurrences SET status = ?, updated_at = datetime('now') WHERE id = ? AND status <> ?", [newStatus, occurrenceId, newStatus]);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
this.eventBus.emit('occurrences:changed', { occurrenceId, action });
|
|
34
|
+
return { id, occurrenceId, action };
|
|
35
|
+
}
|
|
36
|
+
async recordEventsBatch(occurrenceIds, action, meta = {}) {
|
|
37
|
+
if (!Array.isArray(occurrenceIds) || occurrenceIds.length === 0) {
|
|
38
|
+
return { count: 0 };
|
|
39
|
+
}
|
|
40
|
+
const placeholders = occurrenceIds.map(() => '?').join(',');
|
|
41
|
+
const validRows = await this.db.getAllAsync(`SELECT id FROM occurrences WHERE id IN (${placeholders})`, occurrenceIds);
|
|
42
|
+
const validIds = validRows.map(r => r.id);
|
|
43
|
+
if (validIds.length === 0) {
|
|
44
|
+
console.warn(`⚠️ No valid occurrences found in batch`);
|
|
45
|
+
return { count: 0 };
|
|
46
|
+
}
|
|
47
|
+
if (validIds.length < occurrenceIds.length) {
|
|
48
|
+
console.warn(`⚠️ ${occurrenceIds.length - validIds.length} orphan occurrences ignored in batch`);
|
|
49
|
+
}
|
|
50
|
+
const payload = meta.payload ? JSON.stringify(meta.payload) : null;
|
|
51
|
+
await (0, queue_1.enqueueWrite)(async () => {
|
|
52
|
+
await this.db.transaction(async () => {
|
|
53
|
+
for (const occurrenceId of validIds) {
|
|
54
|
+
const id = `evt:${occurrenceId}:${action}:${Date.now()}`;
|
|
55
|
+
await this.db.runAsync('INSERT INTO events (id, occurrence_id, action, source, note, payload) VALUES (?, ?, ?, ?, ?, ?)', [id, occurrenceId, action, meta.source || 'notif', meta.note || null, payload]);
|
|
56
|
+
let newStatus = 'PENDING';
|
|
57
|
+
if (action === 'TAKEN')
|
|
58
|
+
newStatus = 'DONE';
|
|
59
|
+
else if (action === 'SKIPPED')
|
|
60
|
+
newStatus = 'SKIPPED';
|
|
61
|
+
else if (action === 'SNOOZED')
|
|
62
|
+
newStatus = 'SNOOZED';
|
|
63
|
+
if (newStatus !== 'PENDING') {
|
|
64
|
+
await this.db.runAsync("UPDATE occurrences SET status = ?, updated_at = datetime('now') WHERE id = ? AND status <> ?", [newStatus, occurrenceId, newStatus]);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
this.eventBus.emit('occurrences:changed', { occurrenceIds: validIds, action });
|
|
70
|
+
return { count: validIds.length };
|
|
71
|
+
}
|
|
72
|
+
async findByOccurrence(occurrenceId) {
|
|
73
|
+
const rows = await this.db.getAllAsync('SELECT * FROM events WHERE occurrence_id = ? ORDER BY action_at DESC', [occurrenceId]);
|
|
74
|
+
return rows.map(row => ({
|
|
75
|
+
...row,
|
|
76
|
+
payload: row.payload ? JSON.parse(row.payload) : null
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
async cleanupOldEvents(daysOld = 90) {
|
|
80
|
+
const cutoffDate = new Date(Date.now() - daysOld * 24 * 60 * 60 * 1000).toISOString();
|
|
81
|
+
const result = await this.db.runAsync(`DELETE FROM events
|
|
82
|
+
WHERE datetime(action_at) < datetime(?)`, [cutoffDate]);
|
|
83
|
+
const deleted = result?.changes || 0;
|
|
84
|
+
if (deleted > 0) {
|
|
85
|
+
console.log(`🧹 Cleaned up ${deleted} old events (older than ${daysOld} days)`);
|
|
86
|
+
}
|
|
87
|
+
return deleted;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.EventsRepository = EventsRepository;
|
|
91
|
+
//# sourceMappingURL=EventsRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventsRepository.js","sourceRoot":"","sources":["../../../src/database/repositories/EventsRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { StorageDriver, Item } from '../../core/types';
|
|
2
|
+
export declare class ItemsRepository {
|
|
3
|
+
private db;
|
|
4
|
+
constructor(db: StorageDriver);
|
|
5
|
+
upsert(item: Item): Promise<Item>;
|
|
6
|
+
findById(id: string): Promise<Item | null>;
|
|
7
|
+
findByType(type: Item['type']): Promise<Item[]>;
|
|
8
|
+
delete(id: string): Promise<void>;
|
|
9
|
+
private _mapRow;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=ItemsRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemsRepository.d.ts","sourceRoot":"","sources":["../../../src/database/repositories/ItemsRepository.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGvD,qBAAa,eAAe;IACd,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,aAAa;IAO/B,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BjC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAa1C,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAY/C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWvC,OAAO,CAAC,OAAO;CAMhB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ItemsRepository = void 0;
|
|
4
|
+
const queue_1 = require("../../utils/queue");
|
|
5
|
+
class ItemsRepository {
|
|
6
|
+
constructor(db) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
}
|
|
9
|
+
async upsert(item) {
|
|
10
|
+
const meta = item.meta ? JSON.stringify(item.meta) : null;
|
|
11
|
+
await (0, queue_1.enqueueWrite)(async () => {
|
|
12
|
+
const nowISO = new Date().toISOString();
|
|
13
|
+
await this.db.runAsync(`INSERT INTO items (id, type, ean, name, dosage, meta, created_at, updated_at, version)
|
|
14
|
+
VALUES (?, ?, ?, ?, ?, ?, datetime('now'), ?, COALESCE(?, 1))
|
|
15
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
16
|
+
type = excluded.type,
|
|
17
|
+
ean = excluded.ean,
|
|
18
|
+
name = excluded.name,
|
|
19
|
+
dosage = excluded.dosage,
|
|
20
|
+
meta = excluded.meta,
|
|
21
|
+
updated_at = excluded.updated_at`, [item.id, item.type, item.ean || null, item.name, item.dosage || null, meta, nowISO, item.version || 1]);
|
|
22
|
+
});
|
|
23
|
+
return item;
|
|
24
|
+
}
|
|
25
|
+
async findById(id) {
|
|
26
|
+
const row = await this.db.getFirstAsync('SELECT * FROM items WHERE id = ? AND deleted_at IS NULL', [id]);
|
|
27
|
+
return row ? this._mapRow(row) : null;
|
|
28
|
+
}
|
|
29
|
+
async findByType(type) {
|
|
30
|
+
const rows = await this.db.getAllAsync('SELECT * FROM items WHERE type = ? ORDER BY created_at DESC', [type]);
|
|
31
|
+
return rows.map(row => this._mapRow(row));
|
|
32
|
+
}
|
|
33
|
+
async delete(id) {
|
|
34
|
+
await this.db.runAsync("UPDATE items SET deleted_at = datetime('now'), updated_at = datetime('now') WHERE id = ?", [id]);
|
|
35
|
+
}
|
|
36
|
+
_mapRow(row) {
|
|
37
|
+
return {
|
|
38
|
+
...row,
|
|
39
|
+
meta: row.meta ? JSON.parse(row.meta) : null
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.ItemsRepository = ItemsRepository;
|
|
44
|
+
//# sourceMappingURL=ItemsRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemsRepository.js","sourceRoot":"","sources":["../../../src/database/repositories/ItemsRepository.ts"],"names":[],"mappings":";;;AAOA,6CAAiD;AAEjD,MAAa,eAAe;IAC1B,YAAoB,EAAiB;QAAjB,OAAE,GAAF,EAAE,CAAe;IAAG,CAAC;IAOzC,KAAK,CAAC,MAAM,CAAC,IAAU;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAE1D,MAAM,IAAA,oBAAY,EAAC,KAAK,IAAI,EAAE;YAC5B,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACxC,MAAM,IAAI,CAAC,EAAE,CAAC,QAAQ,CACpB;;;;;;;;4CAQoC,EACpC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,IAAI,EAAE,MAAM,EAAG,IAAY,CAAC,OAAO,IAAI,CAAC,CAAC,CACjH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC;IACd,CAAC;IAOD,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,aAAa,CACrC,yDAAyD,EACzD,CAAC,EAAE,CAAC,CACL,CAAC;QACF,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxC,CAAC;IAOD,KAAK,CAAC,UAAU,CAAC,IAAkB;QACjC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,WAAW,CACpC,6DAA6D,EAC7D,CAAC,IAAI,CAAC,CACP,CAAC;QACF,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC;IAMD,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,IAAI,CAAC,EAAE,CAAC,QAAQ,CACpB,0FAA0F,EAC1F,CAAC,EAAE,CAAC,CACL,CAAC;IACJ,CAAC;IAMO,OAAO,CAAC,GAAQ;QACtB,OAAO;YACL,GAAG,GAAG;YACN,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;SAC7C,CAAC;IACJ,CAAC;CACF;AA7ED,0CA6EC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { StorageDriver } from '../../core/types';
|
|
2
|
+
export interface MedicationRecord {
|
|
3
|
+
id: string;
|
|
4
|
+
ean?: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
concentration?: string;
|
|
8
|
+
qty_label?: string;
|
|
9
|
+
current_qty_label?: string;
|
|
10
|
+
total_quantity?: number;
|
|
11
|
+
used_quantity?: number;
|
|
12
|
+
remaining?: number;
|
|
13
|
+
form?: string;
|
|
14
|
+
lab?: string;
|
|
15
|
+
active?: string;
|
|
16
|
+
complement?: string;
|
|
17
|
+
status?: 'normal' | 'low' | 'critical';
|
|
18
|
+
added_at?: string;
|
|
19
|
+
updated_at?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare class MedicationsRepository {
|
|
22
|
+
private db;
|
|
23
|
+
constructor(db: StorageDriver);
|
|
24
|
+
private calculateStatus;
|
|
25
|
+
upsert(med: MedicationRecord): Promise<void>;
|
|
26
|
+
findByEan(ean: string): Promise<MedicationRecord | null>;
|
|
27
|
+
getAll(): Promise<MedicationRecord[]>;
|
|
28
|
+
searchByName(term: string): Promise<MedicationRecord[]>;
|
|
29
|
+
updateUsedQuantity(ean: string, newUsedQuantity: number): Promise<void>;
|
|
30
|
+
incrementUsedQuantity(ean: string, delta: number): Promise<void>;
|
|
31
|
+
getStatusCounts(): Promise<Array<{
|
|
32
|
+
status: string;
|
|
33
|
+
count: number;
|
|
34
|
+
}>>;
|
|
35
|
+
delete(id: string): Promise<void>;
|
|
36
|
+
removeByEan(ean: string): Promise<void>;
|
|
37
|
+
clearAll(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
export default MedicationsRepository;
|
|
40
|
+
//# sourceMappingURL=MedicationsRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MedicationsRepository.d.ts","sourceRoot":"","sources":["../../../src/database/repositories/MedicationsRepository.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,UAAU,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,qBAAqB;IACpB,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,aAAa;IAErC,OAAO,CAAC,eAAe;IASjB,MAAM,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C5C,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAOxD,MAAM,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAOrC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAQvD,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBvE,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAehE,eAAe,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAOpE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAShC;AAED,eAAe,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MedicationsRepository = void 0;
|
|
4
|
+
class MedicationsRepository {
|
|
5
|
+
constructor(db) {
|
|
6
|
+
this.db = db;
|
|
7
|
+
}
|
|
8
|
+
calculateStatus(used, total) {
|
|
9
|
+
if (!total || total <= 0)
|
|
10
|
+
return 'normal';
|
|
11
|
+
const remaining = Math.max(0, total - Math.max(0, used));
|
|
12
|
+
const pct = (remaining / total) * 100;
|
|
13
|
+
if (pct <= 30)
|
|
14
|
+
return 'critical';
|
|
15
|
+
if (pct <= 50)
|
|
16
|
+
return 'low';
|
|
17
|
+
return 'normal';
|
|
18
|
+
}
|
|
19
|
+
async upsert(med) {
|
|
20
|
+
const total = Number(med.total_quantity ?? 1);
|
|
21
|
+
const used = Math.max(0, Number(med.used_quantity ?? 0));
|
|
22
|
+
const remaining = Math.max(0, total - used);
|
|
23
|
+
const status = this.calculateStatus(used, total);
|
|
24
|
+
const id = med.id;
|
|
25
|
+
await this.db.runAsync(`INSERT INTO medications (id, ean, name, description, concentration, qty_label, current_qty_label, total_quantity, used_quantity, remaining, form, lab, active, complement, status, added_at, updated_at, version)
|
|
26
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'), 1)
|
|
27
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
28
|
+
ean=excluded.ean,
|
|
29
|
+
name=excluded.name,
|
|
30
|
+
description=excluded.description,
|
|
31
|
+
concentration=excluded.concentration,
|
|
32
|
+
qty_label=excluded.qty_label,
|
|
33
|
+
current_qty_label=excluded.current_qty_label,
|
|
34
|
+
total_quantity=excluded.total_quantity,
|
|
35
|
+
used_quantity=excluded.used_quantity,
|
|
36
|
+
remaining=excluded.remaining,
|
|
37
|
+
form=excluded.form,
|
|
38
|
+
lab=excluded.lab,
|
|
39
|
+
active=excluded.active,
|
|
40
|
+
complement=excluded.complement,
|
|
41
|
+
status=excluded.status,
|
|
42
|
+
updated_at=datetime('now')
|
|
43
|
+
`, [
|
|
44
|
+
id,
|
|
45
|
+
med.ean ?? id,
|
|
46
|
+
med.name,
|
|
47
|
+
med.description || null,
|
|
48
|
+
med.concentration || null,
|
|
49
|
+
med.qty_label || null,
|
|
50
|
+
med.current_qty_label || null,
|
|
51
|
+
total,
|
|
52
|
+
used,
|
|
53
|
+
remaining,
|
|
54
|
+
med.form || null,
|
|
55
|
+
med.lab || null,
|
|
56
|
+
med.active || null,
|
|
57
|
+
med.complement || null,
|
|
58
|
+
status
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
async findByEan(ean) {
|
|
62
|
+
return await this.db.getFirstAsync('SELECT * FROM medications WHERE ean = ?', [ean]);
|
|
63
|
+
}
|
|
64
|
+
async getAll() {
|
|
65
|
+
return await this.db.getAllAsync('SELECT * FROM medications ORDER BY name ASC', []);
|
|
66
|
+
}
|
|
67
|
+
async searchByName(term) {
|
|
68
|
+
const like = `%${term}%`;
|
|
69
|
+
return await this.db.getAllAsync('SELECT * FROM medications WHERE name LIKE ? ORDER BY name ASC', [like]);
|
|
70
|
+
}
|
|
71
|
+
async updateUsedQuantity(ean, newUsedQuantity) {
|
|
72
|
+
const med = await this.findByEan(ean);
|
|
73
|
+
if (!med)
|
|
74
|
+
return;
|
|
75
|
+
const total = Math.max(0, Number(med.total_quantity ?? 0));
|
|
76
|
+
const prevRemaining = Number(med.remaining ?? 0);
|
|
77
|
+
const isUnitary = total === 1;
|
|
78
|
+
const unclamped = Math.max(0, isUnitary ? newUsedQuantity : Math.floor(newUsedQuantity));
|
|
79
|
+
const used = (total > 0 && !isUnitary) ? Math.max(0, Math.min(unclamped, total)) : unclamped;
|
|
80
|
+
const remaining = total > 0 ? Math.max(0, total - used) : prevRemaining;
|
|
81
|
+
const status = this.calculateStatus(used, total);
|
|
82
|
+
await this.db.runAsync("UPDATE medications SET used_quantity = ?, remaining = ?, status = ?, updated_at = datetime('now') WHERE ean = ?", [used, remaining, status, ean]);
|
|
83
|
+
}
|
|
84
|
+
async incrementUsedQuantity(ean, delta) {
|
|
85
|
+
const med = await this.findByEan(ean);
|
|
86
|
+
if (!med)
|
|
87
|
+
return;
|
|
88
|
+
const total = Math.max(0, Number(med.total_quantity ?? 0));
|
|
89
|
+
const isUnitary = total === 1;
|
|
90
|
+
const increment = isUnitary ? delta : Math.floor(delta);
|
|
91
|
+
const newUsed = Math.max(0, Number(med.used_quantity ?? 0) + increment);
|
|
92
|
+
await this.updateUsedQuantity(ean, newUsed);
|
|
93
|
+
}
|
|
94
|
+
async getStatusCounts() {
|
|
95
|
+
return await this.db.getAllAsync('SELECT status, COUNT(*) as count FROM medications GROUP BY status', []);
|
|
96
|
+
}
|
|
97
|
+
async delete(id) {
|
|
98
|
+
try {
|
|
99
|
+
try {
|
|
100
|
+
await this.db.runAsync('INSERT INTO sync_deletes (entity, entity_id) VALUES (?, ?)', ['medications', id]);
|
|
101
|
+
}
|
|
102
|
+
catch { }
|
|
103
|
+
}
|
|
104
|
+
catch { }
|
|
105
|
+
await this.db.runAsync('DELETE FROM medications WHERE id = ?', [id]);
|
|
106
|
+
}
|
|
107
|
+
async removeByEan(ean) {
|
|
108
|
+
try {
|
|
109
|
+
const row = await this.db.getFirstAsync('SELECT id FROM medications WHERE ean = ?', [ean]);
|
|
110
|
+
if (row?.id) {
|
|
111
|
+
try {
|
|
112
|
+
await this.db.runAsync('INSERT INTO sync_deletes (entity, entity_id) VALUES (?, ?)', ['medications', row.id]);
|
|
113
|
+
}
|
|
114
|
+
catch { }
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch { }
|
|
118
|
+
await this.db.runAsync('DELETE FROM medications WHERE ean = ?', [ean]);
|
|
119
|
+
}
|
|
120
|
+
async clearAll() {
|
|
121
|
+
try {
|
|
122
|
+
const rows = await this.db.getAllAsync('SELECT id FROM medications', []);
|
|
123
|
+
for (const r of rows || []) {
|
|
124
|
+
try {
|
|
125
|
+
await this.db.runAsync('INSERT INTO sync_deletes (entity, entity_id) VALUES (?, ?)', ['medications', r.id]);
|
|
126
|
+
}
|
|
127
|
+
catch { }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch { }
|
|
131
|
+
await this.db.runAsync('DELETE FROM medications', []);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.MedicationsRepository = MedicationsRepository;
|
|
135
|
+
exports.default = MedicationsRepository;
|
|
136
|
+
//# sourceMappingURL=MedicationsRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MedicationsRepository.js","sourceRoot":"","sources":["../../../src/database/repositories/MedicationsRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { StorageDriver, Occurrence } from '../../core/types';
|
|
2
|
+
export declare class OccurrencesRepository {
|
|
3
|
+
private db;
|
|
4
|
+
constructor(db: StorageDriver);
|
|
5
|
+
insertMany(occurrences: Array<{
|
|
6
|
+
id: string;
|
|
7
|
+
plan_id: string;
|
|
8
|
+
scheduled_at: string;
|
|
9
|
+
kind: Occurrence['kind'];
|
|
10
|
+
qty?: number;
|
|
11
|
+
}>): Promise<void>;
|
|
12
|
+
getUpcomingWindow(options: {
|
|
13
|
+
days: number;
|
|
14
|
+
limit?: number;
|
|
15
|
+
pastDays?: number;
|
|
16
|
+
}): Promise<Occurrence[]>;
|
|
17
|
+
getToday(now: Date): Promise<Occurrence[]>;
|
|
18
|
+
getPendingUntil(now: Date): Promise<Occurrence[]>;
|
|
19
|
+
findById(id: string): Promise<Occurrence | null>;
|
|
20
|
+
updateStatus(id: string, status: Occurrence['status']): Promise<void>;
|
|
21
|
+
deleteByPlanIdAfter(planId: string, afterDate: string): Promise<void>;
|
|
22
|
+
private _mapRow;
|
|
23
|
+
cleanupOldOccurrences(daysOld?: number): Promise<number>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=OccurrencesRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OccurrencesRepository.d.ts","sourceRoot":"","sources":["../../../src/database/repositories/OccurrencesRepository.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAgB,MAAM,kBAAkB,CAAC;AAI3E,qBAAa,qBAAqB;IACpB,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,aAAa;IAM/B,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC;QAClC,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACzB,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA+BZ,iBAAiB,CAAC,OAAO,EAAE;QAC/B,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAkCnB,QAAQ,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IA+B1C,eAAe,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;IAmBjD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA4BhD,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAYrE,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B3E,OAAO,CAAC,OAAO;IAcT,qBAAqB,CAAC,OAAO,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC;CA6BnE"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OccurrencesRepository = void 0;
|
|
4
|
+
const queue_1 = require("../../utils/queue");
|
|
5
|
+
const timezone_1 = require("../../utils/timezone");
|
|
6
|
+
class OccurrencesRepository {
|
|
7
|
+
constructor(db) {
|
|
8
|
+
this.db = db;
|
|
9
|
+
}
|
|
10
|
+
async insertMany(occurrences) {
|
|
11
|
+
if (!occurrences.length)
|
|
12
|
+
return;
|
|
13
|
+
await (0, queue_1.enqueueWrite)(async () => {
|
|
14
|
+
await this.db.transaction(async () => {
|
|
15
|
+
const ids = occurrences.map(o => o.id);
|
|
16
|
+
const placeholders = ids.map(() => '?').join(',');
|
|
17
|
+
const existingRows = await this.db.getAllAsync(`SELECT id FROM occurrences WHERE id IN (${placeholders})`, ids);
|
|
18
|
+
const existingIds = new Set((existingRows || []).map((r) => r.id));
|
|
19
|
+
const toInsert = occurrences.filter(o => !existingIds.has(o.id));
|
|
20
|
+
for (const occ of toInsert) {
|
|
21
|
+
await this.db.runAsync(`INSERT INTO occurrences (id, plan_id, scheduled_at, kind, qty)
|
|
22
|
+
VALUES (?, ?, ?, ?, ?)`, [occ.id, occ.plan_id, occ.scheduled_at, occ.kind, occ.qty || 1]);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async getUpcomingWindow(options) {
|
|
28
|
+
const { days, limit = 50, pastDays = 0 } = options;
|
|
29
|
+
const now = new Date();
|
|
30
|
+
const [startISO, endISO] = (0, timezone_1.getLocalWindowBoundsUTCISO)(now, pastDays, days);
|
|
31
|
+
const rows = await this.db.getAllAsync(`SELECT o.*, i.id as item_id, i.name as item_name, i.type as item_type, i.meta as item_meta,
|
|
32
|
+
p.schedule_rule,
|
|
33
|
+
(SELECT e.action FROM events e
|
|
34
|
+
WHERE e.occurrence_id = o.id
|
|
35
|
+
ORDER BY e.action_at DESC LIMIT 1) as last_action,
|
|
36
|
+
(SELECT e.action_at FROM events e
|
|
37
|
+
WHERE e.occurrence_id = o.id
|
|
38
|
+
ORDER BY e.action_at DESC LIMIT 1) as last_action_at,
|
|
39
|
+
(SELECT e.payload FROM events e
|
|
40
|
+
WHERE e.occurrence_id = o.id
|
|
41
|
+
ORDER BY e.action_at DESC LIMIT 1) as last_action_payload
|
|
42
|
+
FROM occurrences o
|
|
43
|
+
JOIN plans p ON o.plan_id = p.id
|
|
44
|
+
JOIN items i ON p.item_id = i.id
|
|
45
|
+
WHERE datetime(o.scheduled_at) BETWEEN datetime(?) AND datetime(?)
|
|
46
|
+
ORDER BY o.scheduled_at ASC
|
|
47
|
+
LIMIT ?`, [startISO, endISO, limit]);
|
|
48
|
+
return rows.map(row => this._mapRow(row));
|
|
49
|
+
}
|
|
50
|
+
async getToday(now) {
|
|
51
|
+
const [startISO, endISO] = (0, timezone_1.getLocalDayBoundsUTCISO)(now);
|
|
52
|
+
const rows = await this.db.getAllAsync(`SELECT o.*, i.id as item_id, i.name as item_name, i.type as item_type, i.meta as item_meta,
|
|
53
|
+
p.schedule_rule,
|
|
54
|
+
(SELECT e.action FROM events e
|
|
55
|
+
WHERE e.occurrence_id = o.id
|
|
56
|
+
ORDER BY e.action_at DESC LIMIT 1) as last_action,
|
|
57
|
+
(SELECT e.action_at FROM events e
|
|
58
|
+
WHERE e.occurrence_id = o.id
|
|
59
|
+
ORDER BY e.action_at DESC LIMIT 1) as last_action_at,
|
|
60
|
+
(SELECT e.payload FROM events e
|
|
61
|
+
WHERE e.occurrence_id = o.id
|
|
62
|
+
ORDER BY e.action_at DESC LIMIT 1) as last_action_payload
|
|
63
|
+
FROM occurrences o
|
|
64
|
+
JOIN plans p ON o.plan_id = p.id
|
|
65
|
+
JOIN items i ON p.item_id = i.id
|
|
66
|
+
WHERE datetime(o.scheduled_at) BETWEEN datetime(?) AND datetime(?)
|
|
67
|
+
ORDER BY o.scheduled_at ASC`, [startISO, endISO]);
|
|
68
|
+
return rows.map(row => this._mapRow(row));
|
|
69
|
+
}
|
|
70
|
+
async getPendingUntil(now) {
|
|
71
|
+
const rows = await this.db.getAllAsync(`SELECT o.*, i.name as item_name, i.type as item_type, i.meta as item_meta
|
|
72
|
+
FROM occurrences o
|
|
73
|
+
JOIN plans p ON o.plan_id = p.id
|
|
74
|
+
JOIN items i ON p.item_id = i.id
|
|
75
|
+
WHERE datetime(o.scheduled_at) <= datetime(?) AND o.status = 'PENDING'
|
|
76
|
+
ORDER BY o.scheduled_at ASC`, [now.toISOString()]);
|
|
77
|
+
return rows.map(row => this._mapRow(row));
|
|
78
|
+
}
|
|
79
|
+
async findById(id) {
|
|
80
|
+
const row = await this.db.getFirstAsync(`SELECT o.*, i.id as item_id, i.name as item_name, i.type as item_type, i.meta as item_meta,
|
|
81
|
+
p.schedule_rule,
|
|
82
|
+
(SELECT e.action FROM events e
|
|
83
|
+
WHERE e.occurrence_id = o.id
|
|
84
|
+
ORDER BY e.action_at DESC LIMIT 1) as last_action,
|
|
85
|
+
(SELECT e.action_at FROM events e
|
|
86
|
+
WHERE e.occurrence_id = o.id
|
|
87
|
+
ORDER BY e.action_at DESC LIMIT 1) as last_action_at,
|
|
88
|
+
(SELECT e.payload FROM events e
|
|
89
|
+
WHERE e.occurrence_id = o.id
|
|
90
|
+
ORDER BY e.action_at DESC LIMIT 1) as last_action_payload
|
|
91
|
+
FROM occurrences o
|
|
92
|
+
JOIN plans p ON o.plan_id = p.id
|
|
93
|
+
JOIN items i ON p.item_id = i.id
|
|
94
|
+
WHERE o.id = ?`, [id]);
|
|
95
|
+
return row ? this._mapRow(row) : null;
|
|
96
|
+
}
|
|
97
|
+
async updateStatus(id, status) {
|
|
98
|
+
await this.db.runAsync("UPDATE occurrences SET status = ?, updated_at = datetime('now') WHERE id = ?", [status, id]);
|
|
99
|
+
}
|
|
100
|
+
async deleteByPlanIdAfter(planId, afterDate) {
|
|
101
|
+
const rows = await this.db.getAllAsync('SELECT id FROM occurrences WHERE plan_id = ? AND datetime(scheduled_at) >= datetime(?)', [planId, afterDate]);
|
|
102
|
+
await (0, queue_1.enqueueWrite)(async () => {
|
|
103
|
+
await this.db.transaction(async () => {
|
|
104
|
+
for (const r of rows) {
|
|
105
|
+
try {
|
|
106
|
+
await this.db.runAsync('INSERT INTO sync_deletes (entity, entity_id) VALUES (?, ?)', ['occurrences', r.id]);
|
|
107
|
+
}
|
|
108
|
+
catch { }
|
|
109
|
+
}
|
|
110
|
+
await this.db.runAsync('DELETE FROM occurrences WHERE plan_id = ? AND datetime(scheduled_at) >= datetime(?)', [planId, afterDate]);
|
|
111
|
+
if (rows.length) {
|
|
112
|
+
console.log(`🗑️ [Sync] Audited ${rows.length} occurrence deletes for plan ${planId} (>= ${afterDate})`);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
_mapRow(row) {
|
|
118
|
+
return {
|
|
119
|
+
...row,
|
|
120
|
+
schedule_rule: row.schedule_rule ? JSON.parse(row.schedule_rule) : undefined,
|
|
121
|
+
item_meta: row.item_meta ? JSON.parse(row.item_meta) : null
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async cleanupOldOccurrences(daysOld = 90) {
|
|
125
|
+
const cutoffDate = new Date(Date.now() - daysOld * 24 * 60 * 60 * 1000).toISOString();
|
|
126
|
+
const rows = await this.db.getAllAsync(`SELECT id FROM occurrences WHERE datetime(scheduled_at) < datetime(?)`, [cutoffDate]);
|
|
127
|
+
const result = await (0, queue_1.enqueueWrite)(async () => {
|
|
128
|
+
let changes = 0;
|
|
129
|
+
await this.db.transaction(async () => {
|
|
130
|
+
for (const r of rows) {
|
|
131
|
+
try {
|
|
132
|
+
await this.db.runAsync('INSERT INTO sync_deletes (entity, entity_id) VALUES (?, ?)', ['occurrences', r.id]);
|
|
133
|
+
}
|
|
134
|
+
catch { }
|
|
135
|
+
}
|
|
136
|
+
const res = await this.db.runAsync(`DELETE FROM occurrences
|
|
137
|
+
WHERE datetime(scheduled_at) < datetime(?)`, [cutoffDate]);
|
|
138
|
+
changes = res?.changes || 0;
|
|
139
|
+
});
|
|
140
|
+
return { changes };
|
|
141
|
+
});
|
|
142
|
+
const deleted = result?.changes || 0;
|
|
143
|
+
if (deleted > 0) {
|
|
144
|
+
console.log(`🧹 Cleaned up ${deleted} old occurrences (older than ${daysOld} days)`);
|
|
145
|
+
}
|
|
146
|
+
return deleted;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
exports.OccurrencesRepository = OccurrencesRepository;
|
|
150
|
+
//# sourceMappingURL=OccurrencesRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OccurrencesRepository.js","sourceRoot":"","sources":["../../../src/database/repositories/OccurrencesRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StorageDriver, Plan } from '../../core/types';
|
|
2
|
+
export declare class PlansRepository {
|
|
3
|
+
private db;
|
|
4
|
+
constructor(db: StorageDriver);
|
|
5
|
+
upsert(plan: Plan): Promise<Plan>;
|
|
6
|
+
findActive(now: Date): Promise<Plan[]>;
|
|
7
|
+
findById(id: string): Promise<Plan | null>;
|
|
8
|
+
delete(id: string): Promise<void>;
|
|
9
|
+
endActiveByItem(itemId: string, endsAtISO?: string): Promise<number>;
|
|
10
|
+
findAllByItem(itemId: string): Promise<Plan[]>;
|
|
11
|
+
private _mapRow;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=PlansRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlansRepository.d.ts","sourceRoot":"","sources":["../../../src/database/repositories/PlansRepository.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,IAAI,EAAgB,MAAM,kBAAkB,CAAC;AAGrE,qBAAa,eAAe;IACd,OAAO,CAAC,EAAE;gBAAF,EAAE,EAAE,aAAa;IAO/B,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BjC,UAAU,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAsBtC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAe1C,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAajC,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBpE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAYpD,OAAO,CAAC,OAAO;CAQhB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PlansRepository = void 0;
|
|
4
|
+
const queue_1 = require("../../utils/queue");
|
|
5
|
+
class PlansRepository {
|
|
6
|
+
constructor(db) {
|
|
7
|
+
this.db = db;
|
|
8
|
+
}
|
|
9
|
+
async upsert(plan) {
|
|
10
|
+
const scheduleRule = JSON.stringify(plan.schedule_rule);
|
|
11
|
+
const meta = plan.meta ? JSON.stringify(plan.meta) : null;
|
|
12
|
+
const tz = plan.tz || 'America/Sao_Paulo';
|
|
13
|
+
await (0, queue_1.enqueueWrite)(async () => {
|
|
14
|
+
const nowISO = new Date().toISOString();
|
|
15
|
+
await this.db.runAsync(`INSERT INTO plans (id, item_id, schedule_rule, tz, starts_at, ends_at, meta, created_at, updated_at, version)
|
|
16
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), ?, COALESCE(?, 1))
|
|
17
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
18
|
+
item_id = excluded.item_id,
|
|
19
|
+
schedule_rule = excluded.schedule_rule,
|
|
20
|
+
tz = excluded.tz,
|
|
21
|
+
starts_at = excluded.starts_at,
|
|
22
|
+
ends_at = excluded.ends_at,
|
|
23
|
+
meta = excluded.meta,
|
|
24
|
+
updated_at = excluded.updated_at`, [plan.id, plan.item_id, scheduleRule, tz, plan.starts_at, plan.ends_at || null, meta, nowISO, plan.version || 1]);
|
|
25
|
+
});
|
|
26
|
+
return plan;
|
|
27
|
+
}
|
|
28
|
+
async findActive(now) {
|
|
29
|
+
const nowISO = now.toISOString();
|
|
30
|
+
const rows = await this.db.getAllAsync(`SELECT p.*, i.name as item_name, i.type as item_type, i.meta as item_meta
|
|
31
|
+
FROM plans p
|
|
32
|
+
JOIN items i ON p.item_id = i.id
|
|
33
|
+
WHERE i.deleted_at IS NULL
|
|
34
|
+
AND p.deleted_at IS NULL
|
|
35
|
+
AND (p.ends_at IS NULL OR p.ends_at > ?)
|
|
36
|
+
ORDER BY p.starts_at ASC`, [nowISO]);
|
|
37
|
+
return rows.map(row => this._mapRow(row));
|
|
38
|
+
}
|
|
39
|
+
async findById(id) {
|
|
40
|
+
const row = await this.db.getFirstAsync(`SELECT p.*, i.name as item_name, i.type as item_type, i.meta as item_meta
|
|
41
|
+
FROM plans p
|
|
42
|
+
JOIN items i ON p.item_id = i.id
|
|
43
|
+
WHERE p.id = ?`, [id]);
|
|
44
|
+
return row ? this._mapRow(row) : null;
|
|
45
|
+
}
|
|
46
|
+
async delete(id) {
|
|
47
|
+
try {
|
|
48
|
+
await this.db.runAsync('INSERT INTO sync_deletes (entity, entity_id) VALUES (?, ?)', ['plans', id]);
|
|
49
|
+
}
|
|
50
|
+
catch { }
|
|
51
|
+
await this.db.runAsync('DELETE FROM plans WHERE id = ?', [id]);
|
|
52
|
+
}
|
|
53
|
+
async endActiveByItem(itemId, endsAtISO) {
|
|
54
|
+
const nowISO = endsAtISO || new Date().toISOString();
|
|
55
|
+
let changesCount = 0;
|
|
56
|
+
await (0, queue_1.enqueueWrite)(async () => {
|
|
57
|
+
const result = await this.db.runAsync(`UPDATE plans SET ends_at = ? WHERE item_id = ? AND (ends_at IS NULL OR datetime(ends_at) > datetime(?))`, [nowISO, itemId, nowISO]);
|
|
58
|
+
changesCount = result?.changes || 0;
|
|
59
|
+
});
|
|
60
|
+
return changesCount;
|
|
61
|
+
}
|
|
62
|
+
async findAllByItem(itemId) {
|
|
63
|
+
const rows = await this.db.getAllAsync(`SELECT * FROM plans WHERE item_id = ? ORDER BY created_at ASC`, [itemId]);
|
|
64
|
+
return rows.map(row => this._mapRow(row));
|
|
65
|
+
}
|
|
66
|
+
_mapRow(row) {
|
|
67
|
+
return {
|
|
68
|
+
...row,
|
|
69
|
+
schedule_rule: JSON.parse(row.schedule_rule),
|
|
70
|
+
meta: row.meta ? JSON.parse(row.meta) : undefined,
|
|
71
|
+
item_meta: row.item_meta ? JSON.parse(row.item_meta) : null
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.PlansRepository = PlansRepository;
|
|
76
|
+
//# sourceMappingURL=PlansRepository.js.map
|