def GenerateReport(start): outfile = open("./DssSessionReport.txt", 'w') scs = [1, 13, 5, 3, 6, 4, 10, 11, 6, 6, 6, 6, 5, 15] scss = " %s" * len(scs) hdrFormat = "\n\n\t " + scss dataFormat = "\n\t " + scss semesters = sorted(Semester.objects.all() , lambda x,y:cmp(x.semester,y.semester)) ta = TimeAccounting() for s in semesters: projects = sorted([p for p in s.project_set.all() if not p.complete] , lambda x,y:cmp(x.pcode, y.pcode)) if projects: outfile.write("\n\n") outfile.write("=" * 100) outfile.write("\n\nTrimester: %s\n" %s.semester) outfile.write("-" * 14) for p in projects: outfile.write("\n\n\t%s, %s, PI: %s, Rem/Tot: %.1f/%.1f" % \ (p.pcode , p.name[:50] , p.principal_investigator().last_name if p.principal_investigator() else "None" , ta.getTimeLeft(p) , ta.getProjectTotalTime(p))) outfile.write(hdrFormat % \ (ljust("", scs[0]) , ljust("name", scs[1]) , center("orgID", scs[2]) , center("sch", scs[3]) , center("obs", scs[4]) , ljust("type", scs[5]) , center("RA", scs[6]) , center("Dec", scs[7]) , center("rem", scs[8]) , center("tot", scs[9]) , rjust("min", scs[10]) , rjust("max", scs[11]) , rjust("btwn", scs[12]) , ljust("rcvrs", scs[13]))) sessions = sorted(p.sesshun_set.all() , lambda x,y:cmp(x.name, y.name)) for s in sessions: target = s.getTarget() outfile.write(dataFormat % \ (ljust(bl(s.status.complete), scs[0]) , ljust(s.name, scs[1]) , rjust(s.original_id, scs[2]) , center(scheduable(s), scs[3]) , center(s.observing_type.type[:6], scs[4]) , center(s.session_type.type[0], scs[5]) , ljust(target.get_horizontal(), scs[6]) , ljust(target.get_vertical(), scs[7]) , rjust(ta.getTimeLeft(s), scs[8]) , rjust(s.allotment.total_time, scs[9]) , rjust(s.min_duration, scs[10]) , rjust(s.max_duration, scs[11]) , rjust(s.time_between, scs[12]) , ljust("".join(s.rcvrs_specified()), scs[13]) )) if p != projects[-1]: outfile.write("\n\t" + ("-" * 96))
class DBReporter: """ This class is responsible for reporting on the state of the database. It provides statistical summarries, as well as alerts on any potential issues. Right now it is currently used to report on newly projects transferred from Carl's system, w/ a format similar to Carl's for cross checking purposes. Using the revision system, one can view how the old 'report' method used to try and replicate Carl's report exactly, but this is no longer used. """ def __init__(self, quiet = False, filename = None): self.ta = TimeAccounting() self.lines = "" self.quiet = quiet self.filename = filename def add(self, lines): if not self.quiet: print lines self.lines += lines def printLines(self): if not self.quiet: print self.lines if self.filename: f = file(self.filename, 'w') f.writelines(self.lines) f.close() def reportProjectSummaryBySemester(self, semester = None): if semester is None: projs = Project.objects.order_by("pcode").all() else: projs = Project.objects.filter(semester__semester = semester ).order_by("pcode") self.reportProjectSummary(projs) def reportProjectSummaryByPcode(self, pcodes): projs = [] for pcode in pcodes: ps = Project.objects.filter(pcode = pcode) projs.extend(ps) self.reportProjectSummary(projs) def reportProjectSummary(self, projs): # *** General Info *** # gather stats on projects - how many, how many of what type, total hrs .. numProjs = len(projs) totalProjHrs = sum([self.ta.getProjectTotalTime(p) for p in projs]) self.add("\n*** Projects ***\n") self.add("Total # of Projects: %d, Total Hrs: %5.2f\n\n" % (numProjs, totalProjHrs)) semesters = Semester.objects.all().order_by('semester') proj_types = Project_Type.objects.all() projSems = self.binProject(projs, semesters, "semester") self.printInfo(projSems, "Projects by Semester: ", "Semester") projTypes = self.binProject(projs, proj_types, "project_type") self.printInfo(projTypes, "Projects by Type: ", "Type") projThesis = self.binProject(projs, [True, False], "thesis") self.printInfo(projThesis, "Projects by Thesis: ", "Thesis") # project summary: for each project, how many sess, hrs, etc. self.add("\n") self.add("Project Summary (modeled from Carl's report): \n") header = ["Name", "#", "Hrs", "Original IDs"] cols = [10, 5, 6, 50] self.printData(header, cols, True) for p in projs: ss = p.sesshun_set.all() hrs = self.ta.getProjSessionsTotalTime(p) ssIds = ["%s" % s.original_id for s in ss] ssIds.sort() ssIdStrs = " ".join(ssIds) data = [p.pcode, str(len(ss)), "%5.2f" % hrs, ssIdStrs] self.printData(data, cols) self.add("\n") self.printLines() def binWindow(self, windows, bins, attrib): r = {} for bin in bins: binW = [w for w in windows if w.__getattribute__(attrib) == bin] r[str(bin)] = (len(binW), 0) # Hrs is N/A return r def binProject(self, projs, bins, attrib): r = {} for bin in bins: binProj = [p for p in projs if p.__getattribute__(attrib) == bin] binProjHrs = sum([self.ta.getProjectTotalTime(p) for p in binProj]) r[str(bin)] = (len(binProj), binProjHrs) return r def binSesshun(self, sess, bins, attribs, attribFnc = False): r = {} # attributes can be "attrib", or "attrib.attrib" parts = attribs.split(".") for bin in bins: if len(parts) == 1: attrib = parts[0] # for one attrib, we must support difference between a member # var and a method if attribFnc: binSess = [s for s in sess if s.__getattribute__(attrib)() == bin] else: binSess = [s for s in sess if s.__getattribute__(attrib) == bin] elif len(parts) == 2: # in this case, we don't support methods a1, a2 = parts binSess = [s for s in sess \ if s.__getattribute__(a1).__getattribute__(a2) == bin] else: raise "binSesshun only supports <= 2 attributes" binSessHrs = sum([s.allotment.total_time for s in binSess]) r[str(bin)] = (len(binSess), binSessHrs) return r def binSesshunNumTargets(self, sess): nums = {} nums['0'] = (len([s for s in sess if s.target_set.all() == []]) , 0) nums['1'] = (len([s for s in sess if len(s.target_set.all()) == 1]), 0) nums['>1'] = (len([s for s in sess if len(s.target_set.all()) > 1]) , 0) return nums def binSesshunNumCadences(self, ss): nums = {} nums['0'] = (len([s for s in ss if len(s.cadence_set.all()) == 0]), 0) nums['1'] = (len([s for s in ss if len(s.cadence_set.all()) == 1]), 0) nums['>1'] = (len([s for s in ss if len(s.cadence_set.all()) > 1]) , 0) return nums def printInfo(self, info, title, header): # the first col should have a width to accomodate the biggest thing keys = info.keys() keys.sort() maxKeys = max(keys, key=len) col1 = len(max([header, maxKeys], key=len)) cols = [col1, 5, 10] self.add("\n" + title + "\n") self.printData([header, "#", "Hrs"], cols, True) for k in keys: self.add(" ".join([k[0:cols[0]].rjust(cols[0]), \ str(info[k][0])[0:cols[1]].rjust(cols[1]), \ str(info[k][1])[0:cols[2]].rjust(cols[2])]) + "\n") def printData(self, data, cols, header = False): self.add(" ".join([h[0:c].rjust(c) for h, c in zip(data, cols)]) + "\n") if header: self.add(("-" * (sum(cols) + len(cols))) + "\n") def reportProjectDetails(self, projects, pcodes): self.add("Project Details for projects: %s\n" % (", ".join(pcodes))) projs = [p for p in projects if p.pcode in pcodes] for proj in projs: self.projectDetails(proj) def projectDetails(self, project): self.add("Project %s:\n" % project.pcode) for a in project.allotments.all(): self.add("Allotment: %s\n" % a) for s in project.sesshun_set.all(): self.add(" Session name : %s, vpkey : %d, hrs : %5.2f\n" % \ (s.name, s.original_id, s.allotment.total_time)) for p in s.period_set.all(): self.add(" Period %s for %5.2f Hrs\n" % (p.start, p.duration))