measureProjectIdeaCoverage function

ProjectIdeaCoverageReport measureProjectIdeaCoverage(
  1. ProjectIdea idea,
  2. Map<String, String> sources, {
  3. List<CoverageExclusion> exclusions = const [],
})

Measures the coverage of idea by the requirements sources.

Implementation

ProjectIdeaCoverageReport measureProjectIdeaCoverage(
  ProjectIdea idea,
  Map<String, String> sources, {
  List<CoverageExclusion> exclusions = const [],
}) {
  final citedBy = <String, List<String>>{};
  for (final r in sources.entries) {
    for (final id in citations(r.value)) {
      (citedBy[id] ??= []).add(r.key);
    }
  }
  final superseded = <String, String>{};
  for (final d in idea.items.where((i) => i.kind == 'decision')) {
    for (final m in _supersedes.allMatches(d.text)) {
      superseded[m.group(1)!] = d.id;
    }
  }
  final outOfScope = citations(idea.outOfScopeText);
  final excluded = {for (final e in exclusions) e.id: e};

  final direct = <String, CoverageEntry>{};
  for (final item in idea.items) {
    final by = citedBy[item.id];
    if (by != null) {
      direct[item.id] = CoverageEntry(
        item,
        CoverageClass.covered,
        by.join(', '),
      );
    } else if (item.area != null && citedBy[item.area] != null) {
      direct[item.id] = CoverageEntry(
        item,
        CoverageClass.area,
        '${item.area} cited by ${citedBy[item.area]!.join(', ')}',
      );
    }
  }

  final entries = <CoverageEntry>[];
  for (final item in idea.items) {
    final d = direct[item.id];
    if (d != null) {
      entries.add(d);
      continue;
    }
    if (item.kind == 'decision') {
      final carriers = [
        for (final c in direct.values)
          if (c.item.kind != 'decision' &&
              citations(c.item.text).contains(item.id))
            c.item.id,
      ];
      if (carriers.isNotEmpty) {
        entries.add(
          CoverageEntry(
            item,
            CoverageClass.via,
            'carried by ${carriers.join(', ')}',
          ),
        );
        continue;
      }
      final answered = [
        for (final m in _answers.allMatches(item.text))
          for (final q in citations(m.group(1)!))
            if (q.startsWith('BQ-') && citedBy[q] != null) q,
      ];
      if (answered.isNotEmpty) {
        entries.add(
          CoverageEntry(
            item,
            CoverageClass.via,
            'answers ${answered.join(', ')}, cited by '
            '${{for (final q in answered) ...citedBy[q]!}.join(', ')}',
          ),
        );
        continue;
      }
    }
    final e = excluded[item.id];
    if (e != null) {
      entries.add(
        CoverageEntry(
          item,
          CoverageClass.excluded,
          '${e.reason} (${e.record})',
        ),
      );
    } else if (item.kind == 'decision' && outOfScope.contains(item.id)) {
      entries.add(
        CoverageEntry(
          item,
          CoverageClass.excluded,
          'cited by the out-of-scope table (Project Idea ยง 6.2)',
        ),
      );
    } else if (superseded[item.id] != null) {
      entries.add(
        CoverageEntry(
          item,
          CoverageClass.excluded,
          'superseded by ${superseded[item.id]}',
        ),
      );
    } else {
      entries.add(CoverageEntry(item, CoverageClass.uncovered, ''));
    }
  }
  final known = {for (final i in idea.items) i.id};
  return ProjectIdeaCoverageReport(entries, sources.length, [
    for (final e in exclusions)
      if (!known.contains(e.id)) e.id,
  ]);
}