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.