Package npsgd :: Module model_task
[hide private]
[frames] | no frames]

Source Code for Module npsgd.model_task

  1  # Author: Thomas Dimson [tdimson@gmail.com] 
  2  # Date:   January 2011 
  3  # For distribution details, see LICENSE 
  4  """Module containing the main superclass for all models.""" 
  5  import os 
  6  import sys 
  7  import uuid 
  8  import random 
  9  import string 
 10  import logging 
 11  import subprocess 
 12  from email_manager import Email 
 13  import shutil 
 14   
 15  from config import config 
16 17 -class LatexError(RuntimeError): pass
18 -class ModelTask(object):
19 """Abstract base class for all user-defined models. 20 21 Users should create models that inherit from this class. It contains _all_ information 22 specific to a given model including parameters, attachments, titles and methods for 23 producing the e-mails with results. 24 """ 25 26 abstractModel = "ModelTask" 27 28 #Every model short implement a subset of these 29 short_name = "unspecified_name" 30 subtitle = "Unspecified Subtitle" 31 attachments = [] 32
33 - def __init__(self, emailAddress, taskId, modelParameters={}, failureCount=0, visibleId=None):
34 self.emailAddress = emailAddress 35 self.taskId = taskId 36 self.failureCount = failureCount 37 self.modelParameters = [] 38 self.visibleId = visibleId 39 if self.visibleId == None: 40 self.visibleId = "".join(random.choice(string.letters + string.digits)\ 41 for i in xrange(8)) 42 43 self.workingDirectory = "/var/tmp/npsgd/%s" % str(uuid.uuid4()) 44 45 for k,v in modelParameters.iteritems(): 46 param = self.parameterType(k).fromDict(v) 47 setattr(self, param.name, param) 48 self.modelParameters.append(param) 49 50 #Recover ordering 51 parameterIndexes = dict((e.name, i) for (i,e) in enumerate(self.__class__.parameters)) 52 self.modelParameters.sort(key=lambda x: parameterIndexes[x.name])
53
54 - def createWorkingDirectory(self):
55 try: 56 os.makedirs(self.workingDirectory, 0777) 57 except OSError, e: 58 logging.warning(e)
59
60 - def parameterType(self, parameterName):
61 """Returns an empty version the parameter class for a given parameter name.""" 62 63 for pClass in self.__class__.parameters: 64 if parameterName == pClass.name: 65 return pClass 66 67 return None
68 69 @classmethod
70 - def fromDict(cls, dictionary):
71 emailAddress = dictionary["emailAddress"] 72 taskId = dictionary["taskId"] 73 visibleId = dictionary["visibleId"] 74 failureCount = dictionary["failureCount"] 75 76 return cls(emailAddress, taskId, failureCount=failureCount, 77 modelParameters=dictionary["modelParameters"], visibleId=visibleId)
78
79 - def asDict(self):
80 return { 81 "emailAddress" : self.emailAddress, 82 "taskId": self.taskId, 83 "visibleId": self.visibleId, 84 "failureCount": self.failureCount, 85 "modelName": self.__class__.short_name, 86 "modelFullName": self.__class__.full_name, 87 "modelVersion": self.__class__.version, 88 "modelParameters": dict((p.name, p.asDict()) for p in self.modelParameters) 89 }
90
91 - def latexBody(self):
92 """Returns the body of the LaTeX PDF used to generate result e-mails.""" 93 94 return "This is a test for %s" % self.emailAddress
95
96 - def latexParameterTable(self):
97 """Returns LaTeX markup with a table containing the values for all input parameters.""" 98 99 paramRows = "\\\\\n".join(p.asLatexRow() for p in self.modelParameters) 100 return """ 101 \\begin{centering} 102 \\begin{tabular*}{5in}{@{\\extracolsep{\\fill}} l l} 103 \\textbf{Parameter} & \\textbf{Value} \\\\ 104 \\hline 105 %s 106 \\end{tabular*} 107 \\end{centering}""" % paramRows
108
109 - def textParameterTable(self):
110 """Returns an ascii representation of all parameters (e.g. for the body of e-mails).""" 111 112 return "\n".join(p.asTextRow() for p in self.modelParameters)
113
114 - def getAttachments(self):
115 pdf = self.generatePDF() 116 117 attach = [('results.pdf', pdf)] 118 for attachment in self.__class__.attachments: 119 with open(os.path.join(self.workingDirectory, attachment)) as f: 120 attach.append((attachment, f.read())) 121 122 return attach
123
124 - def prepareGraphs(self):
125 """A step in the standard model run to prepare output graphs.""" 126 pass
127
128 - def prepareExecution(self):
129 """A step in the standard model run to prepare model execution.""" 130 pass
131
132 - def generatePDF(self):
133 """Generates a PDF using the LaTeX template, our model's LaTeX body and PDFLatex.""" 134 135 latex = config.latexResultTemplate.generate(model_results=self.latexBody(), task=self) 136 logging.info(latex) 137 138 texPath = os.path.join(self.workingDirectory, "test_task.tex") 139 pdfOutputPath = os.path.join(self.workingDirectory, "test_task.pdf") 140 141 with open(texPath, 'w') as f: 142 f.write(latex) 143 144 logging.info("Will run PDFLatex %d times", config.latexNumRuns) 145 for i in xrange(config.latexNumRuns): 146 logging.info("Calling PDFLatex (run %d) to generate pdf output", i+1) 147 retCode = subprocess.call([config.pdfLatexPath, "-halt-on-error", texPath], cwd=self.workingDirectory) 148 logging.info("PDFLatex terminated with error code %d", retCode) 149 150 if retCode != 0: 151 raise LatexError("Bad exit code from latex") 152 153 with open(pdfOutputPath, 'rb') as f: 154 pdf = f.read() 155 156 return pdf
157 158
159 - def failureEmail(self):
160 """Returns an e-mail object for notifying the user of a failure to execute this model.""" 161 return Email(self.emailAddress, 162 config.failureEmailSubject.generate(task=self), 163 config.failureEmailTemplate.generate(task=self))
164
165 - def resultsEmail(self, attachments):
166 """Returns an e-mail object for yielding a results e-mail for the user.""" 167 return Email(self.emailAddress, 168 config.resultsEmailSubject.generate(task=self), 169 config.resultsEmailBodyTemplate.generate(task=self), 170 attachments)
171
172 - def runModel(self):
173 """Performs model-specific steps for execution.""" 174 logging.warning("Called default run model - this should be overridden")
175
176 - def run(self):
177 """Runs the model with parameters, and returns results email object.""" 178 179 logging.info("Running default task for '%s'", self.emailAddress) 180 self.createWorkingDirectory() 181 try: 182 self.prepareExecution() 183 self.runModel() 184 self.prepareGraphs() 185 return self.resultsEmail(self.getAttachments()) 186 finally: 187 if os.path.exists(self.workingDirectory): 188 shutil.rmtree(self.workingDirectory)
189