Skip to content

PDF Import Integration #61

@amfgrantpro

Description

@amfgrantpro

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:

  1. Open services/api/app/main.py.
  2. Search for @app.post("/v1/ingest/pdf").
  3. Confirm it imports from .core.pdf_extractor.
  4. Open services/api/app/core/pdf_extractor.py.
  5. Ensure the file is not empty and contains def extract_pdf_text(...).
  6. Action:
    • If these exist, this ticket is DONE.
    • If not, copy demo/pdf_utils.py to services/api/app/core/pdf_extractor.py and 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.

  1. 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.

  2. Terminal 2 (Frontend):

    cd accessible-word-craft-main
    npm run dev

    Keep 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:

  1. Ensure both Terminal 1 and Terminal 2 are running without errors.
  2. Open http://localhost:5173.
  3. Try uploading a real PDF.
  4. Verify text appears in the input box.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions