Skip to content

Reporting ​

discendo-sdk/reporting is a data consumer module. It records and aggregates attendance marks, computes course progress percentages, and formats arbitrary collections into downloadable CSV files.


At a Glance ​

Importimport { ReportingService, toCsv, computeCompletionPercent } from 'discendo-sdk/reporting'
RepositoriesAttendanceRepository
LocatorsSessionLocator
Eventsreporting.attendanceRecorded
Permissionsreporting.recordAttendance (delegable), reporting.view
EnforcementOpt-in via constructor options { sessions, enforcement: { policy, repos } }

Attendance Tracking ​

ts
type AttendanceStatus = "present" | "absent" | "excused" | "late";

interface AttendanceRecord {
  sessionId: string; // The class meeting or occurrence ID
  userId: string;
  status: AttendanceStatus;
  recordedAt: Date;
  recordedBy?: string; // Actor ID who took roll call
}

interface AttendanceRepository {
  record(entry: AttendanceRecord): Promise<void>;
  listForSession(sessionId: string): Promise<AttendanceRecord[]>;
}

The SessionLocator Pattern ​

Because reporting cannot import scheduling directly (preserving the zero-cross-domain import rule), you provide a small locator that resolves session IDs to course sections:

ts
const sessionLocator: SessionLocator = {
  sectionOf: async (sessionId) => {
    const occurrence = await schedulingRepo.findOccurrence(sessionId);
    if (!occurrence) return null;
    const template = await schedulingRepo.findTemplate(occurrence.templateId);
    return template?.sectionId ?? null;
  },
  sessionsInSection: async (sectionId) => {
    const occurrences =
      await schedulingRepo.listOccurrencesForSection(sectionId);
    return occurrences.map((o) => o.id);
  },
};

ReportingService Operations ​

ts
const reporting = new ReportingService(attendanceRepo, {
  sessions: sessionLocator,
  enforcement: { policy, repos },
});

// 1. Record attendance marks
await reporting.recordAttendance(
  "session-oct14",
  [
    { userId: "u-alice", status: "present" },
    { userId: "u-bob", status: "late" },
  ],
  { actorId: "u-instructor" },
);

// 2. Fetch student attendance summary
const summary = await reporting.attendanceForStudent("sec-101", "u-alice", {
  actorId: "u-instructor",
});
// { present: 14, late: 1, excused: 0, absent: 1, attendanceRate: 0.9375 }

Course Completion Percentage ​

A pure calculation utility to determine student progress through course modules:

ts
import { computeCompletionPercent } from "discendo-sdk/reporting";

const percent = computeCompletionPercent({
  completedNodeIds: ["node-1", "node-2", "node-3"],
  totalRequiredNodeIds: ["node-1", "node-2", "node-3", "node-4"],
});

console.log(`${percent}% completed`); // 75%

CSV Export (toCsv) ​

Formats arrays of arbitrary objects into standard CSV text with RFC 4180 escaping:

ts
import { toCsv } from "discendo-sdk/reporting";

const csvString = toCsv(
  [
    { id: "101", name: "Alice Smith", grade: "92.5%", status: "Pass" },
    { id: "102", name: "Bob Jones, Jr.", grade: "84.0%", status: "Pass" },
  ],
  {
    columns: [
      { key: "id", header: "Student ID" },
      { key: "name", header: "Full Name" },
      { key: "grade", header: "Final Grade" },
      { key: "status", header: "Status" },
    ],
  },
);

Handles comma escaping, double quotes, and line breaks automatically.


Known Limitations ​

  • No historical attendance point-in-time state: Attendance marks represent the current mark recorded for a session.

Released under the GPL-3.0 License.