# Changelog of TED Contract Expiry Radar - Recompete Leads (`datagrit/ted-contract-expiry-radar`) Actor

- **URL**: https://apify.com/datagrit/ted-contract-expiry-radar/changelog.md
- **Full Actor documentation**: https://apify.com/datagrit/ted-contract-expiry-radar.md

## Changelog

### 0.6

- Fixed: per-lot fields (end dates, durations, titles, CPV codes, renewal terms, framework type) are now matched to each awarded lot by the lot identifier from TED's lot list (identifier-lot) instead of by position in the list of results. The two lists differ in order (notice 57849-2024: results 2-1-4-3) and in length (205546-2024: 48 results, 46 lots, 45 end dates); a list is used only when it has exactly one entry per lot, otherwise the fields stay empty.
- Fixed: lotCount is the number of lots in the notice, not the number of results.
- Fixed: TED allows at most 15,000 notices per query (page x limit). Queries above that are split into shorter date windows instead of failing the run with HTTP 400 after billing thousands of rows; a single day above the limit is read up to the limit and reported as INCOMPLETE in the run status.
- Changed: README and differentiator give figures from two complete live runs (0-1 month: 7,053 stated + 9,052 computed; 2-3 months: 7,465 + 12,064), the real request count and run time of the default window, and the 15,000-notice limit.

### 0.5

- Fixed: contract conclusion dates and contract titles are published per contract, not per lot. They are now used only when a notice has one contract or all contracts share the value; otherwise awardDate and contractTitle stay empty and no end date is estimated from a date that may belong to another contract.
- Added: the run status reports how many notices found by the duration search produced a row.
- Changed: README and field descriptions give the coverage measured on live data instead of a figure from 2023 notices.

### 0.4

- Fixed: a single end date, duration or conclusion date published for a multi-lot notice is no longer copied to every lot.
- Fixed: lots that repeat the same identifier with several winners get distinct ids, so no winner is dropped as a duplicate.
- Changed: queries are paginated up to 100,000 notices (TED pages further than the previous 10,000 cap); a cut query is reported in the run status.

### 0.3

- Changed: keywords are enforced per lot — a lot is returned only when a keyword appears in its title or description, or in the contract or notice title; new fields lotTitle and lotDescription.
- Fixed: the incumbent filter now matches the winner of each lot; the list of all winners of a notice is used only when a lot cannot be matched (previously lots won by other suppliers of the same notice passed the filter).
- Fixed: CPV code and CPV filter use the lot's own code (main-classification-lot); new field cpvScope.
- Added: status message reports how many lots had a stated, estimated or missing end date; the run fails when the stated-end-date field disappears or stops being filled.
- README: explains requests versus queries and that TED matches notices, not single lots.

### 0.2

- Renamed to `ted-contract-expiry-radar` (before publication) and rewrote the description around computed end dates.
- Requests to TED are paced at one per second (TED answers HTTP 429 after about 12 unpaced requests).
- Run state is saved on platform events, so a migrated run does not emit or charge twice.
- A run where TED returns awarded lots without any end date or duration now fails instead of reporting "no results".
- README: FAQ about coverage of estimated end dates, run time and source limits.

### 0.1

- Initial release: expiring EU contracts from TED award notices, one row per awarded lot, with stated or computed end dates.

Manual test inputs used before release:

- Typical: `{"queries": ["software"], "expiresBeforeMonths": 12, "maxItems": 20}`
- Edge: `{"countries": ["DE", "FR"], "cpvPrefixes": ["72"], "incumbents": ["siemens"], "minValue": 100000, "includeFrameworkAgreements": false, "expiresAfterMonths": 3, "expiresBeforeMonths": 9, "maxItems": 15}`
- No results: `{"queries": ["zzzxqvnonexistentterm"], "maxItems": 10}` returns one row with `found: false`
