Commit 55c1e583 by Pablo Molina

Solved #428, uso del panel libre en la aplicación android

- Actualizado soporte de android para la nueva API
- Actualizado /supervisor/id/students en la API
- Actualizados atributos de StuPicto y Student
- Actualizado manejo de expresiones de un picto nuevo en la web
- Actualizada la carga de pictos en android
- Refactorizado el código de android para el uso del panel libre
- Arreglados problemas en los tests originados por los cambios en la API
- Actualizado valor devuelto por StudentController.update
parent 5f0948b6
......@@ -153,6 +153,12 @@ public class RestapiWrapper {
// convert inputstream to string
if (inputStream!=null) result = convertInputStreamToString(inputStream);
if (result.equals("") && urlConnection.getResponseCode() == 200) {
result = "{ result: \"OK\" }";
} else if (result.equals("")) {
result = "{ error: " + urlConnection.getResponseCode() + " }";
}
return result;
}
......@@ -209,6 +215,9 @@ public class RestapiWrapper {
while ((line=br.readLine()) != null) {
response+=line;
}
if (response.equals("") && responseCode != HttpsURLConnection.HTTP_OK) {
response = "{ error: " + responseCode + " }";
}
return response;
}
......
......@@ -28,6 +28,8 @@ public class Picto extends Img {
public static String CATEGORY = "id_cat";
public static String COLUMN = "coord_x";
public static String ROW = "coord_y";
public static String FREE_COLUMN = "free_category_coord_x";
public static String FREE_ROW = "free_category_coord_y";
public static String MAGNIFY = "magnify";
public static String HIGHLIGHT = "highlight";
public static String STATUS = "status";
......@@ -48,12 +50,14 @@ public class Picto extends Img {
private JSONObject attributes;
private String translation;
public Picto(int id, String url, String translation, int cat, int row, int column) throws JSONException {
public Picto(int id, String url, String translation, int cat, int row, int column, int freeRow, int freeColumn) throws JSONException {
this(id, url, translation, new JSONObject()
.put(JSON_ATTTRS.CATEGORY,cat)
.put(JSON_ATTTRS.COLUMN,column)
.put(JSON_ATTTRS.ROW,row)
.put(JSON_ATTTRS.STATUS,JSON_ATTTR_STATUS_VALUES.ENABLED));
.put(JSON_ATTTRS.CATEGORY, cat)
.put(JSON_ATTTRS.COLUMN, column)
.put(JSON_ATTTRS.ROW, row)
.put(JSON_ATTTRS.FREE_ROW, freeRow)
.put(JSON_ATTTRS.FREE_COLUMN, freeColumn)
.put(JSON_ATTTRS.STATUS, JSON_ATTTR_STATUS_VALUES.ENABLED));
}
public Picto(int id, String url,String translation, String attributes) throws JSONException {
this(id, url, translation, new JSONObject(attributes));
......@@ -216,11 +220,7 @@ public class Picto extends Img {
* @return the row of the picto
*/
public int get_row() {
try {
return Integer.parseInt(this.attributes.getString(JSON_ATTTRS.ROW));
} catch (JSONException e) {
return -1;
}
return this.attributes.optInt(JSON_ATTTRS.ROW, -1);
}
/**
......@@ -228,11 +228,23 @@ public class Picto extends Img {
* @return the column of the picto
*/
public int get_column() {
try {
return Integer.parseInt(this.attributes.getString(JSON_ATTTRS.COLUMN));
} catch (JSONException e) {
return -1;
}
return this.attributes.optInt(JSON_ATTTRS.COLUMN, -1);
}
/**
*
* @return the free row of the picto
*/
public int getFreeRow() {
return this.attributes.optInt(JSON_ATTTRS.FREE_ROW, -1);
}
/**
*
* @return the free column of the picto
*/
public int getFreeColumn() {
return this.attributes.optInt(JSON_ATTTRS.FREE_COLUMN, -1);
}
/**
......@@ -303,7 +315,10 @@ public class Picto extends Img {
* @return
*/
public boolean is_category() {
return this.get_category()==Picto.NO_CATEGORY && this.get_column()!=Picto.ROW_UNCATEGORIZED_CONCEPTS;
return this.get_category()==Picto.NO_CATEGORY &&
this.get_column() != Picto.ROW_UNCATEGORIZED_CONCEPTS &&
this.getFreeColumn() == -1 &&
this.getFreeRow() == -1;
}
/**
*
......
......@@ -26,7 +26,7 @@ public class User {
}
public final static class JSON_STUDENT_ATTTRS{
static String CATEGORIES = "category";
static String CATEGORIES = "categories";
static String INPUT_FEEDBACK = "input feedback";
static String INPUT_SELECTION = "input selection";
static String PICTOGRAM_SIZE = "pictogram size";
......@@ -232,11 +232,7 @@ public class User {
* @return true if the collection is organized by categories (default: True)
*/
public boolean has_categories() {
try {
return this.attributes_stu.getString(JSON_STUDENT_ATTTRS.CATEGORIES).equalsIgnoreCase("on");
} catch (JSONException e) {
return true;
}
return this.attributes_stu.optBoolean(JSON_STUDENT_ATTTRS.CATEGORIES, true);
}
/**
......
......@@ -4,6 +4,7 @@ import android.os.AsyncTask;
import android.util.Log;
import com.yottacode.pictogram.action.VocabularyAction;
import com.yottacode.pictogram.dao.PCBDBHelper;
import com.yottacode.pictogram.dao.Picto;
import com.yottacode.pictogram.net.ImgDownloader;
import com.yottacode.pictogram.net.PictoUploader;
......@@ -311,7 +312,19 @@ public class Vocabulary implements Iterable<Picto> {
* @return list of pictos which should be selectable at the beginning of a sentence. Empty categories are removed
*/
public LinkedList<Picto> startSentence(){
return this.pictos.get(new Integer(Picto.NO_CATEGORY));
if (PCBcontext.getPcbdb().getCurrentUser().has_categories()) {
return this.pictos.get(new Integer(Picto.NO_CATEGORY));
} else {
LinkedList<Picto> freePictos = new LinkedList<>();
for (LinkedList<Picto> category : pictos.values()) {
for (Picto picto : category) {
if (picto.getFreeRow() != -1 && picto.getFreeColumn() != -1) {
freePictos.add(picto);
}
}
}
return freePictos;
}
}
/**
......@@ -334,11 +347,11 @@ public class Vocabulary implements Iterable<Picto> {
/*
* It saves locally a new picto obtained from the PCB
*/
public Picto saveLocalPicto(String url, String exp, int cat, int coord_x, int coord_y, final iLocalPicto listener) {
public Picto saveLocalPicto(String url, String exp, int cat, int coord_x, int coord_y, int free_category_coord_x, int free_category_coord_y, final iLocalPicto listener) {
int id= PCBcontext.getDevice().getNextLocalPictoID();
final Picto picto[]=new Picto[1];
try {
picto[0] = new Picto(id, url, exp, cat, coord_x, coord_y);
picto[0] = new Picto(id, url, exp, cat, coord_x, coord_y, free_category_coord_x, free_category_coord_y);
addPicto(picto[0], ImgDownloader.tsource.local, new iImgDownloaderListener() {
@Override
public void loadComplete() {
......
......@@ -47,7 +47,7 @@ public class PictoItemViewGenerator {
try {
pictoImage.setImageBitmap(picto.get_bitmap(PCBcontext.getContext()));
if (!picto.is_invisible() || PCBcontext.getPcbdb().getCurrentUser().is_supervisor()) {
if (!picto.is_invisible() || PCBcontext.getPcbdb().getCurrentUser().is_supervisor()) {
layoutWrapper.setVisibility(View.VISIBLE);
pictoImage.setVisibility(View.VISIBLE);
layoutWrapper.setBackground(convertView.getResources()
......
......@@ -210,5 +210,4 @@ public final class PCBcontext {
}
return actionLog;
}
}
......@@ -64,7 +64,7 @@
android:scaleType="fitCenter" />
<GridView
android:id="@+id/picto_grid_view"
android:id="@+id/picto_category_grid_view"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_below="@+id/tape_grid_view"
......@@ -83,7 +83,7 @@
</GridView>
<GridView
android:id="@+id/picto_categories_grid_view"
android:id="@+id/picto_main_grid_view"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_below="@+id/tape_grid_view"
......
/* global Student, PictoCoreCat, VStuLastInstruction, StuPicto, StuSup, sailsTokenAuth, sails */
/* global Student, PictoCoreCat, VStuLastInstruction, StuPicto, StuSup, sailsTokenAuth, sails,
Picto */
/**
/* StudentController
......@@ -55,13 +56,17 @@ module.exports = {
.getTime()
});
} else {
sails.log.error(`Invalid student login: user ${student.username}, password\
"${req.body.password}"`);
res.badRequest();
}
} else {
sails.log.error(`Tried to login as non-existing student ${req.body.username}`);
res.badRequest();
}
})
.catch(function () {
sails.log.error(`Error getting student ${req.body.username} for login`);
res.serverError();
});
},
......@@ -220,7 +225,6 @@ module.exports = {
Student.findOne(req.params.id_stu)
.exec(function (err, stu) {
var k;
if (err || !stu)
return res.json(500, {
error: "No student found"
......@@ -238,9 +242,7 @@ module.exports = {
error: 'Error when saving student'
});
}
return res.json({
result: 'Deleted'
});
return res.json(stu);
});
});
},
......@@ -752,65 +754,69 @@ module.exports = {
/**
* Add an existing picto to the student's collection
* @param {request} req
* @param {request} req (with studentId and pictoId as url parameter)
* {
* attributes: { @see StuPicto.getValidAttributes() }
* }
* @param {response} res
* {
* id: stuPictoId (association betweet student and picto)
* picto: {
* id: pictoId,
* }
* student: studentId (speficied as url parameter)
* picto: { @see Picto model}
* attributes: { @see StuPicto.getValidAttributes() }
* expression: { @see Picto model }
* }
*/
add_picto: function (req, res) {
var params = req.allParams();
StuPicto.create({
student: params.id_stu,
picto: params.id_picto,
attributes: params.attributes
})
.then(function (stuPicto) {
if (!stuPicto) {
sails.log.error(`Can't create StuPicto for picto ${params.id_picto} \
and student ${params.id_stu}`);
throw new Error('stupicto not added');
var student;
var picto;
Student.findOne({ id: params.id_stu })
.then((studentData) => {
if (studentData) {
student = studentData;
} else {
sails.log.error(`Student ${params.id_stu} not found`);
throw new Error();
}
res.ok({
id: stuPicto.id,
picto: { id: stuPicto.picto },
attributes: stuPicto.attributes
});
})
.catch(function () {
sails.log.debug(`Trying to find an existing StuPicto for picto ${params.id_picto} \
and student ${params.id_stu}`);
StuPicto.findOne({
student: params.id_stu,
picto: params.id_picto
Picto.findOne({ id: params.id_picto })
.populate('expressions', {
lang: student.lang
})
.then((stuPicto) => {
if (!stuPicto) {
sails.log.error(`Can't create or find StuPicto for picto ${params.id_picto} \
and student ${params.id_stu}. Check if both picto and student exist and check`);
res.badRequest();
.then((pictoData) => {
if (pictoData) {
picto = pictoData;
} else {
res.ok({
id: stuPicto.id,
picto: { id: stuPicto.picto },
attributes: stuPicto.attributes
});
sails.log.error(`Picto ${params.id_picto} not found`);
throw new Error();
}
StuPicto.create({
student: student.id,
picto: picto.id,
attributes: params.attributes
})
.then((stuPicto) => {
if (stuPicto) {
sails.log.debug('StuPicto created: %j', stuPicto);
res.ok(Object.assign(
{},
stuPicto.toObject(),
{ picto: picto.toObject() },
{ expression: picto.expressions.pop() }
));
} else {
sails.log.error('StuPicto not created');
throw new Error();
}
})
.catch(() => res.serverError());
})
.catch(() => {
sails.log.error(`Server error finding StuPicto for picto ${params.id_picto} \
and student ${params.id_stu}`);
res.serverError();
});
});
.catch(() => res.badRequest());
})
.catch(() => res.badRequest());
},
/**
......
/* global sails, sailsTokenAuth, Supervisor, Office, Student, StuSup, Picto */
const fs = require('fs');
const path = require('path');
const lodash = require('lodash');
const bcrypt = require('bcrypt-nodejs');
/**
* SupervisorController
*
......@@ -50,7 +55,6 @@ module.exports = {
* }
*/
login: function (req, res) {
var bcrypt = require('bcrypt-nodejs');
var email = req.body.email;
var password = req.body.password;
......@@ -331,62 +335,54 @@ module.exports = {
},
/**
* Get the list of students linked to this supervisor
* @param {request} req {}
* @param {response} res
* [
* {
* "student": {
* "id": 1234
* "name": "John"
* "surname": "Doe"
* "birthdate": "2005-06-19T22:00:00.000Z"
* "notes": null // @TODO 357
* "supervision": 1 // @TODO 357
* "lang": "es"
* "attributes": {
* "categories": "off" // @TODO 357
* "input_feedback": {
* "vibration": false,
* "ligh_up": true,
* "beep": true,
* "tts": false
* },
* "input_selection": "double click" // @TODO 357
* "pictogram_size": "normal/large"
* "tts_engine": "IVONA Text-to-Speech HQ",
* "tts_voice": "child" // @TODO 357
* "picto_select": "enlarge" // @TODO 357
* "legend": "no" // @TODO 357
* "animation": "yes" // @TODO 357
* "tts_options": {
* "picto": true // @TODO 357
* "phrase": false // @TODO 357
* }
* "picto_background": "#00ff5a"
* "phrase_background": "#ff0000" // @TODO 357
* }
* },
* "supervisor": 10,
* "id": 31 // @TODO 357
* },
* {
* ...
* }
* ]
*/
* Get the list of students linked to this supervisor.
* The extra supervision attribute indicates the existing relationship between the supervisor and
* the student:
*
* 0. There is no relationship (but the students is part of the same office)
* 1. The supervisor is his/her tutor
* 2. The supervisor is his/her teacher
*
* @param {request} req {}
* @param {response} res
* [
* {
* "id": 1234
* "username": "johndoe"
* "name": "John"
* "surname": "Doe"
* "birthdate": "2005-06-19T22:00:00.000Z"
* "gender": "M/F"
* "country": "ES/EN..."
* "pic": "student/avatar/url.jpg"
* "notes": null
* "lang": "es-es/en-gb..."
* "office": 1234
* "supervision": 0/1/2
* "lang": "es"
* "attributes": { @see Student.getValidAttributes() documentation },
* },
* ...
* ]
*/
students: function (req, res) {
Supervisor.findOne({ id: req.params.id }).then(function (supervisor) {
if (supervisor) {
StuSup.find({ supervisor: supervisor.id }).populate('student').then(function (stuSups) {
var students = stuSups.map(function (stuSup) {
return stuSup.student;
var student = stuSup.student;
student.supervision = req.token.office ? 2 : 1;
return student;
});
if (req.token.isSupAdmin && req.token.office.id) {
if (req.token.isSupAdmin && req.token.office && req.token.office.id) {
Student.find({ office: req.token.office.id }).then(function (officeStudents) {
students.concat(officeStudents);
res.ok(require('lodash').uniq(students, false, 'id'));
students = students.map((student) => {
student.supervision = student.supervision || 0;
return student;
});
res.ok(lodash.uniq(students, false, 'id'));
})
.catch(function () {
res.serverError();
......@@ -453,8 +449,6 @@ module.exports = {
* {} // Just returns an empty 'ok' response
*/
upload: function (req, res) {
var fs = require('fs');
var path = require('path');
var newAvatarFileName;
var newAvatarFileDescriptor;
var newAvatarDirectory = sails.config.pictogram.paths.supervisorAvatarDirectory;
......
......@@ -43,6 +43,12 @@ module.exports = {
columnName: 'attributes',
required: true,
type: 'json'
},
toJSON: function () {
var stuPicto = this.toObject();
stuPicto.attributes = StuPicto.getValidAttributes(stuPicto.attributes);
return stuPicto;
}
},
......@@ -66,7 +72,7 @@ module.exports = {
getValidAttributes(attributes) {
'use strict';
sails.log.debug('Requested attributes for StuPicto', attributes);
sails.log.verbose('Requested attributes for StuPicto', attributes);
const validAttributes = {};
const defaultAttributes = {
id_cat: null,
......@@ -114,7 +120,7 @@ module.exports = {
delete validAttributes.color;
}
}
sails.log.debug('Valid attributes for StuPicto', validAttributes);
sails.log.verbose('Valid attributes for StuPicto', validAttributes);
return Object.assign({}, defaultAttributes, validAttributes);
},
......
......@@ -104,6 +104,7 @@ module.exports = {
toJSON: function () {
var student = this.toObject();
student.pic = sails.config.pictogram.urls.getStudentAvatarUrl(student.pic);
student.attributes = Student.getValidAttributes(student.attributes);
delete student.password;
return student;
},
......@@ -211,8 +212,8 @@ module.exports = {
delete validAttributes.tape_background;
}
}
sails.log.debug('Requested attributes for Student', attributes);
sails.log.debug('Valid attributes for Student', validAttributes);
sails.log.verbose('Requested attributes for Student', attributes);
sails.log.verbose('Valid attributes for Student', validAttributes);
return Object.assign({}, defaultAttributes, validAttributes);
},
......
......@@ -76,7 +76,7 @@ dashboardControllers.controller('AddPictoCtrl', function (
//
$scope.load_own_pictos = function () {
$scope.source = 'ownpictos';
$http.get(config.backend + '/sup/' + $scope.sup.id + '/pictos')
$http.get(config.backend + '/sup/' + supervisor.id + '/pictos')
.success(function (data) {
if (data) {
$scope.pictos = data;
......@@ -159,7 +159,7 @@ dashboardControllers.controller('AddPictoCtrl', function (
$upload.upload({
url: '/picto/upload',
method: 'POST',
fields: { owner: $scope.sup.id },
fields: { owner: supervisor.id },
file: file
}).success(function (picto) {
$translate('picto_upload_success').then(function (translation) {
......@@ -314,39 +314,27 @@ dashboardControllers.controller('AddPictoCtrl', function (
//
// Delete own picto
//
$scope.remove_own_picto = function (id_picto){
console.log("delete_picto:" + id_picto);
$scope.remove_own_picto = function (pictoId) {
var deletePicto = $window.confirm('Are you absolutely sure you want to delete?');
if(deletePicto){
$http
.delete(config.backend+'/picto/'+ id_picto)
.success(function(data, status, headers, config) {
// Eliminar de la vista: Se recorre el array de objetos json para buscarlo
for(var i=0; i < $scope.pictos.length; i++) {
if(id_picto == $scope.pictos[i].id)
$scope.pictos.splice(i,1);
if (deletePicto) {
$http.delete(config.backend + '/picto/' + pictoId)
.success(function () {
var i;
for (i = 0; i < $scope.pictos.length; i++) {
if (pictoId === $scope.pictos[i].id) {
$scope.pictos.splice(i, 1);
}
}
console.log("Supervisor picto deleted");
})
.error(function(data, status, headers, config) {
console.log("Error from API: " + data.error);
});
.error(function () {});
}
};
//
//
//
$scope.close = function () {
$modalInstance.close("Ejemplo de elemento devuelto");
$modalInstance.close('Ejemplo de elemento devuelto');
};
// No usado
$scope.cancel = function () {
// Deshacer todos los pictos asignados a las categorías??
$modalInstance.dismiss('cancel');
};
......@@ -354,18 +342,17 @@ dashboardControllers.controller('AddPictoCtrl', function (
// Modal window to open picto expression
//
$scope.open_exp = function (picto) {
var modalInstance = $modal.open({
animation: true,
templateUrl: 'modules/student/views/pictoexp.html',
controller: 'PictoExpCtrl',
size: 'md',
resolve: { // Passing data to the controller of the window
picto: function(){
resolve: {
picto: function () {
return picto;
},
sup: function(){
return $scope.sup;
sup: function () {
return supervisor;
}
}
});
......@@ -373,13 +360,8 @@ dashboardControllers.controller('AddPictoCtrl', function (
// Returned data from the modal window
modalInstance.result.then(
function (exp) {
// Output from the window by clicking cancel or outside its borders
console.log(exp);
picto.expressions.push(exp);
}
);
};
// End Modal window to manage picto config
});
......@@ -298,12 +298,17 @@ describe('Student API', function () {
.expect((response) => {
assert.isObject(response.body);
assert.isNumber(response.body.id);
assert.isNumber(response.body.student);
assert.isObject(response.body.picto);
assert.isObject(response.body.expression);
assert.equal(response.body.picto.id, 1234);
assert.equal(response.body.expression.text, 'test-expression');
delete response.body.picto;
delete response.body.expression;
delete response.body.id;
delete response.body.student;
})
.expect({
picto: {
id: 1234
},
attributes: {
coord_x: 3,
coord_y: 3,
......@@ -350,7 +355,7 @@ describe('Student API', function () {
var stuPictoId;
supervisorAgent
.post(`/stu/${studentAgent.data.id}/picto/1234`)
.post(`/stu/${studentAgent.data.id}/picto/1235`)
.send({
attributes: {
coord_x: 3,
......
......@@ -22,9 +22,12 @@ describe('Supervisor API', function () {
response.body.forEach(function (student) {
assert.isObject(student);
assert.isNumber(student.id);
assert.isString(student.username);
assert.isString(student.name);
assert.isString(student.surname);
assert.isUndefined(student.password);
assert.isObject(student.attributes);
assert.oneOf(student.supervision, [0, 1, 2]);
});
})
.end(done);
......
/* eslint-disable no-console */
var DATABASE_BACKUP_FILE = '/tmp/pictogram_test_backup.sql';
var UPLOAD_FOLDER = '';
var UPLOAD_FOLDER_BACKUP = '';
var UPLOAD_FOLDER;
var UPLOAD_FOLDER_BACKUP;
var Agent = require('supertest').agent;
var AuthAgent = require('./test-auth-agent');
var chai = require('chai');
......@@ -73,10 +73,12 @@ before(function (serverLoadDone) {
after(function (done) {
// Restore the database and the upload folder
childProcess.execSync('mysql -u pictodbuser -pp1KT015 pictodb < ' + DATABASE_BACKUP_FILE);
childProcess.execSync('rm ' + DATABASE_BACKUP_FILE);
childProcess.execSync('rm -r ' + UPLOAD_FOLDER);
childProcess.execSync('mv ' + UPLOAD_FOLDER_BACKUP + ' ' + UPLOAD_FOLDER);
if (UPLOAD_FOLDER && UPLOAD_FOLDER_BACKUP) {
childProcess.execSync('mysql -u pictodbuser -pp1KT015 pictodb < ' + DATABASE_BACKUP_FILE);
childProcess.execSync('rm ' + DATABASE_BACKUP_FILE);
childProcess.execSync('rm -r ' + UPLOAD_FOLDER);
childProcess.execSync('mv ' + UPLOAD_FOLDER_BACKUP + ' ' + UPLOAD_FOLDER);
}
sails.lower(done);
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment