-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Ticket 1.1: Verify Backend PDF Support
Context: The backend code for PDF extraction appears to exist in the repository. We must verify it is complete and active.
Prerequisites: None.
Validation Steps:
- Open
services/api/app/main.py. - Search for
@app.post("/v1/ingest/pdf"). - Confirm it imports from
.core.pdf_extractor. - Open
services/api/app/core/pdf_extractor.py. - Ensure the file is not empty and contains
def extract_pdf_text(...). - Action:
- If these exist, this ticket is DONE.
- If not, copy
demo/pdf_utils.pytoservices/api/app/core/pdf_extractor.pyand ensure imports are corrected for the API structure.
Ticket 1.2: Implement Frontend PDF Upload UI
Context: The frontend needs a file input to send PDFs to the /v1/ingest/pdf endpoint.
File to Modify: accessible-word-craft-main/src/components/TranslationSection.tsx
Critical Environment Setup (Read carefully):
Since Docker is NOT yet set up, you must run the backend and frontend manually in two separate terminals.
-
Terminal 1 (Backend):
cd services/api source .venv/bin/activate # or source venv/bin/activate pip install pymupdf # Ensure PDF dependency is installed uvicorn app.main:app --reload --port 8000
Keep this terminal open.
-
Terminal 2 (Frontend):
cd accessible-word-craft-main npm run devKeep this terminal open.
Step 1: Add State & Handlers
Inside TranslationSection, add the following state and handler function before the return statement:
// [ADD] New state imports if missing
import { useState } from 'react';
// ... other imports
import { Loader2, FileText, Upload } from 'lucide-react'; // Add FileText, Upload icons
// [inside component]
const [isUploading, setIsUploading] = useState(false);
// [ADD] Handler function
const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
if (file.type !== 'application/pdf') {
toast({ title: "Error", description: "Only PDF files are supported.", variant: "destructive" });
return;
}
// Max 10MB
if (file.size > 10 * 1024 * 1024) {
toast({ title: "Error", description: "File is too large (max 10MB).", variant: "destructive" });
return;
}
setIsUploading(true);
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('http://localhost:8000/v1/ingest/pdf', {
method: 'POST',
body: formData,
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.detail || 'Upload failed');
}
const data = await response.json();
setInputText(data.extracted_text);
toast({ title: "Success", description: `Extracted ${data.pages} pages successfully!` });
} catch (error) {
console.error(error);
toast({ title: "Error", description: "Failed to extract text from PDF.", variant: "destructive" });
} finally {
setIsUploading(false);
// Reset input
event.target.value = '';
}
};Step 2: Add UI Element
Locate the "Original Text" card (Input area). Add the upload button just above or inside the card header.
{/* [ADD] File Upload Button near Input Text Area */}
<div className="flex justify-end mb-2">
<input
type="file"
accept=".pdf"
id="pdf-upload"
className="hidden"
onChange={handleFileUpload}
disabled={isUploading || isLoading}
/>
<label htmlFor="pdf-upload">
<Button
variant="secondary"
size="sm"
className="cursor-pointer"
asChild
disabled={isUploading || isLoading}
>
<span>
{isUploading ? <Loader2 className="w-4 h-4 mr-2 animate-spin" /> : <Upload className="w-4 h-4 mr-2" />}
{isUploading ? "Extracting..." : "Upload PDF"}
</span>
</Button>
</label>
</div>Refactor Verification:
- Ensure both Terminal 1 and Terminal 2 are running without errors.
- Open
http://localhost:5173. - Try uploading a real PDF.
- Verify text appears in the input box.