Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions controllers/issueObjects/fetchSigWeeklyRecap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import dbConnect from '../../lib/dbConnect';
import IssueObjectModel from '../../models/IssueObjectModel';

interface FollowUpSummary {
practice: string;
didHappen: boolean | null;
deliverableLink: string | null;
deliverableNotes: string | null;
reflections: { prompt: string; response: string }[];
}

interface PersonFollowUps {
person: string;
followUps: FollowUpSummary[];
}

export interface ProjectRecap {
projectName: string;
byPerson: PersonFollowUps[];
}

export const fetchSigWeeklyRecap = async (
sigName: string,
weekStart: Date
): Promise<ProjectRecap[]> => {
await dbConnect();

const weekEnd = new Date(weekStart);
weekEnd.setDate(weekEnd.getDate() + 7);

const issues = await IssueObjectModel.find({
sig: sigName,
date: { $gte: weekStart, $lt: weekEnd },
wasDeleted: { $ne: true },
}).sort({ project: 1 });

const projectMap = new Map<string, Map<string, FollowUpSummary[]>>();

for (const issue of issues) {
if (!projectMap.has(issue.project)) {
projectMap.set(issue.project, new Map());
}
const personMap = projectMap.get(issue.project)!;

for (const fu of issue.followUps) {
const person = fu.parsedPractice?.person || 'Unknown';
if (!personMap.has(person)) {
personMap.set(person, []);
}

const didHappen: boolean | null = fu.outcome?.didHappen ?? null;
const reflectionIndex = didHappen === true ? 1 : 0;
const reflectionArray = fu.outcome?.reflections?.[reflectionIndex] ?? [];

personMap.get(person)!.push({
practice: fu.parsedPractice?.practice || fu.practice,
didHappen,
deliverableLink: fu.outcome?.deliverableLink ?? null,
deliverableNotes: fu.outcome?.deliverableNotes ?? null,
reflections: reflectionArray
.filter((r: any) => r.response)
.map((r: any) => ({ prompt: r.prompt, response: r.response })),
});
}
}

return Array.from(projectMap.entries()).map(([projectName, personMap]) => ({
projectName,
byPerson: Array.from(personMap.entries()).map(([person, followUps]) => ({
person,
followUps,
})),
}));
};
12 changes: 12 additions & 0 deletions models/FollowUpObjectModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface FollowUpObjectStruct {
deliverableNotes: string | null;
reflection: [FollowUpReflection[], FollowUpReflection[]]; // false for didHappen corresponds to questions in to 0, true to 1
};
valueStatement?: string;
interventions?: string[];
}

interface ReflectionQuestion {
Expand Down Expand Up @@ -114,5 +116,15 @@ export const FollowUpObjectSchema = new mongoose.Schema({
outcome: {
type: PracticeOutcomeSchema,
required: true
},
valueStatement: {
type: String,
required: false,
default: null
},
interventions: {
type: [String],
required: false,
default: []
}
});
55 changes: 54 additions & 1 deletion pages/api/ai-draft/[id].ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import fs from 'fs';
import path from 'path';
import type { NextApiRequest, NextApiResponse } from 'next';
import OpenAI from 'openai';
import dbConnect from '../../../lib/dbConnect';
import CAPNoteModel from '../../../models/CAPNoteModel';
import IssueObjectModel from '../../../models/IssueObjectModel';
import PracticeGapObjectModel from '../../../models/PracticeGapObjectModel';

const PAPER_CONTEXT = fs.readFileSync(
path.join(process.cwd(), 'pages/api/ai-draft/papercontext.txt'),
'utf-8'
);

export interface AIDraftIssue {
title: string;
context: string[];
Expand Down Expand Up @@ -95,6 +102,10 @@ Cognitive: goal-setting, task analysis, planning, monitoring, reflection
Metacognitive: knowledge of cognition, regulation of cognition
Emotional/Motivational: emotional regulation, motivation

## Research Framework

${PAPER_CONTEXT}

## Plan Tags

Each plan item begins with one primary tag:
Expand Down Expand Up @@ -162,6 +173,7 @@ const buildUserMessage = (
coachReflections: string,
priorNotesText: string,
practiceGapsText: string,
followUpOutcomesText: string,
allPeople: { name: string; slack_id: string }[]
): string => {
let msg = `Generate CAP notes for the following meeting. Write in the same style as the examples — direct, specific, coach-voice.\n\n`;
Expand All @@ -181,6 +193,10 @@ const buildUserMessage = (
msg += `## Prior CAP Notes for This Team\n\n${priorNotesText}\n\n`;
}

if (followUpOutcomesText) {
msg += `## Follow-Up Outcomes From Last Meeting\n\n${followUpOutcomesText}\n\n`;
}

msg += `## Meeting Transcript\n\n${transcript}\n\n`;

if (coachReflections?.trim()) {
Expand Down Expand Up @@ -233,6 +249,41 @@ const formatPracticeGaps = (gaps: any[]): string => {
.join('\n');
};

const formatFollowUpOutcomes = (note: any): string => {
if (!note) return '';
const issues: any[] = note.currentIssues ?? [];
const lines: string[] = [];

for (const issue of issues) {
if (issue.wasDeleted || issue.wasMerged) continue;
for (const fu of issue.followUps ?? []) {
const didHappen: boolean | null = fu.outcome?.didHappen ?? null;
const deliverableNotes: string | null = fu.outcome?.deliverableNotes ?? null;
const reflectionIndex = didHappen === true ? 1 : 0;
const reflections: { prompt: string; response: string }[] = (
fu.outcome?.reflections?.[reflectionIndex] ?? []
).filter((r: any) => r.response?.trim());

if (didHappen === null && !reflections.length && !deliverableNotes) continue;

const person = fu.parsedPractice?.person || 'Unknown';
const practice = fu.parsedPractice?.practice || fu.practice || '(no practice text)';

lines.push(`**${issue.title}** — ${person}`);
lines.push(`Practice: ${practice}`);
lines.push(`Did it happen: ${didHappen === null ? 'not reported' : didHappen ? 'yes' : 'no'}`);
if (deliverableNotes) lines.push(`Notes: ${deliverableNotes}`);
for (const r of reflections) {
lines.push(`Q: ${r.prompt}`);
lines.push(`A: ${r.response}`);
}
lines.push('');
}
}

return lines.join('\n').trim();
};

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<AIDraftResponse>
Expand Down Expand Up @@ -285,7 +336,7 @@ export default async function handler(
_id: { $ne: capNote._id }
})
.sort({ date: -1 })
.limit(3)
.limit(6)
.populate({ path: 'currentIssues', model: IssueObjectModel });

if (priorNotes.length < 2) {
Expand All @@ -306,6 +357,7 @@ export default async function handler(

const priorNotesText = formatPriorNotes(priorNotes);
const practiceGapsText = formatPracticeGaps(activeGaps);
const followUpOutcomesText = formatFollowUpOutcomes(priorNotes[0] ?? null);

const initialUserMessage = buildUserMessage(
capNote.project,
Expand All @@ -314,6 +366,7 @@ export default async function handler(
coachReflections,
priorNotesText,
practiceGapsText,
followUpOutcomesText,
allPeople
);

Expand Down
Loading