def presentTaskFilter(self, args: ReportTaskArgs) -> XMLContent: yield 'Showing data from ', execState = args.execState if execState is not ExecutionState.ALL: yield xhtml.b[execState.name.lower()], ' ' taskNames = args.task yield xhtml[', '].join(xhtml.b[name] for name in sorted(taskNames)) yield ' tasks' owners = args.owner if owners: yield ' owned by ' yield xhtml[', '].join(xhtml.b[name] for name in sorted(owners)) targets = args.target if targets: yield ' for ', pluralize('target', len(targets)), ' ' yield xhtml[', '].join(xhtml.b[name] for name in sorted(targets)) ctabove = args.ctabove ctbelow = args.ctbelow if ctabove and ctbelow: yield (' created between ', xhtml.b[formatTime(ctabove)], ' and ', xhtml.b[formatTime(ctbelow)]) elif ctabove: yield ' created after ', xhtml.b[formatTime(ctabove)] elif ctbelow: yield ' created before ', xhtml.b[formatTime(ctbelow)] yield '.'
def testScheduleWeeklyStartCorrection(sim): """Test weekly schedule for which the start time should be corrected to the next available selected day. """ configId = sim.createConfig().getId() def timeOnDay(day): return int(time.mktime((2007, 1, day, 13, 0, 0, 0, 1, 0))) # Create 3 schedules always for Wednesday: # 1st is dated 2007-01-01, should be corrected to 2007-01-03 # 2nd is dated 2007-01-03, should not changed # 3rd is dated 2007-01-05, should be corrected to 2007-01-10 scheduledTimes = [] for scheduledDay, correctedDay in (1, 3), (3, 3), (5, 10): startTime = timeOnDay(scheduledDay) schedId = 'WeeklyStartCorrection_%d' % scheduledDay scheduledTimes.append((schedId, startTime, timeOnDay(correctedDay))) sim.createSchedule(schedId, False, startTime, ScheduleRepeat.WEEKLY, days='0010000', configFactory=sharedConfigFactory(configId)) scheduled = sim.databases.scheduleDB.get(schedId) assert getScheduleStatus(sim.databases.configDB, scheduled) == 'ok' # Run simulation for 4 weeks. sim.wait(4 * secondsPerWeek, check=lambda: None) # Validate the results. assert set(sim.databases.jobDB) == set(sim.jobs) for schedId, scheduledTime, correctedTime in scheduledTimes: jobsFromSchedule = [ job for job in sim.jobs if job.getScheduledBy() == schedId ] assert 3 <= len(jobsFromSchedule) <= 4, jobsFromSchedule prevCreationTime = None for job in jobsFromSchedule: creationTime = job['timestamp'] if prevCreationTime is None: assert scheduledTime <= creationTime, '%s > %s' % \ ( formatTime(scheduledTime), formatTime(creationTime) ) assert creationTime == correctedTime, '%s != %s' % \ ( formatTime(creationTime), formatTime(correctedTime) ) else: assert creationTime - prevCreationTime == secondsPerWeek, \ 'There is not exactly 1 week between two job runs' prevCreationTime = creationTime
def abort(self, user: Optional[str] = None) -> Tuple[bool, str]: '''Attempts to abort this task. Returns a pair of a boolean and a message string. The boolean is True if this task was cancelled or marked for abortion and False if this task cannot be aborted. ''' whoAborted = ' by user%s at %s' % ( '' if user is None else f' "{user}"', formatTime(getTime()) ) if self.isRunning(): if 'abort' in self._properties: return False, 'was already being aborted' else: self._properties['abort'] = 'true' self._properties['summary'] = 'aborted' + whoAborted self._notify() return True, 'will be aborted shortly' elif self.isWaiting(): self.cancelled('cancelled' + whoAborted) return True, 'has been aborted' elif self.isWaitingForInspection(): self.__setState( ResultCode.ERROR, 'done', 'postponed inspection cancelled' + whoAborted ) return True, 'had its postponed inspection cancelled' else: return False, 'was not waiting or running'
def iterRows(self, proc: Processor) -> Iterator[Sequence[str]]: tasks = proc.tasks taskRunDB = proc.taskRunDB # Determine all keys that exist for the given task names. keys = sorted(union( taskRunDB.getKeys(taskName) for taskName in proc.args.task )) yield [ 'create date', 'create time', 'result' ] + keys for task in tasks: taskName = task.getName() taskKeys = taskRunDB.getKeys(taskName) # TODO: Which properties are useful to export? timestamp = formatTime(task.getJob().getCreateTime()) # Assuming format "2008-09-16 15:21" results = [timestamp[:10], timestamp[-5:], getTaskStatus(task)] for key in keys: if key in taskKeys: # TODO: Querying one run at a time is not really efficient. data = list(taskRunDB.getData( taskName, [ task.getLatestRun().getId() ], key )) if len(data) == 0: results.append('') else: assert len(data) == 1 value = data[0][1] results.append(str(value)) else: results.append('') yield results
def describeNextRun(schedule: Scheduled) -> XMLContent: '''Returns a description of when the schedule is next expected to run. ''' # A "once" schedule that is finished never runs again. if schedule.isDone(): return 'done' # Compute some useful predicates. repeat = schedule.repeat waiting = repeat in (ScheduleRepeat.CONTINUOUSLY, ScheduleRepeat.TRIGGERED) and schedule.isRunning() suspended = schedule.isSuspended() # Look for future start time. startTime = schedule.startTime if startTime != asap: return (('\u2265 ' if waiting or suspended else '= ') # ">=" or "=" + formatTime(startTime)) # Schedule should start ASAP; tell user why it hasn't started yet. if repeat is ScheduleRepeat.TRIGGERED and not schedule['trigger']: return 'not triggered' if waiting: return 'waiting for last run to finish' # Suspend is checked last to be consistent with getScheduleStatus(). if suspended: return 'suspended' # We are out of reasons. # This can happen between a schedule becoming ready to start and # the ScheduleManager actually starting it, but because that time # is really short, it is unlikely the user will see this value. return 'now'
def iterRows(self, **kwargs: Any) -> Iterator[XMLContent]: job: Job = kwargs['job'] for task in job.getTaskSequence(): yield row(class_=task.result)[ task.getName(), formatTime(task.startTime), cell(class_='rightalign')[formatDuration(task.getDuration())], taskSummary(task), task.result]
def iterRows(self, **kwargs: object) -> Iterator[XMLContent]: proc = cast(FabPage.Processor, kwargs['proc']) yield 'Up since', (formatTime(getBootTime())) dbVersion = proc.project.dbVersion yield 'Database version', ( dbVersion if parseVersion(dbVersion)[:2] == parseVersion(softFabVersion)[:2] else xhtml.span( style='color: red')[dbVersion + ' (database must be upgraded)'])
def presentHeader(self, **kwargs: object) -> XMLContent: proc = cast(ProcT, kwargs['proc']) ccURL = cast(str, kwargs['ccURL']) userName = proc.user.name return xhtml.div(class_='titlebar')[ xhtml.div(class_='title')[self.__title(proc)], xhtml.div(class_='info')[xhtml.a( href=ccURL + self.loginURL(**kwargs))['log in'] if userName is None else (createUserDetailsLink(userName).present( **kwargs), ' \u2013 ', xhtml.a(href=ccURL + logoutURL(proc.req))['log out']), xhtml.br, formatTime(getTime())], xhtml.div( class_='logo' )[xhtml.a(href=ccURL + 'About', title=f'SoftFab {VERSION}')[_logoIcon.present( **kwargs)]]]
def iterRows(self, **kwargs: object) -> Iterator[XMLContent]: proc = cast(TaskRunnerDetails_GET.Processor, kwargs['proc']) runner = proc.taskRunner if runner is None: # Note: This is also reachable through auto-refresh of older # renderings of the page. yield '-', ('Task Runner ', xhtml.b[proc.args.runnerId], ' does not exist (anymore).') return yield 'Description', runner.description yield 'Version', runner['runnerVersion'] yield 'Host', runner['host'] yield 'Token ID', runner.token.getId() targets = proc.project.getTargets() yield 'Targets', presentCapabilities(runner.capabilities & targets, taskRunnerResourceTypeName) yield 'Other capabilities', presentCapabilities( runner.capabilities - targets, taskRunnerResourceTypeName) lastSync = cast(Optional[int], runner['lastSync']) yield 'Time since last sync', formatDuration(lastSync) run = runner.getRun() yield 'Current job', ('-' if run is None else createJobLink( cast(str, run['jobId']))) yield 'Current task', createTaskLink(runner) yield 'Duration', formatDuration(run.getDuration() if run else None) status = getResourceStatus(runner) yield row(class_=status)['Status', status] yield 'Exit when idle', 'yes' if runner.shouldExit() else 'no' yield 'Suspended', 'yes' if runner.isSuspended() else 'no' label = 'Last suspended' if runner.isSuspended() else 'Last resumed' changedTime = runner.getChangedTime() if changedTime: changeDesc = 'by %s at %s' % (runner.getChangedUser() or 'unknown', formatTime(changedTime)) else: changeDesc = 'never' yield label, changeDesc
def generateBars() -> XMLContent: assert maxValue is not None # work around mypy issue 2608 for task, value in dataPoints: run = task.getLatestRun() if value is None: valueDescription = 'no value' barClass = 'graphbarnoval' height = graphHeight else: valueDescription = str(value) barClass = 'graphbar' # We cannot plot negative values, so clip to 0. height = max(value, 0) * graphHeight // maxValue url = createRunURL(run, 'data') yield xhtml.td( title='%s - %s' % (formatTime(run.getJob().getCreateTime()), valueDescription), onclick=f"document.location='{url}'")[xhtml.table( class_=barClass, style=f'width: {barWidth:d}px; height: {height:d}px')[ xhtml.tbody[xhtml.tr[xhtml.td]]]] yield xhtml.td(class_='raxis')[((str(mark * roundMarkValue), xhtml.br) for mark in range(numMarks, 0, -1))]
def presentCell(self, record: Record, **kwargs: object) -> XMLContent: key = self.keyDisplay assert key is not None return formatTime(cast(Optional[int], record[key]))
def timeValue(seconds: Optional[int]) -> str: return '' if seconds is None else formatTime(seconds)
def createLastJobLink(schedule: Scheduled) -> XML: return maybeLink(createJobsURL(schedule.getLastJobs()))[formatTime( schedule.lastRunTime)]
def externalize(self, value: Optional[int]) -> str: return formatTime(value)