|
|
@@ -113,6 +113,54 @@ export class PalmOilService {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ async getBatchDetails(batchId: string): Promise<{
|
|
|
+ batchId: string;
|
|
|
+ frameCount: number;
|
|
|
+ totalDetections: number;
|
|
|
+ avgInferenceMs: number;
|
|
|
+ avgProcessingMs: number;
|
|
|
+ batchStart: Date | null;
|
|
|
+ batchEnd: Date | null;
|
|
|
+ classTally: Record<string, number>;
|
|
|
+ frames: History[];
|
|
|
+ }> {
|
|
|
+ const frames = await this.historyRepository.find({
|
|
|
+ where: { batch_id: batchId },
|
|
|
+ order: { created_at: 'ASC' },
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!frames.length) {
|
|
|
+ return { batchId, frameCount: 0, totalDetections: 0, avgInferenceMs: 0, avgProcessingMs: 0, batchStart: null, batchEnd: null, classTally: {}, frames: [] };
|
|
|
+ }
|
|
|
+
|
|
|
+ const frameCount = frames.length;
|
|
|
+ const totalDetections = frames.reduce((s, f) => s + (f.total_count ?? 0), 0);
|
|
|
+ const avgInferenceMs = parseFloat((frames.reduce((s, f) => s + (f.inference_ms ?? 0), 0) / frameCount).toFixed(2));
|
|
|
+ const avgProcessingMs = parseFloat((frames.reduce((s, f) => s + (f.processing_ms ?? 0), 0) / frameCount).toFixed(2));
|
|
|
+
|
|
|
+ const classTally: Record<string, number> = {};
|
|
|
+ for (const frame of frames) {
|
|
|
+ const summary = frame.industrial_summary as Record<string, number>;
|
|
|
+ if (summary && typeof summary === 'object') {
|
|
|
+ for (const [cls, count] of Object.entries(summary)) {
|
|
|
+ classTally[cls] = (classTally[cls] ?? 0) + (count ?? 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ batchId,
|
|
|
+ frameCount,
|
|
|
+ totalDetections,
|
|
|
+ avgInferenceMs,
|
|
|
+ avgProcessingMs,
|
|
|
+ batchStart: frames[0].created_at,
|
|
|
+ batchEnd: frames[frameCount - 1].created_at,
|
|
|
+ classTally,
|
|
|
+ frames,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
async getRecordByArchiveId(archiveId: string): Promise<History | null> {
|
|
|
return this.historyRepository.findOne({ where: { archive_id: archiveId } });
|
|
|
}
|