Example #1
0
 def recentlyCompleted(self) -> []:
     recentlyCompleted = []
     currentArrow = arrow.now()
     affectedDays = list(filter(lambda day : day.date >= self.lastWorkConfirmed.date() and day.date <= currentArrow.date(), self.__days))
     for affectedDay in affectedDays:
         timeSlots = [ts for ts in affectedDay.timeSlots if ts.taskOrAppointment is not None]
         if affectedDay.date == self.lastWorkConfirmed.date(): # First day of area
             timeSlots = list(filter(lambda ts: ts.endTime > Time(self.lastWorkConfirmed.hour, self.lastWorkConfirmed.minute), timeSlots))
             # Remove all timeslots prior
         if affectedDay.date == currentArrow.date(): # NOT an elif, as a day can be both start and end day
             timeSlots = list(filter(lambda ts: ts.endTime < Time(self.lastWorkConfirmed.hour, self.lastWorkConfirmed.minute), timeSlots))
             # Remove all timeslot after
         for timeSlot in timeSlots:
             recentlyCompleted.append({"dateString" : util.dateString(affectedDay.date), "timeSlot" : timeSlot, "task" : timeSlot.taskOrAppointment})
     return recentlyCompleted
Example #2
0
def timeInput(info: str) -> Time:
    while True:
        try:
            timeString = input(info)
            time = Time.fromString(timeString)
            return time
        except (IndexError, ValueError):
            print("Invalid Time!")
def getFreeTimeBetweenPoints(days: [Day], start: arrow.Arrow,
                             end: arrow.Arrow) -> int:  # In Minutes
    if start == end:
        return 0

    sortedDays = sorted(days, key=lambda d: d.date)

    startDate = start.date()
    endDate = end.date()
    startTime = Time(start.hour, start.minute)
    endTime = Time(end.hour, end.minute)

    # Collect time inbetween
    freeMinutes = 0
    for day in sortedDays:

        # Days before or equal to the start date
        if day.date < startDate:
            continue
        elif day.date == startDate:
            if day.date == endDate:  # If it is ALSO equal to the endDate (i.e. startDate and endDate are the same day)
                return day.freeTimeInMinutes(
                    after=startTime, before=endTime
                )  # Return immediately, as this is the only day (since startDate==endDate)
            else:
                freeMinutes += day.freeTimeInMinutes(after=startTime)
                continue

        # Days after or equal to the end date
        if day.date > endDate:
            print(
                f"WARNING: This scenario [debug-id: schedule_alg:AAA123] should not occur! Last day of time windows was likely not calculated correctly. DayDate: {day.date} | EndDate: {endDate}"
            )
            print(f"Start: {start} | End: {end}")
            return freeMinutes
        elif day.date == endDate:
            freeMinutes += day.freeTimeInMinutes(before=endTime)
            return freeMinutes  # This is the last valid day, return immediately

        # Normal day
        freeMinutes += day.freeTimeInMinutes()

    return freeMinutes
Example #4
0
    def freeTimeSlots(
        self,
        before=None,
        after=None
    ) -> [TimeSlot
          ]:  # Returns a NEW DEEPCOPIED LIST containing new objects / copies
        # At this point, priority could be used to determine if appointments should get thrown out
        blocking_appointments = self.appointments.copy()
        virtualAppointments = [
        ]  # Blocking appointments to simulate some constraint, i.e. after/before
        if before is not None and before != Time(23, 59):
            before = Appointment("VIRTUAL_APP_BEFORE",
                                 TimeSlot(before, Time(23, 59)))
            virtualAppointments.append(before)
        if after is not None and after != Time(0, 0):
            after = Appointment("VIRTUAL_APP_AFTER",
                                TimeSlot(Time(0, 0), after))
            virtualAppointments.append(after)

        blocking_appointments += virtualAppointments

        freeSlots = []
        for timeSlot in self.timeSlots:
            # TimeSlots with a TaskOrAppointment assigned to it are not free
            if timeSlot.taskOrAppointment is not None:
                continue

            # Calculate overlap with appointments and perhaps split up TimeSlots as necessary
            currentTimeSlots = [timeSlot.copy()]
            for appointment in blocking_appointments:
                newTimeSlots = []
                for currentTimeSlot in currentTimeSlots:
                    newTimeSlots += currentTimeSlot.nonOverlap(
                        appointment.timeSlot)
                currentTimeSlots = newTimeSlots.copy(
                )  # .copy() is very important; mutation danger
            freeSlots += currentTimeSlots.copy()
        return freeSlots
Example #5
0
def arrowToTime(a: arrow.Arrow) -> Time:
    return Time(a.hour, a.minute)
Example #6
0
def exampleTimeSlots() -> [TimeSlot]:
    return [
        TimeSlot(Time(8, 30), Time(12, 30)),
        TimeSlot(Time(13, 00), Time(17, 00))
    ]
Example #7
0
def timeParse(timeString: str) -> Time:
    hours = int(timeString.split(":")[0])
    minutes = int(timeString.split(":")[1])
    return Time(hours, minutes)
def calculateSchedule(globalDays: [Day],
                      tasks: [Task],
                      currentSchedule: Schedule,
                      start: arrow.Arrow,
                      debug=False) -> Schedule:
    currentTime = util.smoothCurrentArrow()
    if start < currentTime:
        raise Exception("Cannot calculate a schedule for the past")

    days = currentSchedule.days()
    oldDays = [day.copy() for day in days if day.date <= start.date()
               ]  # Contains all days PRIOR to start date (last is popped off)

    oldTimeSlots = []
    if oldDays:  # On initial creation, there are no old days
        lastDay = oldDays.pop(
        )  # This day is changed after the current timeslot and kept the same before.
        splitTime = Time(start.hour, start.minute)

        for ts in lastDay.timeSlots:
            if ts.endTime > splitTime and ts.startTime < splitTime:
                # Update start time to be the end of the current timeslot

                # Update Arrow object only if it isn't before the smoothCurrentArrow()
                splitArrow = arrow.Arrow(start.year,
                                         start.month,
                                         start.day,
                                         hour=ts.endTime.hours,
                                         minute=ts.endTime.minutes)
                if splitArrow >= currentTime:
                    start = splitArrow
                    splitTime = Time(start.hour, start.minute)

            if ts.startTime < splitTime and ts.taskOrAppointment is not None:
                oldTimeSlots.append(ts)

    startIndex = None
    for i, day in enumerate(globalDays):
        if day.date == start.date():
            startIndex = i
            break
    else:
        raise Exception("Error in schedule_alg 555255GGG")

    newDays = [day.copy() for day in globalDays[startIndex:]]

    # Remove TimeSlots prior to start
    firstNewDay = newDays[0]

    oldTimeSlotsToRemove = []
    for ts in firstNewDay.timeSlots:
        if ts.startTime < splitTime:
            oldTimeSlotsToRemove.append(ts)
    for otstr in oldTimeSlotsToRemove:
        firstNewDay.timeSlots.remove(otstr)

    for oldTimeSlot in oldTimeSlots:
        firstNewDay.addTimeSlot(oldTimeSlot)

    lastWorkConfirmed = currentSchedule.lastWorkConfirmed

    if lastWorkConfirmed is None:
        lastWorkConfirmed = start.clone()

    # Creates a clean copy of the tasks and days, so that no evil mutation occurs
    newDays = sorted(newDays, key=lambda d: d.date)
    tmpTasks = sorted([task.copy() for task in tasks],
                      key=lambda t: t.deadline)

    if isSolvable(tasks, newDays, start, useMinimum=False):
        happySchedule = calculateHappySchedule(
            tmpTasks, newDays, lastWorkConfirmed, start, debug=debug
        )  # Adds the history (previous schedule) to the newly calculated schedule
        happySchedule.addHistory(oldDays)
        return happySchedule
    elif isSolvable(tasks, newDays, start, useMinimum=True):
        riskySchedule = calculateSadSchedule(tmpTasks,
                                             newDays,
                                             lastWorkConfirmed,
                                             start,
                                             debug=debug)
        riskySchedule.addHistory(oldDays)
        return riskySchedule
        #return calculateRiskySchedule(tmpTasks, newDays, created=start)
    else:
        sadSchedule = calculateSadSchedule(tmpTasks,
                                           newDays,
                                           lastWorkConfirmed,
                                           start,
                                           debug=debug)
        sadSchedule.addHistory(oldDays)
        return sadSchedule
Example #9
0
    def scheduleTask(self, task: Task, start: arrow.Arrow, minutes=None, debug=False):
        # TODO: Consider minBlock
        
        if minutes is None:
            minutesToSchedule = task.maxRemainingTime
        else:
            minutesToSchedule = minutes


        if debug:
            print(f"Schedule is scheduling {minutesToSchedule}min for task {task}")

        scheduledMinutes = 0

        startTime = util.arrowToTime(start)

        for day in self.__days:
            if debug:
                print("\n")
                print(day, end=" -> ")
            if task.maxRemainingTime == 0 or minutesToSchedule == scheduledMinutes:
                if debug: print("A", end="")
                return

            # Skip days prior to the startDate
            if day.date < start.date():
                if debug: print("B", end="")
                continue

            # For the start day, consider only the times that lie after the start time
            if day.date == start.date():
                if debug: print("C", end="")
                freeTimeSlots = day.freeTimeSlots(after=startTime)

            # Days in the future
            if day.date > start.date():
                if debug: print("D", end="")
                freeTimeSlots = day.freeTimeSlots()

            if len(freeTimeSlots) == 0:
                if debug: print("E", end="")
                continue

            # For the valid TimeSlots of the future, fill them with the task until they are either all filled or "minutesToSchedule" minutes are scheduled
            for ts in freeTimeSlots:
                if debug: print("F", end="")

                if task.maxRemainingTime == 0 or minutesToSchedule == scheduledMinutes:
                    if debug: print("G", end="")
                    return

                # If TimeSlot is <= to what still needs to be scheduled, fill it completely
                if ts.durationInMinutes <= (minutesToSchedule - scheduledMinutes):
                    if debug: print("H", end="")
                    # Schedule the Task
                    day.scheduleTask(ts, task, debug=debug)

                    # Update the counters
                    task.addCompletionTime(ts.durationInMinutes) # This mutates the ORIGINAL TASK
                    scheduledMinutes += ts.durationInMinutes

                else: # If TimeSlot is bigger than what needs to be scheduled, fill the first section of it (until "minutesToSchedule" minutes are scheduled)
                    if debug: print("I", end="")
                    # Build the Partial TimeSlot
                    remainingMinutesToSchedule = minutesToSchedule - scheduledMinutes
                    length = Time.fromMinutes(remainingMinutesToSchedule)
                    partialTimeSlot = TimeSlot(ts.startTime, ts.startTime + length)

                    # Schedule the Task
                    day.scheduleTask(partialTimeSlot, task, debug=debug)

                    # Update the counters
                    task.addCompletionTime(partialTimeSlot.durationInMinutes) # This mutates the ORIGINAL TASK
                    scheduledMinutes += partialTimeSlot.durationInMinutes # Unnecessary. ScheduledMinutes will == MinutesToSchedule after this every time.
                    return # Since the TimeSlot was bigger than the remaining minutesToSchedule, we are done here now.
        raise ImpossibleScheduleException("Unable to schedule task!")