Find a Tender API client updates

- Convert OCDS releases to types.Opportunity (internal/types); FetchOnce
  and FetchAll now return opportunities, built from each ocid's latest
  release
- Include each item's main classification as well as its additional ones
- Return ErrUnexpectedStatus for non-200 responses and
  ErrRateLimitExceeded for a repeated 429; close discarded bodies
- Stop fetchAll on a repeated next link and close each page's body once
  read rather than deferring in the loop
- Default a zero Since to 24 hours ago
- Merge date repair and releaseIDs into api.go
- Filter --planning/--opportunities/--awards on each procedure's current
  stage, using OCDS release tags; --opportunities previously matched a
  nonexistent "opportunity" tag and returned nothing
- Reject conflicting filters before fetching (cobra flag group)
This commit is contained in:
bwilliams committed 2026-10-09 17:56:55 +01:00
1 parent 69620e5f7d
commit 399eccf2de
8 files changed
+567 -86

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
package types
type ClassificationCode struct {
ID int64 `db:"id" json:"id"`
IsCPV bool `db:"is_cpv" json:"is_cpv"`
Code string `db:"code" json:"code"`
Classification string `db:"classification" json:"classification"`
}
+41
View File
@@ -0,0 +1,41 @@
package types
import (
"time"
)
type Opportunity struct {
UUID string `db:"uuid" json:"uuid"`
UUIDType string `db:"uuid_type" json:"uuid_type"`
Title string `db:"title" json:"title"`
Status string `db:"status" json:"status"`
Description string `db:"description" json:"description"`
BuyerName string `db:"buyer_name" json:"buyer_name"`
Classifications []ClassificationCode `db:"-" json:"classifications"`
BuyerId string `db:"buyer_id" json:"buyer_id"`
MainCategory string `db:"main_category" json:"main_category"`
ProcurementMethod string `db:"procurement_method" json:"procurement_method"`
MultiLot bool `db:"multi_lot" json:"multi_lot"`
SMESuitable bool `db:"sme_suitable" json:"sme_suitable"`
VCSESuitable bool `db:"vcse_suitable" json:"vcse_suitable"`
StartDate *time.Time `db:"start_date" json:"start_date"`
EndDate *time.Time `db:"end_date" json:"end_date"`
GrossValue float64 `db:"gross_value" json:"gross_value"`
HasDocuments bool `db:"has_documents" json:"has_documents"`
LatestReleaseDate time.Time `db:"latest_release_date" json:"latest_release_date"`
Releases []OpportunityRelease `db:"-" json:"releases"`
}
type OpportunityRelease struct {
UUID string `db:"uuid" json:"uuid"`
ReleaseID string `db:"release_id" json:"release_id"`
ReleaseDate time.Time `db:"release_date" json:"release_date"`
Tags []OpportunityReleaseTag `db:"-" json:"release_tags"`
JSON string `db:"data" json:"data"`
}
type OpportunityReleaseTag struct {
UUID string `db:"uuid" json:"uuid"`
ReleaseID string `db:"release_id" json:"release_id"`
Tag string `db:"tag" json:"tag"`
}