fafccd0d74
- Updated the worker initialization process to include the new GLM SDK, enhancing the worker's capabilities for translation tasks. - Modified the InitWorker function and NewNoticeWorker constructor to accept the GLM service, ensuring a cohesive integration. - Implemented the GLM service initialization and logging for successful setup, improving maintainability and usability. - Updated the NoticeWorker to utilize the GLM SDK for translating notice titles and descriptions, enhancing functionality and user experience.
359 lines
12 KiB
Go
359 lines
12 KiB
Go
package ted
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"tm/internal/notice"
|
|
)
|
|
|
|
// mapToTenderFromParsedDoc maps a parsed TED document to tender entity
|
|
func (s *TEDScraper) mapToTenderFromParsedDoc(parsedDoc *ParsedDocument, sourceFileURL, sourceFileName string) *notice.Notice {
|
|
switch parsedDoc.Type {
|
|
case DocumentTypeContractNotice:
|
|
if parsedDoc.ContractNotice == nil {
|
|
s.logger.Error("ContractNotice is nil", map[string]interface{}{
|
|
"document_type": string(parsedDoc.Type),
|
|
})
|
|
return nil
|
|
}
|
|
return s.mapToTender(parsedDoc.ContractNotice, sourceFileURL, sourceFileName)
|
|
|
|
case DocumentTypeContractAwardNotice:
|
|
if parsedDoc.ContractAwardNotice == nil {
|
|
s.logger.Error("ContractAwardNotice is nil", map[string]interface{}{
|
|
"document_type": string(parsedDoc.Type),
|
|
})
|
|
return nil
|
|
}
|
|
return s.mapContractAwardNoticeToTender(parsedDoc.ContractAwardNotice, sourceFileURL, sourceFileName)
|
|
|
|
case DocumentTypePriorInformationNotice:
|
|
if parsedDoc.PriorInformationNotice == nil {
|
|
s.logger.Error("PriorInformationNotice is nil", map[string]interface{}{
|
|
"document_type": string(parsedDoc.Type),
|
|
})
|
|
return nil
|
|
}
|
|
return s.mapGenericNoticeToTender(parsedDoc.PriorInformationNotice, sourceFileURL, sourceFileName)
|
|
default:
|
|
s.logger.Error("Unknown document type for mapping", map[string]interface{}{
|
|
"document_type": string(parsedDoc.Type),
|
|
})
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// mapGenericNoticeToTender maps any TED document type to tender entity using the common interface
|
|
func (s *TEDScraper) mapGenericNoticeToTender(doc TEDDocument, sourceFileURL, sourceFileName string) *notice.Notice {
|
|
if doc == nil {
|
|
s.logger.Error("Document is nil in mapGenericNoticeToTender", map[string]interface{}{})
|
|
return nil
|
|
}
|
|
|
|
now := time.Now().Unix()
|
|
|
|
t := ¬ice.Notice{
|
|
ContractNoticeID: doc.GetID(),
|
|
ContractFolderID: "", // Not available in generic interface
|
|
NoticeTypeCode: doc.GetNoticeType(),
|
|
NoticeSubTypeCode: "", // Not available in generic interface
|
|
NoticeLanguageCode: doc.GetLanguage(),
|
|
IssueDate: now, // TODO: Parse actual date from document
|
|
IssueTime: now, // TODO: Parse actual time from document
|
|
GazetteID: "", // Not available in generic interface
|
|
NoticePublicationID: "", // Not available in generic interface
|
|
PublicationDate: now, // TODO: Parse actual publication date
|
|
|
|
// Basic tender information
|
|
Title: "Generic Notice " + doc.GetNoticeType(),
|
|
Description: "Processed from " + doc.GetNoticeType() + " document",
|
|
Status: notice.TenderStatusActive,
|
|
Source: notice.TenderSourceTEDScraper,
|
|
SourceFileURL: sourceFileURL,
|
|
SourceFileName: sourceFileName,
|
|
ProcessingMetadata: notice.ProcessingMetadata{
|
|
ScrapedAt: now,
|
|
ProcessedAt: now,
|
|
ProcessingVersion: "1.0",
|
|
},
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
// mapToTender maps a TED ContractNotice to tender entity
|
|
func (s *TEDScraper) mapToTender(cn *ContractNotice, sourceFileURL, sourceFileName string) *notice.Notice {
|
|
now := time.Now().Unix()
|
|
|
|
t := ¬ice.Notice{
|
|
ContractNoticeID: cn.ID,
|
|
ContractFolderID: cn.ContractFolderID,
|
|
NoticeTypeCode: cn.GetNoticeSubType(),
|
|
NoticeSubTypeCode: cn.GetNoticeSubType(),
|
|
NoticeLanguageCode: cn.NoticeLanguageCode,
|
|
IssueDate: ParseDateToUnix(cn.IssueDate),
|
|
IssueTime: ParseTimeToUnix(cn.IssueTime),
|
|
GazetteID: cn.GetOJSID(),
|
|
Title: cn.GetProcurementTitle(),
|
|
Description: cn.GetProcurementDescription(),
|
|
ProcurementTypeCode: cn.GetContractNature(),
|
|
ProcedureCode: cn.GetProcedureCode(),
|
|
MainClassification: cn.GetMainClassification(),
|
|
PlaceOfPerformance: cn.GetPlaceOfPerformance(),
|
|
DocumentURI: cn.GetDocumentURI(),
|
|
Status: notice.TenderStatusActive,
|
|
Source: notice.TenderSourceTEDScraper,
|
|
SourceFileURL: sourceFileURL,
|
|
SourceFileName: sourceFileName,
|
|
ProcessingMetadata: notice.ProcessingMetadata{
|
|
ScrapedAt: now,
|
|
ProcessedAt: now,
|
|
ProcessingVersion: "1.0",
|
|
},
|
|
}
|
|
|
|
// Set publication information
|
|
if pubInfo := cn.GetPublicationInfo(); pubInfo != nil {
|
|
t.NoticePublicationID = pubInfo.NoticePublicationID
|
|
t.PublicationDate = ParseDateToUnix(pubInfo.PublicationDate)
|
|
t.TenderURL = GenerateTenderURL(pubInfo.NoticePublicationID)
|
|
}
|
|
|
|
// Set additional classifications
|
|
if cn.ProcurementProject != nil && cn.ProcurementProject.AdditionalCommodityClassification != nil {
|
|
for _, additionalClass := range cn.ProcurementProject.AdditionalCommodityClassification {
|
|
if additionalClass.ItemClassificationCode != "" {
|
|
t.AdditionalClassifications = append(t.AdditionalClassifications, additionalClass.ItemClassificationCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set estimated value and currency
|
|
if amount, currency := cn.GetBudget(); amount != "" {
|
|
if value, err := strconv.ParseFloat(amount, 64); err == nil {
|
|
t.EstimatedValue = value
|
|
t.Currency = currency
|
|
}
|
|
}
|
|
|
|
// Set duration information
|
|
if duration, unit := cn.GetDuration(); duration != "" {
|
|
t.Duration = duration
|
|
t.DurationUnit = unit
|
|
}
|
|
|
|
// Set tender deadline
|
|
if deadline := cn.GetTenderDeadline(); deadline != "" {
|
|
t.TenderDeadline = ParseDateToUnix(deadline)
|
|
|
|
if deadlineTime, err := ParseDate(deadline); err == nil {
|
|
applicationDeadline := CalculateApplicationDeadline(deadlineTime)
|
|
t.ApplicationDeadline = applicationDeadline.Unix()
|
|
}
|
|
}
|
|
|
|
if pubDate, err := ParseDate(cn.GetPublicationDate()); err == nil {
|
|
submissionDeadline := CalculateSubmissionDeadline(pubDate)
|
|
t.SubmissionDeadline = submissionDeadline.Unix()
|
|
}
|
|
|
|
// Set SubmissionURL from TenderingTerms
|
|
if cn.ProcurementProjectLot != nil && cn.ProcurementProjectLot.TenderingTerms != nil {
|
|
if tenderingTerms := cn.ProcurementProjectLot.TenderingTerms; tenderingTerms.TenderRecipientParty != nil {
|
|
if endpointID := tenderingTerms.TenderRecipientParty.EndpointID; endpointID != "" {
|
|
if strings.HasPrefix(endpointID, "http") {
|
|
t.SubmissionURL = endpointID
|
|
} else {
|
|
t.SubmissionURL = "https://" + endpointID
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set location information
|
|
if cn.ProcurementProject != nil && cn.ProcurementProject.RealizedLocation != nil {
|
|
if addr := cn.ProcurementProject.RealizedLocation.Address; addr != nil {
|
|
t.CityName = addr.CityName
|
|
t.PostalCode = addr.PostalZone
|
|
t.RegionCode = addr.CountrySubentityCode
|
|
if addr.Country != nil {
|
|
t.CountryCode = addr.Country.IdentificationCode
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set buyer organization
|
|
buyerID := cn.GetBuyerID()
|
|
if buyerID != "" {
|
|
if buyerOrg := cn.GetOrganizationByID(buyerID); buyerOrg != nil {
|
|
t.BuyerOrganization = s.mapOrganization(buyerOrg, "buyer")
|
|
}
|
|
}
|
|
|
|
// Set review organization
|
|
reviewID := cn.GetReviewOrganizationID()
|
|
if reviewID != "" {
|
|
if reviewOrg := cn.GetOrganizationByID(reviewID); reviewOrg != nil {
|
|
t.ReviewOrganization = s.mapOrganization(reviewOrg, "review")
|
|
}
|
|
}
|
|
|
|
// Set all organizations
|
|
if orgs := cn.GetOrganizations(); orgs != nil {
|
|
for _, org := range orgs.Organization {
|
|
mappedOrg := s.mapOrganization(&org, "")
|
|
if mappedOrg != nil {
|
|
t.Organizations = append(t.Organizations, *mappedOrg)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set selection criteria
|
|
if criteria := cn.GetSelectionCriteria(); criteria != nil {
|
|
for _, criterion := range criteria {
|
|
t.SelectionCriteria = append(t.SelectionCriteria, notice.SelectionCriterion{
|
|
TypeCode: criterion.CriterionTypeCode,
|
|
Description: criterion.Description,
|
|
LanguageID: criterion.LanguageID,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Set official languages
|
|
if languages := cn.GetOfficialLanguages(); languages != nil {
|
|
t.OfficialLanguages = languages
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
// mapContractAwardNoticeToTender maps a TED ContractAwardNotice to tender entity
|
|
func (s *TEDScraper) mapContractAwardNoticeToTender(can *ContractAwardNotice, sourceFileURL, sourceFileName string) *notice.Notice {
|
|
now := time.Now().Unix()
|
|
|
|
t := ¬ice.Notice{
|
|
ContractNoticeID: can.ID,
|
|
ContractFolderID: can.ContractFolderID,
|
|
NoticeTypeCode: can.NoticeTypeCode,
|
|
NoticeSubTypeCode: can.GetNoticeSubType(),
|
|
NoticeLanguageCode: can.NoticeLanguageCode,
|
|
IssueDate: ParseDateToUnix(can.IssueDate),
|
|
IssueTime: ParseTimeToUnix(can.IssueTime),
|
|
GazetteID: can.GetOJSID(),
|
|
Title: can.GetProcurementTitle(),
|
|
Description: can.GetProcurementDescription(),
|
|
ProcurementTypeCode: can.GetContractNature(),
|
|
ProcedureCode: can.GetProcedureCode(),
|
|
MainClassification: can.GetMainClassification(),
|
|
Status: notice.TenderStatusAwarded, // Award notices are for completed tenders
|
|
Source: notice.TenderSourceTEDScraper,
|
|
SourceFileURL: sourceFileURL,
|
|
SourceFileName: sourceFileName,
|
|
ProcessingMetadata: notice.ProcessingMetadata{
|
|
ScrapedAt: now,
|
|
ProcessedAt: now,
|
|
ProcessingVersion: "1.0",
|
|
},
|
|
}
|
|
|
|
// Set publication information
|
|
if pubInfo := can.GetPublicationInfo(); pubInfo != nil {
|
|
t.NoticePublicationID = pubInfo.NoticePublicationID
|
|
t.PublicationDate = ParseDateToUnix(pubInfo.PublicationDate)
|
|
t.TenderURL = GenerateTenderURL(pubInfo.NoticePublicationID)
|
|
}
|
|
|
|
// Set total award value
|
|
if amount, currency := can.GetTotalAwardValue(); amount != "" {
|
|
if parsedAmount, err := strconv.ParseFloat(amount, 64); err == nil {
|
|
t.EstimatedValue = parsedAmount
|
|
}
|
|
t.Currency = currency
|
|
}
|
|
|
|
// Set location information from main procurement project
|
|
if can.ProcurementProject != nil && can.ProcurementProject.RealizedLocation != nil {
|
|
if addr := can.ProcurementProject.RealizedLocation.Address; addr != nil {
|
|
t.CityName = addr.CityName
|
|
t.PostalCode = addr.PostalZone
|
|
t.RegionCode = addr.CountrySubentityCode
|
|
if addr.Country != nil {
|
|
t.CountryCode = addr.Country.IdentificationCode
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set buyer organization
|
|
buyerID := can.GetBuyerID()
|
|
if buyerID != "" {
|
|
if buyerOrg := can.GetOrganizationByID(buyerID); buyerOrg != nil {
|
|
t.BuyerOrganization = s.mapOrganization(buyerOrg, "buyer")
|
|
}
|
|
}
|
|
|
|
// Set all organizations
|
|
if orgs := can.GetOrganizations(); orgs != nil {
|
|
for _, org := range orgs.Organization {
|
|
mappedOrg := s.mapOrganization(&org, "")
|
|
if mappedOrg != nil {
|
|
t.Organizations = append(t.Organizations, *mappedOrg)
|
|
}
|
|
}
|
|
}
|
|
|
|
return t
|
|
}
|
|
|
|
// mapOrganization maps a TED Organization to tender Organization
|
|
func (s *TEDScraper) mapOrganization(tedOrg *Organization, role string) *notice.Organization {
|
|
if tedOrg == nil || tedOrg.Company == nil {
|
|
return nil
|
|
}
|
|
|
|
company := tedOrg.Company
|
|
org := ¬ice.Organization{
|
|
Role: role,
|
|
}
|
|
|
|
// Set organization ID and name
|
|
if company.PartyIdentification != nil {
|
|
org.ID = company.PartyIdentification.ID
|
|
}
|
|
if company.PartyName != nil {
|
|
org.Name = company.PartyName.Name
|
|
}
|
|
|
|
// Set company ID
|
|
if company.PartyLegalEntity != nil {
|
|
org.CompanyID = company.PartyLegalEntity.CompanyID
|
|
}
|
|
|
|
// Set website
|
|
org.WebsiteURI = company.WebsiteURI
|
|
|
|
// Set contact information
|
|
if company.Contact != nil {
|
|
org.ContactName = company.Contact.Name
|
|
org.ContactTelephone = company.Contact.Telephone
|
|
org.ContactEmail = company.Contact.ElectronicMail
|
|
org.ContactFax = company.Contact.Telefax
|
|
}
|
|
|
|
// Set address
|
|
if company.PostalAddress != nil {
|
|
org.Address = notice.Address{
|
|
StreetName: company.PostalAddress.StreetName,
|
|
CityName: company.PostalAddress.CityName,
|
|
PostalZone: company.PostalAddress.PostalZone,
|
|
CountrySubentityCode: company.PostalAddress.CountrySubentityCode,
|
|
Department: company.PostalAddress.Department,
|
|
}
|
|
if company.PostalAddress.Country != nil {
|
|
org.Address.CountryCode = company.PostalAddress.Country.IdentificationCode
|
|
}
|
|
}
|
|
|
|
return org
|
|
}
|