def set_progress(state, message, rusage, context, **kw): jobtool = getUtility(IJobUtility) if '_jobid' in kw: # TODO: should we do some security check here? # e.g. only admin and user who owns the job can update it? # TODO: jobid may not exist job = jobtool.get_job_by_id(kw['_jobid']) else: jt = IJobTracker(kw['_context']) job = jt.get_job() jobtool.set_progress(job, state, message, rusage) if state in ('COMPLETED', 'FAILED'): jobtool.set_state(job, state) LOG.info("Plone: Update job state %s", state) # FIXME: we sholud probably send emails in another place (or as additional task in chain?) # there are too many things that can go wrong here and this task is not allowed to # fail (throw an exception) otherwise the user will never see a status update # FIXME: should be a better check here, we want to send email only # for experiment results, not for dataset imports (i.e. ala) try: if 'experiment' in context: # check if this is the first or last result jt = IExperimentJobTracker(kw['_context'].__parent__) completed = [st for st in jt.states if st[1] in ('COMPLETED', 'FAILED')] first = len(completed) == 1 last = jt.state in ('COMPLETED', 'FAILED', 'FINISHED') if first or last: # send email fullname = context['user']['fullname'] email_addr = context['user']['email'] experiment_name = context['experiment']['title'] experiment_url = context['experiment']['url'] success = (job.state == 'COMPLETED') if fullname and email_addr and experiment_name and experiment_url: send_mail(fullname, email_addr, experiment_name, experiment_url, success) else: LOG.warn("Not sending email. Invalid parameters") except Exception as e: LOG.error( 'Got an exception in plone.set_progress while trying to send an email: %s', e, exc_info=True) else: jobtool.set_state(job, state) LOG.info("Plone: Update job state RUNNING") if not '_jobid' in kw: kw['_context'].reindexObject() # TODO: reindex job state only? # Compute the experiement run time if all its jobs are completed # The experiment is the parent job jt = IExperimentJobTracker(kw['_context'].__parent__, None) if jt and jt.state in ('COMPLETED', 'FAILED'): exp = jt.context exp.runtime = time.time() - (exp.created().millis() / 1000.0) LOG.info("Plone: Update job progress: %s, %s, %s", state, message, context)
def test_job_state_change(self): portal = self.layer['portal'] job = self._create_new_job() content = portal['d1'] job.content = IUUID(content) self.assertEqual(job.state, 'PENDING') job_tool = getUtility(IJobUtility) job_tool.reindex_job(job) # get job tracker for content jt = IJobTracker(content) # check if we get the same job self.assertEqual(job, jt.get_job()) # progress state jt.state = 'COMPLETED' self.assertEqual(job.state, 'COMPLETED') # but we can't move back jt.state = 'RUNNING' self.assertEqual(job.state, 'COMPLETED')
def upgrade_220_230_1(context, logger=None): if logger is None: logger = LOG # Run GS steps portal = api.portal.get() setup = api.portal.get_tool('portal_setup') setup.runImportStepFromProfile(PROFILE_ID, 'org.bccvl.site.content') setup.runImportStepFromProfile(PROFILE_ID, 'plone.app.registry') setup.runImportStepFromProfile(PROFILE_ID, 'actions') pc = api.portal.get_tool('portal_catalog') # search all experiments and update job object with infos from experiment # -> delete job info on experiment LOG.info('Migrating job data for experiments') EXP_TYPES = ['org.bccvl.content.sdmexperiment', 'org.bccvl.content.projectionexperiment', 'org.bccvl.content.biodiverseexperiment', 'org.bccvl.content.ensemble', 'org.bccvl.content.speciestraitsexperiment' ] from org.bccvl.site.job.interfaces import IJobTracker import json for brain in pc.searchResults(portal_type=EXP_TYPES): # Update job with process statistic i.e. rusage for result in brain.getObject().values(): if not 'pstats.json' in result: continue jt = IJobTracker(result) job = None try: job = jt.get_job() except Exception as e: LOG.info('Could not resolve %s: %s', result, e) if not job: continue pstats = result['pstats.json'] if hasattr(pstats, 'file'): job.rusage = json.loads(pstats.file.data) del result['pstats.json'] # Setup cookie settings sess = portal.acl_users.session sess.manage_changeProperties( mod_auth_tkt=True, secure=True ) # update facet configurations from org.bccvl.site.faceted.interfaces import IFacetConfigUtility from org.bccvl.site.faceted.tool import import_facet_config fct = getUtility(IFacetConfigUtility) for cfgobj in fct.types(): LOG.info("Import facet config for %s", cfgobj.id) import_facet_config(cfgobj) # set cookie secret from celery configuration from org.bccvl.tasks.celery import app cookie_cfg = app.conf.get('bccvl', {}).get('cookie', {}) if cookie_cfg.get('secret', None): sess._shared_secret = cookie_cfg.get('secret').encode('utf-8') sess = portal.acl_users.session sess.manage_changeProperties( mod_auth_tkt=True, secure=cookie_cfg.get('secure', True) )