示例#1
0
    def schedule(self, totalDayNum):
        scheduleResult = ScheduleResult()
        totalDayNum = int(totalDayNum)
        if self.workerNum < self.dailyRequiredWorkerNum:
            print('worker number less than workload, cannot schedule')
            scheduleResult.message = u'总员工人数小于每天出勤人数,无法排班'
            return scheduleResult
        if self.workerNum == self.dailyRequiredWorkerNum:
            print('worker number equals to workload, don\'t need to schedule at all')
            scheduleResult.message = u'总员工人数等于每天出勤人数,无需排班'
            return scheduleResult
        if self.MIN_WORK_DAY > self.maxWorkDay:
            print('min day > max day')
            scheduleResult.message = u'最大连续出勤天数设置无法保证连续出勤'
            return scheduleResult
        if self.MIN_WORK_DAY == self.maxWorkDay:
            if self.workerNum < self.dailyRequiredWorkerNum * 2:
                print('min=max but workers < workload * 2, not enough worker to handle the workload')
                scheduleResult.message = u'最大最小连续出勤数相等且总员工数小于每天出勤人数的两倍,人手不足'
                return scheduleResult
            else:
                # 固定班次的话,则误差可能为固定连续出勤数
                self.MAX_DELTA_DAY = self.MIN_WORK_DAY
        # 这种情况包含了上一种情况
        if self.workerNum < self.dailyRequiredWorkerNum * 2:
            if self.maxWorkDay < self.MIN_WORK_DAY * 2:
                print('min * 2 > max while workers < workload * 2, not enough worker to handle the workload')
                scheduleResult.message = u'总员工数小于每天出勤人数的两倍,且最大连续出勤天数小于最小连续出勤天数的两倍,人手不足'
                return scheduleResult
        workerNum = self.workerNum
        # 平均工时每人(天),向上取整
        targetTotalWorkDay = int(totalDayNum * self.dailyRequiredWorkerNum + workerNum - 1) / workerNum

        if self.MIN_WORK_DAY > targetTotalWorkDay:
            scheduleResult.message = u'最小连续工时大于平均工时,不能平均安排工时'
            return scheduleResult

        retryCnt = 0
        minDelta = 99999
        unBalancedResult = dict()
        while retryCnt < self.MAX_RETRY_TIME:
            retryCnt += 1
            [resultCalendar, workStats] = self.doSchedule(totalDayNum)
            if self.validateSchedule(resultCalendar):
                currentDelta = self.getMaxDelta(resultCalendar, totalDayNum)
                if currentDelta <= self.MAX_DELTA_DAY:
                    logging.debug('after %d time\'s retry', retryCnt)
                    scheduleResult.message = u'排班成功且工时较为平均'
                    scheduleResult.workCalendar = resultCalendar
                    scheduleResult.personalTotalWorkDay = self.calculateWorkDayPerWorker(resultCalendar)
                    scheduleResult.restCalendar = self.getRestCalendar(resultCalendar)
                    return scheduleResult
                else:
                    # 如果不够平均,则取目前最平均的排班返回
                    logging.debug('minDelta %d current %d', minDelta, currentDelta)
                    if minDelta > currentDelta:
                        unBalancedResult = resultCalendar
                        minDelta = currentDelta

        logging.debug('Cannot meet min delta requirement after %d retries', self.MAX_RETRY_TIME)

        if unBalancedResult:
            scheduleResult.message = u'排班成功但没有找到工时最平均方案'
            scheduleResult.workCalendar = unBalancedResult
            scheduleResult.personalTotalWorkDay = self.calculateWorkDayPerWorker(unBalancedResult)
            scheduleResult.restCalendar = self.getRestCalendar(unBalancedResult)
            self.printFormatedCalendar(unBalancedResult)
            logging.debug(self.getMaxDelta(unBalancedResult, totalDayNum))
        else:
            logging.debug('empty unbalanceResult')
            scheduleResult.message = u'没有找到符合条件的排班方案,建议调整参数'

        return scheduleResult
    def schedule(self, totalScheduleDays):
        scheduleResult = ScheduleResult()
        totalScheduleDays = int(totalScheduleDays)
        if totalScheduleDays % 7 != 0:
            print('totalScheduleDays % 7 != 0')
            scheduleResult.message = u'排班周期必须为7的倍数'
            return scheduleResult
        if self.totalWorkerNum < self.workerNumRequired:
            print('worker number less than workload, cannot schedule')
            scheduleResult.message = u'总员工人数小于每天休息人数,无法排班'
            return scheduleResult
        if self.totalWorkerNum == self.workerNumRequired:
            print('worker number equals to workload, don\'t need to schedule at all')
            scheduleResult.message = u'总员工人数等于每天休息人数,无需排班'
            return scheduleResult
        if self.minRestDay > self.maxRestDay:
            print('min day > max day')
            scheduleResult.message = u'最小连续休息天数大于最大连续休息天数,无法排班'
            return scheduleResult
        try:
            [restCalendar, workStats] = self.doSchedule(totalScheduleDays)
            if not self.validateSchedule(restCalendar):
                scheduleResult.message = u'没有找到符合条件的排班方案,请调整参数'
            else:
                scheduleResult.restCalendar = restCalendar
                scheduleResult.workCalendar = self.getWorkCalendar(restCalendar)
                scheduleResult.personalTotalWorkDay = self.calculateWorkDayPerWorker(scheduleResult.workCalendar)
                scheduleResult.message = u'排班成功'
        except:
            scheduleResult.message = u'遇到未知错误,没有找到符合条件的排班方案,请调整参数'

        return scheduleResult
    def schedule(self, targetDays):
        scheduleResult = ScheduleResult()
        targetDays = int(targetDays)
        if len(self.workers) < self.workload:
            print('worker number less than workload, cannot schedule')
            scheduleResult.message = u'总员工人数小于每天出勤人数,无法排班'
            return scheduleResult
        if len(self.workers) == self.workload:
            print('worker number equals to workload, don\'t need to schedule at all')
            scheduleResult.message = u'总员工人数等于每天出勤人数,无需排班'
            return scheduleResult
        if self.minWorkDay > self.maxWorkDay:
            print('min day > max day')
            scheduleResult.message = u'最小连续出勤天数大于最大连续出勤天数,无法排班'
            return scheduleResult
        if self.minWorkDay == self.maxWorkDay:
            if len(self.workers) < self.workload * 2:
                print('min=max but workers < workload * 2, not enough worker to handle the workload')
                scheduleResult.message = u'最大最小连续出勤数相等且总员工数小于每天出勤人数的两倍,人手不足'
                return scheduleResult
            else:
                # 固定班次的话,则误差可能为固定连续出勤数
                self.MAX_DELTA_DAY = self.minWorkDay
        # 这种情况包含了上一种情况
        if len(self.workers) < self.workload * 2:
            if self.maxWorkDay < self.minWorkDay * 2:
                print('min * 2 > max while workers < workload * 2, not enough worker to handle the workload')
                scheduleResult.message = u'总员工数小于每天出勤人数的两倍,且最大连续出勤天数小于最小连续出勤天数的两倍,人手不足'
                return scheduleResult
        workerNum = len(self.workers)
        # 平均工时每人(天),向上取整
        targetTotalWorkDay = int(targetDays * self.workload + workerNum - 1) / workerNum

        if self.minWorkDay > targetTotalWorkDay:
            scheduleResult.message = u'最小连续工时大于平均工时,不能平均安排工时'
            return scheduleResult

        retryCnt = 0
        minDelta = 99999
        unBalancedResult = dict()
        while retryCnt < self.MAX_RETRY_TIME:
            retryCnt += 1
            [resultCalendar, workStats] = self.doSchedule(targetDays)
            # self.printSchedule(resultCalendar)
            if self.validateSchedule(resultCalendar):
                currentDelta = self.getMaxDelta(resultCalendar, targetDays)
                if currentDelta <= self.MAX_DELTA_DAY:
                    print('after', retryCnt, 'time\'s retry')
                    self.printSchedule(resultCalendar)
                    scheduleResult.message = u'排班成功且工时较为平均'
                    scheduleResult.workCalendar = resultCalendar
                    scheduleResult.personalTotalWorkDay = self.calculateWorkDayPerWorker(resultCalendar)
                    scheduleResult.restCalendar = self.getRestCalendar(resultCalendar)
                    # scheduleResult.workStats = workStats
                    return scheduleResult
                else:
                    # 如果不够平均,则取目前最平均的排班返回
                    print('minDelta', minDelta, 'current', currentDelta)
                    if minDelta > currentDelta:
                        unBalancedResult = resultCalendar
                        unBalancedWorkStats = workStats
                        minDelta = currentDelta

        print('fail to schedule after', self.MAX_RETRY_TIME, 'retries')
        if unBalancedResult:
            # print('before rebalance'
            # print('delta', self.getMaxDelta(unBalancedResult, targetDays)
            # print(self.calculateWorkDayPerWorker(unBalancedResult)
            # self.printSchedule(unBalancedResult)
            newUnBalancedResult = self.rebalance(unBalancedResult, unBalancedWorkStats)
            # print('revalidate', self.validateSchedule(newUnBalancedResult)
            # print('after rebalance'
            # print('new delta', self.getMaxDelta(newUnBalancedResult, targetDays)
            # print(self.calculateWorkDayPerWorker(newUnBalancedResult)
            # self.printSchedule(newUnBalancedResult)
            if (self.validateSchedule(newUnBalancedResult)):
                scheduleResult.message = u'排班成功但没有找到工时最平均方案'
                scheduleResult.workCalendar = newUnBalancedResult
                scheduleResult.personalTotalWorkDay = self.calculateWorkDayPerWorker(newUnBalancedResult)
                scheduleResult.restCalendar = self.getRestCalendar(newUnBalancedResult)
                # scheduleResult.workStats = unBalancedWorkStats
            else:
                scheduleResult.message = u'尝试重新平衡化工时失败,如果不满意请重试'
                scheduleResult.workCalendar = unBalancedResult
                scheduleResult.personalTotalWorkDay = self.calculateWorkDayPerWorker(unBalancedResult)
                scheduleResult.restCalendar = self.getRestCalendar(unBalancedResult)
                # scheduleResult.workStats = unBalancedWorkStats
        else:
            print('empty unbalanceResult')
            scheduleResult.message = u'没有找到符合条件的排班方案,请调整参数'

        self.printFormatedCalendar(scheduleResult.workCalendar)
        self.getMaxDelta(scheduleResult.workCalendar, targetDays)

        return scheduleResult