Skip to content

Commit 843e639

Browse files
committed
Refactor DocumentProcessorController and enhance report processing logic to include isMedicalReport flag. Update ProcessingPage and ProcessingError components for improved error handling and user feedback. Introduce new ProcessingMedicalReport component for non-medical report scenarios. Ensure all user-facing text is internationalized.
1 parent 8507fbe commit 843e639

File tree

6 files changed

+77
-26
lines changed

6 files changed

+77
-26
lines changed

backend/src/document-processor/controllers/document-processor.controller.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,7 @@ export class DocumentProcessorController {
3838
}
3939

4040
// Extract userId from the request (attached by auth middleware)
41-
const userId = request.user?.sub;
42-
if (!userId) {
43-
throw new UnauthorizedException('User ID not found in request');
44-
}
41+
const userId = this.extractUserId(request);
4542

4643
this.logger.log(`Queueing document for processing, report ID: ${reportId}`);
4744

@@ -138,11 +135,9 @@ export class DocumentProcessorController {
138135
}
139136

140137
if (!result.analysis.metadata.isMedicalReport) {
141-
this.logger.log(`Report ${reportId} is not a medical report.`);
142-
report.processingStatus = ProcessingStatus.FAILED;
143-
report.errorMessage = 'Document is not a medical report';
144-
report.updatedAt = new Date().toISOString();
145-
await this.reportsService.updateReport(report);
138+
const errorMessage = `Report ${reportId} is not a medical report.`;
139+
this.logger.log(errorMessage);
140+
await this.failReport(reportId, userId, errorMessage, false);
146141

147142
return;
148143
}
@@ -151,7 +146,7 @@ export class DocumentProcessorController {
151146
report.title = result.analysis.title;
152147
report.category = result.analysis.category;
153148
report.processingStatus = ProcessingStatus.PROCESSED;
154-
149+
report.isMedicalReport = true;
155150
// Extract lab values
156151
report.labValues = result.analysis.labValues || [];
157152

@@ -184,13 +179,15 @@ export class DocumentProcessorController {
184179
reportId: string,
185180
userId: string,
186181
errorMessage: string | undefined = undefined,
182+
isMedicalReport: boolean | undefined = undefined,
187183
): Promise<void> {
188184
try {
189185
const report = await this.reportsService.findOne(reportId, userId);
190186
if (report) {
191187
report.processingStatus = ProcessingStatus.FAILED;
192188
report.updatedAt = new Date().toISOString();
193189
report.errorMessage = errorMessage;
190+
report.isMedicalReport = isMedicalReport;
194191
await this.reportsService.updateReport(report);
195192
this.logger.log(`Updated status of report ${reportId} to FAILED`);
196193
}
@@ -283,10 +280,7 @@ export class DocumentProcessorController {
283280
}
284281

285282
// Extract userId from the request (attached by auth middleware)
286-
const userId = request.user?.sub;
287-
if (!userId) {
288-
throw new UnauthorizedException('User ID not found in request');
289-
}
283+
const userId = this.extractUserId(request);
290284

291285
try {
292286
// Fetch the associated report record from DynamoDB
@@ -299,6 +293,7 @@ export class DocumentProcessorController {
299293
reportId: report.id,
300294
status: report.processingStatus,
301295
isComplete: report.processingStatus === ProcessingStatus.PROCESSED,
296+
isMedicalReport: report.isMedicalReport,
302297
};
303298
} catch (error: unknown) {
304299
this.logger.error(
@@ -307,4 +302,12 @@ export class DocumentProcessorController {
307302
throw error;
308303
}
309304
}
305+
306+
private extractUserId(request: RequestWithUser): string {
307+
const userId = request.user?.sub;
308+
if (!userId) {
309+
throw new UnauthorizedException('User ID not found in request');
310+
}
311+
return userId;
312+
}
310313
}

backend/src/reports/models/report.model.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ export class Report {
3535
})
3636
processingStatus: ProcessingStatus;
3737

38+
@ApiProperty({ description: 'Optional flag to indicate if the report is a medical report' })
39+
isMedicalReport?: boolean;
40+
3841
@ApiProperty({ description: 'List of lab values' })
3942
labValues: Array<{
4043
name: string;

frontend/src/common/models/medicalReport.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export interface MedicalReport {
5757
fileSize: number;
5858
status: ReportStatus;
5959
errorMessage?: string; // Optional error message for the report
60+
isMedicalReport?: boolean; // Optional flag to indicate if the report is a medical report
6061
createdAt: string; // ISO date string
6162
updatedAt: string; // ISO date string
6263
}

frontend/src/pages/Processing/ProcessingPage.tsx

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const ProcessingPage: React.FC = () => {
2424
// States to track processing
2525
const [isProcessing, setIsProcessing] = useState(true);
2626
const [processingError, setProcessingError] = useState<string | null>(null);
27+
const [errorHeading, setErrorHeading] = useState<string | null>(null);
2728
const statusCheckIntervalRef = useRef<number | null>(null);
2829
const lastTriggeredTime = useRef<number | null>(null);
2930

@@ -42,6 +43,14 @@ const ProcessingPage: React.FC = () => {
4243
const checkReportStatus = async () => {
4344
if (!reportId) return;
4445

46+
const failedHeading = 'Processing Error';
47+
const failedMessage =
48+
'There was a problem processing your uploaded file. Please try again or upload another.';
49+
50+
const missingDataHeading = 'Missing Data';
51+
const missingDataMessage =
52+
'The system was unable to extract meaningful health data from your uploaded file. Please try again or upload another.';
53+
4554
try {
4655
const response = await axios.get(
4756
`${API_URL}/api/document-processor/report-status/${reportId}`,
@@ -50,27 +59,27 @@ const ProcessingPage: React.FC = () => {
5059

5160
const data = response.data;
5261

53-
// If processing is complete, clear the interval and redirect to the report page
5462
if (data.isComplete) {
5563
setIsProcessing(false);
56-
57-
// Clear the interval
5864
clearStatusCheckInterval();
5965

6066
console.log('Processing complete');
6167

62-
// Redirect to report detail page
6368
history.push(`/tabs/reports/${reportId}`);
69+
} else if (data.isMedicalReport === false) {
70+
setIsProcessing(false);
71+
clearStatusCheckInterval();
72+
setErrorHeading(missingDataHeading);
73+
setProcessingError(missingDataMessage);
6474
} else if (data.status === 'failed') {
65-
throw new Error('Processing failed');
75+
throw new Error(failedMessage);
6676
}
6777
} catch (error) {
68-
// Clear the interval on error
78+
setIsProcessing(false);
6979
clearStatusCheckInterval();
7080

71-
console.error('Error checking report status:', error);
72-
setProcessingError('An error occurred while processing the report. Please try again.');
73-
setIsProcessing(false);
81+
setErrorHeading(failedHeading);
82+
setProcessingError(error instanceof Error ? error.message : failedMessage);
7483
}
7584
};
7685

@@ -148,7 +157,13 @@ const ProcessingPage: React.FC = () => {
148157
{isProcessing && <ProcessingAnimation firstName={firstName} />}
149158

150159
{/* Error state - shows when processing fails */}
151-
{processingError && <ProcessingError errorMessage={processingError} onRetry={execute} />}
160+
{processingError && errorHeading && (
161+
<ProcessingError
162+
errorHeading={errorHeading}
163+
errorMessage={processingError}
164+
onRetry={execute}
165+
/>
166+
)}
152167
</div>
153168
</IonContent>
154169
</IonPage>

frontend/src/pages/Processing/components/ProcessingError.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,19 @@ import warning from '../../../assets/icons/warning.svg';
55

66
interface ProcessingErrorProps {
77
errorMessage: string;
8+
errorHeading: string;
89
onRetry: () => void;
910
}
1011

1112
/**
1213
* Component that displays processing error information and actions
1314
* Exactly matches the design from the provided screenshot
1415
*/
15-
const ProcessingError: React.FC<ProcessingErrorProps> = ({ errorMessage, onRetry }) => {
16+
const ProcessingError: React.FC<ProcessingErrorProps> = ({
17+
errorMessage,
18+
errorHeading,
19+
onRetry,
20+
}) => {
1621
const history = useHistory();
1722

1823
return (
@@ -27,7 +32,7 @@ const ProcessingError: React.FC<ProcessingErrorProps> = ({ errorMessage, onRetry
2732
<img src={warning} alt="Warning Icon" className="processing-page__error-icon-img" />
2833
</div>
2934

30-
<h3 className="processing-page__error-subheading">Processing Error</h3>
35+
<h3 className="processing-page__error-subheading">{errorHeading}</h3>
3136

3237
<p className="processing-page__error-message">{errorMessage}</p>
3338
</div>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import { IonButton } from '@ionic/react';
3+
import { useHistory } from 'react-router-dom';
4+
import { useTranslation } from 'react-i18next';
5+
6+
/**
7+
* Component displayed when the uploaded document is not a medical report
8+
*/
9+
const ProcessingMedicalReport: React.FC = () => {
10+
const history = useHistory();
11+
const { t } = useTranslation();
12+
13+
return (
14+
<div className="processing-error">
15+
<h2>{t('error.not_medical_report', { ns: 'processing' })}</h2>
16+
<p>{t('error.not_medical_report_message', { ns: 'processing' })}</p>
17+
<IonButton onClick={() => history.push('/tabs/upload')} className="retry-button">
18+
{t('button.try_another', { ns: 'common' })}
19+
</IonButton>
20+
</div>
21+
);
22+
};
23+
24+
export default ProcessingMedicalReport;

0 commit comments

Comments
 (0)