Ejemplo n.º 1
0
def shed_evt_job_executed(event):
    """
    Scheduler event listener callback function,
    triggered by the scheduler on a job execution event

    When called this function updates the experiment status.

    :param event: event object sent by scheduler
    :type event: event

    :returns: None
    :rtype: None
    """

    expid = event.job_id
    exp = Experiment(directory=os.path.join(Config.WORKING_DIR, expid))
    job = scheduler.get_job(expid)
    if job is None:
        exp.status = "ENDED"
        exp.dump()
        return
    exp.next_run_time = "%s" % job.next_run_time
    if job.next_run_time < datetime.now(pytz.timezone('Europe/Paris')):
        exp.status = "ENDED"
    exp.dump()
    return
Ejemplo n.º 2
0
def get_experiment(expid):
    """
    Return an experiment

    Args:
      expid : str
        The id of the experiment
    """
    try:
        exp = Experiment(directory=os.path.join(Config.WORKING_DIR, expid))
    except FileNotFoundError:
        abort(404)
    return jsonify(exp.to_dict())
Ejemplo n.º 3
0
def shed_evt_job_error(event):
    """Scheduler event listener callback function,
    triggered when scheduler report job execution error

    When called this function updates the experiment status.

    """

    expid = event.job_id
    exp = Experiment(directory=os.path.join(Config.WORKING_DIR, expid))
    exp.message = "Job execution failed. Error : %s" % event.exception
    exp.dump()
    return
Ejemplo n.º 4
0
def delete_experiment(expid):
    """
    Delete an experiment

    Args:
      expid : str
        The id of the experiment
    """
    try:
        exp = Experiment(directory=os.path.join(Config.WORKING_DIR, expid))
    except FileNotFoundError:
        abort(404)
    exp.delete()
    return jsonify({'result': True})
Ejemplo n.º 5
0
def shed_evt_job_missed(event):
    """Callback function,
    triggered when a job is is missed by the scheduler

    :param event: event object sent by scheduler
    :type event: event

    :returns: None
    :rtype: None
    """

    expid = event.job_id
    exp = Experiment(directory=os.path.join(Config.WORKING_DIR, expid))
    exp.message = "Step missed. Error : %s" % event.exception
    #TODO : ajouter des steps vides
    exp.dump()
    return
Ejemplo n.º 6
0
    def test_dump_and_load(self):
        """Dump an XP, load it. The two has to be equal
        """

        self.newExp.dump()
        otherExp = Experiment(app=self.app, directory=self.newExp.workdir)
        import shutil
        shutil.rmtree(otherExp.workdir)
        self.assertEqual(self.newExp, otherExp)
Ejemplo n.º 7
0
    def create_and_schedule(self, xpid):
        """handle experiment creation and schedule

        :param: expid
        :rtype: string
        :returns: None
        :rtype: None
        """

        self.logger.info("create_and_schedule")
        exp = Experiment(directory=os.path.join(Config.WORKING_DIR, xpid))
        exp_id = xpid
        exp.status = "SCHEDULING_IN_PROGRESS"

        ##scheduler accepts loosely converted strings
        start = arrow.get(exp.start).format('YYYY-MM-DD HH:mm:ss').replace(
            '+0000', '')
        end = arrow.get(exp.end).format('YYYY-MM-DD HH:mm:ss').replace(
            '+0000', '')

        job = scheduler.add_job(
            RpiModule.take_picture,  ## PICTURE IT !
            args=(exp_id, ),
            trigger='interval',
            start_date=start,
            end_date=end,
            minutes=exp.interval,
            id=exp.expid,
            replace_existing=True)
        exp.next_run_time = "%s" % job.next_run_time
        self.logger.info("Exp %s scheduled for %s" %
                         (exp.expid, exp.next_run_time))
        scheduler.print_jobs()
        exp.dump()
Ejemplo n.º 8
0
    def setUp(self):
        self.app = Flask(__name__)
        #expid = str(uuid.uuid1())
        now = arrow.now()

        _scheduler = getattr(self.app, 'scheduler', None)
        if _scheduler is None:
            _scheduler = BackgroundScheduler()
            self.app.scheduler = _scheduler
        if not _scheduler.running:
            _scheduler.start()

        self.tmpfolder = tempfile.mkdtemp()
        exp = {
            "desc": "Fake test XP",
            "status": "Setup",
            "ir": False,
            "cameras": [1, 2, 3, 4],
            "message": 'msg',
        }
        self.newExp = Experiment(app=self.app)
        self.newExp.from_dict(exp)
        self.newExp.workdir = os.path.join(self.tmpfolder, self.newExp.xp_id())
Ejemplo n.º 9
0
def new_experiment():
    """
    Creation of a new experiment
    """
    form = SettingsForm()

    for name, item in form.camera.settings.items():
        if item['type'] == 'list':
            tmpfield = SelectMultipleField( name,
                                            choices=[(value, effect) for effect, value in item['values'].items()],
                                            default=item['default'],
                                            coerce=int,
                                            description=name)
            setattr(form, name, tmpfield)

    actions = "new"
    exp = Experiment()

    if form.validate_on_submit():
        form.populate_obj(exp)
        exp.create()
        flash('Experience %s was added' % exp.expid, 'success')
    return render_template('experiment.html', form=form, exp=exp,
                            config=Config, actions=actions)
Ejemplo n.º 10
0
def create_experiment():
    """
    Create an experiment

    required fields:
        start, end, timepoint_nb and camera
    """
    if (not request.json or 'start' not in request.json
            or 'end' not in request.json or 'timepoint_nb' not in request.json
            or 'cameras' not in request.json):
        abort(400)
    exp = Experiment()
    exp.from_dict(request.json)
    exp.create()
    return jsonify(exp.to_dict()), 201
Ejemplo n.º 11
0
def update_experiment(expid):
    """
    Update an experiment

    Args:
      expid : str
        The id of the experiment
    """
    try:
        exp = Experiment(directory=os.path.join(Config.WORKING_DIR, expid))
    except FileNotFoundError:
        abort(404)
    exp.from_dict(request.json)
    exp.create()
    return jsonify(exp.to_dict())
Ejemplo n.º 12
0
    def sched_cancel_xp(self, xpid):
        """handle experiment cancelation

        :param: expid
        :rtype: string
        :returns: None
        :rtype: None
        """

        exp = Experiment(directory=os.path.join(Config.WORKING_DIR, xpid))
        self.logger.info("Canceling Exp %s" % xpid)
        exp.status = "Canceled"
        exp.message = "Canceled & removed from scheduler."
        exp.next_run_time = "Never"
        scheduler.remove_job('%s' % (exp.expid))
        exp.dump()
Ejemplo n.º 13
0
def setuped_experiment(expid):
    """
    Edit an existing experiemnt

    Args:
      expid : str
        The experiment id

    GET: display
    POST: three action possible
       - edit
       - cancel
       - delete
    """
    try:
        exp = Experiment(directory=os.path.join(Config.WORKING_DIR, expid))
    except FileNotFoundError:
        abort(404)
    form = SettingsForm(obj=exp)
    if request.method == 'POST':
        if request.form['action'] == "edit":
            if form.validate_on_submit():
                form.populate_obj(exp)
                exp.create()
                flash('The experiment %s has been modified' % exp.expid, 'info')
        elif request.form['action'] == "cancel":
            exp.cancel()
            flash('The experiment %s has been canceled' % exp.expid, 'warning')
        elif request.form['action'] == "delete":
            exp.delete()
            flash('The experiment %s has been deleted' % exp.expid, 'danger')
            # return redirect(url_for('main_page.experiment_status'))
    if exp.status in ["ENDED", "Error", "Canceled"]:
        actions = "readonly"
    elif exp.status == "RUNNING":
        actions = "cancelable"
    else:
        actions = "editable"
    return render_template('experiment.html', form=form, exp=exp, config=Config,
                           actions=actions)
Ejemplo n.º 14
0
    def load(self, directory=None):
        """
        Load the information of the experiment list from the working directory

        Args:
          directory: str
            The working directory path
        """
        self.exps = []
        if directory is None:
            directory = Config.WORKING_DIR
        for exp_dir in os.listdir(directory):
            try:
                self.exps.append(
                    Experiment(directory=os.path.join(directory, exp_dir)))
            # In case of empty dir or dir without info.json file
            except FileNotFoundError:
                continue
            except Exception as e:
                app.logger.error(
                    'Unknown error occured while loading json files from experiment folder: %s'
                    % e, )
Ejemplo n.º 15
0
def shed_evt_job_added(event):
    """Scheduler event listener callback function,
    triggered when a job is added to scheduler

    When called this function updates the experiment status.

    :param event: event object sent by scheduler
    :type event: event

    :returns: None
    :rtype: None
    """

    expid = event.job_id
    exp = Experiment(directory=os.path.join(Config.WORKING_DIR, expid))
    job = scheduler.get_job(expid)
    exp.next_run_time = "%s" % job.next_run_time
    exp.status = "RUNNING"
    exp.dump()
    return
Ejemplo n.º 16
0
class TestExperimentMethods(unittest.TestCase):
    def setUp(self):
        self.app = Flask(__name__)
        #expid = str(uuid.uuid1())
        now = arrow.now()

        _scheduler = getattr(self.app, 'scheduler', None)
        if _scheduler is None:
            _scheduler = BackgroundScheduler()
            self.app.scheduler = _scheduler
        if not _scheduler.running:
            _scheduler.start()

        self.tmpfolder = tempfile.mkdtemp()
        exp = {
            "desc": "Fake test XP",
            "status": "Setup",
            "ir": False,
            "cameras": [1, 2, 3, 4],
            "message": 'msg',
        }
        self.newExp = Experiment(app=self.app)
        self.newExp.from_dict(exp)
        self.newExp.workdir = os.path.join(self.tmpfolder, self.newExp.xp_id())

    def tearDown(self):
        import shutil
        shutil.rmtree(self.tmpfolder)

    def test_dump_and_load(self):
        """Dump an XP, load it. The two has to be equal
        """

        self.newExp.dump()
        otherExp = Experiment(app=self.app, directory=self.newExp.workdir)
        import shutil
        shutil.rmtree(otherExp.workdir)
        self.assertEqual(self.newExp, otherExp)

    def test_time_point_setter(self):
        """
        """
        somepath = "/tmp/%s" % self.newExp.expid
        self.newExp.new_step(4, somepath)
        #print(self.newExp.to_dict())
        self.assertEqual((4, somepath, 1),
                         (self.newExp.steps[0][1], self.newExp.steps[0][2],
                          self.newExp.steps_nb))

    def test_insert_in_scheduler(self):
        inserted = False
        now = arrow.now()
        self.newExp.start = now.format('YYYY-MM-DD HH:mm:ssZ')
        self.newExp.interval = 5
        self.newExp.end = now.shift(hours=+1).format('YYYY-MM-DD HH:mm:ssZ')

        jobid = self.newExp.insert_in_scheduler()
        myjob = self.app.scheduler.get_job(jobid)
        next_run_time = myjob.next_run_time
        if next_run_time is not None and self.newExp.expid == myjob.id:
            inserted = True

        self.assertTrue(inserted)

    def test_remove_from_scheduler(self):
        """Insert a job in schendular and remove it
        """
        now = arrow.now()
        wasin = False
        self.newExp.start = now.format('YYYY-MM-DD HH:mm:ssZ')
        self.newExp.interval = 5
        self.newExp.end = now.shift(hours=+1).format('YYYY-MM-DD HH:mm:ssZ')

        jobid = self.newExp.insert_in_scheduler()
        myjob = self.app.scheduler.get_job(jobid)

        if myjob.id in (job.id for job in self.app.scheduler.get_jobs()):
            wasin = True

        self.newExp.remove_from_scheduler()

        if myjob.id in (job.id for job in self.app.scheduler.get_jobs()):
            stillin = True
        else:
            stillin = False

        self.assertTrue(wasin and not stillin)

    def test_delete(self):
        """Test XP files remooval
        """
        deleted = False
        hadworkdir = False
        self.newExp.dump()
        if os.path.exists(self.newExp.workdir):
            hadworkdir = True

        self.newExp.delete()

        if not os.path.exists(self.newExp.workdir):
            deleted = True

        self.assertTrue(hadworkdir and deleted)
Ejemplo n.º 17
0
formatter = logging.Formatter(Config.LOG_FORMAT)
handler = logging.FileHandler(Config.LOGFILE, mode='a')
handler.setFormatter(formatter)
handler.setLevel(logging.INFO)

app.logger.addHandler(handler)
app.logger.info('Starting Flask app :  %s' % app.name)

#load XPs from json files
for root, dirs, files in os.walk(Config.WORKING_DIR):
    for afile in files:
        if fnmatch.fnmatch(afile, '*.json'):
            #use foder name as experiment ID,
            expid = os.path.basename(root)
            try:
                exp = Experiment(directory=root, app=app)
                app.logger.info('Loading exp %s' % expid)
                if exp.expid != expid:
                    exp.expid = expid
                if exp.status not in (
                        'ENDED',
                        'Canceled',
                ):
                    exp.create()
            except Exception as e:
                app.logger.error(
                    'Error while loading experiments from file-system: %s' % e)
                continue


def render_error(e):
Ejemplo n.º 18
0
    def take_picture(xpid):
        """
        Take the picture.

        static method, only needs the experiment ID, parameters are stored in experiment
        description json file

        :param xpid: Ecperiment ID
        :type event: string

        :returns: boolean
        :rtype: boolean
        """

        rpi = RpiModule()
        rpi.logger.info(
            'taking picture for task : %s. RpiModule obj ref : %s' %
            (xpid, rpi))
        light = rpi.light
        exp = Experiment(directory=os.path.join(Config.WORKING_DIR, xpid))

        cameras = exp.cameras
        params = exp.img_params

        #test the multiplexer
        if not rpi.selector.self_check():
            rpi.logger.error('Multiplexer failure')
            #put info in json file ..
            exp.state = "FAILED"
            exp.message = "Multiplexer error fatal error"
            exp.dump()
            return False

        #camera is used -> Experiment in progress ?
        ## wait a while ...
        retries = 0
        while retries < Config.CAM_RETRIES:
            retries += 1
            try:
                with rpi.lock.acquire():
                    rpi.logger.info('lock acquired')
                    #TURN THE LIGHTS
                    if exp.ir:
                        rpi.logger.info('turning lights ON')
                        light.state = Light.ON
                    else:
                        rpi.logger.info('turning lights OFF')
                        light.state = Light.OFF

                    rpi.logger.info('rpimodule requested cams list : %s' %
                                    cameras)

                    step = []
                    for camera in cameras:
                        rpi.logger.debug('Started image capture on cam %s.' %
                                         camera)
                        if camera in Config.CAMS:
                            instant_date = arrow.now().format(
                                'YYYY-MM-DD_HH-mm-ss')
                            #create cam folder & add cam_folder path to imagepath
                            camdir = os.path.join(exp.workdir, "%s" % camera)
                            rpi.logger.debug('# # # # # # new path %s' %
                                             camdir)
                            if not os.path.isdir(camdir):
                                os.mkdir(camdir)

                            imagepath = os.path.join(
                                camdir, '%s_%s.png' % (instant_date, camera))
                            ##CAPTURE IMAGE
                            if rpi.selector.capture(camera, imagepath, params):
                                step.append((instant_date, camera, imagepath))
                            else:
                                exp.message = "Camera %s was buissy. Skipped step" % camera
                        else:
                            rpi.logger.info(
                                'Requested camera (%s) is not present in config file. Please check config.py file.'
                                % (camera))

                    if light.state:
                        rpi.logger.info('turning lights OFF')
                        light.state = Light.OFF
                    if len(step) > 0:
                        exp.new_step(tuple(step))
                        exp.message = "OK"
                    exp.dump()
                    return True
            except Timeout:
                print(
                    "Another instance of this application currently holds the lock."
                )
            finally:
                if rpi.lock.is_locked:
                    rpi.lock.release()
                    rpi.logger.info('lock release forced')
                rpi.logger.info('lock is now free for other experiments')
        rpi.logger.error('Could not acquire cameras after %s retries.' %
                         Config.CAM_RETRIES)
        return False