Added user-specific task list.

This commit is contained in:
2016-10-05 10:30:10 +02:00
parent 60c13615bf
commit 75e6b39069
7 changed files with 110 additions and 15 deletions

View File

@@ -7,8 +7,8 @@ from attract.node_types.task import node_type_task
log = logging.getLogger(__name__)
def fetch_task_parent_info(node):
"""Extends the node with some parent info.
def fetch_task_extra_info(node):
"""Extends the node with some info about its parent and project.
This allows us to link to the shot the task is attached to.
However, such a link requires at least knowing the parent node type,
@@ -18,11 +18,18 @@ def fetch_task_parent_info(node):
if node.get('node_type') != node_type_task['name']:
return
fetch_task_parent_info(node)
fetch_task_project_info(node)
def fetch_task_parent_info(node):
"""Store node parent info in node['_parent_info']."""
parent_id = node.get('parent')
if not parent_id:
return
nodes_coll = current_app.data.driver.db['nodes']
nodes_coll = current_app.db()['nodes']
parent = nodes_coll.find_one({'_id': parent_id},
projection={'node_type': 1,
'name': 1})
@@ -35,11 +42,32 @@ def fetch_task_parent_info(node):
node['_parent_info'] = parent
def fetch_task_project_info(node):
"""Store node project info in node['_project_info']."""
project_id = node.get('project')
if not project_id:
log.warning('Task node %s has no project!', node['_id'])
return
proj_coll = current_app.db()['projects']
project = proj_coll.find_one({'_id': project_id},
projection={'name': 1,
'url': 1})
if project is None:
log.warning("Task node %s has project %s, but the project doesn't exist.",
node['_id'], project_id)
return
project.pop('_id') # always there, but also already included in the node.
node['_project_info'] = project
def fetch_tasks_parent_info(nodes):
for node in nodes['_items']:
fetch_task_parent_info(node)
fetch_task_extra_info(node)
def setup_app(app):
app.on_fetched_item_nodes += fetch_task_parent_info
app.on_fetched_item_nodes += fetch_task_extra_info
app.on_fetched_resource_nodes += fetch_tasks_parent_info

View File

@@ -2,6 +2,7 @@ import logging
from flask import Blueprint, render_template, request
import flask
import flask_login
import pillarsdk
from pillar.web.system_util import pillar_api
@@ -19,8 +20,24 @@ log = logging.getLogger(__name__)
@blueprint.route('/')
def index():
user = flask_login.current_user
if not user.is_authenticated:
return render_template('attract/tasks/index.html')
api = pillar_api()
# TODO: also include tasks assigned to any of the user's groups.
tasks = pillarsdk.Node.all({
'where': {
'properties.assigned_to.users': user.objectid,
'node_type': node_type_task['name'],
}
}, api=api)
return render_template('attract/tasks/for_user.html',
tasks=tasks['_items'],
task_count=tasks['_meta']['total'])
@blueprint.route('/<task_id>', methods=['DELETE'])
def delete(task_id):
@@ -86,7 +103,6 @@ def save(project, task_id):
@perproject_blueprint.route('/create', methods=['POST'])
@attract_project_view()
def create_task(project):
task_type = request.form['task_type']
parent = request.form.get('parent', None)

View File

@@ -15,16 +15,18 @@ function _remove_task_from_list(task_id) {
/**
* Open an item such as tasks/shots in the #item-details div
*/
function item_open(item_id, item_type, pushState)
function item_open(item_id, item_type, pushState, project_url)
{
if (item_id === undefined || item_type === undefined) {
throw new ReferenceError("item_open(" + item_id + ", " + item_type + ") called.");
}
var project_url = ProjectUtils.projectUrl();
if (typeof project_url === 'undefined') {
project_url = ProjectUtils.projectUrl();
if (typeof project_url === 'undefined') {
throw new ReferenceError("ProjectUtils.projectUrl() undefined");
}
}
if ($(window).scrollTop() > 0) {
$("html, body").animate({scrollTop: 0 }, '500', 'swing');
@@ -76,9 +78,10 @@ function item_open(item_id, item_type, pushState)
);
}
function task_open(task_id)
// Fine if project_url is undefined, but that requires ProjectUtils.projectUrl().
function task_open(task_id, project_url)
{
item_open(task_id, 'task');
item_open(task_id, 'task', true, project_url);
if (ProjectUtils.context() == 'shot'){
$('[id^="shot-"]').removeClass('active');
@@ -360,6 +363,7 @@ $(function() {
$("a.task-link[data-task-id]").click(function(e) {
e.preventDefault();
var task_id = e.delegateTarget.dataset.taskId;
task_open(task_id);
var project_url = e.delegateTarget.dataset.projectUrl; // fine if undefined
task_open(task_id, project_url);
});
});

View File

@@ -6,6 +6,8 @@ $color-background-nav: hsl(hue($color-background), 20%, 25%)
$color-background-nav-light: hsl(hue($color-background), 20%, 35%)
$color-background-nav-dark: hsl(hue($color-background), 20%, 15%)
$color-background-active: #dafff5 // background colour for active items.
$font-body: 'Roboto'
$font-headings: 'Lato'
$font-size: 14px

View File

@@ -79,7 +79,7 @@
border-color: $color-background-dark
border-right-color: $color-primary
text-decoration: none
background-color: rgba($color-background, .5)
background-color: $color-background-active
.status-indicator
transform: scale(1.1)

View File

@@ -36,7 +36,7 @@
| {% block footer_scripts %}
script.
{% if open_task_id %}
$(function() { task_open('{{ open_task_id }}'); });
$(function() { item_open('{{ open_task_id }}', 'task', false); });
{% endif %}
script(src="{{ url_for('static_attract', filename='assets/js/vendor/clipboard.min.js')}}")

View File

@@ -0,0 +1,45 @@
| {% extends 'attract/layout.html' %}
| {% block bodyattrs %}{{ super() }} data-context='task'{% endblock %}
| {% block page_title %}Tasks for you{% endblock %}
| {% block body %}
#col_main
.col_header.task-list-header
| Your tasks ({{ task_count }})
#task-list.col-list
| {% for task in tasks %}
//- NOTE: this is tightly linked to the JS in tasks.js, function task_add()
a.col-list-item.task-list-item(
class="status-{{ task.properties.status }} task-link",
title="In project '{{ task._project_info.name }}'",
href="{{ url_for('attract.tasks.perproject.view_task', project_url=task._project_info.url, task_id=task._id) }}")
span.status-indicator
span.name {{ task.name }}
span.type {{ task.properties.task_type }}
| {% endfor %}
.col-splitter
#col_right
.col_header
span.header_text
#status-bar
#item-details
.item-details-empty
| Select a Task
| {% endblock %}
| {% block footer_scripts %}
script.
{% if open_task_id %}
$(function() { task_open('{{ open_task_id }}'); });
{% endif %}
script(src="{{ url_for('static_attract', filename='assets/js/vendor/clipboard.min.js')}}")
script(src="{{ url_for('static_attract', filename='assets/js/vendor/jquery-resizable.min.js')}}")
script.
$("#col_main").resizable({
handleSelector: ".col-splitter",
resizeHeight: false
});
| {% endblock %}