mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
feat(supervisor): team-performance reads AgentSession first, Ozonetel as fallback
Phase 3 — wire the dashboard to the new metrics path without touching the frontend. getTeamPerformance now: 1. Fetches AgentSession rows for the given IST date (keyed by agent UUID) 2. For each agent: uses AgentSession data rendered as HH:MM:SS if a row exists, otherwise falls back to Ozonetel summaryReport 3. Returns timeBreakdownSource so the frontend can optionally show which source was used Frontend continues to parse the existing HH:MM:SS shape via parseTime() — no UI change needed. Historical dates without AgentSession rows still render via Ozonetel. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -274,6 +274,11 @@ export class SupervisorService implements OnModuleInit {
|
|||||||
);
|
);
|
||||||
const agents = agentData?.agents?.edges?.map((e: any) => e.node) ?? [];
|
const agents = agentData?.agents?.edges?.map((e: any) => e.node) ?? [];
|
||||||
|
|
||||||
|
// Fetch AgentSession rows for this date — the authoritative source
|
||||||
|
// for time breakdowns now that Phase 2 ingest is live. Keyed by
|
||||||
|
// agentId (UUID on platform) so we can match back by agent.id.
|
||||||
|
const sessionByAgentId = await this.fetchAgentSessionsByDate(date);
|
||||||
|
|
||||||
// Fetch CDR for the entire account for this date (one call, not per-agent)
|
// Fetch CDR for the entire account for this date (one call, not per-agent)
|
||||||
let allCdr: any[] = [];
|
let allCdr: any[] = [];
|
||||||
try {
|
try {
|
||||||
@@ -282,12 +287,23 @@ export class SupervisorService implements OnModuleInit {
|
|||||||
this.logger.warn(`Failed to fetch CDR for ${date}: ${err}`);
|
this.logger.warn(`Failed to fetch CDR for ${date}: ${err}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch Ozonetel time summary per agent + compute call metrics from CDR
|
// Merge AgentSession → timeBreakdown (Ozonetel shape for UI compat);
|
||||||
|
// fall back to Ozonetel summary when no session row exists.
|
||||||
const summaries = await Promise.all(
|
const summaries = await Promise.all(
|
||||||
agents.map(async (agent: any) => {
|
agents.map(async (agent: any) => {
|
||||||
if (!agent.ozonetelAgentId) return { ...agent, timeBreakdown: null, calls: null };
|
if (!agent.ozonetelAgentId) return { ...agent, timeBreakdown: null, calls: null };
|
||||||
try {
|
try {
|
||||||
const summary = await this.ozonetel.getAgentSummary(agent.ozonetelAgentId, date);
|
let timeBreakdown: any = null;
|
||||||
|
let source: 'AGENT_SESSION' | 'OZONETEL_SUMMARY' | 'NONE' = 'NONE';
|
||||||
|
|
||||||
|
const session = sessionByAgentId.get(agent.id);
|
||||||
|
if (session) {
|
||||||
|
timeBreakdown = this.sessionToTimeBreakdown(session);
|
||||||
|
source = 'AGENT_SESSION';
|
||||||
|
} else {
|
||||||
|
timeBreakdown = await this.ozonetel.getAgentSummary(agent.ozonetelAgentId, date);
|
||||||
|
if (timeBreakdown) source = 'OZONETEL_SUMMARY';
|
||||||
|
}
|
||||||
|
|
||||||
// Filter CDR to this agent
|
// Filter CDR to this agent
|
||||||
const agentCdr = allCdr.filter(
|
const agentCdr = allCdr.filter(
|
||||||
@@ -301,7 +317,8 @@ export class SupervisorService implements OnModuleInit {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...agent,
|
...agent,
|
||||||
timeBreakdown: summary,
|
timeBreakdown,
|
||||||
|
timeBreakdownSource: source,
|
||||||
calls: { total: totalCalls, inbound, outbound, answered, missed },
|
calls: { total: totalCalls, inbound, outbound, answered, missed },
|
||||||
};
|
};
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -323,6 +340,51 @@ export class SupervisorService implements OnModuleInit {
|
|||||||
return { date, agents: summaries, teamTotals };
|
return { date, agents: summaries, teamTotals };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pull AgentSession rows for the given IST date, keyed by agent UUID so
|
||||||
|
// getTeamPerformance can look them up per-agent.
|
||||||
|
private async fetchAgentSessionsByDate(date: string): Promise<Map<string, any>> {
|
||||||
|
const map = new Map<string, any>();
|
||||||
|
try {
|
||||||
|
const data = await this.platform.query<any>(
|
||||||
|
`{ agentSessions(first: 100, filter: { date: { eq: "${date}" } }) {
|
||||||
|
edges { node {
|
||||||
|
agentId loginDurationS busyTimeS idleTimeS pauseTimeS
|
||||||
|
wrapupTimeS dialTimeS avgHandlingTimeS source lastSyncedAt
|
||||||
|
} }
|
||||||
|
} }`,
|
||||||
|
);
|
||||||
|
const edges = data?.agentSessions?.edges ?? [];
|
||||||
|
for (const e of edges) {
|
||||||
|
if (e.node?.agentId) map.set(e.node.agentId, e.node);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.warn(`[PERF] Failed to fetch AgentSession rows for ${date}: ${err}`);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render AgentSession seconds in the HH:MM:SS shape the frontend expects
|
||||||
|
// (matches Ozonetel's summary so team-performance.tsx can parseTime() it
|
||||||
|
// without changing the page code).
|
||||||
|
private sessionToTimeBreakdown(session: any): any {
|
||||||
|
const hms = (sec: number | null | undefined): string => {
|
||||||
|
const s = Math.max(0, Math.round(sec ?? 0));
|
||||||
|
const h = Math.floor(s / 3600);
|
||||||
|
const m = Math.floor((s % 3600) / 60);
|
||||||
|
const r = s % 60;
|
||||||
|
return `${h}:${String(m).padStart(2, '0')}:${String(r).padStart(2, '0')}`;
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
totalLoginTime: hms(session.loginDurationS),
|
||||||
|
totalBusyTime: hms(session.busyTimeS),
|
||||||
|
totalIdleTime: hms(session.idleTimeS),
|
||||||
|
totalPauseTime: hms(session.pauseTimeS),
|
||||||
|
totalWrapupTime: hms(session.wrapupTimeS),
|
||||||
|
totalDialTime: hms(session.dialTimeS),
|
||||||
|
avgHandlingTime: hms(session.avgHandlingTimeS),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// --- Barge session management ---
|
// --- Barge session management ---
|
||||||
|
|
||||||
getBargeSession(agentId: string) {
|
getBargeSession(agentId: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user