Example #1
0
def do(request, task_id):
    """ Respond to the user doing the given task.

        We display a page where the user can click on "Finished" to finish the
        task.  We then return back to the main "suggest" page.
    """

    if request.method == "POST":

        # Respond to the user clicking on one of our "Submit" buttons.

        button_clicked = request.POST.get("button_clicked")

        if button_clicked == "cancel":
            # The user cancelled -> redirect back to the "suggest" view.
            return HttpResponseRedirect("/do")

        if button_clicked == "finished":
            # The user clicked on the "Finished" button.  Record this activity,
            # and then go back to the "suggest" view.
            task = Task.objects.get(id=task_id)
            seconds = int(request.POST.get("start_time"))

            start_time = utils.seconds_to_datetime(seconds)
            end_time = utils.current_datetime()

            activities.record_activity(task, start_time, end_time)

            return HttpResponseRedirect("/do")

    # If we get here, we're displaying the form for the first time.  Do so.

    suggested_task = Task.objects.get(id=task_id)
    start_time = utils.datetime_to_seconds(utils.current_datetime())

    if suggested_task.min_time == suggested_task.max_time:
        suggested_time = "%d minutes" % suggested_task.min_time
    else:
        suggested_time = "a minimum of %d minutes, and no more than %d minutes," % (
            suggested_task.min_time,
            suggested_task.max_time,
        )

    beep_time = suggested_task.min_time * 60  # Minutes -> seconds.

    return render(
        request,
        "do.html",
        {
            "suggested_task": suggested_task,
            "start_time": start_time,
            "suggested_time": suggested_time,
            "beep_time": beep_time,
        },
    )
Example #2
0
def suggest(request):
    """ Select a task and suggest that the user works on it.

        This is the main view for the "do" mode.
    """
    # Get the details of the most recently worked-on task, if any.

    activity = activities.latest_activity()
    if activity['task'] == None:
        recent_task = None
    else:
        age = utils.current_datetime() - activity['ended_at']
        if age > datetime.timedelta(hours=1):
            recent_task = None
        else:
            time_spent = \
                utils.format_seconds_for_display(activity['time_spent'])

            did_minimum = (activity['time_spent'] > activity['task'].min_time)

            recent_task = {'label'       : activity['task'].label,
                           'time_spent'  : time_spent,
                           'did_minimum' : did_minimum}

    # Calculate the summary of the tasks worked on so far today.

    started_at = utils.start_of_day() #.replace(day=17)
    ended_at   = utils.current_datetime()

    summary = activities.calc_task_summary(started_at, ended_at)

    # Convert the task tree into a "table" of nested activities.

    task_table = []

    def _add_tree(tree, task_table, indent=0):
        """ Recursively add the tree to the task table.
        """
        for node in tree:
            time_spent = utils.format_seconds_for_display(node['time_spent'])
            indent_str = " " * 8
            task_table.append({'task'       : node['task'],
                               'time_spent' : time_spent,
                               'prefix'     : indent_str * indent})
            _add_tree(node['children'], task_table, indent=indent+1)

    _add_tree(summary['summary'], task_table)

    # Choose the suggested task to work on next.

    suggested_task = chooser.choose_next_task()
    if suggested_task != None:
        start_url = "/do/" + str(suggested_task.id)
    else:
        start_url = None

    # Finally, display the suggested task to the user.

    total_time   = utils.format_seconds_for_display(summary['tot_time'])
    finished_url = "/"

    return render(request, "suggest.html",
                  {'recent_task'    : recent_task,
                   'task_table'     : task_table,
                   'total_time'     : total_time,
                   'suggested_task' : suggested_task,
                   'start_url'      : start_url,
                   'finished_url'   : finished_url})
Example #3
0
def reflect(request):
    """ Respond to the "/reflect" URL.

        This is the main view for the "reflect" mode.
    """
    # Calculate a summary of the tasks worked on over the past 7 days.

    started_at = utils.start_of_day() - datetime.timedelta(days=7)
    ended_at   = utils.current_datetime()

    weekly_summary = activities.calc_task_summary(started_at, ended_at)

    # Now calculate a summary for each day of the week, grouping the results by
    # day and storing the results in a format which makes it easy for us to
    # generate the final table.

    day_start = started_at
    day_end   = started_at + datetime.timedelta(days=1) \
              - datetime.timedelta(seconds=1)

    daily_task_times = [] # List of times spent on the various tasks each day.
                          # For each day, the list item will be a dictionary
                          # with the following entries:
                          #
                          #     'day_label'
                          #
                          #         The label to use to identify this day.
                          #
                          #     'task_times'
                          #
                          #         A dictionary mapping each Task record ID to
                          #         the time spent on that task, as an integer
                          #         number of seconds.

    start_of_today = utils.start_of_day()
    while day_start <= start_of_today:
        weekday = day_start.date().weekday()
        day_label = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
                     "Saturday", "Sunday"][weekday]

        daily_summary = activities.calc_task_summary(day_start, day_end)

        def _add_task_times(daily_summary, task_times):
            """ Recursively add 'daily_summary' to the 'task_times' dictionary.
            """
            for node in daily_summary:
                task_times[node['task'].id] = node['time_spent']
                _add_task_times(node['children'], task_times)

        task_times = {}
        _add_task_times(daily_summary['summary'], task_times)

        daily_task_times.append({'day_label'  : day_label,
                                 'task_times' : task_times})

        day_start = day_start + datetime.timedelta(days=1)
        day_end   = day_end   + datetime.timedelta(days=1)

    daily_task_times.reverse() # Show newest day first.

    # Process the weekly task summary, building a master list of all tasks
    # worked on during the week.

    tasks = [] # List of tasks worked on during the week.  Each list item is a
               # dictionary with the following entries:
               #
               #     'task'
               #
               #         The Task object to work on.
               #
               #     'label'
               #
               #         The (possibly indented) label to use for this task.
               #
               #     'time_spent'
               #
               #         The total amount of time spent on this task over the
               #         last week, as an integer number of seconds.

    def _add_tree(tree, task, indent=0):
        """ Recursively add a summary tree to the list of tasks.
        """
        for node in tree:
            label = "&nbsp;" * indent * 8 + node['task'].label
            tasks.append({'task'       : node['task'],
                          'label'      : label,
                          'time_spent' : node['time_spent']})

            _add_tree(node['children'], tasks, indent=indent+1)

    _add_tree(weekly_summary['summary'], tasks)

    # Calculate the totals to display at the bottom of the table.

    weekly_total = 0 # Total time spent for the week, in seconds.
    for task in tasks:
        weekly_total = weekly_total + task['time_spent']

    daily_totals = [] # Total time spent per day, in seconds.
    for day in daily_task_times:
        daily_total = 0
        for task_time in day['task_times'].values():
            daily_total = daily_total + task_time
        daily_totals.append(daily_total)

    # Using the calculated information, build a single large table representing
    # the entire summary.

    summary_table = [] # List of [col][row] entries.  Each list item is a
                       # dictionary with the following entries:
                       #
                       #    'text'
                       #
                       #        The text to be displayed.
                       #
                       #    'style'
                       #
                       #        The CSS style to apply to this table item.

    # Add the heading row to the top of the table.

    row = []
    row.append({'text'  : "&nbsp;",
                'style' : "heading"})
    row.append({'text'  : "Last 7 Days",
                'style' : "heading border-b"})
    row.append({'text'  : "&nbsp;",
                'style' : "heading"})

    for day in daily_task_times:
        row.append({'text'  : day['day_label'],
                    'style' : "heading border-b"})

    summary_table.append(row)

    # Add each task in turn.

    for task in tasks:
        row = []
        row.append({'text'  : task['label'] + "&nbsp;&nbsp;",
                    'style' : "heading left"})
        row.append({'text'  : utils.seconds_to_hms(task['time_spent']),
                    'style' : "body border-lrb"})
        row.append({'text'  : "&nbsp;",
                    'style' : "body"})

        task_id = task['task'].id
        for day in daily_task_times:
            if task_id in day['task_times']:
                task_time = utils.seconds_to_hms(day['task_times'][task_id])
            else:
                task_time = "&nbsp;"

            row.append({'text'  : task_time,
                        'style' : "body border-lrb"})

        summary_table.append(row)

    # Add the total times to the bottom of the table.

    row = []
    row.append({'text'  : "&nbsp;",
                'style' : "heading right"})
    row.append({'text'  : utils.seconds_to_hms(weekly_total),
                'style' : "body border-t"})
    row.append({'text'  : "&nbsp;",
                'style' : "body"})

    for daily_total in daily_totals:
        row.append({'text'  : utils.seconds_to_hms(daily_total),
                    'style' : "body border-t"})

    summary_table.append(row)

    # Finally, display the table to the user.

    return render(request, "reflect.html",
                  {'table' : summary_table})
Example #4
0
def choose_next_task(parent):
    """ Choose the next task to work on from among the given task's children.

        Upon completion, we return the Task object to work on next, or None if
        there are no active task objects.
    """
    # Build a list of the active tasks which have the given parent.

    tasks = []
    for task in Task.objects.filter(parent=parent, status=Task.STATUS_ACTIVE):
        tasks.append({'task' : task})

    # Calculate the latest activity for each task.

    for task in tasks:
        task['latest_activity'] = \
            activities.latest_activity_for_task(task['task'])

    # If any of these tasks has never been worked on, choose the first one.

    for task in tasks:
        if task['latest_activity'] == None:
            return task['task']

    # If we get here, every task has been worked on at least once.  Choose the
    # date of the oldest task as our starting point.

    start_time = None
    for task in tasks:
        if start_time == None or start_time > task['latest_activity']:
            start_time = task['latest_activity']

    end_time = utils.current_datetime()

    # If the selected start time is less than one hour ago, extend it out to
    # the last hour to give a more representative sample.

    if end_time - start_time < datetime.timedelta(hours=1):
        start_time = end_time - datetime.timedelta(hours=1)

    # Calculate the amount of time spent on each of these activities since
    # the oldest task.

    for task in tasks:
        task['time_spent'] = activities.time_spent_on_task(task['task'],
                                                           start_time,
                                                           end_time)

    # Calculate the total amount of time spent on all tasks thus far.

    tot_time_spent = 0
    for task in tasks:
        tot_time_spent = tot_time_spent + task['time_spent']

    # Calculate the relative amount of time spent on each task, and store this
    # as a number in the range 0..1.

    for task in tasks:
        task['relative_time_spent'] = \
                float(task['time_spent']) / float(tot_time_spent)

    # Given the desired weightings of each task, calculate the desired relative
    # time spent.

    tot_weighting = 0.00
    for task in tasks:
        tot_weighting = tot_weighting + task['task'].weighting

    if tot_weighting == 0:
        # We can't decide -> return the first task (if any).
        if len(tasks) > 0:
            return tasks[0]['task']
        else:
            return None

    for task in tasks:
        task['desired_relative_time_spent'] = \
                task['task'].weighting / tot_weighting

    # Calculate the discrepency between the desired time spent and the actual
    # time spent.

    for task in tasks:
        task['discrepency'] = task['desired_relative_time_spent'] \
                            - task['relative_time_spent']

    # Finally, choose the task with the biggest (positive) discrepency.

    biggest_task        = None
    biggest_discrepency = 0.00

    for task in tasks:
        discrepency = task['discrepency']
        if biggest_task == None or discrepency > biggest_discrepency:
            biggest_discrepency = discrepency
            biggest_task        = task['task']

    # Testing:

    print
    print "Choosing a task:"
    print
    for task in tasks:
        print "  " + task['task'].label + ":"
        print "    time_spent = ", task['time_spent']
        print "    relative_time_spent = ", task['relative_time_spent']
        print "    desired_relative_time_spent = ", \
                            task['desired_relative_time_spent']
        print "    discrepency = ", task['discrepency']
        print

    # Return the task back to the caller.

    return biggest_task