コード例 #1
0
class BreakItem(LegItem):
    item_type = 'Break'
    paid = Time(0)
    unpaid = Time(0)
    split_shift = False

    def __init__(self, prev_job, next_job):
        super().__init__(prev_job, next_job)
        self.paid = self.total_time

    def output(self):
        return [self.item_type, self.start_time.to_time(), '-', '-', self.finish_time.to_time(), None, 
            self.paid.to_time() if self.paid.seconds else None, 
            self.unpaid.to_time() if self.unpaid.seconds else None, 
            self.paid.to_time() if self.paid.seconds else None]
コード例 #2
0
    def check_max_working_time(self):
        idx = 0
        min_idx = 0
        items = self.items[:]            
        time_worked = Time(0)

        while(items):
            item = items.pop(0)
            idx += 1
            if isinstance(item, BreakItem): 
                time_worked = Time(0)
                min_idx = idx
            else:
                time_worked += item.total_time
                if time_worked > settings.MAX_TIME_BEFORE_BREAK:
                    # greedy grab all jobs until the next break
                    if items and not isinstance(item, BreakItem):
                        continue
                    alert = ExceedFatigueMgmtAlert(item, time_worked)
                    self.items.insert(idx, alert)
                    for item in self.items[min_idx:idx + 1]:
                        item.colour = settings.BREAK_ALERT_COLOUR
                    return  
コード例 #3
0
def load_jobs(path: str) -> list:
    # read from xlsx spreadsheet
    workbook = xlrd.open_workbook(path)
    sheet = workbook.sheet_by_index(0)

    jobs = []

    for index in range(2, sheet.nrows):
        row = sheet.row_values(index)
        driver_code, driver_name, signon_time, start_time, pickup_place, dest_place, _, finish_time, signoff_time, _, pickup_lat, pickup_long, dest_lat, dest_long, *_ = row

        # header rows
        if driver_code.startswith('Coach Manager') or driver_code.startswith(
                'Driver') or driver_code.startswith(
                    'Record Count') or driver_code.startswith('WHERE ('):
            continue

        # empy jobs
        if not driver_code.strip():
            continue

        # job sign on and signoff times
        try:
            signon_time = Time(signon_time)
        except ValueError:
            raise ParserException('Cannot convert sign on time', row,
                                  index + 1)

        try:
            signoff_time = Time(signoff_time)
        except ValueError:
            raise ParserException('Cannot convert sign off time', row,
                                  index + 1)

        # pickup
        try:
            start_time = Time(start_time)
        except ValueError:
            raise ParserException('Cannot convert start time', row, index + 1)

        pickup_location = mapping.Location(
            pickup_place.strip(), start_time,
            mapping.GPS(pickup_lat, pickup_long))

        # destination
        try:
            finish_time = Time(finish_time)
        except ValueError:
            raise ParserException('Cannot convert finish time', row, index + 1)

        dest_location = mapping.Location(dest_place.strip(), finish_time,
                                         mapping.GPS(dest_lat, dest_long))

        # check for weird times
        if finish_time < start_time:
            raise TimeException('Finish time cannot be before start time', row,
                                index + 1)

        # job
        driver = Driver.get_driver(driver_code, driver_name)
        job = Job(pickup_location, dest_location, signon_time, signoff_time)
        driver.add_job(job)
        jobs.append(job)

    return jobs
コード例 #4
0
 def __init__(self, prev_job, next_job):
     super().__init__(prev_job, next_job)
     self.paid = Time(0)
     self.unpaid = self.total_time
コード例 #5
0
    def process(self):
        breaks = []
        split_shift = False
        prev_job = self.jobs.pop(0)

        if prev_job.pickup.place != 'Depot':
            self.items.append(FromDepotItem(prev_job))

        self.items.append(JobItem(prev_job))

        while self.jobs:
            next_job = self.jobs.pop(0)

            # split shift
            if next_job.signon_time - prev_job.signoff_time > settings.SPLIT_SHIFT_THRESHOLD:
                to_depot = ToDepotItem(prev_job)
                from_depot = FromDepotItem(next_job)
                split_shift = SplitShiftItem(to_depot, from_depot)
                self.items.append(to_depot)
                self.items.append(split_shift)
                self.items.append(from_depot)
                breaks.append(split_shift)
                split_shift = True

            else:
                # repositioning
                if prev_job.destination != next_job.pickup:
                    item = PositioningItem(prev_job, next_job)
                    if item.is_material:
                        self.items.append(item)

                # break
                last_item = self.items[-1]
                if last_item.finish_time < next_job.start_time:
                    break_ = BreakItem(last_item, next_job)
                    self.items.append(break_)
                    breaks.append(break_)

            self.items.append(JobItem(next_job))
            prev_job = next_job

        if prev_job.pickup.place != 'Depot':
            self.items.append(ToDepotItem(prev_job))

        # appply EBA
        breaks = list(reversed(sorted(breaks, key=operator.attrgetter('total_time'))))
        if breaks:
            # if split shift - all other breaks are paid
            if split_shift:
                breaks.pop(0)
                for break_ in breaks:
                    break_.paid = break_.total_time
                    break_.unpaid = Time(0)
            
            else:
                breaks = list(filter(lambda item: item.total_time >= settings.MIN_BREAK_TIME, breaks))
                blocks = []
                for break_ in breaks:
                    blocks.extend(self.break_blocks(break_))
                # sort the blocks by size and take top BREAK_COUNT blocks to be unpaid
                blocks = sorted(blocks, key=operator.itemgetter(1))
                unpaid_blocks = blocks[-settings.BREAK_COUNT:]
                for break_, block in unpaid_blocks:
                    break_.unpaid += Time(block)
                    break_.paid -= Time(block)

        # generate shifts
        split_shift_finder = lambda item: isinstance(item, SplitShiftItem) and item.unpaid and not item.paid
        shifts = split(self.items, split_shift_finder)
        self.shifts = [Shift(item, len(shifts) > 1) for item in shifts]
        
        if len(shifts) > 1:
            self.split = first(self.items, split_shift_finder)

        # check minimum total shift time
        elif self.total_payable_hours < settings.MIN_SHIFT_DURATION:
            time = Time(settings.MIN_SHIFT_DURATION - self.total_payable_hours.seconds)
            adjustment = MinimumShiftAdjustment(self.shifts[0].items[-1], time)
            self.shifts[0].items.append(adjustment)
コード例 #6
0
 def total_adjustments(self):
     time = Time(sum([item.total_adjustments for item in self.shifts]))
     return time
コード例 #7
0
 def total_unpaid_breaks(self):
     breaks = Time(sum([shift.total_unpaid_breaks.seconds for shift in self.shifts]))
     if self.split:
         breaks += self.split.total_time
     return breaks
コード例 #8
0
 def total_paid_breaks(self):
     return Time(sum([shift.total_paid_breaks.seconds for shift in self.shifts]))
コード例 #9
0
 def adjustment(self):
     return Time(settings.MIN_SPLIT_DURATION - self.total_payable_hours.seconds if self.split else settings.MIN_SHIFT_DURATION - self.total_payable_hours.seconds)
コード例 #10
0
 def total_adjustments(self):
     time = Time(sum([item.total_time for item in self.items if isinstance(item, Adjustment)]))
     return time
コード例 #11
0
 def total_unpaid_breaks(self):
     return Time(sum([item.unpaid.seconds for item in self.items if isinstance(item, BreakItem)]))
コード例 #12
0
 def total_time(self):
     return Time(self.duration)
コード例 #13
0
 def total_time(self):
     return Time(0)