@trafficgroup/knex-rel 0.1.3 → 0.1.5

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.
Files changed (30) hide show
  1. package/dist/dao/VideoMinuteResultDAO.d.ts +0 -4
  2. package/dist/dao/VideoMinuteResultDAO.js +7 -48
  3. package/dist/dao/VideoMinuteResultDAO.js.map +1 -1
  4. package/dist/dao/camera/camera.dao.d.ts +8 -1
  5. package/dist/dao/camera/camera.dao.js +27 -8
  6. package/dist/dao/camera/camera.dao.js.map +1 -1
  7. package/dist/dao/report-configuration/report-configuration.dao.d.ts +94 -0
  8. package/dist/dao/report-configuration/report-configuration.dao.js +352 -0
  9. package/dist/dao/report-configuration/report-configuration.dao.js.map +1 -0
  10. package/dist/dao/video/video.dao.d.ts +10 -0
  11. package/dist/dao/video/video.dao.js +40 -16
  12. package/dist/dao/video/video.dao.js.map +1 -1
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.js +3 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/interfaces/report-configuration/report-configuration.interfaces.d.ts +26 -0
  17. package/dist/interfaces/report-configuration/report-configuration.interfaces.js +3 -0
  18. package/dist/interfaces/report-configuration/report-configuration.interfaces.js.map +1 -0
  19. package/migrations/20250930200521_migration.ts +52 -0
  20. package/package.json +1 -1
  21. package/plan.md +755 -212
  22. package/src/dao/VideoMinuteResultDAO.ts +7 -64
  23. package/src/dao/camera/camera.dao.ts +30 -10
  24. package/src/dao/report-configuration/report-configuration.dao.ts +402 -0
  25. package/src/dao/video/video.dao.ts +46 -18
  26. package/src/index.ts +8 -0
  27. package/src/interfaces/report-configuration/report-configuration.interfaces.ts +30 -0
  28. package/cameras_analysis.md +0 -199
  29. package/folder_cameraid_analysis.md +0 -167
  30. package/migrations/20250924000000_camera_name_search_index.ts +0 -22
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export { CameraDAO } from "./dao/camera/camera.dao";
4
4
  export { ChatDAO } from "./dao/chat/chat.dao";
5
5
  export { FolderDAO } from "./dao/folder/folder.dao";
6
6
  export { MessageDAO } from "./dao/message/message.dao";
7
+ export { ReportConfigurationDAO } from "./dao/report-configuration/report-configuration.dao";
7
8
  export { StudyDAO } from "./dao/study/study.dao";
8
9
  export { UserDAO } from "./dao/user/user.dao";
9
10
  export { UserPushNotificationTokenDAO } from "./dao/user-push-notification-token/user-push-notification-token.dao";
@@ -25,6 +26,13 @@ export {
25
26
  IMessageCreate,
26
27
  IMessageUpdate,
27
28
  } from "./interfaces/message/message.interfaces";
29
+ export {
30
+ IReportConfiguration,
31
+ IReportConfigurationData,
32
+ IReportConfigurationInput,
33
+ ICustomClass,
34
+ IValidationResult,
35
+ } from "./interfaces/report-configuration/report-configuration.interfaces";
28
36
  export { IStudy } from "./interfaces/study/study.interfaces";
29
37
  export { IUser } from "./interfaces/user/user.interfaces";
30
38
  export { IUserPushNotificationToken } from "./interfaces/user-push-notification-token/user-push-notification-token.interfaces";
@@ -0,0 +1,30 @@
1
+ export interface IReportConfiguration {
2
+ id: number;
3
+ uuid: string;
4
+ name: string;
5
+ description?: string;
6
+ configuration: IReportConfigurationData;
7
+ created_at: Date;
8
+ updated_at: Date;
9
+ }
10
+
11
+ export interface IReportConfigurationData {
12
+ version: string;
13
+ customClasses: ICustomClass[];
14
+ }
15
+
16
+ export interface ICustomClass {
17
+ name: string; // Max 30 chars
18
+ fhwaClasses: number[]; // Array of 1-13
19
+ }
20
+
21
+ export interface IReportConfigurationInput {
22
+ name: string;
23
+ description?: string;
24
+ configuration: IReportConfigurationData;
25
+ }
26
+
27
+ export interface IValidationResult {
28
+ valid: boolean;
29
+ errors: string[];
30
+ }
@@ -1,199 +0,0 @@
1
- # Database Schema Analysis - Cameras Table Implementation
2
-
3
- ## Current Schema Analysis
4
-
5
- ### 1. Videos Table (Current Structure)
6
-
7
- - **Table Name**: `video`
8
- - **Primary Key**: `id` (auto-increment number)
9
- - **UUID**: `uuid` (unique, not null) - for external references
10
- - **Foreign Keys**:
11
- - `folderId`: references `folders.id` (CASCADE delete)
12
- - `cameraId`: references `cameras.id` (SET NULL on delete) ✅ **ALREADY EXISTS**
13
- - `annotationSourceId`: self-reference to `video.id` (SET NULL)
14
- - **Key Fields**:
15
- - `videoLocation`: S3 path/URL for video file
16
- - `videoRate`: frame rate (FPS)
17
- - `videoType`: enum ['TMC', 'ATR', 'JUNCTION', 'ROUNDABOUT', 'PATHWAY']
18
- - `status`: enum ['QUEUED', 'PROCESSING', 'COMPLETED', 'FAILED', 'PENDING']
19
- - `metadata`: JSONB for lane annotations
20
- - `results`: JSONB for processing results
21
- - **Timestamps**: `created_at`, `updated_at`
22
- - **Indexes**:
23
- - `uuid` (unique)
24
- - `cameraId`
25
- - `annotationSourceId`
26
- - Composite: `(folderId, videoType, status)`
27
-
28
- ### 2. Folders Table (Current Structure)
29
-
30
- - **Table Name**: `folders`
31
- - **Primary Key**: `id` (auto-increment number)
32
- - **UUID**: `uuid` (unique, not null) - for external references
33
- - **Foreign Keys**:
34
- - `createdBy`: references `user.id` (CASCADE delete)
35
- - `studyId`: references `study.id` (CASCADE delete)
36
- - `cameraId`: references `cameras.id` (SET NULL on delete) ✅ **ALREADY EXISTS**
37
- - **Key Fields**:
38
- - `name`: folder name
39
- - `status`: enum ['UPLOADING', 'COMPLETE']
40
- - **Timestamps**: `created_at`, `updated_at`
41
- - **Indexes**:
42
- - `uuid` (unique)
43
- - `cameraId`
44
-
45
- ### 3. Cameras Table (Current Structure) ✅ **ALREADY IMPLEMENTED**
46
-
47
- - **Table Name**: `cameras`
48
- - **Primary Key**: `id` (auto-increment number)
49
- - **UUID**: `uuid` (unique, not null) - for external references
50
- - **Fields**:
51
- - `name`: string(100), not null
52
- - `longitude`: decimal(10,7), not null
53
- - `latitude`: decimal(10,7), not null
54
- - **Timestamps**: `created_at`, `updated_at`
55
- - **Indexes**:
56
- - `uuid` (unique)
57
- - Composite: `(longitude, latitude)` for geospatial queries
58
-
59
- ## Relationship Analysis
60
-
61
- ### Current Relationships ✅ **FULLY IMPLEMENTED**
62
-
63
- 1. **Study → Folders** (One-to-Many)
64
- - Foreign key: `folders.studyId → study.id`
65
- - Cascade delete: deleting study removes all folders
66
-
67
- 2. **Folders → Videos** (One-to-Many)
68
- - Foreign key: `video.folderId → folders.id`
69
- - Cascade delete: deleting folder removes all videos
70
-
71
- 3. **Cameras → Folders** (One-to-Many) ✅
72
- - Foreign key: `folders.cameraId → cameras.id`
73
- - SET NULL on delete: deleting camera keeps folders but removes reference
74
-
75
- 4. **Cameras → Videos** (One-to-Many) ✅
76
- - Foreign key: `video.cameraId → cameras.id`
77
- - SET NULL on delete: deleting camera keeps videos but removes reference
78
-
79
- 5. **Videos → Videos** (Self-Reference for Templates)
80
- - Foreign key: `video.annotationSourceId → video.id`
81
- - Used for lane annotation templates
82
-
83
- ## DAO Implementation Status
84
-
85
- ### CameraDAO ✅ **FULLY IMPLEMENTED**
86
-
87
- - **File**: `src/dao/camera/camera.dao.ts`
88
- - **Standard Methods**: create, getById, getByUuid, update, delete, getAll ✅
89
- - **Custom Methods**:
90
- - `getByName(name: string)`: Find camera by name ✅
91
- - `getCamerasNearCoordinates(lng, lat, radius)`: Geospatial search using PostGIS ✅
92
- - **Performance Features**: Uses PostGIS ST_DWithin for geographic queries ✅
93
-
94
- ### VideoDAO - Camera Integration ✅ **ALREADY INTEGRATED**
95
-
96
- - **JOIN Queries**: Already includes folder data in getById/getByUuid ✅
97
- - **Camera Support**: Interface includes `cameraId?: number` and `camera?: ICamera` ✅
98
- - **Note**: DAO doesn't currently JOIN camera data, but interface supports it
99
-
100
- ### FolderDAO - Camera Integration ✅ **ALREADY INTEGRATED**
101
-
102
- - **JOIN Queries**: Already includes study data in getById/getByUuid ✅
103
- - **Camera Support**: Interface includes `cameraId?: number` and `camera?: ICamera` ✅
104
- - **Note**: DAO doesn't currently JOIN camera data, but interface supports it
105
-
106
- ## Migration Status ✅ **COMPLETE**
107
-
108
- **Migration**: `20250911000000_migration.ts` ✅ **ALREADY DEPLOYED**
109
-
110
- - Creates `cameras` table with proper schema ✅
111
- - Adds `cameraId` to `video` table with foreign key ✅
112
- - Adds `cameraId` to `folders` table with foreign key ✅
113
- - Proper indexes for performance ✅
114
- - Safe rollback implementation ✅
115
-
116
- ## Interface Implementation Status ✅ **COMPLETE**
117
-
118
- ### ICamera ✅ **FULLY IMPLEMENTED**
119
-
120
- - **File**: `src/interfaces/camera/camera.interfaces.ts`
121
- - **Fields**: id, uuid, name, longitude, latitude, created_at, updated_at ✅
122
- - **Export**: Properly exported in `src/index.ts` ✅
123
-
124
- ### IVideo ✅ **CAMERA INTEGRATION COMPLETE**
125
-
126
- - **Camera Fields**: `cameraId?: number`, `camera?: ICamera` ✅
127
- - **Relationships**: Imports ICamera interface ✅
128
-
129
- ### IFolder ✅ **CAMERA INTEGRATION COMPLETE**
130
-
131
- - **Camera Fields**: `cameraId?: number`, `camera?: ICamera` ✅
132
- - **Relationships**: Imports ICamera interface ✅
133
-
134
- ## Performance Considerations ✅ **OPTIMIZED**
135
-
136
- ### Database Indexes ✅
137
-
138
- - `cameras.uuid` (unique) - for UUID lookups ✅
139
- - `cameras(longitude, latitude)` - for geospatial queries ✅
140
- - `video.cameraId` - for camera-to-videos lookups ✅
141
- - `folders.cameraId` - for camera-to-folders lookups ✅
142
-
143
- ### Query Optimization ✅
144
-
145
- - **VideoDAO**: Uses JOINs to eliminate N+1 queries (folder data) ✅
146
- - **FolderDAO**: Uses JOINs to eliminate N+1 queries (study data) ✅
147
- - **CameraDAO**: Geospatial queries use PostGIS for performance ✅
148
-
149
- ### Missing JOIN Optimizations (Minor Enhancement Opportunity)
150
-
151
- - VideoDAO could JOIN camera data in getById/getByUuid queries
152
- - FolderDAO could JOIN camera data in getById/getByUuid queries
153
- - These would eliminate additional queries when camera data is needed
154
-
155
- ## System Architecture Compliance ✅
156
-
157
- ### Pattern Compliance ✅
158
-
159
- - **Naming**: Follows `entityUuid` pattern for external references ✅
160
- - **Timestamps**: Uses `created_at`/`updated_at` consistently ✅
161
- - **Primary Keys**: Auto-increment `id` for internal use ✅
162
- - **Foreign Keys**: Proper referential integrity with appropriate cascade rules ✅
163
-
164
- ### TypeScript Integration ✅
165
-
166
- - **Type Safety**: Full TypeScript interfaces with proper typing ✅
167
- - **Optional Fields**: Camera relationships marked as optional ✅
168
- - **Export Structure**: All entities properly exported from main index ✅
169
-
170
- ## Current Implementation Status: ✅ **COMPLETE**
171
-
172
- ### What's Already Working:
173
-
174
- 1. **Database Schema**: Cameras table exists with proper relationships ✅
175
- 2. **Migrations**: All database changes deployed ✅
176
- 3. **DAOs**: CameraDAO fully implemented with geospatial features ✅
177
- 4. **Interfaces**: All TypeScript interfaces support camera relationships ✅
178
- 5. **Exports**: All components properly exported ✅
179
- 6. **Relationships**: Many-to-one relationships correctly implemented ✅
180
- 7. **Performance**: Proper indexing for UUID and geospatial queries ✅
181
-
182
- ### Minor Optimization Opportunities:
183
-
184
- 1. **JOIN Queries**: VideoDAO and FolderDAO could include camera data in their JOIN queries
185
- 2. **Query Performance**: Consider adding camera data to reduce roundtrips
186
-
187
- ## Conclusion
188
-
189
- The cameras table implementation is **COMPLETE and FULLY FUNCTIONAL**. The database schema, migrations, DAOs, and interfaces are all properly implemented following the project's architectural patterns. The system supports:
190
-
191
- - ✅ Camera management with geographic coordinates
192
- - ✅ Many-to-one relationships between cameras and folders/videos
193
- - ✅ UUID-based external references
194
- - ✅ Geospatial queries for location-based camera searches
195
- - ✅ Proper foreign key constraints with appropriate cascade behavior
196
- - ✅ Full TypeScript type safety
197
- - ✅ Performance optimization through proper indexing
198
-
199
- **No database changes are required** - the system is ready for camera-based video processing workflows.
@@ -1,167 +0,0 @@
1
- # Folder Table CameraId Analysis
2
-
3
- ## Issue Summary
4
-
5
- The `cameraId` column in the `folders` table is being saved as `null` despite being defined as optional in the schema and interface.
6
-
7
- ## Database Schema Analysis
8
-
9
- ### Current Schema (from migrations)
10
-
11
- #### Initial Folder Table Creation (20250717161310_migration.ts)
12
-
13
- ```sql
14
- CREATE TABLE folders (
15
- id SERIAL PRIMARY KEY,
16
- name VARCHAR NOT NULL,
17
- createdBy INTEGER NOT NULL REFERENCES user(id) ON DELETE CASCADE,
18
- status ENUM('UPLOADING', 'COMPLETE') NOT NULL DEFAULT 'UPLOADING',
19
- studyId INTEGER NOT NULL REFERENCES study(id) ON DELETE CASCADE,
20
- created_at TIMESTAMP,
21
- updated_at TIMESTAMP
22
- );
23
- ```
24
-
25
- #### Camera Integration (20250911000000_migration.ts)
26
-
27
- ```sql
28
- -- Added cameraId column to folders table
29
- ALTER TABLE folders ADD COLUMN cameraId INTEGER NULLABLE
30
- REFERENCES cameras(id) ON DELETE SET NULL;
31
- CREATE INDEX ON folders (cameraId);
32
- ```
33
-
34
- ### Current Interface Definition
35
-
36
- ```typescript
37
- export interface IFolder {
38
- id: number;
39
- uuid: string;
40
- name: string;
41
- createdBy: number; // user.id
42
- status: "UPLOADING" | "COMPLETE";
43
- studyId: number; // study.id
44
- cameraId?: number; // camera.id - OPTIONAL field
45
- created_at: string;
46
- updated_at: string;
47
- study?: IStudy;
48
- camera?: ICamera;
49
- }
50
- ```
51
-
52
- ## DAO Implementation Analysis
53
-
54
- ### Current FolderDAO Issues
55
-
56
- 1. **No Camera Relationship Joins**: The DAO queries join with `study` table but don't include `camera` joins
57
- 2. **Missing Camera Data Population**: Queries don't populate the optional `camera` field
58
- 3. **Basic CRUD Operations**: The `create()` method simply inserts the provided data without validation
59
-
60
- ### Key Methods Analysis
61
-
62
- #### Create Method
63
-
64
- ```typescript
65
- async create(item: IFolder): Promise<IFolder> {
66
- const [createdFolder] = await this._knex("folders")
67
- .insert(item)
68
- .returning("*");
69
- return createdFolder;
70
- }
71
- ```
72
-
73
- - **Issue**: This will insert whatever `cameraId` is provided in the `item` parameter
74
- - **Behavior**: If `cameraId` is undefined or not provided, it will be stored as `NULL` (which is valid)
75
-
76
- #### Query Methods (getById, getByUuid, getAll)
77
-
78
- ```typescript
79
- // Current pattern - only joins with study
80
- const folder = await this._knex("folders as f")
81
- .innerJoin("study as s", "f.studyId", "s.id")
82
- .select("f.*", this._knex.raw("to_jsonb(s.*) as study"))
83
- .where("f.id", id)
84
- .first();
85
- ```
86
-
87
- - **Missing**: No LEFT JOIN with cameras table to populate camera data
88
- - **Impact**: Even if `cameraId` has a valid value, the `camera` object won't be populated
89
-
90
- ## Root Cause Analysis
91
-
92
- ### Why CameraId is Null
93
-
94
- 1. **Optional Field Behavior**: Since `cameraId` is optional (`cameraId?: number`), if not explicitly provided in create requests, it defaults to `null`
95
-
96
- 2. **No Default Value**: Unlike other fields, there's no database-level default value for `cameraId`
97
-
98
- 3. **Application Logic Gap**: No validation or business logic to ensure `cameraId` is populated when appropriate
99
-
100
- ### Database Schema is Correct
101
-
102
- The database schema properly supports `cameraId`:
103
-
104
- - ✅ Column exists and is nullable
105
- - ✅ Foreign key constraint to `cameras(id)`
106
- - ✅ Proper ON DELETE SET NULL behavior
107
- - ✅ Index created for performance
108
-
109
- ## Recommended Solutions
110
-
111
- ### 1. Immediate Fix: Verify Input Data
112
-
113
- Check what data is being passed to the `create()` method:
114
-
115
- - Log the `item` parameter in FolderDAO.create()
116
- - Verify if `cameraId` is being provided in the request
117
-
118
- ### 2. Enhanced DAO Queries
119
-
120
- Update FolderDAO to include camera relationships:
121
-
122
- ```typescript
123
- // Enhanced getById with camera join
124
- async getById(id: number): Promise<IFolder | null> {
125
- const folder = await this._knex("folders as f")
126
- .innerJoin("study as s", "f.studyId", "s.id")
127
- .leftJoin("cameras as c", "f.cameraId", "c.id")
128
- .select(
129
- "f.*",
130
- this._knex.raw("to_jsonb(s.*) as study"),
131
- this._knex.raw("to_jsonb(c.*) as camera")
132
- )
133
- .where("f.id", id)
134
- .first();
135
- return folder || null;
136
- }
137
- ```
138
-
139
- ### 3. API Layer Investigation
140
-
141
- Check the API endpoints that create folders:
142
-
143
- - Verify if `cameraId` is being extracted from request body
144
- - Ensure DTO/validation layer passes `cameraId` through
145
- - Check if frontend is sending `cameraId` in requests
146
-
147
- ### 4. Business Logic Validation
148
-
149
- Consider if folders should always have a camera:
150
-
151
- - If required: Add validation in create method
152
- - If optional: Current behavior is correct (null is valid)
153
-
154
- ## Implementation Priority
155
-
156
- 1. **High**: Investigate API request data flow
157
- 2. **High**: Add camera relationship to DAO queries
158
- 3. **Medium**: Add logging to track cameraId values
159
- 4. **Low**: Consider business logic changes
160
-
161
- ## Files to Examine Next
162
-
163
- 1. `api-rel/src/controllers/folder/folder.controller.ts` - Controller logic
164
- 2. `api-rel/src/routes/folder/` - Route definitions and DTOs
165
- 3. Frontend folder creation forms - Check if cameraId is being sent
166
-
167
- The database schema and DAO patterns are correctly implemented. The issue likely lies in the data flow from the API/frontend layers not providing `cameraId` values during folder creation.
@@ -1,22 +0,0 @@
1
- import type { Knex } from "knex";
2
-
3
- export async function up(knex: Knex): Promise<void> {
4
- await knex.schema.alterTable("cameras", (table) => {
5
- // Add index for case-insensitive name searches
6
- table.index(knex.raw("LOWER(name)"), "idx_cameras_name_lower");
7
- });
8
-
9
- // Add GIN index for full-text search if needed for fuzzy matching
10
- await knex.raw(`
11
- CREATE INDEX idx_cameras_name_gin
12
- ON cameras
13
- USING GIN (to_tsvector('english', name))
14
- `);
15
- }
16
-
17
- export async function down(knex: Knex): Promise<void> {
18
- await knex.raw("DROP INDEX IF EXISTS idx_cameras_name_gin");
19
- await knex.schema.alterTable("cameras", (table) => {
20
- table.dropIndex(knex.raw("LOWER(name)"), "idx_cameras_name_lower");
21
- });
22
- }