Beispiel #1
0
    def get_users_stats(self, users_per_id):
        entity_length = len(users_per_id)
        if not entity_length:
            return

        # retrieve today's squad off members
        today = datetime.now()
        today_off = []
        today_requests = []
        requests = Request.get_active(self.session)
        for req in requests:
            if req.user.id not in users_per_id:
                continue
            today_requests.append(req)
            if req.user not in today_off:
                today_off.append(req.user)

        # retrieve active requests since 15 days ago
        date_from = today - relativedelta(days=15)

        all_reqs = []
        for user_id, user in list(users_per_id.items()):
            user_req = Request.by_user_future_approved(self.session, user,
                                                       date_from=date_from)
            all_reqs.extend(user_req)

        # compute current month squad presence percentages
        data_months = {}
        for req in all_reqs:
            for dt in req.dates:
                if dt.month not in data_months:
                    data_months[dt.month] = {}
                if dt.day not in data_months[dt.month]:
                    data_months[dt.month][dt.day] = []
                if req.user.login not in data_months[dt.month][dt.day]:
                    data_months[dt.month][dt.day].append(req.user.login)

        data_days_current = []
        labels = []
        start_date = today - timedelta(days=15)
        stop_date = today + timedelta(days=15)
        for x in daterange(start_date, stop_date):
            labels.append("'%s'" % x.strftime('%d/%m'))
            perc = ((entity_length - len(data_months.get(x.month, {}).get(x.day, []))) / float(entity_length) * 100)  # noqa
            perc = round(perc, 2)
            if x.isoweekday() in [6, 7]:
                perc = 0.0
            data_days_current.append(perc)

        labels = '[%s]' % ','.join(labels)
        return {'users_per_id': users_per_id,
                'data_days_current': data_days_current,
                'today_requests': today_requests,
                'labels': labels,
                'today': today,
                'today_off': today_off,
                'today_off_length': len(today_off),
                'entity_length': entity_length,
                }
Beispiel #2
0
    def timestamps(self):
        """
        Return request dates as list of timestamps

        timestamp are in javascript format
        """
        return [utcify(date)
                for date in daterange(self.date_from, self.date_to)
                if date.isoweekday() not in [6, 7]]
Beispiel #3
0
    def render(self):
        try:
            form_date_from = self.request.params.get('date_from')
            if ' - ' not in form_date_from:
                msg = 'Invalid format for period.'
                self.request.session.flash('error;%s' % msg)
                return HTTPFound(location=route_url('home', self.request))

            dates = self.request.params.get('date_from').split(' - ')
            date_from = datetime.strptime(dates[0], '%d/%m/%Y')
            date_to = datetime.strptime(dates[1], '%d/%m/%Y')
            breakdown = self.request.params.get('breakdown')

            # retrieve holidays for user so we can remove them from selection
            holidays = get_holiday(self.user,
                                   year=date_from.year,
                                   use_datetime=True)

            submitted = [
                d for d in daterange(date_from, date_to)
                if d.isoweekday() not in [6, 7] and d not in holidays
            ]
            days = float(len(submitted))
            pool = None

            days_diff = (date_to - date_from).days
            if days_diff < 0:
                msg = 'Invalid format for period.'
                self.request.session.flash('error;%s' % msg)
                return HTTPFound(location=route_url('home', self.request))

            if (date_to == date_from) and days > 1:
                # same day, asking only for one or less day duration
                msg = 'Invalid value for days.'
                self.request.session.flash('error;%s' % msg)
                return HTTPFound(location=route_url('home', self.request))

            if days <= 0:
                msg = 'Invalid value for days.'
                self.request.session.flash('error;%s' % msg)
                return HTTPFound(location=route_url('home', self.request))

            # check if user is sudoed
            check_user = self.get_target_user(self.user)
            # retrieve future requests for user so we can check overlap
            futures = [
                d for req in Request.by_user_future(self.session, check_user)
                for d in daterange(req.date_from, req.date_to)
            ]
            intersect = set(futures) & set(submitted)
            if intersect:
                err_intersect = True
                # must check for false warning in case of half day requests
                if len(intersect) == 1:
                    # only one date in conflict, check if it's for an half-day
                    dt = intersect.pop()
                    # retrieve the request for this date
                    req = [
                        req for req in Request.by_user_future(
                            self.session, check_user)
                        for d in daterange(req.date_from, req.date_to)
                        if d == dt
                    ]
                    if len(req) < 2:
                        req = req.pop()
                        if req.label != breakdown:
                            # intersect is false, it's not the same halfday
                            err_intersect = False
                            log.debug(
                                'False positive on intersect '
                                'for %s (%s): request: %d (%s)' %
                                (date_from, breakdown, req.id, req.label))

                if err_intersect:
                    msg = 'Invalid period: days already requested.'
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))

            vac_type = VacationType.by_id(self.session,
                                          int(self.request.params.get('type')))

            if not self.user.is_admin:
                # check if vacation requires user role
                if (vac_type.visibility
                        and self.user.role not in vac_type.visibility):
                    msg = 'You are not allowed to use type: %s' % vac_type.name
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))

            # check RTT usage access
            if vac_type.name == u'RTT':
                if self.user.has_feature('disable_rtt'):
                    msg = 'You are not allowed to use type: %s' % vac_type.name
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))

            # label field is used when requesting half day
            label = u''
            if breakdown != 'FULL':
                # handle half day
                if (days > 1):
                    msg = ('AM/PM option must be used only when requesting a '
                           'single day.')
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))
                else:
                    days = 0.5
                    label = unicode(breakdown)

            # check RTT usage
            if vac_type.name == u'RTT':
                pool = rtt_data = check_user.get_rtt_usage(self.session)
                if rtt_data is not None and rtt_data['left'] <= 0:
                    msg = 'No RTT left to take.'
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))
                # check that we have enough RTT to take
                if rtt_data is not None and days > rtt_data['left']:
                    msg = 'You only have %s RTT to use.' % rtt_data['left']
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))
                # check that we request vacations in the allowed year
                if rtt_data is not None and (date_from.year != rtt_data['year']
                                             or
                                             date_to.year != rtt_data['year']):
                    msg = ('RTT can only be used for year %d.' %
                           rtt_data['year'])
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))

            message = None
            # check Exceptionnel mandatory field
            if vac_type.name == u'Exceptionnel':
                message = self.request.params.get('exception_text')
                message = message.strip() if message else message
                if not message:
                    msg = ('You must provide a reason for %s requests' %
                           vac_type.name)
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))
                # check size
                if len(message) > 140:
                    msg = ('%s reason must not exceed 140 characters' %
                           vac_type.name)
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))

            # check for Compensatoire type (LU holiday recovery)
            if vac_type.name == u'Compensatoire':
                to_recover = self.request.params.get('recovered_holiday')
                if to_recover == '-1':
                    msg = 'You must select a date for %s' % vac_type.name
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))

                recover_date = datetime.strptime(to_recover, '%d/%m/%Y')
                vac_class = vac_type.get_class(check_user.country)
                if vac_class:
                    error = vac_class.validate_request(check_user, None, days,
                                                       recover_date, date_to)
                    if error is not None:
                        self.request.session.flash('error;%s' % error)
                        return HTTPFound(
                            location=route_url('home', self.request))
                    message = to_recover

            # check Récupération reason field
            if vac_type.name == u'Récupération':
                message = self.request.params.get('exception_text')
                message = message.strip() if message else message
                # check size
                if message and len(message) > 140:
                    msg = ('%s reason must not exceed 140 characters' %
                           vac_type.name)
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))

            # check CP usage
            if vac_type.name == u'CP':
                cp_class = check_user.get_cp_class(self.session)
                pool = check_user.get_cp_usage(self.session)

                if cp_class:
                    # only FR and LU have a dedicated CP class to use

                    # convert days to hours for LU if needed
                    days = cp_class.convert_days(days)

                    error = cp_class.validate_request(check_user, pool, days,
                                                      date_from, date_to)
                    if error is not None:
                        self.request.session.flash('error;%s' % error)
                        return HTTPFound(
                            location=route_url('home', self.request))

                if pool:
                    # remove expire datetimes as it's not json serializable
                    if 'n_1' in pool:
                        pool['n_1'].pop('expire', None)
                    if 'extra' in pool:
                        pool['extra'].pop('expire', None)
                    pool['acquis'].pop('expire', None)
                    pool['restant'].pop('expire', None)

            # create the request
            # default values
            target_status = u'PENDING'
            target_user = self.user
            target_notified = False

            sudo_use = False
            if self.user.is_admin:
                sudo_user_id = int(self.request.params.get('sudo_user'))
                if sudo_user_id != -1:
                    user = User.by_id(self.session, sudo_user_id)
                    if user:
                        sudo_use = True
                        target_user = user
                        target_status = u'APPROVED_ADMIN'
                        target_notified = True

            # save pool status when making the request
            if pool:
                pool_status = json.dumps(pool)
            else:
                pool_status = json.dumps({})

            request = Request(
                date_from=date_from,
                date_to=date_to,
                days=days,
                vacation_type=vac_type,
                status=target_status,
                user=target_user,
                notified=target_notified,
                label=label,
                message=message,
                pool_status=pool_status,
            )
            self.session.add(request)
            self.session.flush()
            # create history entry
            sudo_user = None
            if sudo_use:
                sudo_user = self.user
            RequestHistory.new(self.session,
                               request,
                               '',
                               target_status,
                               target_user,
                               pool_status,
                               message=message,
                               sudo_user=sudo_user)

            if request and not sudo_use:
                msg = 'Request sent to your manager.'
                self.request.session.flash('info;%s' % msg)
                # call celery task directly, do not wait for polling
                from celery.registry import tasks
                from celery.task import subtask
                req_task = tasks['worker_pending']
                data = {'req_id': request.id}
                subtask(req_task).apply_async(kwargs={'data': data},
                                              countdown=5)
                log.info('scheduling task worker_pending for %s' % data)

            if request and sudo_use:
                settings = self.request.registry.settings
                if 'pyvac.celery.yaml' in settings:
                    with open(settings['pyvac.celery.yaml']) as fdesc:
                        Conf = yaml.load(fdesc, YAMLLoader)
                    caldav_url = Conf.get('caldav').get('url')
                    request.add_to_cal(caldav_url, self.session)
                    msg = 'Request added to calendar and DB.'
                    self.request.session.flash('info;%s' % msg)

        except Exception as exc:
            log.error(exc)
            msg = ('An error has occured while processing this request: %r' %
                   exc)
            self.request.session.flash('error;%s' % msg)

        return HTTPFound(location=route_url('home', self.request))
Beispiel #4
0
    def render(self):
        try:
            form_date_from = self.request.params.get('date_from')
            if ' - ' not in form_date_from:
                msg = 'Invalid format for period.'
                self.request.session.flash('error;%s' % msg)
                return HTTPFound(location=route_url('home', self.request))

            dates = self.request.params.get('date_from').split(' - ')
            date_from = datetime.strptime(dates[0], '%d/%m/%Y')
            date_to = datetime.strptime(dates[1], '%d/%m/%Y')

            # retrieve holidays for user so we can remove them from selection
            holidays = get_holiday(self.user, year=date_from.year,
                                   use_datetime=True)

            submitted = [d for d in daterange(date_from, date_to)
                         if d.isoweekday() not in [6, 7]
                         and d not in holidays]
            days = float(len(submitted))
            pool = None

            days_diff = (date_to - date_from).days
            if days_diff < 0:
                msg = 'Invalid format for period.'
                self.request.session.flash('error;%s' % msg)
                return HTTPFound(location=route_url('home', self.request))

            if (date_to == date_from) and days > 1:
                # same day, asking only for one or less day duration
                msg = 'Invalid value for days.'
                self.request.session.flash('error;%s' % msg)
                return HTTPFound(location=route_url('home', self.request))

            if days <= 0:
                msg = 'Invalid value for days.'
                self.request.session.flash('error;%s' % msg)
                return HTTPFound(location=route_url('home', self.request))

            # retrieve future requests for user so we can check overlap
            futures = [d for req in
                       Request.by_user_future(self.session, self.user)
                       for d in daterange(req.date_from, req.date_to)]

            intersect = set(futures) & set(submitted)
            if intersect:
                msg = 'Invalid period: days already requested.'
                self.request.session.flash('error;%s' % msg)
                return HTTPFound(location=route_url('home', self.request))

            vac_type = VacationType.by_id(self.session,
                                          int(self.request.params.get('type')))

            # check if vacation requires user role
            if (vac_type.visibility
                    and self.user.role not in vac_type.visibility):
                msg = 'You are not allowed to use type: %s' % vac_type.name
                self.request.session.flash('error;%s' % msg)
                return HTTPFound(location=route_url('home', self.request))

            # label field is used when requesting half day
            label = u''
            breakdown = self.request.params.get('breakdown')
            if breakdown != 'FULL':
                # handle half day
                if (days > 1):
                    msg = ('AM/PM option must be used only when requesting a '
                           'single day.')
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))
                else:
                    days = 0.5
                    label = unicode(breakdown)

            # check RTT usage
            if vac_type.name == u'RTT':
                pool = rtt_data = self.user.get_rtt_usage(self.session)
                if rtt_data is not None and rtt_data['left'] <= 0:
                    msg = 'No RTT left to take.'
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))
                # check that we have enough RTT to take
                if rtt_data is not None and days > rtt_data['left']:
                    msg = 'You only have %s RTT to use.' % rtt_data['left']
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))
                # check that we request vacations in the allowed year
                if rtt_data is not None and (
                        date_from.year != rtt_data['year'] or
                        date_to.year != rtt_data['year']):
                    msg = ('RTT can only be used for year %d.' %
                           rtt_data['year'])
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))

            message = None
            # check Exceptionnel mandatory field
            if vac_type.name == u'Exceptionnel':
                message = self.request.params.get('exception_text')
                message = message.strip() if message else message
                if not message:
                    msg = ('You must provide a reason for %s requests' %
                           vac_type.name)
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))
                # check size
                if len(message) > 140:
                    msg = ('%s reason must not exceed 140 characters' %
                           vac_type.name)
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))

            # check Récupération reason field
            if vac_type.name == u'Récupération':
                message = self.request.params.get('exception_text')
                message = message.strip() if message else message
                # check size
                if message and len(message) > 140:
                    msg = ('%s reason must not exceed 140 characters' %
                           vac_type.name)
                    self.request.session.flash('error;%s' % msg)
                    return HTTPFound(location=route_url('home', self.request))

            # create the request
            # default values
            target_status = u'PENDING'
            target_user = self.user
            target_notified = False

            sudo_use = False
            if self.user.is_admin:
                sudo_user_id = int(self.request.params.get('sudo_user'))
                if sudo_user_id != -1:
                    user = User.by_id(self.session, sudo_user_id)
                    if user:
                        sudo_use = True
                        target_user = user
                        target_status = u'APPROVED_ADMIN'
                        target_notified = True

            # save pool status when making the request
            if pool:
                pool_status = json.dumps(pool)
            else:
                pool_status = json.dumps({})

            request = Request(date_from=date_from,
                              date_to=date_to,
                              days=days,
                              vacation_type=vac_type,
                              status=target_status,
                              user=target_user,
                              notified=target_notified,
                              label=label,
                              message=message,
                              pool_status=pool_status,
                              )
            self.session.add(request)
            self.session.flush()

            if request and not sudo_use:
                msg = 'Request sent to your manager.'
                self.request.session.flash('info;%s' % msg)
                # call celery task directly, do not wait for polling
                from celery.registry import tasks
                from celery.task import subtask
                req_task = tasks['worker_pending']
                data = {'req_id': request.id}
                subtask(req_task).apply_async(kwargs={'data': data},
                                              countdown=5)
                log.info('scheduling task worker_pending for %s' % data)

            if request and sudo_use:
                settings = self.request.registry.settings
                if 'pyvac.celery.yaml' in settings:
                    with open(settings['pyvac.celery.yaml']) as fdesc:
                        Conf = yaml.load(fdesc, YAMLLoader)
                    caldav_url = Conf.get('caldav').get('url')
                    request.add_to_cal(caldav_url)
                    msg = 'Request added to calendar and DB.'
                    self.request.session.flash('info;%s' % msg)

        except Exception as exc:
            log.error(exc)
            msg = ('An error has occured while processing this request: %r'
                   % exc)
            self.request.session.flash('error;%s' % msg)

        return HTTPFound(location=route_url('home', self.request))