repomatic.metric_chart module

Draw an accumulated metric history as a standalone, themeable SVG.

Written by hand rather than through a plotting library: the output is committed, so a docs build never needs the dependency, and the file stays a few kilobytes of readable vector.

An SVG rather than a client-side canvas: GitHub strips <script> and <canvas> from rendered Markdown, so a scripted chart is invisible to every reader of the repository, while the third-party embeds these replace were images that rendered there. Committing it also drops the pinned CDN artifact and its subresource-integrity digest, which is the point of moving off a service that died without notice.

repomatic.metric_chart.CHART_MODES = ('absolute', 'relative')

Horizontal axes a chart can measure against.

absolute shares one calendar across every curve, answering when a project gathered its following. relative starts each curve at its own repository’s creation, which is the only origin they all share, so a project that took eight years to reach a figure another hit in two is read at a glance.

Kept separate from CHART_SCALES, which measures the vertical one: a comparison chart routinely wants both, and folding them into a single setting would make each pair of choices a new name.

repomatic.metric_chart.CHART_SCALES = ('linear', 'logarithmic')

Vertical axes a chart can measure against.

linear reads a difference, and is right whenever the series are the same size. logarithmic reads a rate, and is what puts a project of 57 stars on one chart with a peer of 25,000 without flattening it onto the axis: equal slopes mean equal growth in percentage terms, whatever the counts.

A count of zero has no logarithm, and every series carries one, since the day a repository was created is the only date its count is known exactly. So the bottom LOG_ZERO_BAND of the plot is kept linear, spanning nothing but the step from zero to one. The curve then leaves the axis where the first star landed rather than beginning in mid-air or being silently dropped.

repomatic.metric_chart.LABEL_CHAR_WIDTH = 7.6

Pixels a direct label’s average character occupies, for margin arithmetic.

Measured against the 13px semibold system-ui the labels are drawn in. An SVG carries no text metrics and this generator loads no font, so the width of a label can only be estimated: erring high costs a few pixels of plot, erring low clips the name off the edge of the chart.

repomatic.metric_chart.LOG_ZERO_BAND = 0.06

Fraction of a logarithmic plot’s height reserved for the zero-to-one step.

Small enough to read as a baseline rather than as a decade of its own, and large enough that a curve sitting at zero for years is visibly on the floor instead of indistinguishable from one at a count of one.

repomatic.metric_chart.MIN_LABEL_MARGIN = 168

Floor on the right margin, in pixels, whatever the labels measure.

Holds the plot’s proportions steady across the charts a project draws: a single-series chart would otherwise stretch nearly to the edge and read as a different shape from the comparison beside it.

repomatic.metric_chart.SERIES_PALETTE: tuple[tuple[str, str], ...] = (('#2a78d6', '#3987e5'), ('#eb6834', '#d95926'), ('#1baf7a', '#199e70'), ('#eda100', '#c98500'), ('#e87ba4', '#d55181'), ('#8250df', '#a371f7'), ('#0a7c8a', '#22b8cf'), ('#cf222e', '#ff7b72'), ('#5a7f10', '#8fc832'), ('#8a6240', '#c19a6b'), ('#57606a', '#9198a1'), ('#bf3989', '#e878b8'))

Light and dark hex pair per categorical slot, in fixed order.

Assigned positionally and never cycled: a chart declaring more series than there are slots raises rather than reusing a hue, since a repeated colour on a chart whose curves are told apart by colour is a defect the reader cannot see. Override any of them by name through [tool.repomatic.metrics] colors.

A few light-mode steps sit below 3:1 against a white surface. The direct label drawn at the end of every line is what answers that: identity is never colour alone.

class repomatic.metric_chart.ChartSpec(output, metric='stars', mode='absolute', only=(), scale='linear', title='')[source]

Bases: object

One chart a repository asked for.

output: Path

Where to write the rendered SVG.

metric: str = 'stars'

Which accruing metric to plot, from METRICS.

Defaults to the one that motivated the whole collector. Only a metric the store accrues can be charted: an attribute holds a single current value, which is a table cell rather than a curve.

mode: str = 'absolute'

Which of CHART_MODES measures the horizontal axis.

only: tuple[str, ...] = ()

Series to plot, in draw order. Every declared subject when empty.

scale: str = 'linear'

Which of CHART_SCALES measures the vertical axis.

title: str = ''

Accessible name for the chart, describing what it shows.

property logarithmic: bool

Whether the vertical axis measures by powers of ten.

property relative: bool

Whether the horizontal axis measures project age.

classmethod from_mapping(entry)[source]

Build a spec from one [[tool.repomatic.metrics.charts]] entry.

Parameters:

entry (Mapping[str, object]) – The entry as configuration parsed it.

Return type:

ChartSpec

Returns:

The corresponding spec.

Raises:

ValueError – When output is missing, the mode is unknown, or the named metric has no history to chart.

class repomatic.metric_chart.ChartData(points=<factory>, colors=<factory>)[source]

Bases: object

A chart’s plotted series, already grouped and ordered.

Holds what render_chart() draws, so the renderer never touches the store and stays testable against synthetic points.

points: dict[str, list[tuple[date, int]]]

One sorted list of (day, value) per series, in draw order.

colors: dict[str, tuple[str, str]]

Light and dark hex pair per series, keyed like points.

repomatic.metric_chart.css_class(name)[source]

Fold a series name into a CSS class fragment.

Parameters:

name (str) – A series name as the repository declared it.

Return type:

str

Returns:

The lowercased name with every unsafe run replaced by a dash.

repomatic.metric_chart.assign_colors(names, overrides=None)[source]

Give every series a light and dark hue.

Positional from SERIES_PALETTE in names order, so a chart’s first curve is always the first slot, with overrides winning by name. A hue is a property of the series rather than of the chart, which is what keeps a repository plotted on two charts recognizable across both.

Parameters:
Return type:

dict[str, tuple[str, str]]

Returns:

The light and dark pair of each name.

Raises:

ValueError – When more series need a slot than the palette holds, or when an override is not a light and dark pair.

repomatic.metric_chart.build_chart_data(grouped, spec, overrides=None)[source]

Select, order and colour the series one chart plots.

A forerunner rides along with the series it precedes rather than being selected on its own, and borrows that series’ hue instead of claiming a slot of its own.

Parameters:
Return type:

ChartData

Returns:

The chart’s points and colours.

Raises:

ValueError – When the chart plots nothing, when two series fold onto one CSS class, or when the palette runs out.

repomatic.metric_chart.render_chart(data, *, relative=False, logarithmic=False, title='', label='Stars', stamp=None)[source]

Draw the line chart as a standalone, themeable SVG.

Parameters:
  • data (ChartData) – The series to plot and their hues.

  • relative (bool) – Measure the horizontal axis from each repository’s own first point rather than from the calendar.

  • logarithmic (bool) – Measure the vertical axis by powers of ten, so series orders of magnitude apart stay legible on one chart. See CHART_SCALES for how the zero every series carries is placed.

  • title (str) – Accessible name for the chart. Derived from the metric and the mode when empty.

  • label (str) – What the vertical axis counts, from the plotted metric’s label.

  • stamp (str | None) – Sampling date shown in the caption, in YYYY-MM-DD form. Today (UTC) when None.

Return type:

str

Returns:

The complete SVG document.

repomatic.metric_chart.write_chart(grouped, spec, overrides=None, stamp=None)[source]

Render one chart and write it, leaving an unchanged file alone.

Parameters:
Return type:

bool

Returns:

True when the file content changed.

Raises:

ValueError – When the chart cannot be built (see build_chart_data()).