Show the end date of the active sprint as subtitle of the source name, the same way the calendar source shows its configured date as subtitle.
Background
The source-name subtitle is currently driven entirely by parameters: Source.identifying_parameters lists parameter keys, and the frontend (identifyingParameterValues / formatParameterValue) reads and formats the source's configured parameter values. This works for the calendar source, whose date is a date parameter (identifying_parameters=["date"]).
For Jira with the time remaining metric, the end date is not a parameter — it is computed by the collector from the active sprint's endDate and turned into a day count. There is currently no mechanism to display a measured value as the source subtitle.
Approach
Store the active sprint(s) as measurement entities, with the sprint name, start date, and end date as attributes. Two pieces of information determine what to display as the subtitle:
- Which attribute to display — declared statically in the data model. Add an
identifying_attribute field to the Entity model (the end date), mirroring the existing measured_attribute field. Because entities are keyed per metric, this is automatically scoped to time_remaining only.
- Which entity to display it from — determined dynamically per measurement by the collector. Add an
identifying_entity field to the source measurement holding the key of the entity that drove the measurement value (the sprint with the earliest end date), rather than relying on the convention that e.g. the first entity is the value-driving one.
Work
-
Entity meta model (meta/entity.py): add identifying_attribute: str | None = None, with a validator (shaped like check_measured_attribute) asserting the referenced attribute exists on the entity. No type restriction is needed — the frontend formats dates specially and renders any other type as a string, matching how identifying_parameters already handles non-date parameters.
-
Source measurement model (collector/model/measurement.py): add identifying_entity: str | None = None to SourceMeasurement and include it in as_dict().
-
Jira data model (sources/jira.py): add a time_remaining entity for the active sprints, with identifying_attribute pointing at the end date, e.g.:
"time_remaining": Entity(
name="sprint",
attributes=[
EntityAttribute(name="Sprint name", url="url"),
EntityAttribute(name="Start date", type=EntityAttributeType.DATE),
EntityAttribute(name="End date", type=EntityAttributeType.DATE),
],
identifying_attribute="end_date",
),
-
Collector (JiraTimeRemaining):
- Emit one measurement entity per active sprint, with the sprint id as key and
sprint_name, start_date, and end_date attributes.
- Compute the measurement value from the minimum (earliest) end date of the active sprints instead of the first active sprint. Note that the
time_remaining metric is more-is-better, so the base TimeCollector.minimum() selects the latest date — the collector must override this to use the earliest end date.
- Set
identifying_entity to the key of the sprint with the earliest end date.
- Point
_landing_url at the earliest-ending sprint as well, instead of the first active sprint, so the link is consistent with the measured value and the subtitle.
-
Frontend: add an identifyingEntityValues(measurementSource, dataModelSource, metricType) companion to identifyingParameterValues that looks up the entity named by measurementSource.identifying_entity, reads the attribute named by dataModelSource.entities[metricType].identifying_attribute off it, and formats date/datetime attributes with dayjs(...).format("ll") — the same format used for the calendar source's date. When identifying_entity is absent (e.g. older measurements), render no entity-based subtitle. SourceStatus concatenates the parameter-based and entity-based identifying values into the same caption subtitle.
Out of scope
- Switching all time collectors to storing a timestamp instead of days left/to go.
Show the end date of the active sprint as subtitle of the source name, the same way the calendar source shows its configured date as subtitle.
Background
The source-name subtitle is currently driven entirely by parameters:
Source.identifying_parameterslists parameter keys, and the frontend (identifyingParameterValues/formatParameterValue) reads and formats the source's configured parameter values. This works for the calendar source, whose date is adateparameter (identifying_parameters=["date"]).For Jira with the time remaining metric, the end date is not a parameter — it is computed by the collector from the active sprint's
endDateand turned into a day count. There is currently no mechanism to display a measured value as the source subtitle.Approach
Store the active sprint(s) as measurement entities, with the sprint name, start date, and end date as attributes. Two pieces of information determine what to display as the subtitle:
identifying_attributefield to theEntitymodel (the end date), mirroring the existingmeasured_attributefield. Because entities are keyed per metric, this is automatically scoped totime_remainingonly.identifying_entityfield to the source measurement holding the key of the entity that drove the measurement value (the sprint with the earliest end date), rather than relying on the convention that e.g. the first entity is the value-driving one.Work
Entity meta model (
meta/entity.py): addidentifying_attribute: str | None = None, with a validator (shaped likecheck_measured_attribute) asserting the referenced attribute exists on the entity. No type restriction is needed — the frontend formats dates specially and renders any other type as a string, matching how identifying_parameters already handles non-date parameters.Source measurement model (
collector/model/measurement.py): addidentifying_entity: str | None = NonetoSourceMeasurementand include it inas_dict().Jira data model (
sources/jira.py): add atime_remainingentity for the active sprints, withidentifying_attributepointing at the end date, e.g.:Collector (
JiraTimeRemaining):sprint_name,start_date, andend_dateattributes.time_remainingmetric is more-is-better, so the baseTimeCollector.minimum()selects the latest date — the collector must override this to use the earliest end date.identifying_entityto the key of the sprint with the earliest end date._landing_urlat the earliest-ending sprint as well, instead of the first active sprint, so the link is consistent with the measured value and the subtitle.Frontend: add an
identifyingEntityValues(measurementSource, dataModelSource, metricType)companion toidentifyingParameterValuesthat looks up the entity named bymeasurementSource.identifying_entity, reads the attribute named bydataModelSource.entities[metricType].identifying_attributeoff it, and formatsdate/datetimeattributes withdayjs(...).format("ll")— the same format used for the calendar source's date. Whenidentifying_entityis absent (e.g. older measurements), render no entity-based subtitle.SourceStatusconcatenates the parameter-based and entity-based identifying values into the same caption subtitle.Out of scope