# Constituency Batch Import - Hierarchical Folder Structure

## Overview

The voter image batch import system now supports a hierarchical folder structure for processing multiple constituencies and booths:

```
/var/www/Constituency/              (Shared across all client subdomains)
├── Lawspet/
│   ├── 1/
│   │   ├── booth_info.png
│   │   ├── voters 48.png
│   │   └── voters 75.png
│   └── 2/
│       ├── booth_info.png
│       └── voters...
├── Orleanpet/
│   ├── 1/
│   └── 2/
└── [OtherConstituencies]/

/var/www/lawspet-vaithianathan.ps25.in/    (Client 1 subdomain)
/var/www/kathirkamam-vadivel.ps25.in/      (Client 2 subdomain)
```

**Multi-Tenant Architecture:** The `Constituency` folder is placed at the parent level (outside the project) and shared across all client subdomains. Each client (e.g., `lawspet-vaithianathan.ps25.in`, `kathirkamam-vadivel.ps25.in`) accesses the same centralized constituency data.

## API Endpoints

### 1. Process Single Booth

**Endpoint:** `POST /api/image-import/run`

**Description:** Process a specific booth within a constituency.

**Required Parameters:**
- `constituency` (string) - Name of the constituency folder (e.g., "Lawspet", "Orleanpet")
- `booth_number` (string|number) - Booth number/folder name (e.g., "1", "2")

**Optional Parameters:**
- `start_index` (integer, default: 0) - Zero-based index of first voter page to process
- `max_images` (integer, default: null) - Maximum number of voter page images to process
- `use_crops` (boolean, default: true) - Use cropped grid-based OCR
- `keep_crops` (boolean, default: false) - Keep cropped images after processing
- `reset_booth_voters` (boolean, default: true) - Delete existing voters before import
- `crop_options` (object) - Override default crop settings

**Example Request:**
```bash
curl -X POST http://localhost:8000/api/image-import/run \
  -H "Content-Type: application/json" \
  -d '{
    "constituency": "Lawspet",
    "booth_number": "1",
    "start_index": 0,
    "max_images": 10,
    "use_crops": true,
    "reset_booth_voters": true
  }'
```

**Example Response:**
```json
{
  "success": true,
  "message": "Batch import completed",
  "constituency": "Lawspet",
  "booth_number": "1",
  "directory": "/path/to/project/Constituency/Lawspet/1",
  "booth": {
    "booth_number": "123",
    "booth_address": "Community Hall, Main Street",
    "streets": ["MAIN STREET", "TEMPLE ROAD"]
  },
  "voters_files": ["voters 48.png", "voters 75.png"],
  "summary": {
    "total_imported": 60,
    "total_deleted": 2,
    "total_skipped": 0
  },
  "skipped_voters": [],
  "warning_data": []
}
```

### 2. Process All Booths in Constituency

**Endpoint:** `POST /api/image-import/run-constituency`

**Description:** Process all booth folders within a constituency sequentially.

**Required Parameters:**
- `constituency` (string) - Name of the constituency folder

**Optional Parameters:**
- Same as single booth endpoint (applied to all booths)

**Example Request:**
```bash
curl -X POST http://localhost:8000/api/image-import/run-constituency \
  -H "Content-Type: application/json" \
  -d '{
    "constituency": "Lawspet",
    "use_crops": true,
    "reset_booth_voters": true
  }'
```

**Example Response:**
```json
{
  "success": true,
  "message": "Constituency import completed",
  "constituency": "Lawspet",
  "booths_processed": 2,
  "booths_failed": 0,
  "total_summary": {
    "total_imported": 120,
    "total_deleted": 5,
    "total_skipped": 0
  },
  "booth_results": {
    "1": {
      "booth": {
        "booth_number": "123",
        "booth_address": "...",
        "streets": []
      },
      "summary": {
        "total_imported": 60,
        "total_deleted": 2,
        "total_skipped": 0
      }
    },
    "2": {
      "booth": {
        "booth_number": "124",
        "booth_address": "...",
        "streets": []
      },
      "summary": {
        "total_imported": 60,
        "total_deleted": 3,
        "total_skipped": 0
      }
    }
  },
  "errors": {}
}
```

## Error Handling

### Constituency Not Found (404)
```json
{
  "success": false,
  "message": "Constituency folder not found",
  "error": "Constituency 'InvalidName' does not exist under Constituency folder",
  "path_checked": "/path/to/project/Constituency/InvalidName"
}
```

### Booth Not Found (404)
```json
{
  "success": false,
  "message": "Booth folder not found",
  "error": "Booth '99' does not exist under Lawspet",
  "path_checked": "/path/to/project/Constituency/Lawspet/99"
}
```

### Missing Parameters (400)
```json
{
  "success": false,
  "message": "Constituency name is required",
  "error": "Please provide constituency parameter"
}
```

## Configuration

### Environment Variable

Add to your `.env` file:

```env
# Constituency folder path (shared across all client subdomains)
# Leave empty to use default: parent directory of the project
CONSTITUENCY_PATH=/var/www/Constituency
```

**Default Behavior:** If `CONSTITUENCY_PATH` is not set, the system defaults to `../Constituency` (parent directory of your project).

**Production Setup:**
```bash
# Create shared constituency folder
sudo mkdir -p /var/www/Constituency
sudo chown www-data:www-data /var/www/Constituency

# Set in .env for each client subdomain
CONSTITUENCY_PATH=/var/www/Constituency
```

## Folder Structure Requirements

### Constituency Folder
- Must be named `Constituency` (case-sensitive)
- Located at parent level (outside project, shared across all clients)
- Configurable via `CONSTITUENCY_PATH` environment variable

### Booth Folders
- Must be numeric (1, 2, 3, etc.)
- Contain voter page images with naming convention:
  - `booth_info.png` - Contains booth number, address, and street information
  - `voters [number].png` - Individual voter page images (e.g., `voters 48.png`, `voters 75.png`)

### Image Files
- Supported formats: `.jpg`, `.jpeg`, `.png`
- Files with `booth_info` in name are processed for booth metadata
- Files with `voter` in name are processed for voter data

## Processing Flow

1. **Validate Parameters**: Check constituency and booth_number provided
2. **Validate Paths**: Ensure constituency and booth folders exist
3. **Extract Booth Info**: Process `booth_info.png` to extract:
   - Booth number
   - Booth address
   - Street names
4. **Process Voter Pages**: For each `voters [n].png` file:
   - Crop image into individual voter cards (3 columns × 10 rows by default)
   - Extract OCR text from each card
   - Parse voter details (serial, name, relation, age, gender, etc.)
   - Detect deleted voters (marked with 'S' prefix or "DELETED" text)
   - Save or update voter records in database
5. **Return Results**: Summary of imported, deleted, and skipped voters

## Database Operations

### Booth Table
- Creates or updates booth record with extracted booth_number
- Updates booth_address if extracted
- Associates street IDs in `streets` JSON column

### Voter Table
- Creates new voter records with all extracted fields
- Updates existing voters if serial_number and booth_id match
- Deletes voters marked as deleted in images
- Optional: Purges all existing voters for booth if `reset_booth_voters=true`

## Advanced Configuration

### Crop Options
Override default grid detection parameters:

```json
{
  "constituency": "Lawspet",
  "booth_number": "1",
  "crop_options": {
    "cols": 3,
    "rows": 10,
    "x_offset": 0,
    "y_offset": 75,
    "bottom_offset": 100,
    "pad_x": 0,
    "pad_y": 6,
    "row_pad_y": {
      "10": 2
    },
    "row_extend_bottom": {
      "10": 50
    },
    "dynamic": false
  }
}
```

## Testing Examples

### Test Single Booth
```bash
# Process booth 1 in Lawspet constituency
php artisan tinker --execute="
\$request = new \Illuminate\Http\Request([
    'constituency' => 'Lawspet',
    'booth_number' => '1',
    'use_crops' => true,
    'reset_booth_voters' => true
]);
\$controller = new \App\Http\Controllers\VoterImageBatchController();
\$response = \$controller->run(\$request);
echo json_encode(\$response->getData(), JSON_PRETTY_PRINT);
"
```

### Test Full Constituency
```bash
# Process all booths in Lawspet
php artisan tinker --execute="
\$request = new \Illuminate\Http\Request([
    'constituency' => 'Lawspet',
    'use_crops' => true,
    'reset_booth_voters' => true
]);
\$controller = new \App\Http\Controllers\VoterImageBatchController();
\$response = \$controller->runConstituency(\$request);
echo json_encode(\$response->getData(), JSON_PRETTY_PRINT);
"
```

## Migration Notes

### Old Structure (Deprecated)
```
Output/
├── booth_info.png
├── voters 48.png
└── voters 75.png
```

### New Structure (Current)
```
Constituency/
└── [ConstituencyName]/
    └── [BoothNumber]/
        ├── booth_info.png
        └── voters [n].png
```

The old direct directory structure is no longer supported. All imports must now specify constituency and booth_number parameters.

## Multi-Tenant Deployment Guide

### Server Structure for Multiple Clients

```
/var/www/
├── Constituency/                          ← Shared data folder
│   ├── Lawspet/
│   │   ├── 1/
│   │   └── 2/
│   ├── Orleanpet/
│   │   ├── 1/
│   │   └── 2/
│   └── Kathirkamam/
│       └── 1/
├── lawspet-vaithianathan.ps25.in/        ← Client 1 (Vaithianathan)
│   ├── .env → CONSTITUENCY_PATH=/var/www/Constituency
│   └── [Laravel app files]
├── kathirkamam-vadivel.ps25.in/          ← Client 2 (Vadivel)
│   ├── .env → CONSTITUENCY_PATH=/var/www/Constituency
│   └── [Laravel app files]
└── orleanpet-kumar.ps25.in/              ← Client 3 (Kumar)
    ├── .env → CONSTITUENCY_PATH=/var/www/Constituency
    └── [Laravel app files]
```

### Deployment Steps

1. **Create Shared Constituency Folder**
```bash
# Create the shared folder at parent level
sudo mkdir -p /var/www/Constituency
sudo chown -R www-data:www-data /var/www/Constituency
sudo chmod -R 755 /var/www/Constituency
```

2. **Upload Constituency Data**
```bash
# Upload constituency folders with booth data
scp -r Constituency/* user@server:/var/www/Constituency/
```

3. **Configure Each Client Subdomain**

For each client (e.g., `lawspet-vaithianathan.ps25.in`):

```bash
# Clone/deploy Laravel app
cd /var/www/lawspet-vaithianathan.ps25.in

# Configure .env
echo "CONSTITUENCY_PATH=/var/www/Constituency" >> .env

# Set permissions
sudo chown -R www-data:www-data /var/www/lawspet-vaithianathan.ps25.in
```

4. **Nginx Configuration Example**

```nginx
server {
    listen 80;
    server_name lawspet-vaithianathan.ps25.in;
    root /var/www/lawspet-vaithianathan.ps25.in/public;

    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

# Repeat for other subdomains
server {
    listen 80;
    server_name kathirkamam-vadivel.ps25.in;
    root /var/www/kathirkamam-vadivel.ps25.in/public;
    # ... same config
}
```

5. **Test Configuration**

```bash
# Test from each client subdomain
curl -X POST https://lawspet-vaithianathan.ps25.in/api/image-import/run \
  -H "Content-Type: application/json" \
  -d '{"constituency": "Lawspet", "booth_number": "1"}'

curl -X POST https://kathirkamam-vadivel.ps25.in/api/image-import/run \
  -H "Content-Type: application/json" \
  -d '{"constituency": "Kathirkamam", "booth_number": "1"}'
```

### Benefits of This Architecture

✅ **Single Data Source** - All clients access the same constituency data  
✅ **Easy Updates** - Update constituency data once, affects all clients  
✅ **Reduced Storage** - No data duplication across client instances  
✅ **Centralized Management** - Manage all constituency data from one location  
✅ **Scalable** - Add new clients without duplicating constituency folders  

### Security Considerations

- Ensure `www-data` user has read access to `/var/www/Constituency`
- Use `chmod 755` for directories and `644` for files
- Keep constituency data outside web-accessible directories
- Regular backups of the shared Constituency folder

## Troubleshooting

### "Constituency folder not found"
- Verify the `Constituency` folder exists at project root
- Check constituency name spelling (case-sensitive)
- Ensure folder permissions allow read access

### "No booth folders found"
- Booth folders must be numeric (1, 2, 3, etc.)
- Non-numeric folders are ignored
- Check folder naming and permissions

### "No images found"
- Verify image files exist with `.png`, `.jpg`, or `.jpeg` extensions
- Check file naming contains "booth_info" or "voter"
- Ensure files are not hidden or have permission issues

### OCR Extraction Failures
- Ensure Tesseract OCR is installed: `brew install tesseract` (macOS)
- Check image quality and resolution
- Verify images are not corrupted
- Review logs for detailed error messages
