Skip to Content
📚 MyStoryFlow Docs — Your guide to preserving family stories

F013 - AI-Powered Reporting System

Objective

Build an intelligent reporting system that automatically generates comprehensive, personalized reports for manuscripts, combining AI analysis, visualizations, and actionable insights within the MyStoryFlow platform.

Quick Implementation

Using MyStoryFlow Components

  • PDF generation from @mystoryflow/pdf
  • Email service from @mystoryflow/email
  • Template engine from @mystoryflow/templates
  • Export service from @mystoryflow/export

New Requirements

  • AI-powered report generation
  • Dynamic content adaptation
  • Multi-format export capabilities
  • Scheduled report automation
  • Interactive report components

MVP Implementation

1. Report Generation Engine

// packages/reporting-engine/src/services/report-generator.ts import { AIService } from '@mystoryflow/manuscript-analysis' import { PDFGenerator } from '@mystoryflow/pdf' import { TemplateEngine } from '@mystoryflow/templates' import { Logger } from '@mystoryflow/logger' import { ChartGenerator } from '@mystoryflow/charts' export class ReportGenerationEngine { private aiService: AIService private pdfGenerator: PDFGenerator private templateEngine: TemplateEngine private chartGenerator: ChartGenerator private logger: Logger constructor() { this.aiService = new AIService() this.pdfGenerator = new PDFGenerator() this.templateEngine = new TemplateEngine() this.chartGenerator = new ChartGenerator() this.logger = new Logger('ReportGenerator') } async generateComprehensiveReport(params: { manuscriptId: string userId: string reportType: ReportType options: ReportOptions }): Promise<GeneratedReport> { const { manuscriptId, userId, reportType, options } = params // Gather all necessary data const reportData = await this.gatherReportData( manuscriptId, userId, reportType ) // Generate AI-powered executive summary const executiveSummary = await this.generateExecutiveSummary( reportData, options ) // Create report sections based on type const sections = await this.generateReportSections( reportType, reportData, options ) // Generate visualizations const visualizations = await this.generateVisualizations( reportData, reportType ) // Compile the report const compiledReport = await this.compileReport({ metadata: { manuscriptId, userId, reportType, generatedAt: new Date(), version: '2.0' }, executiveSummary, sections, visualizations, appendices: await this.generateAppendices(reportData, options) }) // Generate multiple formats const formats = await this.generateFormats( compiledReport, options.formats || ['pdf', 'html'] ) return { reportId: compiledReport.id, formats, metadata: compiledReport.metadata, shareableLink: await this.createShareableLink(compiledReport.id), expiresAt: this.calculateExpiration(options) } } private async generateExecutiveSummary( data: ReportData, options: ReportOptions ): Promise<ExecutiveSummary> { // Use AI to create a compelling summary const summaryPrompt = ` Create an executive summary for a manuscript analysis report: Manuscript: ${data.manuscript.title} Genre: ${data.manuscript.genre} Overall Score: ${data.analysis.overallScore} Key Strengths: ${JSON.stringify(data.analysis.strengths)} Key Weaknesses: ${JSON.stringify(data.analysis.weaknesses)} Market Position: ${data.marketAnalysis?.position} The summary should be: 1. Concise (200-300 words) 2. Highlight the most important findings 3. Include market potential 4. End with clear next steps 5. Written in ${options.tone || 'professional'} tone ` const aiSummary = await this.aiService.analyzeManuscript( summaryPrompt, 'report-summary', { maxTokens: 1000 } ) return { overview: aiSummary.summary, keyFindings: this.extractKeyFindings(data), scoreSnapshot: { overall: data.analysis.overallScore, dimensions: data.analysis.dimensionScores, improvement: data.analysis.improvementFromLast }, recommendations: this.prioritizeRecommendations( data.recommendations, 3 ), nextSteps: aiSummary.nextSteps } } private async generateReportSections( reportType: ReportType, data: ReportData, options: ReportOptions ): Promise<ReportSection[]> { const sections: ReportSection[] = [] // Define sections based on report type const sectionDefinitions = this.getReportSections(reportType) for (const definition of sectionDefinitions) { const section = await this.generateSection( definition, data, options ) sections.push(section) } // Add custom sections if requested if (options.customSections) { for (const customDef of options.customSections) { const customSection = await this.generateCustomSection( customDef, data ) sections.push(customSection) } } return sections } private async generateSection( definition: SectionDefinition, data: ReportData, options: ReportOptions ): Promise<ReportSection> { switch (definition.type) { case 'analysis-details': return this.generateAnalysisSection(data.analysis, options) case 'character-analysis': return this.generateCharacterSection( data.analysis.characterAnalysis, data.chapters ) case 'plot-structure': return this.generatePlotSection( data.analysis.plotAnalysis, data.visualizations.plotStructure ) case 'writing-style': return this.generateStyleSection( data.analysis.styleAnalysis, data.examples ) case 'market-analysis': return this.generateMarketSection( data.marketAnalysis, data.comparables ) case 'recommendations': return this.generateRecommendationsSection( data.recommendations, data.analysis.scores ) case 'improvement-tracking': return this.generateProgressSection( data.historicalAnalyses, data.improvements ) case 'coach-insights': return this.generateCoachSection( data.coachMatches, data.coachingRecommendations ) default: return this.generateGenericSection(definition, data) } } private async generateAnalysisSection( analysis: ManuscriptAnalysis, options: ReportOptions ): Promise<ReportSection> { // Create detailed analysis breakdown const content = await this.templateEngine.render('analysis-details', { scores: analysis.scores, strengths: analysis.strengths, weaknesses: analysis.weaknesses, insights: analysis.insights }) // Generate AI interpretation const interpretation = await this.generateAIInterpretation( analysis, options.depth || 'standard' ) return { title: 'Comprehensive Analysis', content, subsections: [ { title: 'Score Breakdown', content: this.formatScoreBreakdown(analysis.scores), visualization: 'radar-chart' }, { title: 'Detailed Findings', content: interpretation, highlights: this.extractHighlights(analysis) }, { title: 'Genre-Specific Evaluation', content: this.formatGenreAnalysis( analysis.genreSpecificAnalysis ) } ], callouts: this.generateCallouts(analysis), chartData: { type: 'multi-dimensional', data: this.prepareAnalysisChartData(analysis) } } } }

2. Dynamic Report Templates

// packages/reporting-engine/src/services/template-manager.ts export class DynamicTemplateManager { private templates: Map<string, ReportTemplate> private aiService: AIService async createAdaptiveTemplate( reportType: ReportType, userPreferences: UserPreferences, manuscriptData: ManuscriptData ): Promise<AdaptiveTemplate> { // Get base template const baseTemplate = this.templates.get(reportType)! // Analyze manuscript characteristics const characteristics = await this.analyzeManuscriptCharacteristics( manuscriptData ) // Adapt template based on data const adaptedSections = await this.adaptSections( baseTemplate.sections, characteristics, userPreferences ) // Customize styling const styling = this.customizeStyling( userPreferences.brandPreferences, manuscriptData.genre ) // Generate dynamic elements const dynamicElements = await this.generateDynamicElements( manuscriptData, characteristics ) return { ...baseTemplate, sections: adaptedSections, styling, dynamicElements, conditionalContent: this.defineConditionalContent( characteristics, userPreferences ) } } private async adaptSections( sections: TemplateSection[], characteristics: ManuscriptCharacteristics, preferences: UserPreferences ): Promise<AdaptedSection[]> { return Promise.all( sections.map(async section => { // Determine section relevance const relevance = this.calculateSectionRelevance( section, characteristics ) // Skip low-relevance sections if user prefers concise reports if (relevance < 0.3 && preferences.reportLength === 'concise') { return null } // Adapt content depth const depth = this.determineContentDepth( relevance, preferences.detailLevel, section.importance ) // Generate section-specific insights const insights = await this.generateSectionInsights( section.type, characteristics, depth ) return { ...section, relevance, depth, insights, emphasis: relevance > 0.8 ? 'high' : 'normal', visualizations: this.selectVisualizations( section.type, characteristics, preferences.visualPreference ) } }) ).then(sections => sections.filter(Boolean) as AdaptedSection[]) } async generateDynamicElements( manuscriptData: ManuscriptData, characteristics: ManuscriptCharacteristics ): Promise<DynamicElement[]> { const elements: DynamicElement[] = [] // Success probability gauge if (characteristics.hasHighPotential) { elements.push({ type: 'success-gauge', data: await this.generateSuccessGauge(manuscriptData), placement: 'executive-summary' }) } // Comparative timeline if (characteristics.hasMultipleVersions) { elements.push({ type: 'improvement-timeline', data: await this.generateImprovementTimeline(manuscriptData), placement: 'progress-section' }) } // Market positioning map if (characteristics.genreData.isCompetitive) { elements.push({ type: 'market-position-map', data: await this.generateMarketMap(manuscriptData), placement: 'market-analysis', interactive: true }) } // AI insight bubbles elements.push({ type: 'ai-insights', data: await this.generateAIInsightBubbles(manuscriptData), placement: 'throughout', triggers: 'hover' }) return elements } }

3. Automated Report Scheduling

// packages/reporting-engine/src/services/report-scheduler.ts import { CronService } from '@mystoryflow/scheduler' import { NotificationService } from '@mystoryflow/notifications' export class ReportScheduler { private cronService: CronService private notificationService: NotificationService private reportGenerator: ReportGenerationEngine async scheduleRecurringReport(params: { userId: string manuscriptId: string reportType: ReportType schedule: ReportSchedule deliveryOptions: DeliveryOptions }): Promise<ScheduledReport> { const { userId, manuscriptId, reportType, schedule, deliveryOptions } = params // Create cron expression const cronExpression = this.buildCronExpression(schedule) // Register scheduled job const jobId = await this.cronService.schedule({ name: `report-${reportType}-${manuscriptId}`, cron: cronExpression, task: async () => { await this.generateAndDeliverReport({ userId, manuscriptId, reportType, deliveryOptions }) }, metadata: { userId, manuscriptId, reportType } }) // Store schedule configuration const scheduled = await this.storeSchedule({ jobId, userId, manuscriptId, reportType, schedule, deliveryOptions, status: 'active' }) // Send confirmation await this.notificationService.send({ userId, type: 'report-scheduled', data: { reportType, schedule: this.formatScheduleDescription(schedule), firstDelivery: this.calculateNextDelivery(cronExpression) } }) return scheduled } private async generateAndDeliverReport(params: { userId: string manuscriptId: string reportType: ReportType deliveryOptions: DeliveryOptions }): Promise<void> { try { // Check if there are new changes since last report const hasChanges = await this.checkForChanges( params.manuscriptId, params.reportType ) if (!hasChanges && !params.deliveryOptions.sendEvenIfNoChanges) { this.logger.info('Skipping report - no changes detected') return } // Generate report const report = await this.reportGenerator.generateComprehensiveReport({ ...params, options: { ...params.deliveryOptions.reportOptions, automated: true, compareToLast: true } }) // Deliver based on preferences await this.deliverReport(report, params.deliveryOptions) // Track delivery await this.trackReportDelivery({ reportId: report.reportId, userId: params.userId, deliveryMethod: params.deliveryOptions.method, success: true }) } catch (error) { this.logger.error('Scheduled report generation failed', { error, params }) // Notify user of failure await this.notificationService.send({ userId: params.userId, type: 'report-generation-failed', data: { reportType: params.reportType, error: error.message, nextAttempt: this.calculateRetryTime() } }) } } private async deliverReport( report: GeneratedReport, options: DeliveryOptions ): Promise<void> { switch (options.method) { case 'email': await this.emailReport(report, options.email) break case 'dashboard': await this.saveReportToDashboard(report, options.dashboard) break case 'webhook': await this.sendReportWebhook(report, options.webhook) break case 'multiple': await Promise.all( options.methods!.map(method => this.deliverReport(report, { ...options, method }) ) ) break } } }

4. Interactive Report Components

// packages/reporting-engine/src/components/interactive-reports.ts export class InteractiveReportBuilder { async buildInteractiveReport( reportData: CompiledReport ): Promise<InteractiveReport> { // Create base HTML structure const htmlBase = await this.createResponsiveHTML(reportData) // Add interactive visualizations const interactiveCharts = await this.createInteractiveCharts( reportData.visualizations ) // Build drill-down capabilities const drillDownElements = this.createDrillDownElements( reportData.sections ) // Add AI chat interface const aiChatInterface = this.createAIChatInterface({ reportId: reportData.id, manuscriptId: reportData.metadata.manuscriptId, allowedQueries: ['clarification', 'deeper-analysis', 'examples'] }) // Create interactive recommendations const interactiveRecs = this.createInteractiveRecommendations( reportData.sections.find(s => s.type === 'recommendations') ) // Add progress tracking widgets const progressWidgets = await this.createProgressWidgets( reportData.metadata.manuscriptId ) return { html: this.combineElements({ base: htmlBase, charts: interactiveCharts, drillDown: drillDownElements, aiChat: aiChatInterface, recommendations: interactiveRecs, progress: progressWidgets }), scripts: this.generateInteractiveScripts(), styles: this.generateResponsiveStyles(), metadata: { ...reportData.metadata, interactive: true, features: [ 'drill-down', 'ai-chat', 'live-updates', 'export-sections' ] } } } private createInteractiveCharts( visualizations: Visualization[] ): InteractiveChart[] { return visualizations.map(viz => ({ id: viz.id, type: viz.type, data: viz.data, options: { ...viz.options, interactive: true, animations: true, tooltips: { custom: this.createCustomTooltips(viz.type) }, events: { onClick: 'handleChartClick', onHover: 'handleChartHover' } }, controls: this.createChartControls(viz.type) })) } private createAIChatInterface(config: AIChatConfig): HTMLElement { return { container: 'ai-chat-widget', initialPrompt: 'Ask me anything about this report...', endpoints: { chat: `/api/reports/${config.reportId}/chat`, context: `/api/manuscripts/${config.manuscriptId}/context` }, features: { voiceInput: true, suggestions: true, exportResponses: true }, styling: { position: 'fixed', theme: 'professional' } } } }

5. Export and Distribution API

// apps/story-analyzer/src/app/api/reports/generate/route.ts import { NextRequest, NextResponse } from 'next/server' import { ReportGenerationEngine } from '@mystoryflow/reporting-engine' import { withAuth } from '@mystoryflow/auth' import { trackEvent } from '@mystoryflow/analytics' export async function POST(req: NextRequest) { try { const session = await withAuth(req) if (!session) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const { manuscriptId, reportType, options = {}, schedule } = await req.json() // Validate report access const hasAccess = await validateReportAccess( session.user.id, manuscriptId, reportType ) if (!hasAccess) { return NextResponse.json( { error: 'Insufficient permissions for this report type' }, { status: 403 } ) } // Handle scheduled reports if (schedule) { const scheduler = new ReportScheduler() const scheduled = await scheduler.scheduleRecurringReport({ userId: session.user.id, manuscriptId, reportType, schedule, deliveryOptions: options.delivery || { method: 'email' } }) return NextResponse.json({ scheduled: true, scheduleId: scheduled.id, nextDelivery: scheduled.nextDelivery }) } // Generate immediate report const reportEngine = new ReportGenerationEngine() const report = await reportEngine.generateComprehensiveReport({ manuscriptId, userId: session.user.id, reportType, options: { ...options, userTier: session.user.subscriptionTier } }) // Track report generation await trackEvent({ userId: session.user.id, event: 'report_generated', properties: { reportType, manuscriptId, formats: report.formats.map(f => f.type), automated: false } }) return NextResponse.json({ report: { id: report.reportId, formats: report.formats, shareableLink: report.shareableLink, expiresAt: report.expiresAt }, downloadUrls: report.formats.reduce((urls, format) => ({ ...urls, [format.type]: format.url }), {}) }) } catch (error) { console.error('Report generation failed:', error) return NextResponse.json( { error: 'Failed to generate report' }, { status: 500 } ) } } // Download report endpoint export async function GET(req: NextRequest) { const session = await withAuth(req) if (!session) { return new Response('Unauthorized', { status: 401 }) } const { searchParams } = new URL(req.url) const reportId = searchParams.get('reportId') const format = searchParams.get('format') || 'pdf' try { // Validate access to report const hasAccess = await validateReportDownload( session.user.id, reportId! ) if (!hasAccess) { return new Response('Forbidden', { status: 403 }) } // Get report file const file = await getReportFile(reportId!, format) if (!file) { return new Response('Report not found', { status: 404 }) } // Set appropriate headers const headers = new Headers({ 'Content-Type': getContentType(format), 'Content-Disposition': `attachment; filename="report-${reportId}.${format}"`, 'Cache-Control': 'private, max-age=3600' }) return new Response(file.stream, { headers }) } catch (error) { console.error('Report download failed:', error) return new Response('Internal Server Error', { status: 500 }) } }

Database Schema

-- Generated reports CREATE TABLE reports.generated_reports ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), manuscript_id UUID REFERENCES analyzer.manuscripts(id), user_id UUID REFERENCES auth.users(id), report_type VARCHAR(50), metadata JSONB, sections JSONB, visualizations JSONB, formats JSONB, shareable_link TEXT, expires_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW(), INDEX idx_reports_manuscript (manuscript_id), INDEX idx_reports_user (user_id), INDEX idx_reports_type (report_type) ); -- Report schedules CREATE TABLE reports.scheduled_reports ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), job_id VARCHAR(100) UNIQUE, user_id UUID REFERENCES auth.users(id), manuscript_id UUID REFERENCES analyzer.manuscripts(id), report_type VARCHAR(50), schedule JSONB, delivery_options JSONB, status VARCHAR(50) DEFAULT 'active', last_generated TIMESTAMP, next_delivery TIMESTAMP, created_at TIMESTAMP DEFAULT NOW(), INDEX idx_schedules_user (user_id), INDEX idx_schedules_status (status) ); -- Report templates CREATE TABLE reports.templates ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), name VARCHAR(100), report_type VARCHAR(50), version VARCHAR(20), structure JSONB, styling JSONB, sections JSONB, is_default BOOLEAN DEFAULT false, created_at TIMESTAMP DEFAULT NOW(), UNIQUE(report_type, version), INDEX idx_templates_type (report_type) ); -- Report delivery tracking CREATE TABLE reports.delivery_tracking ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), report_id UUID REFERENCES reports.generated_reports(id), user_id UUID REFERENCES auth.users(id), delivery_method VARCHAR(50), delivery_status VARCHAR(50), delivered_at TIMESTAMP, opened_at TIMESTAMP, error_message TEXT, INDEX idx_delivery_report (report_id), INDEX idx_delivery_user (user_id) ); -- Interactive report sessions CREATE TABLE reports.interactive_sessions ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), report_id UUID REFERENCES reports.generated_reports(id), user_id UUID REFERENCES auth.users(id), session_data JSONB, interactions JSONB, ai_conversations JSONB, started_at TIMESTAMP DEFAULT NOW(), last_activity TIMESTAMP DEFAULT NOW(), INDEX idx_sessions_report (report_id), INDEX idx_sessions_user (user_id) );

MVP Acceptance Criteria

  • AI-powered report generation
  • Multiple report types support
  • Dynamic template adaptation
  • Multi-format export (PDF, HTML, DOCX)
  • Automated scheduling system
  • Interactive report components
  • Shareable report links
  • Email delivery integration
  • API endpoints

Post-MVP Enhancements

  • Custom report builder UI
  • Real-time collaborative reports
  • Video report summaries
  • Podcast-style audio reports
  • AR/VR report visualization
  • Report version control
  • Branded report templates
  • Multi-language reports

Implementation Time

  • Report Engine: 1.5 days
  • Template System: 1 day
  • Scheduling System: 1 day
  • Interactive Components: 1.5 days
  • Export & API: 1 day
  • Testing: 1 day
  • Total: 7 days

Dependencies

  • F007 - Manuscript Analysis (for report data)
  • F009 - Scoring System (for metrics)
  • F010 - Recommendations (for insights)
  • F012 - Advanced Analytics (for visualizations)
  • PDF generation infrastructure
  • Email service configuration

Next Steps

This completes the AI implementation documentation for the MyStoryFlow manuscript analyzer. The system now has comprehensive AI capabilities across analysis, scoring, recommendations, coach matching, analytics, and reporting.