Refactor Tender Management System by Removing Scraping Functionality and Enhancing Tender API
- Removed all scraping-related functionality, including routes, handlers, services, and repository methods, to streamline the tender management system. - Updated the tender API to focus solely on tender management, enhancing endpoints for listing, retrieving, and updating tenders. - Improved Swagger documentation to reflect the removal of scraping endpoints and the addition of new tender-related features. - Ensured adherence to Clean Architecture principles throughout the refactoring process, maintaining a clear separation of concerns.
This commit is contained in:
+4
-228
@@ -128,10 +128,10 @@ type TenderSearchCriteria struct {
|
||||
MinEstimatedValue *float64 `json:"min_estimated_value,omitempty"`
|
||||
MaxEstimatedValue *float64 `json:"max_estimated_value,omitempty"`
|
||||
Currency string `json:"currency,omitempty"`
|
||||
DeadlineFrom *int64 `json:"deadline_from,omitempty"` // Unix milliseconds
|
||||
DeadlineTo *int64 `json:"deadline_to,omitempty"` // Unix milliseconds
|
||||
PublicationDateFrom *int64 `json:"publication_date_from,omitempty"` // Unix milliseconds
|
||||
PublicationDateTo *int64 `json:"publication_date_to,omitempty"` // Unix milliseconds
|
||||
DeadlineFrom *int64 `json:"deadline_from,omitempty"`
|
||||
DeadlineTo *int64 `json:"deadline_to,omitempty"`
|
||||
PublicationDateFrom *int64 `json:"publication_date_from,omitempty"`
|
||||
PublicationDateTo *int64 `json:"publication_date_to,omitempty"`
|
||||
Status []TenderStatus `json:"status,omitempty"`
|
||||
Source []TenderSource `json:"source,omitempty"`
|
||||
BuyerOrganizationID string `json:"buyer_organization_id,omitempty"`
|
||||
@@ -236,230 +236,6 @@ func (t *Tender) UpdateStatus(status TenderStatus) {
|
||||
t.UpdatedAt = time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
// AddProcessingError adds a processing error to metadata
|
||||
func (t *Tender) AddProcessingError(error string) {
|
||||
if t.ProcessingMetadata.ParsingErrors == nil {
|
||||
t.ProcessingMetadata.ParsingErrors = make([]string, 0)
|
||||
}
|
||||
t.ProcessingMetadata.ParsingErrors = append(t.ProcessingMetadata.ParsingErrors, error)
|
||||
t.UpdatedAt = time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
// AddValidationError adds a validation error to metadata
|
||||
func (t *Tender) AddValidationError(error string) {
|
||||
if t.ProcessingMetadata.ValidationErrors == nil {
|
||||
t.ProcessingMetadata.ValidationErrors = make([]string, 0)
|
||||
}
|
||||
t.ProcessingMetadata.ValidationErrors = append(t.ProcessingMetadata.ValidationErrors, error)
|
||||
t.UpdatedAt = time.Now().UnixMilli()
|
||||
}
|
||||
|
||||
// HasErrors returns true if the tender has processing or validation errors
|
||||
func (t *Tender) HasErrors() bool {
|
||||
return len(t.ProcessingMetadata.ParsingErrors) > 0 || len(t.ProcessingMetadata.ValidationErrors) > 0
|
||||
}
|
||||
|
||||
// GetAllErrors returns all processing and validation errors
|
||||
func (t *Tender) GetAllErrors() []string {
|
||||
var allErrors []string
|
||||
allErrors = append(allErrors, t.ProcessingMetadata.ParsingErrors...)
|
||||
allErrors = append(allErrors, t.ProcessingMetadata.ValidationErrors...)
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// ScrapingJobType represents the type of scraping job
|
||||
type ScrapingJobType string
|
||||
|
||||
const (
|
||||
ScrapingJobTypeTEDScraper ScrapingJobType = "ted_scraper"
|
||||
ScrapingJobTypeManual ScrapingJobType = "manual"
|
||||
ScrapingJobTypeAPI ScrapingJobType = "api"
|
||||
ScrapingJobTypeBulkImport ScrapingJobType = "bulk_import"
|
||||
)
|
||||
|
||||
// ScrapingJobStatus represents the status of a scraping job
|
||||
type ScrapingJobStatus string
|
||||
|
||||
const (
|
||||
ScrapingJobStatusPending ScrapingJobStatus = "pending"
|
||||
ScrapingJobStatusRunning ScrapingJobStatus = "running"
|
||||
ScrapingJobStatusCompleted ScrapingJobStatus = "completed"
|
||||
ScrapingJobStatusFailed ScrapingJobStatus = "failed"
|
||||
ScrapingJobStatusCancelled ScrapingJobStatus = "cancelled"
|
||||
)
|
||||
|
||||
// JobProgress represents the progress of a scraping job
|
||||
type JobProgress struct {
|
||||
ProcessedCount int `bson:"processed_count" json:"processed_count"`
|
||||
TotalCount int `bson:"total_count" json:"total_count"`
|
||||
SuccessCount int `bson:"success_count" json:"success_count"`
|
||||
ErrorCount int `bson:"error_count" json:"error_count"`
|
||||
Percentage float64 `bson:"percentage" json:"percentage"`
|
||||
CurrentStep string `bson:"current_step" json:"current_step"`
|
||||
}
|
||||
|
||||
// JobResults represents the results of a scraping job
|
||||
type JobResults struct {
|
||||
TotalProcessed int `bson:"total_processed" json:"total_processed"`
|
||||
TotalCreated int `bson:"total_created" json:"total_created"`
|
||||
TotalUpdated int `bson:"total_updated" json:"total_updated"`
|
||||
TotalSkipped int `bson:"total_skipped" json:"total_skipped"`
|
||||
TotalErrors int `bson:"total_errors" json:"total_errors"`
|
||||
ProcessingErrors []string `bson:"processing_errors,omitempty" json:"processing_errors,omitempty"`
|
||||
Summary string `bson:"summary,omitempty" json:"summary,omitempty"`
|
||||
}
|
||||
|
||||
// ScrapingJob represents a scraping job entity
|
||||
type ScrapingJob struct {
|
||||
ID string `bson:"_id,omitempty" json:"id"`
|
||||
Type ScrapingJobType `bson:"type" json:"type"`
|
||||
Status ScrapingJobStatus `bson:"status" json:"status"`
|
||||
StartedAt int64 `bson:"started_at" json:"started_at"` // Unix seconds
|
||||
CompletedAt *int64 `bson:"completed_at,omitempty" json:"completed_at,omitempty"` // Unix seconds
|
||||
Duration *int64 `bson:"duration,omitempty" json:"duration,omitempty"` // Duration in seconds
|
||||
Progress JobProgress `bson:"progress" json:"progress"`
|
||||
Results JobResults `bson:"results" json:"results"`
|
||||
Errors []string `bson:"errors,omitempty" json:"errors,omitempty"`
|
||||
Config map[string]interface{} `bson:"config,omitempty" json:"config,omitempty"`
|
||||
CreatedAt int64 `bson:"created_at" json:"created_at"` // Unix seconds
|
||||
UpdatedAt int64 `bson:"updated_at" json:"updated_at"` // Unix seconds
|
||||
}
|
||||
|
||||
// ScrapingState represents the state of the scraping process
|
||||
type ScrapingState struct {
|
||||
ID string `bson:"_id,omitempty" json:"id"`
|
||||
LastProcessedYear int `bson:"last_processed_year" json:"last_processed_year"`
|
||||
LastProcessedNumber int `bson:"last_processed_number" json:"last_processed_number"`
|
||||
LastRunAt int64 `bson:"last_run_at" json:"last_run_at"` // Unix seconds
|
||||
NextRunAt int64 `bson:"next_run_at" json:"next_run_at"` // Unix seconds
|
||||
IsRunning bool `bson:"is_running" json:"is_running"`
|
||||
CurrentJobID string `bson:"current_job_id,omitempty" json:"current_job_id,omitempty"`
|
||||
CreatedAt int64 `bson:"created_at" json:"created_at"` // Unix seconds
|
||||
UpdatedAt int64 `bson:"updated_at" json:"updated_at"` // Unix seconds
|
||||
}
|
||||
|
||||
// GetJobID returns the scraping job ID
|
||||
func (j *ScrapingJob) GetJobID() string {
|
||||
return j.ID
|
||||
}
|
||||
|
||||
// IsActive returns true if the job is actively running
|
||||
func (j *ScrapingJob) IsActive() bool {
|
||||
return j.Status == ScrapingJobStatusRunning || j.Status == ScrapingJobStatusPending
|
||||
}
|
||||
|
||||
// IsCompleted returns true if the job has completed (successfully or with failure)
|
||||
func (j *ScrapingJob) IsCompleted() bool {
|
||||
return j.Status == ScrapingJobStatusCompleted || j.Status == ScrapingJobStatusFailed || j.Status == ScrapingJobStatusCancelled
|
||||
}
|
||||
|
||||
// GetProgressPercentage calculates and returns the progress percentage
|
||||
func (j *ScrapingJob) GetProgressPercentage() float64 {
|
||||
if j.Progress.TotalCount == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(j.Progress.ProcessedCount) / float64(j.Progress.TotalCount) * 100
|
||||
}
|
||||
|
||||
// UpdateProgress updates the job progress
|
||||
func (j *ScrapingJob) UpdateProgress(processed, total, success, errors int, step string) {
|
||||
j.Progress.ProcessedCount = processed
|
||||
j.Progress.TotalCount = total
|
||||
j.Progress.SuccessCount = success
|
||||
j.Progress.ErrorCount = errors
|
||||
j.Progress.CurrentStep = step
|
||||
j.Progress.Percentage = j.GetProgressPercentage()
|
||||
j.UpdatedAt = time.Now().Unix()
|
||||
}
|
||||
|
||||
// AddError adds an error to the job
|
||||
func (j *ScrapingJob) AddError(error string) {
|
||||
if j.Errors == nil {
|
||||
j.Errors = make([]string, 0)
|
||||
}
|
||||
j.Errors = append(j.Errors, error)
|
||||
j.UpdatedAt = time.Now().Unix()
|
||||
}
|
||||
|
||||
// MarkCompleted marks the job as completed
|
||||
func (j *ScrapingJob) MarkCompleted(status ScrapingJobStatus, results JobResults) {
|
||||
j.Status = status
|
||||
j.Results = results
|
||||
completedAt := time.Now().Unix()
|
||||
j.CompletedAt = &completedAt
|
||||
if j.StartedAt > 0 {
|
||||
duration := completedAt - j.StartedAt
|
||||
j.Duration = &duration
|
||||
}
|
||||
j.UpdatedAt = completedAt
|
||||
}
|
||||
|
||||
// IsStateRunning returns true if the scraping state indicates a job is running
|
||||
func (s *ScrapingState) IsStateRunning() bool {
|
||||
return s.IsRunning
|
||||
}
|
||||
|
||||
// MarkAsRunning marks the state as running with a job ID
|
||||
func (s *ScrapingState) MarkAsRunning(jobID string) {
|
||||
s.IsRunning = true
|
||||
s.CurrentJobID = jobID
|
||||
s.LastRunAt = time.Now().Unix()
|
||||
s.UpdatedAt = time.Now().Unix()
|
||||
}
|
||||
|
||||
// MarkAsCompleted marks the state as completed
|
||||
func (s *ScrapingState) MarkAsCompleted(year, number int) {
|
||||
s.IsRunning = false
|
||||
s.CurrentJobID = ""
|
||||
s.LastProcessedYear = year
|
||||
s.LastProcessedNumber = number
|
||||
s.NextRunAt = time.Now().Add(4 * time.Hour).Unix() // Schedule next run in 4 hours
|
||||
s.UpdatedAt = time.Now().Unix()
|
||||
}
|
||||
|
||||
// CalculateMatchPercentage calculates the match percentage between tender classifications and company CPV codes
|
||||
func (t *Tender) CalculateMatchPercentage(companyCPVCodes []string) int {
|
||||
if len(companyCPVCodes) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
mainMatch := false
|
||||
additionalMatch := false
|
||||
|
||||
// Check if main classification matches any company CPV codes
|
||||
if t.MainClassification != "" {
|
||||
for _, companyCode := range companyCPVCodes {
|
||||
if companyCode == t.MainClassification {
|
||||
mainMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if any additional classification matches any company CPV codes
|
||||
if len(t.AdditionalClassifications) > 0 {
|
||||
for _, additionalCode := range t.AdditionalClassifications {
|
||||
for _, companyCode := range companyCPVCodes {
|
||||
if companyCode == additionalCode {
|
||||
additionalMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if additionalMatch {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate percentage based on matches
|
||||
if mainMatch && additionalMatch {
|
||||
return 100
|
||||
} else if mainMatch || additionalMatch {
|
||||
return 50
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetDaysUntilDeadline returns the number of days until the tender deadline
|
||||
func (t *Tender) GetDaysUntilDeadline() int {
|
||||
if t.TenderDeadline == 0 {
|
||||
|
||||
Reference in New Issue
Block a user