[chronojump-server] Tasks funcionality enabled again. - Now the tasks can be parametrized or free - The description of
- From: Marcos Venteo Garcia <mventeo src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [chronojump-server] Tasks funcionality enabled again. - Now the tasks can be parametrized or free - The description of
- Date: Sun, 25 Jun 2017 09:30:23 +0000 (UTC)
commit 14ed23ab28ed6eb84ededd7113f7091696100d58
Author: Marcos Venteo García <mventeo gmail com>
Date: Sun Jun 25 11:30:10 2017 +0200
Tasks funcionality enabled again.
- Now the tasks can be parametrized or free
- The description of the parametrized tasks is built based on values (In progress)
- Task Modal Form has not validation for the moment.
chronojumpserver/__init__.py | 7 +-
chronojumpserver/api.py | 77 +++-
chronojumpserver/js/player_detail.js | 2 -
chronojumpserver/js/players.js | 490 +++++++++++++-------
chronojumpserver/js/results.js | 2 -
chronojumpserver/models.py | 75 ++--
.../static/{ => images}/chronojump-logo.png | Bin 18840 -> 18840 bytes
chronojumpserver/static/images/no_image.png | Bin 0 -> 307 bytes
chronojumpserver/templates/index.html | 4 +-
chronojumpserver/templates/layout.html | 4 +-
chronojumpserver/templates/player_list.html | 190 +++++----
chronojumpserver/tests/populate_test_data.py | 89 +++-
chronojumpserver/views.py | 2 +-
13 files changed, 625 insertions(+), 317 deletions(-)
---
diff --git a/chronojumpserver/__init__.py b/chronojumpserver/__init__.py
index 1f6ae6c..57f2002 100755
--- a/chronojumpserver/__init__.py
+++ b/chronojumpserver/__init__.py
@@ -52,7 +52,12 @@ def populate_test_data():
"""Populate the tables with some test data, for demo purpose."""
click.echo('Creating players...')
from chronojumpserver.tests.populate_test_data import TestData
- TestData().create_players()
+ # Create test players
+ #TestData().create_players()
+ # Create test stations
+ TestData().create_stations()
+ # Create Test exercises
+ TestData().create_exercises()
# IMPORTANT: imports always at the end of the module, after app is created
diff --git a/chronojumpserver/api.py b/chronojumpserver/api.py
index 8cebc99..7ce1b55 100755
--- a/chronojumpserver/api.py
+++ b/chronojumpserver/api.py
@@ -4,7 +4,8 @@
"""
from chronojumpserver import app
-from chronojumpserver.models import Person, ResultEncoder, Station
+from chronojumpserver.database import db_session
+from chronojumpserver.models import Person, ResultEncoder, Station, Task
from flask import jsonify, request
from time import sleep
import os
@@ -25,7 +26,19 @@ def get_all_players():
_player['tasks'] = []
# Add description of active tasks
for task in player.active_tasks:
- _player['tasks'].append({'description': task.description})
+ _player['tasks'].append({'id': task.id,
+ 'description': task.comment,
+ 'type': task.taskType,
+ 'stationId': task.stationId,
+ 'station': task.station.name,
+ 'exerciseId': task.exerciseId,
+ 'exercise': task.exercise.name,
+ 'sets': task.sets,
+ 'nreps': task.nreps,
+ 'load': task.load,
+ 'speed': task.speed,
+ 'percentMaxSpeed': task.percentMaxSpeed,
+ 'laterality': task.laterality})
players.append(_player)
return jsonify(data=players)
@@ -82,7 +95,61 @@ def register_rfid():
return response
-@app.route('/api/v1/tasks/new')
-def add_new_task():
+@app.route('/api/v1/tasks', methods=['PUT', 'DELETE'])
+def add_modify_delete_task():
"""API to register a new task that could be free or parametrized."""
- pass
+ if request.method == 'PUT':
+ personId = request.form['playerId']
+ stationId = request.form['stationId']
+ exerciseId = request.form['exerciseId']
+ taskType = request.form['taskType']
+ taskId = request.form.get('taskId', None)
+ description = request.form.get('description', '')
+ # task_id = request.form['taskId']
+ sets = request.form.get('sets', -1)
+ nreps = request.form.get('reps', -1)
+ load = request.form.get('load', -1)
+ speed = request.form.get('speed', -1)
+ percentMaxSpeed = request.form.get('percentMaxSpeed', -1)
+ laterality = request.form.get('laterality', -1)
+ if taskType == 'F':
+ load = -1
+ speed = -1
+ sets = -1
+ nreps = -1
+ percentMaxSpeed = -1
+ laterality = ''
+ # Create the Task
+ if int(taskId) == -1:
+ """ New task."""
+ t = Task(taskType=taskType, personId=personId, stationId=stationId,
+ exerciseId=exerciseId, sets=sets, nreps=nreps, load=load,
+ speed=speed, percentMaxSpeed=percentMaxSpeed,
+ laterality=laterality, comment=description)
+ db_session.add(t)
+ db_session.commit()
+ else:
+ # Get the task with the id."""
+ db_session.query(Task).filter_by(id=taskId).update({
+ 'taskType': taskType,
+ 'stationId':stationId,
+ 'exerciseId':exerciseId,
+ 'sets':sets,
+ 'nreps':nreps,
+ 'load':load,
+ 'speed':speed,
+ 'percentMaxSpeed':percentMaxSpeed,
+ 'laterality':laterality,
+ 'comment':description
+ })
+ db_session.commit()
+ elif request.method == "DELETE":
+ """Delete the task."""
+ taskId = request.form.get('taskId', None)
+ t = Task.query.filter(Task.id == taskId).first()
+ db_session.delete(t)
+ db_session.commit()
+
+
+
+ return jsonify(msg='Task has been successfully added')
diff --git a/chronojumpserver/js/player_detail.js b/chronojumpserver/js/player_detail.js
index 0c99e77..2c33d61 100644
--- a/chronojumpserver/js/player_detail.js
+++ b/chronojumpserver/js/player_detail.js
@@ -3,8 +3,6 @@
* Author: Marcos Venteo <mventeo gmail com>
* version: 1.0
*
- * Copyright (C) 2017 Xavier de Blas <xaviblas gmail com>
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
diff --git a/chronojumpserver/js/players.js b/chronojumpserver/js/players.js
index ef88e19..9f05db9 100755
--- a/chronojumpserver/js/players.js
+++ b/chronojumpserver/js/players.js
@@ -3,8 +3,6 @@
* Author: Marcos Venteo <mventeo gmail com>
* version: 1.0
*
- * Copyright (C) 2017 Xavier de Blas <xaviblas gmail com>
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -20,32 +18,83 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+/* Function to add/Modify or delete a task called from Task Modal Form. */
function addModifyDeleteTask(action) {
- var player_id = $('#player-id').val();
- var description = $('#task-comment').val();
- var task_id = $('#task-id').val();
-
- // Set the method
- if (action == 2) {
- method = 'DELETE';
- } else {
- // For both Add or Modify we'll use PUT method
- method = 'PUT';
- }
- $('#myModal').modal('hide');
- $.ajax({
- url: '/api/v1/tasks',
- method: method,
- data: {
- player_id: player_id,
- description: description,
- task_id: task_id
+ /*
+ action could be : 0 - Add
+ 1 - Modify
+ 2 - delete
+ */
+ /* Function to add or modify the task */
+ console.log('Adding or Modifying a task');
+ var playerId = $('#playerId').val();
+ var description = $('#taskComment').val();
+ var taskId = $('#taskId').val();
+ if ($('#paramTask').hasClass('active')) {
+ var taskType = 'P';
+ } else {
+ var taskType = 'F';
+ }
+ var stationId = $('#stationSelect').val();
+ var exerciseId = $('#exerciseSelect').val();
+ var sets = $('#numSets').val();
+ var reps = $('#numReps').val();
+ var load = $('#taskLoad').val();
+ var speed = $('#taskSpeed').val();
+ var percentMaxSpeed = $('#taskpercentMaxSpeed').val();
+ var laterality = $('#taskLaterality').val();
+
+
+ console.log('PlayerId: ' + playerId);
+ console.log('TaskId: ' + taskId);
+ console.log('Description: ' + description);
+ console.log('Task Type: ' + taskType);
+ console.log('Station selected: ' + stationId);
+ console.log('Exercise selected: ' + exerciseId);
+ console.log('Sets selected: ' + sets);
+ console.log('Repeats selected: ' + reps);
+ console.log('Speed selected: ' + load);
+ console.log('percentMaxSpeed selected: ' + percentMaxSpeed);
+ console.log('laterality selected: ' + laterality);
+
+
+
+ // Set the method
+ if (action == 2) {
+ method = 'DELETE';
+ } else {
+ // For both Add or Modify we'll use PUT method
+ method = 'PUT';
}
- }).done(function() {
- var table = $('#players').DataTable();
- table.ajax.reload(null, false);
+ $('#myModal').modal('hide');
+ $.ajax({
+ url: '/api/v1/tasks',
+ method: method,
+ data: {
+ playerId: playerId,
+ description: description,
+ taskId: taskId,
+ taskType: taskType,
+ stationId: stationId,
+ exerciseId: exerciseId,
+ sets: sets,
+ reps: reps,
+ load: load,
+ speed: speed,
+ percentMaxSpeed: percentMaxSpeed,
+ laterality: laterality
+ }
+ }).done(function(data) {
+ console.log(data);
+ console.log('The task has been added');
+ var table = $('#players').DataTable();
+ table.ajax.reload(null, false);
+ }).fail(function(xhr, status, error){
+ var err = eval("(" + xhr.responseText + ")");
+ alert(err.msg);
+ }).always(function(){
- });
+ });;
}
@@ -54,155 +103,256 @@ function addModifyDeleteTask(action) {
Load Players and tasks
*/
$(document).ready(function() {
-
- var table = $('#players').DataTable({
- "columns": [{
- type: "num",
- title: "id",
- data: 'id',
- visible: false
- },
- {
- type: "html",
- title: "",
- data: 'imageName',
- orderable: false,
- render: function(value) {
- var href = '/static/images/photos/' + value;
- var src = '/static/images/photos/' + value;
- var html = '<a href="' + href + '" class="player-link"><img src="' + src + '" class="img-circle"
height="60"></a>';
- return html;
+
+ var table = $('#players').DataTable({
+ "columns": [{
+ type: "num",
+ title: "id",
+ data: 'id',
+ visible: false
+ },
+ {
+ type: "html",
+ title: "",
+ data: 'imageName',
+ orderable: false,
+ render: function(value, type, row) {
+ // If player has a photo saved, used it
+ var href = '/player/' + row.id;
+ if (value) {
+ var src = '/static/images/photos/' + value;
+ } else {
+ // Otherwise show the no_image icon
+ var src = '/static/images/no_image.png';
+ }
+ var html = '<a href="' + href + '" class="player-link"><img src="' + src + '"
class="img-circle" height="32"></a>';
+ return html;
+ }
+ },
+ {
+ type: "html",
+ data: "name",
+ title: "Nom",
+ render: function(data, type, row) {
+ return '<a href="/player/' + row.id + '">' + data + '</a>';
+ }
+ },
+ {
+ type: "num",
+ data: 'weight',
+ title: "Pes",
+ render: $.fn.dataTable.render.number('', ',', 2)
+ },
+ {
+ type: "num",
+ data: "height",
+ title: "Alçada",
+ render: $.fn.dataTable.render.number('', ',', 2)
+ },
+ /*{
+ type: "html",
+ data: "rfid",
+ title: "RFID"
+ },*/
+ {
+ type: "html",
+ title: "Tasques",
+ data: "tasks",
+ orderable: false,
+ render: function(value) {
+ if (value.length > 0) {
+ var html = "<ol>";
+ $.each(value, function(index, task) {
+ console.log(value);
+ if (task.type == 'F') {
+ var _desc = task.description;
+ } else {
+ // Compose the description based on values
+ var _desc = task.nreps + " repeticions ";
+ _desc += "de " + task.exercise;
+ _desc += " a l'estació " + task.station;
+ }
+ html += "<li><a class='task-link' "
+ html += "data-task-id='" + task.id + "' "
+ html += "data-task-type='" + task.type + "'"
+ html += "data-task-station-id='" + task.stationId + "'"
+ html += "data-task-exercise-id='" + task.exerciseId + "'"
+ html += "data-task-sets='" + task.sets + "'"
+ html += "data-task-nreps='" + task.nreps + "'"
+ html += "data-task-load='" + task.load + "'"
+ html += "data-task-speed='" + task.speed + "'"
+ html += "data-task-percent-max-speed='" + task.percentMaxSpeed + "'"
+ html += "data-task-laterality='" + task.laterality + "'"
+ html += ">" + _desc + "</a></li>";
+ });
+ html += "</ol>";
+ return html;
+ } else {
+ return '<ol></ol>';
+ }
+ }
+ },
+ {
+ type: "html",
+ data: null,
+ title: "",
+ orderable: false,
+ render: function(val) {
+ return "<button class='btn btn-primary'>Afegeix</button>"
+ }
+ }
+ ],
+ "pageLength": 10,
+ "order": [
+ [2, 'asc']
+ ],
+ "ajax": "/api/v1/players",
+ "processing": true,
+ "severSide": true,
+ "language": {
+ "lengthMenu": "Mostrant _MENU_ jugadors per pàgina",
+ "zeroRecords": "No hi han jugadors per mostrar",
+ "info": "Mostrant els jugadors _START_ a _END_ d'un total de _TOTAL_",
+ "infoEmpty": "La busqueda no ha retornat resultats",
+ "infoFiltered": "(filtrat de _MAX_ jugadors)",
+ "decimal": ",",
+ "thousands": ".",
+ "paginate": {
+ "first": '<i class="fa fa-fast-backward"></i>',
+ "last": '<i class="fa fa-fast-forward"></i>',
+ "next": '<i class="fa fa-forward"></i>',
+ "previous": '<i class="fa fa-backward"></i>'
+ },
+ "search": "Cerca:"
}
- },
- {
- type: "html",
- data: "name",
- title: "Nom",
- render: function(data, type, row) {
- return '<a href="/player/' + row.id + '">' + data + '</a>';
+ });
+
+ /* To show the modal form to add the tasks */
+ $('#players').on('click', 'button', function() {
+ var player = table.row($(this).parents('tr')).data();
+
+ $('#modal-title').text('Afegir nova tasca per ' + player.name);
+ $('#myModal').modal();
+ $('#playerId').val(player.id);
+ $('#taskId').val('-1');
+ // Modal is empty
+ $('#stationSelect').val('');
+ refreshExercises(null);
+ $('#numSets').val(-1);
+ $('#numReps').val(-1);
+ $('#taskLoad').val(-1);
+ $('#taskSpeed').val(-1);
+ $('#taskpercentMaxSpeed').val(-1);
+ $('#taskLaterality').val('');
+ $('#taskComment').val('');
+ $('#taskParamSelector').removeClass('active');
+ $('#taskFreeSelector').removeClass('active');
+ $('#paramTask').removeClass('active');
+ $('#paramFree').removeClass('active');
+ $('#taskParamSelector').addClass('active');
+ $('#paramTask').addClass('active');
+ $('#btnDeleteTask').removeClass('show').addClass('hidden');
+ $('#btnUpdateTask').removeClass('show').addClass('hidden');
+ $('#btnAddTask').removeClass('hidden').addClass('show');
+ });
+
+ /* To show the model to modify the task */
+ $('#players').on('click', 'a.task-link', function() {
+ var player = table.row($(this).parents('tr')).data();
+ console.log('Modify task ' + $(this).text());
+ $('#taskComment').val($(this).text());
+ $('#modal-title').text('Modificar tasca #' + $(this).attr('data-task-id') + ' per ' + player.name);
+ var taskType = $(this).attr('data-task-type');
+ var taskSets = $(this).attr('data-task-sets');
+ var taskReps = $(this).attr('data-task-nreps');
+ var taskLoad = $(this).attr('data-task-load');
+ var taskSpeed = $(this).attr('data-task-speed');
+ var taskPercentMaxSpeed = $(this).attr('data-task-percent-max-speed');
+ var taskLaterality = $(this).attr('data-task-laterality');
+ var stationId = $(this).attr('data-task-station-id');
+ var exerciseId = $(this).attr('data-task-exercise-id');
+
+ $('#taskParamSelector').removeClass('active');
+ $('#taskFreeSelector').removeClass('active');
+ $('#paramTask').removeClass('active');
+ $('#paramFree').removeClass('active');
+ console.log(taskType);
+ if (taskType == 'P') {
+ // Show panel for parametrized tasks
+ $('#taskParamSelector').addClass('active');
+ $('#paramTask').addClass('active');
+
+ } else {
+ // Show the panel for free tasks
+ $('#taskFreeSelector').addClass('active');
+ $('#freeTask').addClass('active');
}
- },
- {
- type: "num",
- data: 'weight',
- title: "Pes",
- render: $.fn.dataTable.render.number('', ',', 2)
- },
- {
- type: "num",
- data: "height",
- title: "Alçada",
- render: $.fn.dataTable.render.number('', ',', 2)
- },
- /*{
- type: "html",
- data: "rfid",
- title: "RFID"
- },*/
- {
- type: "html",
- title: "Tasques",
- data: "tasks",
- orderable: false,
- render: function(value) {
- if (value.length > 0) {
- var html = "<ol>";
- $.each(value, function(index, task) {
- console.log(value);
- html += "<li><a class='task-link' data-task-id='" + task.id + "'>" + task.description +
"</a></li>";
+
+ // Fill the modal
+ $('#playerId').val(player.id);
+ $('#taskId').val($(this).attr('data-task-id'));
+ console.log('Change the station for' + stationId);
+ $('#stationSelect').val(stationId);
+ refreshExercises(exerciseId);
+ $('#numSets').val(taskSets);
+ $('#numReps').val(taskReps);
+ $('#taskLoad').val(taskLoad);
+ $('#taskSpeed').val(taskSpeed);
+ $('#taskpercentMaxSpeed').val(taskPercentMaxSpeed);
+ $('#taskLaterality').val(taskLaterality);
+
+ // Hide and show the buttons
+ $('#btnDeleteTask').removeClass('hidden').addClass('show');
+ $('#btnUpdateTask').removeClass('hidden').addClass('show');
+ $('#btnAddTask').removeClass('show').addClass('hidden');
+ $('#myModal').modal();
+
+ })
+
+ /* Each time the user selects a station, the exercise from this station is
+ refreshed. */
+ $('#stationSelect').on('change', function() {
+ //Ajax call to retrieve all the exercises. Call the function below.
+ refreshExercises(null)
+ });
+
+ /* Refresh the exercises based on the value selected in station */
+ function refreshExercises(exerciseId) {
+ var stationId = $('#stationSelect').val();
+
+ $('#exerciseSelect').find('option').remove().end();
+ $('#exerciseSelect').attr('disabled', true);
+ if (stationId != '') {
+ // AJAX Call to get the exercises of station selected
+ $.ajax({
+ url: '/api/v1/exercises',
+ method: "GET",
+ data: {
+ station_id: stationId
+ }
+ }).done(function(data) {
+ // Ok, add options in exercise Select
+ // First remove previous options
+
+ // Add all the exercises of the station
+ var count = 0;
+ $.each(data.exercises, function(i, item) {
+ count ++;
+ $('#exerciseSelect').append($('<option>', {
+ value: item.id,
+ text: item.name
+ }));
+
+ })
+ // If exerciseId is not Null, set the values
+ if (exerciseId) {
+ $('#exerciseSelect').val(exerciseId);
+ }
+ if (count > 0) {
+ $('#exerciseSelect').removeAttr('disabled');
+ }
});
- html += "</ol>";
- return html;
- } else {
- return '<ol></ol>';
- }
- }
- },
- {
- type: "html",
- data: null,
- title: "",
- orderable: false,
- render: function(val) {
- return "<button class='btn btn-primary'>Afegeix</button>"
}
- }
- ],
- "pageLength": 10,
- "order": [
- [2, 'asc']
- ],
- "ajax": "/api/v1/players",
- "processing": true,
- "severSide": true,
- "language": {
- "lengthMenu": "Mostrant _MENU_ jugadors per pàgina",
- "zeroRecords": "No hi han jugadors per mostrar",
- "info": "Mostrant els jugadors _START_ a _END_ d'un total de _TOTAL_",
- "infoEmpty": "La busqueda no ha retornat resultats",
- "infoFiltered": "(filtrat de _MAX_ jugadors)",
- "decimal": ",",
- "thousands": ".",
- "paginate": {
- "first": '<i class="fa fa-fast-backward"></i>',
- "last": '<i class="fa fa-fast-forward"></i>',
- "next": '<i class="fa fa-forward"></i>',
- "previous": '<i class="fa fa-backward"></i>'
- },
- "search": "Cerca:"
}
- });
-
- $('#players').on('click', 'button', function() {
- var player = table.row($(this).parents('tr')).data();
-
- $('#task-comment').val('');
- $('#modal-title').text('Afegir nova tasca per ' + player.name);
- $('#myModal').modal();
- $('#player-id').val(player.id);
- $('#task-id').val('');
- $('#task-comment').focus();
- $('#btnDeleteTask').removeClass('show').addClass('hidden');
- $('#btnUpdateTask').removeClass('show').addClass('hidden');
- $('#btnAddTask').removeClass('hidden').addClass('show');
- });
-
- $('#players').on('click', 'a.task-link', function() {
- var player = table.row($(this).parents('tr')).data();
- console.log('Modify task ' + $(this).text());
- $('#task-comment').val($(this).text());
- $('#modal-title').text('Modificar tasca #' + $(this).attr('data-task-id') + ' per ' + player.name);
- $('#myModal').modal();
- $('#player-id').val(player.id);
- $('#task-id').val($(this).attr('data-task-id'));
- $('#task-comment').focus();
- // Hide and show the buttons
- $('#btnDeleteTask').removeClass('hidden').addClass('show');
- $('#btnUpdateTask').removeClass('hidden').addClass('show');
- $('#btnAddTask').removeClass('show').addClass('hidden');
- })
-
- $('#stationSelect').on('change', function() {
- //alert("Load exercises from station ");
- $.ajax({
- url: '/api/v1/exercises',
- method: "GET",
- data: {
- station_id: $('#stationSelect').val()
- }
- }).done(function(data) {
- // Ok, add options in exercise Select
- // First remove previous options
- $('#exerciseSelect').find('option').remove().end();
- $.each(data.exercises, function(i, item) {
- console.log(item);
- $('#exerciseSelect').append($('<option>', {
- value: item.id,
- text: item.name
- }));
- $('#exerciseSelect').removeAttr('disabled');
- })
- });
- });
});
diff --git a/chronojumpserver/js/results.js b/chronojumpserver/js/results.js
index c337b7f..c07442f 100755
--- a/chronojumpserver/js/results.js
+++ b/chronojumpserver/js/results.js
@@ -3,8 +3,6 @@
* Author: Marcos Venteo <mventeo gmail com>
* version: 1.0
*
- * Copyright (C) 2017 Xavier de Blas <xaviblas gmail com>
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
diff --git a/chronojumpserver/models.py b/chronojumpserver/models.py
index 649e331..8dcf05b 100755
--- a/chronojumpserver/models.py
+++ b/chronojumpserver/models.py
@@ -5,15 +5,29 @@ from sqlalchemy import Column, Integer, String, Float, DateTime, Enum, ForeignKe
from sqlalchemy.types import Boolean, Enum
from sqlalchemy.orm import relationship
+class HelperMixin(object):
-class Person(Base):
+ id = Column(Integer, primary_key=True)
+
+ @classmethod
+ def getById(cls, object_id):
+ o = cls.query.filter(cls.id == object_id).first()
+ return o
+
+ @classmethod
+ def getByName(cls, name):
+ o = cls.query.filter(cls.name == name).first()
+ return o
+
+
+class Person(HelperMixin, Base):
"""Person model.
Relationships with other tables:
Person table has a one to many relationship with results
"""
__tablename__ = 'person'
- id = Column(Integer, primary_key=True)
+
name = Column(String(30), unique=True)
weight = Column(Float, nullable=False)
height = Column(Float, nullable=False)
@@ -60,12 +74,9 @@ class Person(Base):
db_session.add(self)
db_session.commit()
- @classmethod
- def getByName(cls, name):
- o = cls.query.filter(cls.name == name).first()
- return o
-class Station(Base):
+
+class Station(HelperMixin, Base):
"""Station model.
Relationships:
@@ -75,7 +86,6 @@ class Station(Base):
"""
__tablename__ = 'station'
- id = Column(Integer, primary_key=True)
name = Column(String(30), unique=True)
type = Column(String(1), nullable=True) # G - gravitory, I - inertial, S - sprint
@@ -90,8 +100,9 @@ class Station(Base):
order_by="asc(Exercise.name)",
primaryjoin="Exercise.stationId==Station.id")
- def __init__(self, name=None):
+ def __init__(self, name=None, type=None):
self.name = name
+ self.type = type
@property
def serialize(self):
@@ -106,7 +117,7 @@ class Station(Base):
-class Exercise(Base):
+class Exercise(HelperMixin, Base):
"""Exercise model.
Relationships:
@@ -122,7 +133,6 @@ class Exercise(Base):
UniqueConstraint('name', 'stationId'),
)
# Columns
- id = Column(Integer, primary_key=True)
name = Column(String(30))
stationId = Column('stationId', ForeignKey('station.id'), nullable=False)
percentBodyMassDisplaced = Column(Float)
@@ -139,12 +149,13 @@ class Exercise(Base):
order_by="desc(Task.ts)",
primaryjoin="Task.exerciseId==Exercise.id")
- def __init__(self, name=None, stationId=None):
+ def __init__(self, name=None, stationId=None, percentBodyMassDisplaced=None):
"""Initialize the object."""
self.name = name
if stationId is not None:
self.station = Station.query.filter(Station.id == stationId).first()
self.stationId = stationId
+ self.percentBodyMassDisplaced = percentBodyMassDisplaced
@property
def serialize(self):
@@ -170,7 +181,7 @@ class Task(Base):
__tablename__ = "task"
id = Column(Integer, primary_key=True)
taskType = Column(String(1)) # F - Free, P - Parametrized
- ts = Column(DateTime, default=datetime.utcnow)
+ ts = Column(DateTime, default=datetime.now)
personId = Column('personId', ForeignKey('person.id'))
person = relationship(Person, primaryjoin=personId == Person.id)
stationId = Column('stationId', ForeignKey('station.id'))
@@ -186,33 +197,33 @@ class Task(Base):
comment = Column(String(150))
done = Column(Boolean)
- def __init__(self, taskType=0, ts=None, stationId=None,
- personId=None,
- exerciseId=None, nreps=None, sets=-1, load=None, laterality=None,
- comment=None, done=False):
+ def __init__(self, taskType=None, ts=None, stationId=None, personId=None,
+ exerciseId=None, nreps=-1, sets=-1, load=-1, speed=-1,
+ percentMaxSpeed=-1,laterality=None, comment="", done=False):
"""Initialize a Task object."""
+ """laterality can have these values:
+ 'RL' is done by both arms or legs at the same time
+ 'R,L' is done first by right and later left (will be two sets on client)
+ 'R' only right
+ 'L' only left
+ """
+
self.taskType = taskType
- self.ts = ts
+ self.ts = ts if ts is not None else datetime.now()
self.personId = personId
self.stationId = stationId
self.exerciseId = exerciseId
- self.exercise = Exercise.query.filter(Exercise.id == exerciseId).first()
- self.station = Station.query.filter(Station.id == stationId).first()
- self.person = Person.query.filter(Person.id == personId).first()
- self.sets = sets
- self.nreps = nreps
+ self.exercise = Exercise.getById(exerciseId)
+ self.station = Station.getById(stationId)
+ self.person = Person.getById(personId)
+ self.sets = int(sets)
+ self.nreps = int(nreps)
self.load = load
+ self.speed = speed
+ self.percentMaxSpeed = percentMaxSpeed
self.laterality = laterality
self.done = done
- if taskType == 'P' and self.comment is None:
- # Use Catalan for the moment
- self.comment = "%d repeticions del exercisi %s " \
- "a l'estació %s amb una velocitat de %.2f " \
- "i pèrdua de %.2f%%" % ( self.nreps,
- self.exercise.name,
- self.station.name,
- self.load,
- self.loss)
+ self.comment = comment
@property
def serialize(self):
diff --git a/chronojumpserver/static/images/no_image.png b/chronojumpserver/static/images/no_image.png
new file mode 100644
index 0000000..360a32f
Binary files /dev/null and b/chronojumpserver/static/images/no_image.png differ
diff --git a/chronojumpserver/templates/index.html b/chronojumpserver/templates/index.html
index 0ebe088..0ee0db9 100755
--- a/chronojumpserver/templates/index.html
+++ b/chronojumpserver/templates/index.html
@@ -6,7 +6,7 @@
<div class="container-fluid header-index">
<div class="row">
<div class="col-md-6" style="margin-top:60px">
- <img class="img-responsive" src="{{ url_for('static', filename='chronojump-logo.png')}}"
height="180px">
+ <img class="img-responsive" src="{{ url_for('static', filename='images/chronojump-logo.png')}}"
height="180px">
</div>
<div class="col-md-6" style="margin-top:60px">
<h1 class="text-center text-uppercase">Chronojump Server</h1>
@@ -25,7 +25,7 @@
<h2 class="text-center text-uppercase">Opcions</h2>
</div>
<div class="col-md-6" >
- <img src="{{url_for('static', filename='images/logo-club.png')}}" class="img-responsive
center-block" width="200px"/>
+ <img src="{{url_for('static', filename='images/logo_club.png')}}" class="img-responsive
center-block" width="200px"/>
</div>
<div class="col-md-6">
<a class="btn btn-primary btn-lg btn-block" href="{{ url_for('show_results')}}">Resultats</a>
diff --git a/chronojumpserver/templates/layout.html b/chronojumpserver/templates/layout.html
index 3905877..335c7ff 100755
--- a/chronojumpserver/templates/layout.html
+++ b/chronojumpserver/templates/layout.html
@@ -24,7 +24,7 @@
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">
- <img alt="Brand" src="{{ url_for('static', filename='chronojump-logo.png')}}"
height="60px">
+ <img alt="Brand" src="{{ url_for('static', filename='images/chronojump-logo.png')}}"
height="48px">
</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
@@ -55,7 +55,7 @@
{% block script %}
<script src="{{ url_for('assets', filename='bootstrap/js/bootstrap.min.js')}}" /></script>
-
+
{% endblock %}
</body>
</html>
diff --git a/chronojumpserver/templates/player_list.html b/chronojumpserver/templates/player_list.html
index ed7f1ba..e427c28 100755
--- a/chronojumpserver/templates/player_list.html
+++ b/chronojumpserver/templates/player_list.html
@@ -1,95 +1,119 @@
-{% extends 'layout.html' %}
+{% extends 'layout.html' %} {% block head %} {{ super() }}
+<link href="{{ url_for('assets', filename='DataTables/media/css/dataTables.bootstrap.min.css') }}"
rel="stylesheet" /> {% endblock %} {% block content %}
-{% block head %}
-{{ super() }}
-<link href="{{ url_for('assets', filename='DataTables/media/css/dataTables.bootstrap.min.css') }}"
rel="stylesheet" />
-{% endblock %}
-
-{% block content %}
+<div class="clearfix">
+ <h2 class="pull-left">Llistat de Jugadors <a class="btn btn-primary " href="{{
url_for('add_player')}}">Afegir Jugador</a></h2>
- <div class="clearfix">
- <h2 class="pull-left">Llistat de Jugadors <a class="btn btn-primary " href="{{
url_for('add_player')}}">Afegir Jugador</a></h2>
+</div>
- </div>
+<div class="row" style="margin-top:20px">
+ <table id="players" class="table table-hovered" cellspacing="0" width="100%">
+ </table>
+</div>
+<div id="myModal" class="modal fade" tabindex="-1" role="dialog">
+ <div class="modal-dialog modal-lg" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal"
aria-label="Close"><span aria-hidden="true">×</span></button>
+ <h4 id="modal-title" class="modal-title">Afegir nova tasca per el jugador</h4>
+ </div>
+ <div class="modal-body">
+ <input type="hidden" id="playerId">
+ <input type="hidden" id="taskId">
+ <div class="" style="margin-bottom: 10px;">
+ <form class="form-inline" >
- <div class="row" style="margin-top:20px">
- <table id="players" class="table table-hovered" cellspacing="0" width="100%">
- </table>
- </div>
- <div id="myModal" class="modal fade" tabindex="-1" role="dialog">
- <div class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">×</span></button>
- <h4 id="modal-title" class="modal-title">Afegir nova tasca per el jugador</h4>
- </div>
- <div class="modal-body">
- <input type="hidden" id="player-id">
- <input type="hidden" id="task-id">
- <h4>Tipus de tasca</h4>
- <ul class="nav nav-pills" style="margin-bottom: 10px">
- <li role="presentation" class="active"><a href="#freeTask"
aria-controls="freeTask" role="tab" data-toggle="pill">Lliure</a></li>
- <li role="presentation"><a href="#paramTask"
aria-controls="freeTask" role="tab" data-toggle="pill">Parametritzada</a></li>
+ </form>
+ </div>
+ <nav class="navbar navbar-default">
+ <ul class="nav navbar-nav">
+ <li id="taskParamSelector" role="presentation"
class="active"><a href="#paramTask" aria-controls="paramTask" role="pill"
data-toggle="tab">Parametritzada</a></li>
+ <li id="taskFreeSelector" role="presentation"><a
href="#freeTask" aria-controls="freeTask" role="tab" data-toggle="pill">Lliure</a></li>
</ul>
- <form class="form-inline">
+
+ <form class="navbar-form navbar-right">
+
<div class="form-group">
- <label for="recipient-name"
class="control-label">Estació:</label>
- <select class="form-control" id="stationSelect">
- <option value="">Selecciona una
estació</option>
- {% for station in stations %}
- <option
value={{station.id}}>{{station.name}}</option>
- {% endfor %}
- </select>
- </div>
- <div class="form-group pull-right">
- <label for="recipient-name"
class="control-label">Exercici:</label>
- <select class="form-control" disabled
id="exerciseSelect">
- <option value=""></option>
- </select>
- </div>
+ <select name="station" class="form-control"
id="stationSelect">
+ <option value="">Selecciona una
estació</option>
+ {% for station in stations %}
+ <option
value={{station.id}}>{{station.name}}</option>
+ {% endfor %}
+ </select>
+ </div>
+ <div class="form-group ">
+ <select name="exercise" class="form-control" disabled
id="exerciseSelect">
+ <option value=""></option>
+ </select>
+ </div>
</form>
- <div class="tab-content">
- <div role="tabpanel" id="freeTask" class="panel panel-default
tab-pane active">
- <div class="panel-heading">Descripció</div>
- <div class="panel-body">
- <textarea rows=2 class="form-control" id="task-comment"></textarea>
- </div>
- </div>
- <div role="tabpanel" id="paramTask" class="tab-pane panel
panel-default">
- <div class="panel-heading">Paràmetres</div>
- <div class="panel-body">
+ <p class="navbar-text navbar-right">Estació/Excercisi:</p>
+ </nav>
- <form class="form-inline" style="margin-top: 10px">
- <div class="form-group">
- <label for="recipient-name"
class="control-label"># Repeticions:</label>
- <input type="number"
class="form-control" style="width:100px"></input>
- </div>
- <div class="form-group">
- <label for="recipient-name"
class="control-label">Velocitat:</label>
- <input type="number"
class="form-control" style="width:100px"></input>
- </div>
- <div class="form-group">
- <label for="recipient-name"
class="control-label">Perdua:</label>
- <input type="number"
class="form-control" style="width:100px"></input>
- </div>
- </form>
- </div>
- </div>
- </div>
- </div>
- <div class="modal-footer">
- <button type="button" class="pull-left btn btn-default"
data-dismiss="modal">Cancelar</button>
- <button id="btnAddTask" type="button" class="pull-right btn btn-primary hidden"
onclick="addModifyDeleteTask(0)">Afegeix la tasca</button>
- <button id="btnUpdateTask" type="button" class="pull-right
btn btn-primary" onclick="addModifyDeleteTask(1)">Modificar la tasca</button>
- <button id="btnDeleteTask" type="button" class="pull-right
btn btn-danger hidden" onclick="addModifyDeleteTask(2)">Eliminar</button>
- </div>
- </div><!-- /.modal-content -->
- </div><!-- /.modal-dialog -->
- </div><!-- /.modal -->
-{% endblock %}
+ <div class="tab-content">
+ <div role="tabpanel" id="paramTask" class="tab-pane active">
+
+ <form class="form-horizontal" style="margin-top: 10px">
+ <div class="form-group">
+ <label for="recipient-name" class="col-sm-2
control-label"># Series:</label>
+ <div class="col-sm-2">
+ <input id="numSets" name="numSets"
type="number" class="form-control" value="1" min="1"></input>
+ </div>
+ <label for="recipient-name"
class="col-sm-offset-4 col-sm-2 control-label"># Repeticions:</label>
+ <div class="col-sm-2">
+ <input id="numReps" name="numReps"
type="number" class="form-control" value="1" min="1"></input>
+ </div>
+ </div>
+ <div class="form-group">
+ <label for="recipient-name" class="col-sm-2
control-label">Carrega:</label>
+ <div class="col-sm-2">
+ <input id="taskLoad" name="taskLoad"
type="number" class="form-control" ></input>
+ </div>
+ <label for="recipient-name" class="col-sm-2
control-label">Velocitat:</label>
+ <div class="col-sm-2">
+ <input id="taskSpeed"
name="taskSpeed" type="number" class="form-control" ></input>
+ </div>
+ <label for="recipient-name" class="col-sm-2
control-label">% Vel. Max:</label>
+ <div class="col-sm-2">
+ <input id="taskpercentMaxSpeed"
name="taskpercentMaxSpeed" type="number" class="form-control" ></input>
+ </div>
+ </div>
+ <div class="form-group">
+ <label for="recipient-name" class="col-sm-2
control-label">Lateralitat:</label>
+ <div class="col-sm-10">
+ <select class="form-control"
id="taskLaterality" name="taskLaterality">
+ <option value="">Selecciona
lateralitat</option>
+ <option value="RL">RL - Ambes
extremitats</option>
+ <option value="R,L">R,L -
Primer amb la dreta, després amb l'esquerra</option>
+ <option value="R">R - Només
extremitat dreta</option>
+ <option value="L">L - Només
extremitat esquerra</option>
+ </select>
+ </div>
+ </div>
+ </form>
+ </div>
+ <div role="tabpanel" id="freeTask" class="tab-pane">
+ <form>
+ <label for="recipient-name"
class="control-label">Descripció de la tasca:</label>
+ <textarea rows=2 class="form-control"
id="taskComment"></textarea>
+ </form>
-{% block script %}
-{{ super() }}
+ </div>
+ </div>
+ </div> <!-- .modal-body -->
+ <div class="modal-footer">
+ <button type="button" class="pull-left btn btn-default"
data-dismiss="modal">Cancelar</button>
+ <button id="btnAddTask" type="button" class="pull-right btn btn-primary
hidden" onclick="addModifyDeleteTask(0)">Afegeix la tasca</button>
+ <button id="btnUpdateTask" type="button" class="pull-right btn btn-primary"
onclick="addModifyDeleteTask(1)">Modificar la tasca</button>
+ <button id="btnDeleteTask" type="button" class="pull-right btn btn-danger
hidden" onclick="addModifyDeleteTask(2)">Eliminar</button>
+ </div>
+ </div>
+ <!-- /.modal-content -->
+ </div>
+ <!-- /.modal-dialog -->
+</div>
+<!-- /.modal -->
+{% endblock %} {% block script %} {{ super() }}
<script src="{{ url_for('assets', filename='DataTables/media/js/jquery.dataTables.min.js') }}"></script>
<script src="{{ url_for('assets', filename='DataTables/media/js/dataTables.bootstrap.min.js') }}"></script>
<script src="{{ url_for('js', filename='players.js') }}"></script>
diff --git a/chronojumpserver/tests/populate_test_data.py b/chronojumpserver/tests/populate_test_data.py
index 53729dc..d5d247d 100755
--- a/chronojumpserver/tests/populate_test_data.py
+++ b/chronojumpserver/tests/populate_test_data.py
@@ -1,25 +1,43 @@
from chronojumpserver.database import db_session
-from chronojumpserver.models import Person
+from chronojumpserver.models import Person, Station, Exercise
from sqlalchemy.exc import IntegrityError, InvalidRequestError
players = [
- {'name': 'Player1', 'weight': 72, 'height': 170, 'imageName': 'players/player1.jpg'},
- {'name': 'Player2', 'weight': 86, 'height': 182, 'imageName': 'players/player2.jpg'},
- {'name': 'Player3', 'weight': 68, 'height': 175, 'imageName': 'players/player3.jpg'},
- {'name': 'Player4', 'weight': 85, 'height': 194, 'imageName': 'players/player4.jpg'},
- {'name': 'Player5', 'weight': 68, 'height': 171, 'imageName': 'players/player5.jpg'},
- {'name': 'Player6', 'weight': 76, 'height': 189, 'imageName': 'players/player6.jpg'},
- {'name': 'Player7', 'weight': 68, 'height': 178, 'imageName': 'players/player7.jpg'},
- {'name': 'Player8', 'weight': 68, 'height': 170, 'imageName': 'players/player8.jpg'},
- {'name': 'Player9', 'weight': 87, 'height': 187, 'imageName': 'players/player9.jpg'},
- {'name': 'Player10', 'weight': 69, 'height': 176, 'imageName': 'players/player10.jpg'},
- {'name': 'Player11', 'weight': 73, 'height': 175, 'imageName': 'players/player11.jpg'},
- {'name': 'Player12', 'weight': 78, 'height': 184, 'imageName': 'players/player12.jpg'},
- {'name': 'Player13', 'weight': 75, 'height': 182, 'imageName': 'players/player13.jpg'},
- ]
+ {'name': 'Player1', 'weight': 72, 'height': 170, 'imageName': None},
+ {'name': 'Player2', 'weight': 86, 'height': 182, 'imageName': None},
+ {'name': 'Player3', 'weight': 68, 'height': 175, 'imageName': None},
+ {'name': 'Player4', 'weight': 85, 'height': 194, 'imageName': None},
+ {'name': 'Player5', 'weight': 68, 'height': 171, 'imageName': None},
+ {'name': 'Player6', 'weight': 76, 'height': 189, 'imageName': None},
+ {'name': 'Player7', 'weight': 68, 'height': 178, 'imageName': None},
+ {'name': 'Player8', 'weight': 68, 'height': 170, 'imageName': None},
+ {'name': 'Player9', 'weight': 87, 'height': 187, 'imageName': None},
+ {'name': 'Player10', 'weight': 69, 'height': 176, 'imageName': None},
+ {'name': 'Player11', 'weight': 73, 'height': 175, 'imageName': None},
+ {'name': 'Player12', 'weight': 78, 'height': 184, 'imageName': None},
+ {'name': 'Player13', 'weight': 75, 'height': 182, 'imageName': None},
+]
+
+stations = [
+ {'name': 'station1', 'type': 'G', 'exercises': [
+ {'name': 'exercise1Station1',
+ 'percentBodyMassDisplaced': 45.4},
+ {'name': 'exercise2Station1',
+ 'percentBodyMassDisplaced': 21}
+ ]},
+ {'name': 'station2', 'type': 'I', 'exercises': [
+ {'name': 'exercise1Station2',
+ 'percentBodyMassDisplaced': 12.9},
+ {'name': 'exercise2Station2',
+ 'percentBodyMassDisplaced': 88}
+ ]},
+ {'name': 'station3', 'type': 'S', 'exercises': []}
+]
+
class TestData:
def create_players(self):
+ rfid_count = 1
for player in players:
p = Person.query.filter(Person.name == player['name']).first()
if p is None:
@@ -27,11 +45,48 @@ class TestData:
p = Person(name=player['name'],
weight=player['weight'],
height=player['height'],
+ rfid='999.999.999.%03d' % rfid_count,
imageName=player['imageName'])
db_session.add(p)
print "\tAdding player %s." % player['name']
+ rfid_count += 1
+ else:
+ """Player already exists."""
+ print "\tPlayer %s exists in the table, skipping." % player['name']
+ # Commit the changes
+ db_session.commit()
+
+ def create_stations(self):
+ for station in stations:
+ s = Station.query.filter(Station.name == station['name']).first()
+ if s is None:
+ s = Station(name=station['name'],
+ type=station['type'])
+ db_session.add(s)
+ print "\tAdding Station %s" % station['name']
+ else:
+ """The station already exists, skipping."""
+ print "\tStation %s exists in the table, skipping." % station['name']
+ # Commit the changes
+ db_session.commit()
+
+ def create_exercises(self):
+ for station in stations:
+ s = Station.query.filter(Station.name == station['name']).first()
+ if s:
+ for exercise in station['exercises']:
+ e = Exercise.query.filter(Exercise.name == exercise['name'] and Exercise.stationId ==
s.id).first()
+ if e is None:
+ e = Exercise(name=exercise['name'],
+ stationId=s.id,
+ percentBodyMassDisplaced=exercise['percentBodyMassDisplaced'])
+ db_session.add(e)
+ print "\tAdding Exercise %s for station %s" % (exercise['name'], station['name'])
+ else:
+ """ The exercise for this station already exists. Skipping."""
+ print "\tThe Exercise %s exists for station %s. Skipping." % (exercise['name'],
station['name'])
else:
- """Player already exists."""
- print "\tPlayer %s exists in the table, skipping." % player['name']
+ """The station already exists, skipping."""
+ print "\tStation %s does not exists in the table, skipping." % station['name']
# Commit the changes
db_session.commit()
diff --git a/chronojumpserver/views.py b/chronojumpserver/views.py
index d3a0ff2..1bcf401 100755
--- a/chronojumpserver/views.py
+++ b/chronojumpserver/views.py
@@ -65,7 +65,7 @@ def player_detail(player_id):
os.unlink(fullpath)
# Set the new photo
new_photo = 'player_' + player_id + \
- '_' + str(int(time())) + '.jpg'
+ '_' + str(int(time()))
fullpath = os.path.join('chronojumpserver',
app.config['UPLOAD_FOLDER'],
new_photo)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]