// // ReportManager.m // Pennyworth Punch Clock // // Created by Chris Karr on 7/24/08. // Copyright 2008 Northwestern University. All rights reserved. // #import "ReportManager.h" #define GENERATED_FILE [NSString stringWithFormat:@"%@/ppc-generated.html", TMP_DIR] #define REPORT_FILE [NSString stringWithFormat:@"%@/ppc-report.xml", TMP_DIR] #define CSS_FILE [NSString stringWithFormat:@"%@/ppc.css", TMP_DIR] @implementation ReportManager - (NSArray *) getReports { NSArray * xsls = [[NSBundle mainBundle] pathsForResourcesOfType:@"xsl" inDirectory:nil]; NSMutableArray * reports = [NSMutableArray array]; for (NSString * xsl in xsls) { NSMutableDictionary * xslDict = [NSMutableDictionary dictionary]; [xslDict setValue:[[xsl lastPathComponent] stringByDeletingPathExtension] forKey:@"name"]; [xslDict setValue:xsl forKey:@"path"]; [xslDict setValue:[xsl pathExtension] forKey:@"kind"]; [reports addObject:xslDict]; } return reports; } - (IBAction) generateReport:(id) sender { [[NSFileManager defaultManager] createDirectoryAtPath:[GENERATED_FILE stringByDeletingLastPathComponent] attributes:nil]; [[NSFileManager defaultManager] copyPath:[[NSBundle mainBundle] pathForResource:@"ppc" ofType:@"css"] toPath:CSS_FILE handler:nil]; NSMutableDictionary * statistics = [NSMutableDictionary dictionary]; NSPredicate * sliceFilter = [[NSApp delegate] getSliceFilter]; NSXMLDocument * xml = [[NSXMLDocument alloc] initWithXMLString:@"" options:0 error:NULL]; NSXMLElement * root = [xml rootElement]; [root addAttribute:[NSXMLNode attributeWithName:@"generated" stringValue:[[NSDate date] description]]]; NSXMLElement * log = [NSXMLElement elementWithName:@"log"]; [root addChild:log]; NSMutableDictionary * streamsStatistics = [NSMutableDictionary dictionary]; for (NSManagedObject * stream in [streamsController arrangedObjects]) { NSXMLElement * streamElement = [NSXMLNode elementWithName:@"stream"]; [streamElement addAttribute:[NSXMLNode attributeWithName:@"name" stringValue:[stream valueForKey:@"name"]]]; NSData * colorData = [stream valueForKey:@"color"]; if (colorData != nil) { NSColor * color = [NSUnarchiver unarchiveObjectWithData:colorData]; [streamElement addAttribute:[NSXMLNode attributeWithName:@"color" stringValue:[color description]]]; } NSSet * slices = [stream mutableSetValueForKey:@"slices"]; NSMutableArray * matches = [NSMutableArray array]; for (NSManagedObject * slice in slices) { if (sliceFilter == nil) [matches addObject:slice]; else if ([sliceFilter evaluateWithObject:slice]) [matches addObject:slice]; } NSSortDescriptor * sort = [[NSSortDescriptor alloc] initWithKey:@"startDate" ascending:YES]; NSMutableDictionary * streamStatistics = [NSMutableDictionary dictionary]; for (NSManagedObject * slice in [matches sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]]) { NSInteger count = 0; CGFloat duration = 0; NSString * sliceName = [slice valueForKey:@"name"]; NSDate * sliceStart = [slice valueForKey:@"startDate"]; NSDate * sliceEnd = [slice valueForKey:@"endDate"]; NSMutableDictionary * sliceStatistics = [streamStatistics valueForKey:sliceName]; if (sliceStatistics == nil) { sliceStatistics = [NSMutableDictionary dictionary]; [sliceStatistics setValue:sliceName forKey:@"name"]; [streamStatistics setValue:sliceStatistics forKey:sliceName]; } else { count = [[sliceStatistics valueForKey:@"count"] integerValue]; duration = [[sliceStatistics valueForKey:@"duration"] floatValue]; } NSXMLElement * sliceElement = [NSXMLNode elementWithName:@"slice"]; [sliceElement addAttribute:[NSXMLNode attributeWithName:@"name" stringValue:sliceName]]; [sliceElement addAttribute:[NSXMLNode attributeWithName:@"startDate" stringValue:[sliceStart description]]]; if (sliceEnd != nil) [sliceElement addAttribute:[NSXMLNode attributeWithName:@"endDate" stringValue:[sliceEnd description]]]; else sliceEnd = [NSDate date]; /* statistics */ [sliceStatistics setValue:[NSNumber numberWithInteger:(count + 1)] forKey:@"count"]; [sliceStatistics setValue:[NSNumber numberWithFloat:(duration + [sliceEnd timeIntervalSinceDate:sliceStart])] forKey:@"duration"]; [streamElement addChild:sliceElement]; } [statistics setObject:streamStatistics forKey:[stream valueForKey:@"name"]]; [streamsStatistics setValue:streamStatistics forKey:[stream valueForKey:@"name"]]; [log addChild:streamElement]; } NSXMLElement * statsElement = [NSXMLElement elementWithName:@"statistics"]; NSSortDescriptor * duration = [[NSSortDescriptor alloc] initWithKey:@"duration" ascending:NO]; for (NSString * stream in [[streamsStatistics allKeys] sortedArrayUsingSelector:@selector(compare:)]) { NSXMLElement * streamElement = [NSXMLElement elementWithName:@"stream"]; [streamElement addAttribute:[NSXMLNode attributeWithName:@"name" stringValue:stream]]; NSDictionary * streamDict = [streamsStatistics valueForKey:stream]; for (NSDictionary * sliceDict in [[streamDict allValues] sortedArrayUsingDescriptors:[NSArray arrayWithObject:duration]]) { NSXMLElement * sliceElement = [NSXMLElement elementWithName:@"slice"]; [sliceElement addAttribute:[NSXMLNode attributeWithName:@"name" stringValue:[sliceDict valueForKey:@"name"]]]; [sliceElement addAttribute:[NSXMLNode attributeWithName:@"duration" stringValue:[NSString stringWithFormat:@"%.2f", [[sliceDict valueForKey:@"duration"] floatValue]]]]; [sliceElement addAttribute:[NSXMLNode attributeWithName:@"count" stringValue:[NSString stringWithFormat:@"%d", [[sliceDict valueForKey:@"count"] integerValue]]]]; [streamElement addChild:sliceElement]; } [statsElement addChild:streamElement]; } [root addChild:statsElement]; [[xml XMLData] writeToFile:REPORT_FILE atomically:YES]; [xml release]; NSDictionary * selection = [[reportsController selectedObjects] lastObject]; if ([[selection valueForKey:@"kind"] isEqual:@"xsl"]) { NSTask * xslTask = [NSTask launchedTaskWithLaunchPath:@"/usr/bin/xsltproc" arguments:[NSArray arrayWithObjects:@"--novalid", @"-o", GENERATED_FILE, [selection valueForKey:@"path"], REPORT_FILE, nil]]; [xslTask waitUntilExit]; } [[reportView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:GENERATED_FILE]]]; [reportWindow setTitle:[selection valueForKey:@"name"]]; [reportWindow makeKeyAndOrderFront:sender]; } @end