# TED XML Scraper A comprehensive TED (Tenders Electronic Daily) XML scraper that downloads and parses daily calendar XML files, converting them to tender entities and storing them in MongoDB. ## Features - **Daily Scheduled Scraping**: Runs automatically at 10:00 AM daily using cron jobs - **TED XML Format Support**: Handles eForms XML, Contract Notices, and TED Calendar formats - **XML Parser Integration**: Uses the custom `xmlparser` package with generic type support - **Robust Error Handling**: Comprehensive error handling, retry mechanisms, and logging - **Data Conversion**: Converts TED XML structures to internal tender entity models - **MongoDB Integration**: Stores processed data with proper indexing and validation - **Progress Tracking**: Job tracking, status monitoring, and statistics - **Configurable**: Environment variables and YAML configuration support ## Architecture ``` cmd/scraper/ ├── main.go # Application entry point ├── config.yaml # Default configuration ├── bootstrap/ │ ├── bootstrap.go # Application initialization │ └── config.go # Configuration structures └── README.md # This file internal/scraper/ ├── service.go # Core scraping service logic ├── repository.go # MongoDB operations for scraping jobs └── scheduler.go # Cron job scheduling pkg/xmlparser/ted/ ├── models.go # TED XML data structures └── converter.go # XML to entity conversion ``` ## Installation 1. **Prerequisites**: - Go 1.21+ - MongoDB 4.4+ 2. **Dependencies**: ```bash cd cmd/scraper go mod tidy ``` 3. **Configuration**: Create a `config.yaml` file or set environment variables: ```yaml database: mongodb: uri: "mongodb://localhost:27017" name: "tender_management" ted: base_url: "https://ted.europa.eu" timeout: 30s max_retries: 3 ``` ## Usage ### Running the Scraper ```bash # Run with default configuration go run main.go # Run with custom config file CONFIG_PATH=./custom-config.yaml go run main.go # Run with environment variables TED_BASE_URL=https://ted.europa.eu go run main.go ``` ### Environment Variables | Variable | Default | Description | |----------|---------|-------------| | `TED_BASE_URL` | `https://ted.europa.eu` | TED website base URL | | `TED_TIMEOUT` | `30s` | HTTP request timeout | | `TED_MAX_RETRIES` | `3` | Maximum retry attempts | | `TED_RETRY_DELAY` | `5s` | Delay between retries | | `TED_USER_AGENT` | `TM-TED-Scraper/1.0` | HTTP User-Agent header | | `TED_MAX_CONCURRENCY` | `5` | Maximum concurrent downloads | | `TED_DOWNLOAD_DIR` | `./downloads` | Download directory | | `TED_CLEANUP_AFTER` | `24h` | File cleanup interval | ### Manual Trigger To manually trigger a scraping job outside the schedule: ```bash # Using the scheduler API (if implemented) curl -X POST http://localhost:8080/api/scraper/trigger # Or restart the application to trigger immediate run ``` ## How It Works ### 1. Daily Calendar Download The scraper downloads TED daily packages using this URL pattern: ``` https://ted.europa.eu/packages/daily/{yyyynnnnn} ``` Where `{yyyynnnnn}` is the year + 5-digit day of year (e.g., `202500001` for January 1st, 2025). ### 2. XML Parsing The scraper supports multiple TED XML formats: - **Contract Notice**: Individual tender announcements - **Tender Calendar**: Daily collections of multiple notices - **eForms**: New European standard format - **Legacy TED Schema**: Older format support ### 3. Data Conversion XML data is converted to internal tender entities with: - **Field Mapping**: TED XML fields → Tender entity fields - **Data Validation**: Required field validation and sanitization - **Unix Timestamps**: All dates converted to Unix timestamps - **Text Normalization**: Clean up whitespace and formatting - **URL Generation**: Generate TED website URLs for notices ### 4. Storage Processed data is stored in MongoDB with: - **Indexes**: Optimized for search and filtering - **Deduplication**: Based on contract notice ID - **Audit Trail**: Creation and update timestamps - **Error Tracking**: Processing errors and warnings ## TED XML Format Analysis ### Contract Notice Structure ```xml 123456-2025 folder-123 2025-01-09+02:00 cn-standard ENG ``` ### Key XML Elements Mapped | XML Element | Tender Field | Description | |-------------|--------------|-------------| | `ID` | `contract_notice_id` | Unique notice identifier | | `ContractFolderID` | `contract_folder_id` | Contract folder reference | | `IssueDate` | `issue_date` | Notice publication date | | `ProcurementProject/Name` | `title` | Tender title | | `ProcurementProject/Description` | `description` | Tender description | | `ContractingParty` | `buyer_organization` | Buyer/contracting authority | | `TenderSubmissionDeadlinePeriod` | `tender_deadline` | Submission deadline | ## Scheduling ### Cron Schedule - **Main Job**: `0 0 10 * * *` (10:00 AM daily UTC) - **Cleanup Job**: `0 0 2 * * *` (2:00 AM daily UTC) ### Job Configuration ```go schedulerConfig := scraper.SchedulerConfig{ CronExpression: "0 0 10 * * *", // 10:00 AM daily (seconds, minutes, hours, day, month, weekday) Timezone: "UTC", JobTimeout: 2 * time.Hour, RetryAttempts: 3, RetryDelay: 30 * time.Minute, } ``` ### Retry Logic - **Automatic Retries**: Up to 3 attempts on failure - **Exponential Backoff**: 30-minute delay between retries - **Context Timeout**: 2-hour maximum per job - **Graceful Shutdown**: Waits for running jobs to complete ## Monitoring ### Job Status Track scraping job progress and status: ```json { "id": "job-123", "type": "ted_scraper", "status": "running", "progress": { "processed_count": 150, "total_count": 500, "success_count": 145, "error_count": 5, "percentage": 30.0, "current_step": "processing_tenders" } } ``` ### Statistics ```json { "period": "Last 7 days", "total_jobs": 7, "completed_jobs": 6, "failed_jobs": 1, "total_processed": 3500, "total_created": 2100, "total_updated": 1200, "total_errors": 200 } ``` ## Error Handling ### Error Types 1. **Download Errors**: Network issues, server errors 2. **Parsing Errors**: Invalid XML, schema mismatches 3. **Validation Errors**: Missing required fields 4. **Database Errors**: Connection issues, write failures ### Error Recovery - **Retry Mechanism**: Automatic retries with backoff - **Partial Success**: Continue processing valid notices - **Error Logging**: Detailed error tracking and reporting - **Graceful Degradation**: Skip problematic entries ### Example Error Response ```json { "job_id": "job-123", "status": "completed", "results": { "total_processed": 100, "total_created": 85, "total_errors": 15, "processing_errors": [ "notice 23: validation failed: title is required", "notice 45: failed to parse deadline: invalid format" ] } } ``` ## Best Practices ### Performance - **Batch Processing**: Process tenders in configurable batches (default: 10) - **Connection Pooling**: MongoDB connection management - **Memory Management**: Stream large XML files - **Concurrent Downloads**: Configurable concurrency limits ### Data Quality - **Validation**: Comprehensive field validation - **Sanitization**: Text cleaning and normalization - **Deduplication**: Prevent duplicate tender entries - **Audit Trail**: Track all changes and updates ### Reliability - **Health Checks**: Monitor service health - **Graceful Shutdown**: Clean exit on signals - **Resource Cleanup**: Automatic file cleanup - **State Persistence**: Track scraping state across restarts ## Development ### Adding New XML Formats 1. **Extend TED Models**: Add new XML structures in `pkg/xmlparser/ted/models.go` 2. **Update Converter**: Add conversion logic in `pkg/xmlparser/ted/converter.go` 3. **Add Parser Support**: Register new parsers in service 4. **Test Thoroughly**: Add comprehensive test cases ### Custom Validation ```go // Add custom validators in converter.go func (c *Converter) ValidateCustomField(value string) error { if len(value) > 1000 { return fmt.Errorf("field too long: %d characters", len(value)) } return nil } ``` ### Extending Scheduling ```go // Add new scheduled jobs in scheduler.go _, err = s.cron.AddFunc("0 */6 * * *", s.runStatisticsJob) // Every 6 hours ``` ## Troubleshooting ### Common Issues 1. **Connection Errors**: Check MongoDB connection and credentials 2. **Parsing Failures**: Verify TED XML format changes 3. **Schedule Issues**: Check timezone and cron expression 4. **Memory Usage**: Monitor for large XML files ### Debug Mode Enable debug logging: ```yaml logging: level: "debug" ``` ### Manual Testing ```bash # Test XML parsing go run main.go --test-xml ./sample.xml # Test specific date go run main.go --test-date 2025-01-09 # Validate configuration go run main.go --validate-config ``` ## License This project is part of the Tender Management system and follows the same licensing terms.