@trafficgroup/knex-rel 0.1.0 → 0.1.2

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.
@@ -16,7 +16,12 @@ export class FolderDAO implements IBaseDAO<IFolder> {
16
16
  async getById(id: number): Promise<IFolder | null> {
17
17
  const folder = await this._knex("folders as f")
18
18
  .innerJoin("study as s", "f.studyId", "s.id")
19
- .select("f.*", this._knex.raw("to_jsonb(s.*) as study"))
19
+ .leftJoin("cameras as c", "f.cameraId", "c.id")
20
+ .select(
21
+ "f.*",
22
+ this._knex.raw("to_jsonb(s.*) as study"),
23
+ this._knex.raw("to_jsonb(c.*) as camera"),
24
+ )
20
25
  .where("f.id", id)
21
26
  .first();
22
27
  return folder || null;
@@ -25,7 +30,12 @@ export class FolderDAO implements IBaseDAO<IFolder> {
25
30
  async getByUuid(uuid: string): Promise<IFolder | null> {
26
31
  const folder = await this._knex("folders as f")
27
32
  .innerJoin("study as s", "f.studyId", "s.id")
28
- .select("f.*", this._knex.raw("to_jsonb(s.*) as study"))
33
+ .leftJoin("cameras as c", "f.cameraId", "c.id")
34
+ .select(
35
+ "f.*",
36
+ this._knex.raw("to_jsonb(s.*) as study"),
37
+ this._knex.raw("to_jsonb(c.*) as camera"),
38
+ )
29
39
  .where("f.uuid", uuid)
30
40
  .first();
31
41
  return folder || null;
@@ -53,7 +63,12 @@ export class FolderDAO implements IBaseDAO<IFolder> {
53
63
 
54
64
  const query = this._knex("folders as f")
55
65
  .innerJoin("study as s", "f.studyId", "s.id")
56
- .select("f.*", this._knex.raw("to_jsonb(s.*) as study"));
66
+ .leftJoin("cameras as c", "f.cameraId", "c.id")
67
+ .select(
68
+ "f.*",
69
+ this._knex.raw("to_jsonb(s.*) as study"),
70
+ this._knex.raw("to_jsonb(c.*) as camera"),
71
+ );
57
72
  if (studyId !== undefined && studyId !== null) {
58
73
  query.where("f.studyId", studyId);
59
74
  }
@@ -196,4 +196,53 @@ export class VideoDAO implements IBaseDAO<IVideo> {
196
196
  totalPages: Math.ceil(totalCount / limit),
197
197
  };
198
198
  }
199
+
200
+ /**
201
+ * Get videos from same folder with same type that have metadata and are COMPLETED
202
+ * Suitable for use as lane annotation templates
203
+ */
204
+ async getTemplateVideos(
205
+ folderId: number,
206
+ videoType: string,
207
+ ): Promise<IVideo[]> {
208
+ try {
209
+ let query = this._knex("video")
210
+ .where("folderId", folderId)
211
+ .where("videoType", videoType)
212
+ .where("status", "COMPLETED")
213
+ .whereNotNull("metadata")
214
+ .whereRaw("metadata != '{}'");
215
+
216
+ // Apply video type specific metadata validation
217
+ if (videoType === "ATR") {
218
+ // ATR videos use lanes array structure
219
+ query = query.whereRaw("jsonb_array_length(metadata->'lanes') > 0");
220
+ } else {
221
+ // TMC/JUNCTION/ROUNDABOUT/PATHWAY videos use objects with pt1/pt2 structure
222
+ // Check if metadata has at least one key with pt1 and pt2 properties
223
+ query = query.whereRaw(`
224
+ EXISTS (
225
+ SELECT 1
226
+ FROM jsonb_each(metadata) as entry(key, value)
227
+ WHERE key != 'lanes'
228
+ AND key != 'finish_line'
229
+ AND jsonb_typeof(value) = 'object'
230
+ AND value ? 'pt1'
231
+ AND value ? 'pt2'
232
+ AND jsonb_typeof(value->'pt1') = 'array'
233
+ AND jsonb_typeof(value->'pt2') = 'array'
234
+ AND jsonb_array_length(value->'pt1') = 2
235
+ AND jsonb_array_length(value->'pt2') = 2
236
+ )
237
+ `);
238
+ }
239
+
240
+ const videos = await query.orderBy("updated_at", "desc").select("*");
241
+
242
+ return videos;
243
+ } catch (error) {
244
+ console.error("Error fetching template videos:", error);
245
+ throw error;
246
+ }
247
+ }
199
248
  }
package/src/index.ts CHANGED
@@ -1,32 +1,34 @@
1
1
  // DAOs
2
- export { UserDAO } from "./dao/user/user.dao";
3
- export { StudyDAO } from "./dao/study/study.dao";
4
- export { FolderDAO } from "./dao/folder/folder.dao";
5
- export { VideoDAO } from "./dao/video/video.dao";
6
2
  export { AuthDAO } from "./dao/auth/auth.dao";
7
- export { UserPushNotificationTokenDAO } from "./dao/user-push-notification-token/user-push-notification-token.dao";
3
+ export { CameraDAO } from "./dao/camera/camera.dao";
8
4
  export { ChatDAO } from "./dao/chat/chat.dao";
5
+ export { FolderDAO } from "./dao/folder/folder.dao";
9
6
  export { MessageDAO } from "./dao/message/message.dao";
7
+ export { StudyDAO } from "./dao/study/study.dao";
8
+ export { UserDAO } from "./dao/user/user.dao";
9
+ export { UserPushNotificationTokenDAO } from "./dao/user-push-notification-token/user-push-notification-token.dao";
10
+ export { VideoDAO } from "./dao/video/video.dao";
10
11
  export { VideoMinuteResultDAO } from "./dao/VideoMinuteResultDAO";
11
12
 
12
13
  // Interfaces
13
14
  export { IDataPaginator } from "./d.types";
14
- export { IUser } from "./interfaces/user/user.interfaces";
15
- export { IStudy } from "./interfaces/study/study.interfaces";
16
- export { IFolder } from "./interfaces/folder/folder.interfaces";
17
- export { IVideo } from "./interfaces/video/video.interfaces";
18
15
  export { IAuth } from "./interfaces/auth/auth.interfaces";
19
- export { IUserPushNotificationToken } from "./interfaces/user-push-notification-token/user-push-notification-token.interfaces";
16
+ export { ICamera } from "./interfaces/camera/camera.interfaces";
20
17
  export {
21
18
  IChat,
22
19
  IChatCreate,
23
20
  IChatUpdate,
24
21
  } from "./interfaces/chat/chat.interfaces";
22
+ export { IFolder } from "./interfaces/folder/folder.interfaces";
25
23
  export {
26
24
  IMessage,
27
25
  IMessageCreate,
28
26
  IMessageUpdate,
29
27
  } from "./interfaces/message/message.interfaces";
28
+ export { IStudy } from "./interfaces/study/study.interfaces";
29
+ export { IUser } from "./interfaces/user/user.interfaces";
30
+ export { IUserPushNotificationToken } from "./interfaces/user-push-notification-token/user-push-notification-token.interfaces";
31
+ export { IVideo } from "./interfaces/video/video.interfaces";
30
32
  export {
31
33
  IVideoMinuteResult,
32
34
  IVideoMinuteResultInput,
@@ -0,0 +1,9 @@
1
+ export interface ICamera {
2
+ id: number;
3
+ uuid: string;
4
+ name: string;
5
+ longitude: number;
6
+ latitude: number;
7
+ created_at: string;
8
+ updated_at: string;
9
+ }
@@ -1,4 +1,5 @@
1
1
  import type { IStudy } from "../study/study.interfaces";
2
+ import type { ICamera } from "../camera/camera.interfaces";
2
3
 
3
4
  export interface IFolder {
4
5
  id: number;
@@ -7,7 +8,9 @@ export interface IFolder {
7
8
  createdBy: number; // user.id
8
9
  status: "UPLOADING" | "COMPLETE";
9
10
  studyId: number; // study.id
11
+ cameraId?: number; // camera.id
10
12
  created_at: string;
11
13
  updated_at: string;
12
14
  study?: IStudy;
15
+ camera?: ICamera;
13
16
  }
@@ -1,9 +1,12 @@
1
1
  import type { IFolder } from "../folder/folder.interfaces";
2
+ import type { ICamera } from "../camera/camera.interfaces";
2
3
 
3
4
  export interface IVideo {
4
5
  id: number;
5
6
  uuid: string;
6
7
  folderId: number;
8
+ cameraId?: number;
9
+ annotationSourceId?: number;
7
10
  name: string;
8
11
  videoLocation: string;
9
12
  videoOutputLocation: string | null;
@@ -28,4 +31,5 @@ export interface IVideo {
28
31
  created_at: string;
29
32
  updated_at: string;
30
33
  folder?: IFolder;
34
+ camera?: ICamera;
31
35
  }