[chronojump-server] Tasks funcionality enabled again. - Now the tasks can be parametrized or free - The description of



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">&times;</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">&times;</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]