Updated SQL script to correct procedure and column sizes. Moved SQL scripts to the project root folder (outside React frontend). Resolved backend container dependency issue to ensure MySQL is up before starting React backend. Moved common values to .env file in the project root. Updated React backend and MySQL ports to use default values. Added code to get last study received, containers status and updated into DB.
272 lines
8.7 KiB
TypeScript
272 lines
8.7 KiB
TypeScript
// src/repositories/DicomStudyRepository.ts
|
|
|
|
import { DicomStudy, CreateDicomStudyDTO, UpdateDicomStudyDTO, DBDicomStudy, DicomStudySearchParams } from '../types/dicom';
|
|
import pool from '../config/db';
|
|
import logger from '../utils/logger';
|
|
import { Pool } from 'mysql2/promise';
|
|
import { RowDataPacket, ResultSetHeader } from 'mysql2';
|
|
|
|
export class DicomStudyRepository {
|
|
constructor(private pool: Pool) {} // Modified constructor
|
|
|
|
private async getRouterStringId(numericId: number): Promise<string> {
|
|
try {
|
|
const [result] = await pool.query(
|
|
'SELECT router_id FROM routers WHERE id = ?',
|
|
[numericId]
|
|
);
|
|
const rows = result as any[];
|
|
if (rows.length === 0) {
|
|
throw new Error(`Router not found with id: ${numericId}`);
|
|
}
|
|
return rows[0].router_id;
|
|
} catch (error) {
|
|
logger.error('Error getting router string ID:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
private async getRouterNumericId(routerId: string): Promise<number> {
|
|
try {
|
|
const [result] = await pool.query(
|
|
'SELECT id FROM routers WHERE router_id = ?',
|
|
[routerId]
|
|
);
|
|
const rows = result as any[];
|
|
if (rows.length === 0) {
|
|
throw new Error(`Router not found with router_id: ${routerId}`);
|
|
}
|
|
return rows[0].id;
|
|
} catch (error) {
|
|
logger.error('Error getting router numeric ID:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
private async mapDBStudyToDicomStudy(dbStudy: DBDicomStudy): Promise<DicomStudy> {
|
|
return {
|
|
id: dbStudy.id,
|
|
router_id: dbStudy.router_id.toString(),
|
|
study_instance_uid: dbStudy.study_instance_uid,
|
|
patient_id: dbStudy.patient_id,
|
|
patient_name: dbStudy.patient_name,
|
|
accession_number: dbStudy.accession_number,
|
|
study_date: dbStudy.study_date,
|
|
modality: dbStudy.modality,
|
|
study_description: dbStudy.study_description,
|
|
series_instance_uid: dbStudy.series_instance_uid,
|
|
procedure_code: dbStudy.procedure_code,
|
|
referring_physician_name: dbStudy.referring_physician_name,
|
|
study_status_code: dbStudy.study_status_code,
|
|
association_id: dbStudy.association_id,
|
|
created_at: dbStudy.created_at,
|
|
updated_at: dbStudy.updated_at
|
|
};
|
|
}
|
|
|
|
async create(studyData: CreateDicomStudyDTO): Promise<DicomStudy> {
|
|
try {
|
|
|
|
const [result] = await pool.query(
|
|
`INSERT INTO dicom_study_overview (
|
|
router_id, study_instance_uid, patient_id, patient_name,
|
|
accession_number, study_date, modality, study_description,
|
|
series_instance_uid, procedure_code, referring_physician_name,
|
|
study_status_code, association_id, created_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`,
|
|
[
|
|
studyData.router_id,
|
|
studyData.study_instance_uid,
|
|
studyData.patient_id,
|
|
studyData.patient_name,
|
|
studyData.accession_number,
|
|
studyData.study_date,
|
|
studyData.modality,
|
|
studyData.study_description,
|
|
studyData.series_instance_uid,
|
|
studyData.procedure_code,
|
|
studyData.referring_physician_name,
|
|
studyData.study_status_code,
|
|
studyData.association_id
|
|
]
|
|
);
|
|
|
|
// Get the created study with the correct router_id format
|
|
const insertId = (result as any).insertId;
|
|
return await this.findById(insertId) as DicomStudy;
|
|
|
|
} catch (error) {
|
|
logger.error('Error creating DICOM study:', error);
|
|
throw new Error('Failed to create DICOM study');
|
|
}
|
|
}
|
|
|
|
async findAll(): Promise<DicomStudy[]> {
|
|
try {
|
|
const [rows] = await pool.query(`
|
|
SELECT d.*, r.router_id as router_string_id
|
|
FROM dicom_study_overview d
|
|
JOIN routers r ON d.router_id = r.id
|
|
ORDER BY d.created_at DESC
|
|
`);
|
|
|
|
return Promise.all((rows as DBDicomStudy[]).map(row => this.mapDBStudyToDicomStudy(row)));
|
|
} catch (error) {
|
|
logger.error('Error fetching all DICOM studies:', error);
|
|
throw new Error('Failed to fetch DICOM studies');
|
|
}
|
|
}
|
|
|
|
async findById(id: number): Promise<DicomStudy | null> {
|
|
try {
|
|
const [rows] = await pool.query(`
|
|
SELECT d.*, r.router_id as router_string_id
|
|
FROM dicom_study_overview d
|
|
JOIN routers r ON d.router_id = r.id
|
|
WHERE d.id = ?
|
|
`, [id]);
|
|
|
|
const results = rows as DBDicomStudy[];
|
|
if (results.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return await this.mapDBStudyToDicomStudy(results[0]);
|
|
} catch (error) {
|
|
logger.error('Error fetching DICOM study by ID:', error);
|
|
throw new Error('Failed to fetch DICOM study');
|
|
}
|
|
}
|
|
|
|
async findByRouterId(routerId: string): Promise<DicomStudy[]> {
|
|
try {
|
|
const numericRouterId = await this.getRouterNumericId(routerId);
|
|
const [rows] = await pool.query(`
|
|
SELECT d.*, r.router_id as router_string_id
|
|
FROM dicom_study_overview d
|
|
JOIN routers r ON d.router_id = r.id
|
|
WHERE d.router_id = ?
|
|
ORDER BY d.created_at DESC
|
|
`, [numericRouterId]);
|
|
|
|
return Promise.all((rows as DBDicomStudy[]).map(row => this.mapDBStudyToDicomStudy(row)));
|
|
} catch (error) {
|
|
logger.error('Error fetching DICOM studies by router ID:', error);
|
|
throw new Error('Failed to fetch DICOM studies');
|
|
}
|
|
}
|
|
|
|
async update(id: number, studyData: UpdateDicomStudyDTO): Promise<DicomStudy | null> {
|
|
try {
|
|
// First check if study exists
|
|
const existingStudy = await this.findById(id);
|
|
if (!existingStudy) {
|
|
return null;
|
|
}
|
|
|
|
// Build update query dynamically based on provided fields
|
|
const updateFields: string[] = [];
|
|
const updateValues: any[] = [];
|
|
|
|
Object.entries(studyData).forEach(([key, value]) => {
|
|
if (value !== undefined) {
|
|
updateFields.push(`${key} = ?`);
|
|
updateValues.push(value);
|
|
}
|
|
});
|
|
|
|
if (updateFields.length > 0) {
|
|
// Add updated_at timestamp
|
|
updateFields.push('updated_at = CURRENT_TIMESTAMP');
|
|
|
|
// Add id for WHERE clause
|
|
updateValues.push(id);
|
|
|
|
await pool.query(`
|
|
UPDATE dicom_study_overview
|
|
SET ${updateFields.join(', ')}
|
|
WHERE id = ?
|
|
`, updateValues);
|
|
}
|
|
|
|
// Return updated study
|
|
return await this.findById(id);
|
|
} catch (error) {
|
|
logger.error('Error updating DICOM study:', error);
|
|
throw new Error('Failed to update DICOM study');
|
|
}
|
|
}
|
|
|
|
async delete(id: number): Promise<boolean> {
|
|
try {
|
|
const [result] = await pool.query(
|
|
'DELETE FROM dicom_study_overview WHERE id = ?',
|
|
[id]
|
|
);
|
|
return (result as any).affectedRows > 0;
|
|
} catch (error) {
|
|
logger.error('Error deleting DICOM study:', error);
|
|
throw new Error('Failed to delete DICOM study');
|
|
}
|
|
}
|
|
|
|
async search(params: DicomStudySearchParams): Promise<DicomStudy[]> {
|
|
try {
|
|
let query = `
|
|
SELECT d.*, r.router_id as router_string_id
|
|
FROM dicom_study_overview d
|
|
JOIN routers r ON d.router_id = r.id
|
|
WHERE 1=1
|
|
`;
|
|
const values: any[] = [];
|
|
|
|
if (params.startDate) {
|
|
query += ' AND d.study_date >= ?';
|
|
values.push(params.startDate);
|
|
}
|
|
if (params.endDate) {
|
|
query += ' AND d.study_date <= ?';
|
|
values.push(params.endDate);
|
|
}
|
|
if (params.modality) {
|
|
query += ' AND d.modality = ?';
|
|
values.push(params.modality);
|
|
}
|
|
if (params.patientName) {
|
|
query += ' AND d.patient_name LIKE ?';
|
|
values.push(`%${params.patientName}%`);
|
|
}
|
|
|
|
// Add sorting
|
|
query += ' ORDER BY d.created_at DESC';
|
|
|
|
const [rows] = await pool.query(query, values);
|
|
return Promise.all((rows as DBDicomStudy[]).map(row => this.mapDBStudyToDicomStudy(row)));
|
|
} catch (error) {
|
|
logger.error('Error searching DICOM studies:', error);
|
|
throw new Error('Failed to search DICOM studies');
|
|
}
|
|
}
|
|
|
|
async findByStudyInstanceUid(studyInstanceUid: string): Promise<DicomStudy | null> {
|
|
try {
|
|
// Use RowDataPacket[] to align with mysql2/promise type expectations
|
|
const [rows] = await pool.query<DBDicomStudy[] & RowDataPacket[]>(`
|
|
SELECT * FROM dicom_study_overview WHERE study_instance_uid = ?`,
|
|
[studyInstanceUid]
|
|
);
|
|
|
|
// Check if rows is empty
|
|
if (rows.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
// Map the first result to your DicomStudy object
|
|
return await this.mapDBStudyToDicomStudy(rows[0]);
|
|
} catch (error) {
|
|
logger.error('Error fetching DICOM study by Study Instance UID:', error);
|
|
throw new Error('Failed to fetch DICOM study');
|
|
}
|
|
}
|
|
|
|
} |