Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
yotta
/
pictogram
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
60
Merge Requests
0
Pipelines
Wiki
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
5413736a
authored
Dec 18, 2016
by
Fernando Martínez Santiago
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge branch 'develop' of
http://scm.ujaen.es/softuno/pictogram
into develop
parents
6b6c2ff8
51c69a8d
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1380 additions
and
1250 deletions
android/Pictogram/commonlibrary/src/main/res/values/colors.xml
android/Pictogram/tabletlibrary/src/main/java/com/yottacode/pictogram/tabletlibrary/gui/SerialActivity.java
android/Pictogram/tabletlibrary/src/main/res/layout/activity_serial.xml
android/Pictogram/tabletlibrary/src/main/res/values/strings.xml
sails/src/api/controllers/StudentController.js
sails/src/assets/scripts/modules/student/controllers/pictoconfig.js
sails/src/assets/scripts/modules/student/views/pictoconfig.html
sails/src/config/policies.js
sails/src/config/routes.js
android/Pictogram/commonlibrary/src/main/res/values/colors.xml
View file @
5413736a
...
...
@@ -13,7 +13,7 @@
<item
name=
"darkpurple"
type=
"color"
>
#FF9933CC
</item>
<item
name=
"darkgreen"
type=
"color"
>
#669900
</item>
<item
name=
"darkorange"
type=
"color"
>
#FFFF8800
</item>
<item
name=
"darkred"
type=
"color"
>
#
FFCC
0000
</item>
<item
name=
"darkred"
type=
"color"
>
#
cc
0000
</item>
<integer-array
name=
"androidcolors"
>
<item>
@color/blue
</item>
...
...
android/Pictogram/tabletlibrary/src/main/java/com/yottacode/pictogram/tabletlibrary/gui/SerialActivity.java
View file @
5413736a
...
...
@@ -3,12 +3,15 @@ package com.yottacode.pictogram.tabletlibrary.gui;
import
android.app.Activity
;
import
android.content.Intent
;
import
android.content.SharedPreferences
;
import
android.content.res.Configuration
;
import
android.os.Bundle
;
import
android.view.View
;
import
android.view.View.OnClickListener
;
import
android.view.Window
;
import
android.view.WindowManager
;
import
android.widget.Button
;
import
android.widget.EditText
;
import
android.widget.LinearLayout
;
import
com.yottacode.pictogram.dao.UserLogin
;
import
com.yottacode.pictogram.tabletlibrary.R
;
...
...
@@ -52,7 +55,8 @@ public class SerialActivity extends Activity {
requestWindowFeature
(
Window
.
FEATURE_NO_TITLE
);
setContentView
(
R
.
layout
.
activity_serial
);
//Initial keyboard hide
getWindow
().
setSoftInputMode
(
WindowManager
.
LayoutParams
.
SOFT_INPUT_STATE_ALWAYS_HIDDEN
);
final
EditText
mSerialViewMail
=
(
EditText
)
findViewById
(
R
.
id
.
serialmail
);
final
EditText
mSerialViewPass
=
(
EditText
)
findViewById
(
R
.
id
.
serialpass
);
...
...
@@ -65,6 +69,29 @@ public class SerialActivity extends Activity {
mSerialViewMail
.
setText
(
username
);
mSerialViewPass
.
setText
(
password
);
final
LinearLayout
stuList
=
(
LinearLayout
)
findViewById
(
R
.
id
.
stuLay
);
final
LinearLayout
supList
=
(
LinearLayout
)
findViewById
(
R
.
id
.
supLay
);
mSerialViewMail
.
setOnFocusChangeListener
(
new
View
.
OnFocusChangeListener
()
{
@Override
public
void
onFocusChange
(
View
view
,
boolean
hasFocus
)
{
if
(
hasFocus
){
stuList
.
setVisibility
(
View
.
INVISIBLE
);
supList
.
setVisibility
(
View
.
INVISIBLE
);
}
}
});
mSerialViewPass
.
setOnFocusChangeListener
(
new
View
.
OnFocusChangeListener
()
{
@Override
public
void
onFocusChange
(
View
view
,
boolean
hasFocus
)
{
if
(
hasFocus
){
stuList
.
setVisibility
(
View
.
INVISIBLE
);
supList
.
setVisibility
(
View
.
INVISIBLE
);
}
}
});
// Escribo el último valor indicado de username
...
...
android/Pictogram/tabletlibrary/src/main/res/layout/activity_serial.xml
View file @
5413736a
...
...
@@ -10,17 +10,15 @@
android:paddingTop=
"@dimen/activity_vertical_margin"
tools:context=
".gui.SerialActivity"
>
<EditText
android:id=
"@+id/serialmail"
android:layout_width=
"400px"
android:layout_height=
"wrap_content"
android:hint=
"@string/prompt_serial_mail"
android:imeActionId=
"@+id/login"
android:imeOptions=
"actionUnspecified"
android:inputType=
"text"
android:maxLines=
"1"
android:singleLine=
"true"
android:layout_alignParentTop=
"true"
/>
<ImageView
android:layout_width=
"200px"
android:layout_height=
"120px"
android:layout_marginLeft=
"30px"
android:orientation=
"horizontal"
android:src=
"@drawable/pictogram_logo"
android:id=
"@+id/imageView"
android:layout_alignParentTop=
"true"
android:layout_toEndOf=
"@+id/serialmail"
/>
<EditText
android:id=
"@+id/serialpass"
...
...
@@ -34,28 +32,91 @@
android:maxLines=
"1"
android:singleLine=
"true"
android:layout_below=
"@+id/serialmail"
/>
android:layout_toStartOf=
"@+id/imageView"
/>
<Button
android:id=
"@+id/entrar_button"
style=
"?android:textAppearanceSmall"
android:layout_width=
"400px"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"1
6
dp"
android:layout_marginTop=
"1
1
dp"
android:text=
"@string/action_entrar"
android:textStyle=
"bold"
android:layout_below=
"@+id/serialpass"
/>
android:layout_below=
"@+id/serialpass"
android:layout_alignStart=
"@+id/serialpass"
/>
<ImageView
android:layout_width=
"200px"
android:layout_height=
"120px"
android:layout_marginLeft=
"30px"
android:orientation=
"horizontal"
android:src=
"@drawable/pictogram_logo"
android:layout_centerHorizontal=
"true"
android:id=
"@+id/imageView"
android:layout_toRightOf=
"@+id/serialmail"
android:layout_alignParentTop=
"true"
/>
<View
android:layout_width=
"match_parent"
android:layout_height=
"1dp"
android:background=
"@android:color/darker_gray"
android:layout_below=
"@+id/entrar_button"
android:layout_alignParentEnd=
"true"
android:layout_marginEnd=
"18dp"
/>
<EditText
android:id=
"@+id/serialmail"
android:layout_width=
"400px"
android:layout_height=
"wrap_content"
android:hint=
"@string/prompt_serial_mail"
android:imeActionId=
"@+id/login"
android:imeOptions=
"actionUnspecified"
android:inputType=
"text"
android:maxLines=
"1"
android:layout_marginStart=
"212dp"
android:layout_alignParentTop=
"true"
android:layout_alignParentStart=
"true"
/>
<LinearLayout
android:orientation=
"vertical"
android:background=
"@color/blue"
android:layout_height=
"350px"
android:layout_width=
"400px"
android:layout_alignParentBottom=
"true"
android:layout_alignStart=
"@+id/entrar_button"
android:layout_marginStart=
"270dp"
android:id=
"@+id/supLay"
>
<TextView
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:id=
"@+id/labelSup"
tools:text=
"Supervisores"
android:text=
"@string/supervisores"
android:textAppearance=
"@style/TextAppearance.AppCompat"
android:textAlignment=
"center"
android:textSize=
"18sp"
/>
<ListView
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:id=
"@+id/supList"
/>
</LinearLayout>
<LinearLayout
android:orientation=
"vertical"
android:layout_height=
"350px"
android:layout_marginStart=
"67dp"
android:background=
"@color/darkred"
android:layout_width=
"400px"
android:layout_alignParentBottom=
"true"
android:layout_alignParentStart=
"true"
android:id=
"@+id/stuLay"
>
<TextView
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:id=
"@+id/labelStu"
tools:text=
"Alumnos"
android:text=
"@string/alumnos"
android:textAppearance=
"@style/TextAppearance.AppCompat"
android:textAlignment=
"center"
android:textSize=
"18sp"
/>
<ListView
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:id=
"@+id/stuList"
/>
</LinearLayout>
</RelativeLayout>
...
...
android/Pictogram/tabletlibrary/src/main/res/values/strings.xml
View file @
5413736a
<resources>
<string
name=
"app_name"
>
com.yottacode.pictogram.Tablet
</string>
<string
name=
"alumnos"
>
Alumnos
</string>
<string
name=
"supervisores"
>
Supervisores
</string>
<item
name=
"maxInTape"
type=
"integer"
>
8
</item>
<item
name=
"maxInTape_big"
type=
"integer"
>
6
</item>
...
...
sails/src/api/controllers/StudentController.js
View file @
5413736a
/* global Student, PictoCore, VStuLastInstruction, StuPicto, StuSup, sailsTokenAuth, sails,
Picto */
/**
/* StudentController
*
* @description :: Server-side logic for managing students
* @help :: See http://links.sailsjs.org/docs/controllers
*/
module
.
exports
=
{
// dummy function to test timeouts
eternal
:
function
(
req
,
res
)
{
setTimeout
(
function
(){
return
;},
1000
*
60
*
6
);
},
/**
* Login in the server as student, getting a toker for interacting with the platform
* @param {request} req
* {
* username: 'johnydoe',
* password: '12312'
* }
* @param {response} res
* {
* user: {
* id: 12,
* office: 1234,
* username: 'johnydoe',
* name: 'Johny',
* surname: 'Doe',
* birthdate: '2009-12-10T00:00:00.000Z',
* gender: 'F',
* country: 'ES',
* pic: 'avatar/nice/url.jpg',
* notes: null,
* lang: 'en-en',
* iat: 123512,
* exp: 1231292,
* attributes: { @see Student.getValidAttributes() }
* },
* token: '... asd90jkas ...',
* server_time: 123912932312
* }
*/
login
:
function
(
req
,
res
)
{
var
bcrypt
=
require
(
'bcrypt-nodejs'
);
Student
.
findOne
({
username
:
req
.
body
.
username
})
.
then
(
function
(
student
)
{
if
(
student
)
{
if
(
bcrypt
.
compareSync
(
req
.
body
.
password
,
student
.
password
))
{
student
.
isStudent
=
true
;
res
.
ok
({
user
:
student
,
token
:
sailsTokenAuth
.
issueToken
(
student
,
sails
.
config
.
jwt
.
expiresInMinutes
),
server_time
:
(
new
Date
())
.
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
();
});
},
/**
* Get a student by id
* @param {request} req {} (with studentId specified as url parameters)
* @param {response} res
* {
* id: 12,
* office: 1234,
* username: 'johnydoe',
* name: 'Johny',
* surname: 'Doe',
* birthdate: '2009-12-10T00:00:00.000Z',
* gender: 'F',
* country: 'ES',
* pic: 'avatar/nice/url.jpg',
* notes: null,
* lang: 'en-en',
* iat: 123512,
* exp: 1231292,
* attributes: { @see Student.getValidAttributes() },
* current_method: 'Do Things', // May be null
* current_instruction: 'Do Stuff', // May be null
* supervision: 0|1|2, // supervision level according to requester 0 -> office admin, 1 -> tutor, 2 -> therapist
* }
*/
getInfo
:
function
(
req
,
res
)
{
Student
.
findOne
({
id
:
req
.
params
.
id_stu
}).
populate
(
'lastInstruction'
)
.
then
(
function
(
student
)
{
if
(
!
student
)
throw
new
Error
(
"student not found"
);
student
.
current_method
=
student
.
lastInstruction
[
0
]
?
student
.
lastInstruction
[
0
].
met_name
:
"no_method"
;
student
.
current_instruction
=
student
.
lastInstruction
[
0
]
?
student
.
lastInstruction
[
0
].
ins_name
:
"no_instruction"
;
// recover last instruction to complete student info
var
stu_last_inst
=
VStuLastInstruction
.
findOne
({
student
:
student
.
id
})
.
then
(
function
(
stu_last_inst
)
{
return
stu_last_inst
;
})
.
error
(
err
=>
{
throw
err
});
// determine supervision level of the requester on the student
var
stu_sup
=
StuSup
.
findOne
({
id_stu
:
student
.
id
,
id_sup
:
req
.
token
.
id
})
.
then
(
function
(
stu_sup
)
{
return
stu_sup
;
})
.
error
(
err
=>
{
throw
err
});
return
[
student
,
stu_last_inst
,
stu_sup
];
})
.
spread
(
function
(
student
,
stu_last_inst
,
stu_sup
)
{
if
(
stu_last_inst
)
{
student
.
current_method
=
stu_last_inst
.
met_name
;
student
.
current_instruction
=
stu_last_inst
.
ins_name
;
}
// requester has no relation
student
.
supervision
=
-
1
;
if
(
!
stu_sup
&&
req
.
token
.
office
&&
student
.
office
==
req
.
token
.
office
.
id
)
student
.
supervision
=
0
;
// requester is admin of the office
else
if
(
stu_sup
&&
!
req
.
token
.
office
)
student
.
supervision
=
1
;
// requester is tutor of the studend
else
if
(
stu_sup
&&
req
.
token
.
office
&&
student
.
office
==
req
.
token
.
office
.
id
)
student
.
supervision
=
2
;
// requester is supervisor of student
if
(
student
.
supervision
==
-
1
)
// should not hace access!!!
return
res
.
forbidden
(
"Access to this student should not be granted to you"
);
return
res
.
ok
(
student
);
})
.
catch
(
function
(
err
)
{
return
res
.
notFound
(
err
);
});
},
//
// Adds a new student into the database
//
create
:
function
(
req
,
res
)
{
var
params
=
req
.
params
.
all
();
Student
.
create
(
params
)
.
then
(
function
(
created
)
{
sails
.
log
.
debug
(
'Student '
+
created
.
id
+
' created: '
+
JSON
.
stringify
(
created
));
return
res
.
ok
(
created
);
})
.
error
(
function
(
err
)
{
if
(
err
.
message
.
search
(
"Maximum number of enrolments reached"
)
>
0
)
{
// This is a MySQL error triggered by TRG_NEW_STUDENT_MAXENROLMENTS trigger
// (see triggers-enroments-integrity-constraints.sql)
// As the format is not that of a normal error, we just get message
sails
.
log
.
debug
(
err
.
message
);
return
res
.
serverError
(
err
.
message
);
}
else
{
sails
.
log
.
debug
(
err
.
message
);
return
res
.
serverError
(
err
.
message
);
}
});
},
/**
* Deletes an existing student by removing him/her from his/her office
* and all his supervisors.
* @param {request} req {} (with id_stu as url parameter)
* @param {response} res {}
*/
delete
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
Student
.
logical_delete
(
req
.
params
.
id_stu
,
function
(
err
)
{
if
(
err
)
{
return
res
.
json
(
500
,
{
error
:
err
});
}
return
res
.
json
({
result
:
'Deleted'
});
});
},
//
// Updates student information
//
update
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
res
.
badRequest
();
}
Student
.
findOne
(
req
.
params
.
id_stu
).
then
(
function
(
stu
)
{
var
k
;
// copy attributes
for
(
k
in
req
.
body
)
stu
[
k
]
=
req
.
body
[
k
];
if
(
!
req
.
body
.
password
)
// to avoid change password when no one is provided
delete
stu
.
password
;
stu
.
save
().
then
(
function
(
saved
)
{
res
.
ok
(
stu
);
// Send websocket message
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
student
(
stu
.
id
),
sails
.
hooks
.
events
.
updateStudent
(
stu
),
(
req
.
isSocket
)
?
req
.
socket
:
undefined
);
})
.
catch
(
function
(
err
)
{
res
.
severError
();
});
})
.
catch
(
function
(
err
)
{
res
.
notFound
();
});
},
/**
* Return all existing supervisor and therapist from a given student
* @param {request} req {} (with studentId as url parameter)
* @param {response} res
* [
* {
* id: supervisorId,
* name: 'John',
* surname: 'Doe',
* gender: 'M/F',
* pic: 'supervisor/avatar.jpg',
* address: 'My Adress, n1',
* country: 'ES',
* email: 'john@doe.es',
* phone: '+123123123',
* lang: 'es-es',
* active: true,
* ttsEngine: 'IVONA-Text',
* office: officeId
* },
* ...
* ]
*/
supervisors
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
}
Student
.
supervisors
(
req
.
params
.
id_stu
,
function
(
err
,
sups
)
{
if
(
err
)
throw
err
;
return
res
.
json
(
sups
);
});
},
/**
* Return all existing therapists from a given student
* @param {request} req {} (with studentId as url parameter)
* @param {response} res
* [
* {
* id: therapistId,
* name: 'John',
* surname: 'Doe',
* gender: 'M/F',
* pic: 'supervisor/avatar.jpg',
* address: 'My Address, n1',
* country: 'ES',
* email: 'john@doe.es',
* phone: '+123123123',
* lang: 'es-es',
* active: true,
* ttsEngine: 'IVONA-Text',
* office: officeId
* },
* ...
* ]
*/
therapists
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
}
Student
.
therapists
(
req
.
params
.
id_stu
,
function
(
err
,
sups
)
{
if
(
err
)
throw
err
;
return
res
.
json
(
sups
);
});
},
/**
* Return all existing tutors from a given student
* @param {request} req {} (with studentId as url parameter)
* @param {response} res
* [
* {
* id: tutorId,
* name: 'John',
* surname: 'Doe',
* gender: 'M/F',
* pic: 'supervisor/avatar.jpg',
* address: 'My Address, n1',
* country: 'ES',
* email: 'john@doe.es',
* phone: '+123123123',
* lang: 'es-es',
* active: true,
* ttsEngine: 'IVONA-Text',
* office: officeId
* },
* ...
* ]
*/
tutors
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
}
Student
.
tutors
(
req
.
params
.
id_stu
,
function
(
err
,
sups
)
{
if
(
err
)
throw
err
;
return
res
.
json
(
sups
);
});
},
/**
* Creates a relation between the student and a given supervisor.
* It broadcasts the event linkSupervisorToStudent to both the student room
* and the supervisor room.
* @param {request} { (with id_stu and id_sup as url parameters)
* asTherapist: true/false (optional) // assigns supervisor to student's office is true, set id_off to null otherwise
* }
* @param {response} {}
*/
link_supervisor
:
function
(
req
,
res
)
{
StuSup
.
create
({
student
:
req
.
param
(
'id_stu'
),
supervisor
:
req
.
param
(
'id_sup'
)
})
.
then
(
function
(
stuSup
)
{
if
(
!
stuSup
)
throw
new
Error
(
'stusup not created'
);
const
socketToOmit
=
(
req
.
isSocket
)
?
req
.
socket
:
undefined
;
const
linkSupervisorToStudentEvent
=
sails
.
hooks
.
events
.
linkSupervisorToStudent
(
stuSup
.
supervisor
,
stuSup
.
student
);
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
supervisor
(
stuSup
.
supervisor
),
linkSupervisorToStudentEvent
,
socketToOmit
);
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
student
(
stuSup
.
student
),
linkSupervisorToStudentEvent
,
socketToOmit
);
return
stuSup
;
})
.
catch
((
err
)
=>
{
StuSup
.
findOne
({
student
:
req
.
param
(
'id_stu'
),
supervisor
:
req
.
param
(
'id_sup'
)
})
.
then
((
stuSup
)
=>
{
// It was already there!
if
(
stuSup
)
return
stuSup
;
else
throw
err
;
});
})
.
then
((
stuSup
)
=>
{
// update supervisor office if it is linked as therapist
Supervisor
.
findOne
({
id
:
req
.
param
(
'id_sup'
)})
.
then
((
sup
)
=>
{
if
(
sup
)
{
Student
.
findOne
({
id
:
req
.
param
(
'id_stu'
)})
.
then
((
stu
)
=>
{
if
(
stu
)
{
if
(
req
.
body
.
asTherapist
)
sup
.
office
=
stu
.
office
;
else
sup
.
office
=
null
;
delete
sup
.
password
;
sup
.
save
();
}
});
}
});
return
res
.
ok
();
})
.
catch
((
err
)
=>
{
return
res
.
serverError
(
"Error: "
+
err
);
});
},
/**
* Destroys a relation (drama queen) between the student and a given supervisor.
* It broadcasts the even unlinkSupervisorFromStudent to both the student room
* and the supervisor room.
* @param {request} {} (with studentId and supervisorId as url parameters)
* @param {response} {}
*/
unlink_supervisor
:
function
(
req
,
res
)
{
StuSup
.
findOne
({
student
:
req
.
param
(
'id_stu'
),
supervisor
:
req
.
param
(
'id_sup'
)
})
.
then
((
stuSup
)
=>
{
if
(
!
stuSup
)
throw
new
Error
(
"student and supervisor are not linked"
);
stuSup
.
destroy
();
const
socketToOmit
=
req
.
isSocket
?
req
.
socket
:
undefined
;
const
unlinkSupervisorFromStudentEvent
=
sails
.
hooks
.
events
.
unlinkSupervisorFromStudent
(
stuSup
.
student
,
stuSup
.
supervisor
);
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
supervisor
(
stuSup
.
supervisor
),
unlinkSupervisorFromStudentEvent
,
socketToOmit
);
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
student
(
stuSup
.
student
),
unlinkSupervisorFromStudentEvent
,
socketToOmit
);
return
res
.
ok
();
})
.
catch
((
err
)
=>
{
res
.
serverError
(
"Error unliking student: "
+
err
);
});
},
/**
* Get all methods from a given student (with their instructions)
* @param {request} req {} (with studentId as url parameter)
* @param {response} res
* [
* {
* id: methodId,
* student: studentId,
* name: 'Method Name',
* description: 'Method description',
* registration: null,
* notes: 'Method notes',
* last_ins: instructionId // Last instruccion executed,
* instructions: [
* {
* id: instructionId,
* name: 'Instruction Name',
* objective: 'Instruction Objective',
* status: 'instruction-status',
* begin: '2015-07-14T07:23:03.000Z',
* end: '2015-07-14T07:28:03.000Z',
* method: methodId
* },
* ...
* ]
* },
* ...
* ]
*/
methods
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
Method
.
find
({
student
:
params
.
id_stu
})
.
populate
(
'instructions'
)
.
exec
(
function
(
err
,
methods
)
{
if
(
err
)
return
res
.
json
(
500
,
{
error
:
err
});
if
(
!
methods
||
methods
.
length
==
0
)
return
res
.
json
([]);
// Return an empty array
if
(
methods
)
{
var
l_met
=
[];
async
.
eachSeries
(
methods
,
function
(
m
,
callback1
)
{
var
l_ins
=
[];
var
last_ins
=
null
;
var
last_time
=
0
;
sails
.
log
.
debug
(
'Loop methods: '
+
m
.
name
);
async
.
eachSeries
(
m
.
instructions
,
function
(
ins
,
callback2
)
{
sails
.
log
.
debug
(
'Loop instructions: '
+
ins
.
name
);
Instruction
.
findOne
({
id
:
ins
.
id
})
.
populate
(
'workingSessions'
,
{
sort
:
'begin DESC'
})
.
exec
(
function
(
err
,
instruction
)
{
if
(
err
)
{
sails
.
log
.
debug
(
'Error in method '
+
m
.
name
);
}
if
(
!
instruction
||
!
instruction
.
workingSessions
||
instruction
.
workingSessions
.
length
==
0
)
{
sails
.
log
.
debug
(
'No working sessions found for instruction '
+
instruction
.
id
);
}
else
{
var
last
=
instruction
.
workingSessions
.
length
-
1
;
instruction
.
begin
=
instruction
.
workingSessions
[
last
].
end
;
instruction
.
end
=
instruction
.
workingSessions
[
0
].
end
;
if
(
instruction
.
end
>
last_time
)
{
last_ins
=
instruction
.
id
;
last_time
=
instruction
.
end
;
}
}
// Add instruction to list (with or without tries)
l_ins
.
push
(
instruction
);
callback2
();
});
// Finish function when each callback is done
// Optionaly it can be passed and err parameter
},
function
(
err
)
{
if
(
err
)
{
// One of the iterations produced an error.
// All processing will now stop.
return
res
.
json
(
500
,
{
error
:
'Error looping in tries: '
+
err
});
}
m
.
instructions
=
l_ins
;
m
.
last_ins
=
last_ins
;
l_met
.
push
(
m
);
callback1
();
});
// Finish function when each callback is done
// Optionaly it can be passed and err parameter
},
function
(
err
)
{
if
(
err
)
{
// One of the iterations produced an error.
// All processing will now stop.
return
res
.
json
(
500
,
{
error
:
'Error looping in method instructions: '
+
err
});
}
else
{
// All end ok
//Assing the built list
return
res
.
json
(
l_met
);
}
});
}
});
},
// read action
// get tries of the last working session
//
lasttries
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
//Student.findOne({ id: params.id_stu }).populate('workingSessions')
VStuLastInstruction
.
findOne
({
where
:
{
student
:
params
.
id_stu
}
})
.
exec
(
function
(
err
,
ws
)
{
if
(
err
)
{
sails
.
log
.
debug
(
'Finding student working sessions: '
+
err
);
return
res
.
json
(
500
,
{
error
:
'No student last working session found'
});
}
if
(
!
ws
||
ws
.
length
==
0
)
{
return
res
.
json
([]);
// Return an empty array of last tries
}
if
(
ws
)
{
// Find the tries of this working session populating actions
sails
.
log
.
debug
(
'Find WS '
+
JSON
.
stringify
(
ws
));
Try
.
find
({
workingSession
:
ws
.
workingSession
})
.
populate
(
'actions'
,
{
//supervisor: null //recovering supervisor actions, too
})
.
exec
(
function
(
err
,
tries
)
{
if
(
err
)
{
return
res
.
json
(
500
,
{
error
:
'No student last tries found'
});
}
if
(
!
tries
||
tries
.
length
==
0
)
{
return
res
.
json
([]);
// Return an empty array of tries
}
if
(
tries
)
{
// A list for one element: The last working session
var
l_ws
=
[];
l_ws
.
push
({
'id'
:
ws
.
workingSession
,
'student'
:
ws
.
student
,
'begin'
:
ws
.
ws_begin
,
'end'
:
ws
.
ws_end
,
'description'
:
ws
.
ws_description
,
'tries'
:
tries
});
return
res
.
json
(
l_ws
);
}
});
}
});
},
/**
* Return all tries from a student
* @param {request} req {} (width studentId as url parameter)
* @param {response} res
* {
"methods": [
{
"student": 24,
"id": 1,
"name": "Test Method",
"description": null,
"registration": null,
"notes": null
"instructions": [
{
"id": 1,
"name": "Test Instruction",
"objective": null,
"status": "started",
"begin": null,
"end": null,
"method": 1,
"working_sessions": [
{
"id": 3,
"begin": "2016-09-09T08:26:24.500Z",
"end": "2016-08-28T23:36:35.000Z",
"current": null,
"description": "",
"supervisor": 23,
"instruction": 1
"tries": [
{
"actions": [],
"id": 1,
"begin": "2016-08-28T23:36:35.474Z",
"end": "2016-08-28T23:36:44.000Z",
"result": null,
"description": null,
"workingSession": 3
},
{
"actions": [],
"id": 2,
"begin": "2016-08-28T23:36:44.050Z",
"end": "2016-08-29T01:36:51.710Z",
"result": "SUCCESS",
"description": null,
"workingSession": 3
},
{
"actions": [],
"id": 3,
"begin": "2016-08-28T23:36:51.942Z",
"end": "2016-08-28T23:36:53.000Z",
"result": "DISCARDED",
"description": null,
"workingSession": 3
},
{
"actions": [],
"id": 4,
"begin": "2016-08-28T23:36:53.877Z",
"end": "2016-08-28T23:37:13.000Z",
"result": "SPONTANEOUS SUCCESS",
"description": null,
"workingSession": 3
}
]
}
}
]
}
]
}
*/
tries
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
return
res
.
badRequest
(
"Student not defined"
);
Student
.
tries
(
req
.
params
.
id_stu
,
function
(
err
,
l_met
)
{
if
(
err
)
return
res
.
serverError
(
err
);
return
res
.
ok
(
l_met
);
});
},
/**
* Get all pictos from a given student
* @param {request} req {} (with studentId as url parameter)
* @param {response} res
* [
* {
* id: student-picto ID,
* picto: {
* id: pictoId,
* uri: 'uri/to/picto.png',
* category: pictoCategoryId,
* source: 1 @TODO Other sources
* owner: supervisorId or null
* },
* expression: {
* id: expressionId,
* lang: 'es-es',
* text: 'Picto Expression',
* picto: pictoId
* },
* attributes: { @see StuPicto.getValidAttributes() }
* },
* ...
* ]
*/
pictos
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
}
sails
.
log
.
debug
(
'Pictos requested for student '
+
req
.
params
.
id_stu
);
Student
.
pictos
(
req
.
params
.
id_stu
,
function
(
err
,
pictos
)
{
if
(
err
)
return
res
.
serverError
(
"Error obtaining pictos: "
+
err
);
return
res
.
ok
(
pictos
);
});
},
//
// Returns all working sessions for the given student
//
ws
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
}
sails
.
log
.
debug
(
'Working Sessions requested for student '
+
req
.
params
.
id_stu
);
Student
.
findOne
(
req
.
params
.
id_stu
)
.
populate
(
'workingSessions'
)
.
exec
(
function
(
err
,
stu
)
{
if
(
err
)
return
res
.
json
(
500
,
{
error
:
err
});
if
(
!
stu
||
!
stu
.
workingSessions
||
stu
.
workingSessions
.
length
==
0
)
return
res
.
json
([]);
// Return an empty array
else
return
res
.
json
(
stu
.
workingSessions
);
});
},
/**
* Add an existing picto to the student's collection
* @param {request} req (with studentId and pictoId as url parameter)
* {
* attributes: { @see StuPicto.getValidAttributes() }
* }
* @param {response} res
* {
* id: stuPictoId (association betweet student and picto)
* 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
();
Student
.
findOne
({
id
:
params
.
id_stu
})
.
then
((
student
)
=>
{
if
(
!
student
)
{
sails
.
log
.
error
(
`Student
${
params
.
id_stu
}
not found`
);
throw
new
Error
(
"Student not found"
);
}
return
[
Picto
.
findOne
({
id
:
params
.
id_picto
})
.
populate
(
'expressions'
,
{
lang
:
student
.
lang
})
,
student
];
})
.
spread
((
picto
,
student
)
=>
{
if
(
!
picto
)
{
sails
.
log
.
error
(
`Picto
${
params
.
id_picto
}
not found`
);
throw
new
Error
(
"Picto not found"
);
}
return
[
StuPicto
.
create
({
student
:
student
.
id
,
picto
:
picto
.
id
,
attributes
:
params
.
attributes
})
,
picto
];
})
.
spread
((
stuPicto
,
picto
)
=>
{
if
(
!
stuPicto
)
throw
new
Error
(
"stu_picto not created"
);
sails
.
log
.
debug
(
"->>"
+
JSON
.
stringify
(
picto
));
return
res
.
ok
({
id
:
stuPicto
.
id
,
student
:
params
.
id_stu
,
attributes
:
stuPicto
.
attributes
,
picto
:
picto
,
expression
:
picto
.
expressions
[
0
]
});
})
.
catch
(
err
=>
res
.
serverError
(
"Error adding picto: "
+
err
));
},
/**
* Removes an existing picto to the student's collection
* Warning: yout must send the **stuPictoId** as url parameter, not the **pictoId**.
* You can obtain this ID from the student picto collection.
* @param {request} req {} (with studentId and stuPictoId as url parameters)
* @param {response} res {}
*/
delete_picto
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
StuPicto
.
destroy
({
id
:
params
.
id_stuPicto
})
.
then
(
destroyed
=>
{
if
(
!
destroyed
)
throw
new
Error
();
return
res
.
ok
();
})
.
catch
(
err
=>
{
return
res
.
serverError
(
'Not removed picto for student: '
+
err
);
});
},
// update action
// update picto atributes for a studentPicto
//
update_picto
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
console
.
log
(
'Updating attributes for picto student '
+
JSON
.
stringify
(
params
));
console
.
log
(
JSON
.
stringify
(
params
));
query
=
params
.
id_stuPicto
?
{
id
:
params
.
id_stuPicto
}
:
{
id_stu
:
params
.
id_stu
,
id_pic
:
params
.
id_pic
}
StuPicto
.
update
(
query
,
{
attributes
:
params
.
attributes
})
.
then
(
updated
=>
{
if
(
!
updated
)
throw
new
Error
(
"error on update"
);
console
.
log
(
'Updated attributes for picto student:'
+
JSON
.
stringify
(
updated
[
0
]));
// return res.json(updated[0]);
return
res
.
ok
({
id
:
updated
[
0
].
id
,
// id of stu_picto
attributes
:
updated
[
0
].
attributes
,
// picto attributes for student
picto
:
{
id
:
updated
[
0
].
picto
// picto information
}
});
})
.
catch
(
err
=>
{
return
res
.
serverError
(
'Unable to update picto for student: '
+
err
);
});
},
/**
* Updates the student profile image
* @param {request} req
* file: [The image binary data]
* {
* "id": "student ID"
* }
* @param {response} res {}
*/
upload
:
function
(
req
,
res
)
{
var
fs
=
require
(
'fs'
);
var
path
=
require
(
'path'
);
var
newAvatarFileName
;
var
newAvatarFileDescriptor
;
var
newAvatarDirectory
=
sails
.
config
.
pictogram
.
paths
.
studentAvatarDirectory
;
Student
.
findOne
({
id
:
req
.
body
.
id
}).
then
(
function
(
student
)
{
if
(
!
student
)
{
throw
new
Error
(
"Student not found"
);
}
newAvatarFileName
=
sails
.
config
.
pictogram
.
paths
.
getStudentAvatarFileName
(
student
.
id
);
req
.
file
(
'file'
).
upload
({
maxBytes
:
1000000
,
dirname
:
newAvatarDirectory
,
saveAs
:
newAvatarFileName
},
function
whenDone
(
error
,
uploadedFiles
)
{
if
(
error
||
(
uploadedFiles
.
length
===
0
))
{
throw
new
Error
(
"upload failed"
);
}
try
{
newAvatarFileDescriptor
=
uploadedFiles
[
0
].
fd
;
if
(
student
.
pic
!==
sails
.
config
.
pictogram
.
paths
.
defaultAvatarFileName
)
{
fs
.
unlinkSync
(
path
.
join
(
newAvatarDirectory
,
student
.
pic
));
}
student
.
pic
=
newAvatarFileName
;
student
.
save
(
function
(
updateStudentError
)
{
if
(
updateStudentError
)
{
throw
updateStudentError
;
}
res
.
ok
();
});
}
catch
(
updateAvatarError
)
{
fs
.
unlinkSync
(
newAvatarFileDescriptor
);
res
.
serverError
(
"Error when updating profile image in server"
);
}
});
})
.
catch
(
function
(
err
)
{
res
.
badRequest
(
"Could not find supervisor: "
+
err
);
});
},
// ***************************************************************
// WEBSOCKETS
// ***************************************************************
//
// Subscribe to websockets events
//
subscribe
:
function
(
req
,
res
)
{
var
action
=
req
.
param
(
'action'
);
var
attributes
=
req
.
param
(
'attributes'
);
attributes
.
ui
=
attributes
.
ui
?
attributes
.
ui
:
'PCB'
;
if
(
req
.
isSocket
)
{
sails
.
hooks
.
rooms
.
subscribeToRoom
(
sails
.
hooks
.
rooms
.
student
(
attributes
.
id_stu
),
req
.
socket
,
attributes
.
ui
);
}
res
.
ok
({
msg
:
"Subscribed to student "
});
},
//
// Unsubscribe to websockets events
//
unsubscribe
:
function
(
req
,
res
)
{
var
action
=
req
.
param
(
'action'
);
//var attributes = req.param('attributes');
if
(
req
.
isSocket
)
{
var
rooms
=
sails
.
sockets
.
socketRooms
(
req
.
socket
);
console
.
log
(
"Subscribed rooms in socket: "
+
JSON
.
stringify
(
rooms
));
// Leave all rooms
for
(
var
i
=
0
;
i
<
rooms
.
length
;
i
++
)
{
//sails.sockets.leave(req.socket, rooms[i]); MODIFICADO POR FERNANDO. SI NO, NO SE ACTUALIZA UPDATE_PEERS
sails
.
hooks
.
rooms
.
unsubscribeFromRoom
(
rooms
[
i
],
req
.
socket
);
sails
.
log
.
debug
(
"Unsusbscribe from room "
+
rooms
[
i
]);
}
res
.
json
({
msg
:
"Unsubscribed from all rooms"
});
}
},
//
// Logs a vocabulary action and broadcast to anyone subscribed to this student
vocabulary
:
function
(
req
,
res
)
{
var
action
=
req
.
param
(
'action'
);
var
attributes
=
req
.
param
(
'attributes'
);
var
roomName
=
'studentRoom'
+
attributes
.
id_stu
;
sails
.
log
.
debug
(
"Inside vocabulary"
);
if
(
req
.
isSocket
)
{
sails
.
log
.
debug
(
"Inside vocabulary - isSocket"
);
// Send to all sockets subscribed to this room except the own socket that sends the message
// Parameters: room, action, data to send, socket to avoid sending (the socket that send this)
sails
.
sockets
.
broadcast
(
roomName
,
'vocabulary'
,
{
"action"
:
action
,
"attributes"
:
attributes
},
req
.
socket
);
res
.
json
({
msg
:
"Vocabulary "
+
action
+
" action from student "
+
attributes
.
id_stu
});
}
},
/**
* Logs a TRY action and broadcast to anyone subscribed to this student
* @param {request} req
* {
* "action": <action> ("add", "delete", ...),
* "attributes": {
* "id_stu": <id_stu>,
* "timestamp": <timestamp_string_in_ISO_format> (e.g.: "2016-07-13 17:50:00.224+0200"),
* "picto": {...}
* }
* }
* @param {response} res {<action_created>}
*/
action
:
function
(
req
,
res
)
{
var
action
=
req
.
param
(
'action'
);
var
attributes
=
req
.
param
(
'attributes'
);
sails
.
log
.
debug
(
"Inside action. Student:"
+
attributes
.
id_stu
);
if
(
!
req
.
isSocket
)
{
sails
.
log
.
debug
(
"No socket request for action"
);
res
.
badRequest
()
}
else
{
sails
.
log
.
debug
(
"websockets - room "
+
sails
.
hooks
.
rooms
.
student
(
attributes
.
id_stu
));
// BROADCAST to everyone subscribed to this student
const
socketToOmit
=
(
req
.
isSocket
)
?
req
.
socket
:
undefined
;
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
student
(
attributes
.
id_stu
),
sails
.
hooks
.
events
.
actionPerformed
(
action
,
attributes
),
socketToOmit
);
var
sup
=
null
;
if
(
attributes
.
id_sup
)
sup
=
attributes
.
id_sup
;
var
desc
=
null
;
if
(
attributes
.
stu_picto
)
desc
=
attributes
.
stu_picto
;
// select, add and delete actions data
if
(
attributes
.
pictos
)
desc
=
attributes
.
pictos
;
// show action data
Action
.
create
({
type
:
action
,
timestamp
:
attributes
.
timestamp
,
// it comes already in ISO format
supervisor
:
sup
,
student
:
attributes
.
id_stu
,
description
:
desc
})
.
then
(
function
(
created
)
{
res
.
json
({
action
:
created
});
})
.
fail
(
function
(
err
)
{
sails
.
log
.
error
(
err
.
details
);
res
.
serverError
(
err
.
details
);
});
}
},
//
// Logs a config action and broadcast to anyone subscribed to this student
config
:
function
(
req
,
res
)
{
var
action
=
req
.
param
(
'action'
);
var
attributes
=
req
.
param
(
'attributes'
);
sails
.
log
.
debug
(
"Inside config"
);
if
(
req
.
isSocket
)
{
const
socketToOmit
=
(
req
.
isSocket
)
?
req
.
socket
:
undefined
;
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
student
(
attributes
.
id_stu
),
'action'
,
{
"action"
:
action
,
"attributes"
:
attributes
},
socketToOmit
);
}
else
{
res
.
ok
({
msg
:
"Config "
+
action
+
" action from student "
+
attributes
.
id_stu
});
}
},
//
// Stores in the action table a bulk of actions loaded from of the
// recorded action log
//
actions_batch
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
var
count
=
0
;
sails
.
log
.
debug
(
"Actions_batch request"
);
if
(
!
params
.
actions
)
return
res
.
json
(
400
,
{
'error'
:
"no actions"
});
// We loop through the actions and store them in the database
async
.
forEach
(
params
.
actions
,
function
(
action
,
cb
)
{
sails
.
log
.
debug
(
"Actions batch is recording action: "
+
JSON
.
stringify
(
action
));
var
id_sup
=
null
;
if
(
action
.
attributes
.
id_sup
)
id_sup
=
action
.
attributes
.
sup
;
var
id_stu
=
null
;
if
(
action
.
attributes
.
id_stu
)
id_stu
=
action
.
attributes
.
id_stu
;
var
desc
=
null
;
if
(
action
.
attributes
.
picto
)
desc
=
action
.
attributes
.
picto
;
// select, add and delete actions data
if
(
action
.
attributes
.
pictos
)
desc
=
action
.
attributes
.
pictos
;
// show action data
Action
.
create
({
type
:
action
.
action
,
timestamp
:
action
.
attributes
.
timestamp
,
supervisor
:
id_sup
,
student
:
id_stu
,
description
:
desc
})
.
exec
(
function
(
err
,
created
)
{
if
(
err
)
{
console
.
log
(
err
.
details
);
sails
.
log
.
error
(
err
.
details
);
return
cb
(
err
);
}
else
if
(
created
)
count
++
;
cb
();
});
},
function
(
err
)
{
// function called when loop is done
if
(
err
)
{
console
.
log
(
err
.
details
);
sails
.
log
.
error
(
err
.
details
);
return
res
.
json
({
'error'
:
err
.
details
});
}
else
return
res
.
json
({
'result'
:
'Ok'
,
'total'
:
count
});
});
},
//
// Returns the last instruction for the student
//
last_instruction
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
return
res
.
json
(
400
,
{
err
:
'id_stu parameter is missing'
});
VStuLastInstruction
.
find
({
id_stu
:
req
.
params
.
id_stu
})
.
exec
(
function
(
err
,
found
)
{
if
(
err
)
return
res
.
json
(
500
,
err
);
if
(
!
found
)
return
res
.
json
({});
return
res
.
json
(
found
);
});
}
};
/* global Student, PictoCore, VStuLastInstruction, StuPicto, StuSup, sailsTokenAuth, sails,
Picto */
/**
/* StudentController
*
* @description :: Server-side logic for managing students
* @help :: See http://links.sailsjs.org/docs/controllers
*/
module
.
exports
=
{
// dummy function to test timeouts
eternal
:
function
(
req
,
res
)
{
setTimeout
(
function
(){
return
;},
1000
*
60
*
6
);
},
/**
* Login in the server as student, getting a toker for interacting with the platform
* @param {request} req
* {
* username: 'johnydoe',
* password: '12312'
* }
* @param {response} res
* {
* user: {
* id: 12,
* office: 1234,
* username: 'johnydoe',
* name: 'Johny',
* surname: 'Doe',
* birthdate: '2009-12-10T00:00:00.000Z',
* gender: 'F',
* country: 'ES',
* pic: 'avatar/nice/url.jpg',
* notes: null,
* lang: 'en-en',
* iat: 123512,
* exp: 1231292,
* attributes: { @see Student.getValidAttributes() }
* },
* token: '... asd90jkas ...',
* server_time: 123912932312
* }
*/
login
:
function
(
req
,
res
)
{
var
bcrypt
=
require
(
'bcrypt-nodejs'
);
Student
.
findOne
({
username
:
req
.
body
.
username
})
.
then
(
function
(
student
)
{
if
(
student
)
{
if
(
bcrypt
.
compareSync
(
req
.
body
.
password
,
student
.
password
))
{
student
.
isStudent
=
true
;
res
.
ok
({
user
:
student
,
token
:
sailsTokenAuth
.
issueToken
(
student
,
sails
.
config
.
jwt
.
expiresInMinutes
),
server_time
:
(
new
Date
())
.
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
();
});
},
/**
* Get a student by id
* @param {request} req {} (with studentId specified as url parameters)
* @param {response} res
* {
* id: 12,
* office: 1234,
* username: 'johnydoe',
* name: 'Johny',
* surname: 'Doe',
* birthdate: '2009-12-10T00:00:00.000Z',
* gender: 'F',
* country: 'ES',
* pic: 'avatar/nice/url.jpg',
* notes: null,
* lang: 'en-en',
* iat: 123512,
* exp: 1231292,
* attributes: { @see Student.getValidAttributes() },
* current_method: 'Do Things', // May be null
* current_instruction: 'Do Stuff', // May be null
* supervision: 0|1|2, // supervision level according to requester 0 -> office admin, 1 -> tutor, 2 -> therapist
* }
*/
getInfo
:
function
(
req
,
res
)
{
Student
.
findOne
({
id
:
req
.
params
.
id_stu
}).
populate
(
'lastInstruction'
)
.
then
(
function
(
student
)
{
if
(
!
student
)
throw
new
Error
(
"student not found"
);
student
.
current_method
=
student
.
lastInstruction
[
0
]
?
student
.
lastInstruction
[
0
].
met_name
:
"no_method"
;
student
.
current_instruction
=
student
.
lastInstruction
[
0
]
?
student
.
lastInstruction
[
0
].
ins_name
:
"no_instruction"
;
// recover last instruction to complete student info
var
stu_last_inst
=
VStuLastInstruction
.
findOne
({
student
:
student
.
id
})
.
then
(
function
(
stu_last_inst
)
{
return
stu_last_inst
;
})
.
error
(
err
=>
{
throw
err
});
// determine supervision level of the requester on the student
var
stu_sup
=
StuSup
.
findOne
({
id_stu
:
student
.
id
,
id_sup
:
req
.
token
.
id
})
.
then
(
function
(
stu_sup
)
{
return
stu_sup
;
})
.
error
(
err
=>
{
throw
err
});
return
[
student
,
stu_last_inst
,
stu_sup
];
})
.
spread
(
function
(
student
,
stu_last_inst
,
stu_sup
)
{
if
(
stu_last_inst
)
{
student
.
current_method
=
stu_last_inst
.
met_name
;
student
.
current_instruction
=
stu_last_inst
.
ins_name
;
}
// requester has no relation
student
.
supervision
=
-
1
;
if
(
!
stu_sup
&&
req
.
token
.
office
&&
student
.
office
==
req
.
token
.
office
.
id
)
student
.
supervision
=
0
;
// requester is admin of the office
else
if
(
stu_sup
&&
!
req
.
token
.
office
)
student
.
supervision
=
1
;
// requester is tutor of the studend
else
if
(
stu_sup
&&
req
.
token
.
office
&&
student
.
office
==
req
.
token
.
office
.
id
)
student
.
supervision
=
2
;
// requester is supervisor of student
if
(
student
.
supervision
==
-
1
)
// should not hace access!!!
return
res
.
forbidden
(
"Access to this student should not be granted to you"
);
return
res
.
ok
(
student
);
})
.
catch
(
function
(
err
)
{
return
res
.
notFound
(
err
);
});
},
//
// Adds a new student into the database
//
create
:
function
(
req
,
res
)
{
var
params
=
req
.
params
.
all
();
Student
.
create
(
params
)
.
then
(
function
(
created
)
{
sails
.
log
.
debug
(
'Student '
+
created
.
id
+
' created: '
+
JSON
.
stringify
(
created
));
return
res
.
ok
(
created
);
})
.
error
(
function
(
err
)
{
if
(
err
.
message
.
search
(
"Maximum number of enrolments reached"
)
>
0
)
{
// This is a MySQL error triggered by TRG_NEW_STUDENT_MAXENROLMENTS trigger
// (see triggers-enroments-integrity-constraints.sql)
// As the format is not that of a normal error, we just get message
sails
.
log
.
debug
(
err
.
message
);
return
res
.
serverError
(
err
.
message
);
}
else
{
sails
.
log
.
debug
(
err
.
message
);
return
res
.
serverError
(
err
.
message
);
}
});
},
/**
* Deletes an existing student by removing him/her from his/her office
* and all his supervisors.
* @param {request} req {} (with id_stu as url parameter)
* @param {response} res {}
*/
delete
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
Student
.
logical_delete
(
req
.
params
.
id_stu
,
function
(
err
)
{
if
(
err
)
{
return
res
.
json
(
500
,
{
error
:
err
});
}
return
res
.
json
({
result
:
'Deleted'
});
});
},
//
// Updates student information
//
update
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
res
.
badRequest
();
}
Student
.
findOne
(
req
.
params
.
id_stu
).
then
(
function
(
stu
)
{
var
k
;
// copy attributes
for
(
k
in
req
.
body
)
stu
[
k
]
=
req
.
body
[
k
];
if
(
!
req
.
body
.
password
)
// to avoid change password when no one is provided
delete
stu
.
password
;
stu
.
save
().
then
(
function
(
saved
)
{
res
.
ok
(
stu
);
// Send websocket message
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
student
(
stu
.
id
),
sails
.
hooks
.
events
.
updateStudent
(
stu
),
(
req
.
isSocket
)
?
req
.
socket
:
undefined
);
})
.
catch
(
function
(
err
)
{
res
.
severError
();
});
})
.
catch
(
function
(
err
)
{
res
.
notFound
();
});
},
/**
* Return all existing supervisor and therapist from a given student
* @param {request} req {} (with studentId as url parameter)
* @param {response} res
* [
* {
* id: supervisorId,
* name: 'John',
* surname: 'Doe',
* gender: 'M/F',
* pic: 'supervisor/avatar.jpg',
* address: 'My Adress, n1',
* country: 'ES',
* email: 'john@doe.es',
* phone: '+123123123',
* lang: 'es-es',
* active: true,
* ttsEngine: 'IVONA-Text',
* office: officeId
* },
* ...
* ]
*/
supervisors
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
}
Student
.
supervisors
(
req
.
params
.
id_stu
,
function
(
err
,
sups
)
{
if
(
err
)
throw
err
;
return
res
.
json
(
sups
);
});
},
/**
* Return all existing therapists from a given student
* @param {request} req {} (with studentId as url parameter)
* @param {response} res
* [
* {
* id: therapistId,
* name: 'John',
* surname: 'Doe',
* gender: 'M/F',
* pic: 'supervisor/avatar.jpg',
* address: 'My Address, n1',
* country: 'ES',
* email: 'john@doe.es',
* phone: '+123123123',
* lang: 'es-es',
* active: true,
* ttsEngine: 'IVONA-Text',
* office: officeId
* },
* ...
* ]
*/
therapists
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
}
Student
.
therapists
(
req
.
params
.
id_stu
,
function
(
err
,
sups
)
{
if
(
err
)
throw
err
;
return
res
.
json
(
sups
);
});
},
/**
* Return all existing tutors from a given student
* @param {request} req {} (with studentId as url parameter)
* @param {response} res
* [
* {
* id: tutorId,
* name: 'John',
* surname: 'Doe',
* gender: 'M/F',
* pic: 'supervisor/avatar.jpg',
* address: 'My Address, n1',
* country: 'ES',
* email: 'john@doe.es',
* phone: '+123123123',
* lang: 'es-es',
* active: true,
* ttsEngine: 'IVONA-Text',
* office: officeId
* },
* ...
* ]
*/
tutors
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
}
Student
.
tutors
(
req
.
params
.
id_stu
,
function
(
err
,
sups
)
{
if
(
err
)
throw
err
;
return
res
.
json
(
sups
);
});
},
/**
* Creates a relation between the student and a given supervisor.
* It broadcasts the event linkSupervisorToStudent to both the student room
* and the supervisor room.
* @param {request} { (with id_stu and id_sup as url parameters)
* asTherapist: true/false (optional) // assigns supervisor to student's office is true, set id_off to null otherwise
* }
* @param {response} {}
*/
link_supervisor
:
function
(
req
,
res
)
{
StuSup
.
create
({
student
:
req
.
param
(
'id_stu'
),
supervisor
:
req
.
param
(
'id_sup'
)
})
.
then
(
function
(
stuSup
)
{
if
(
!
stuSup
)
throw
new
Error
(
'stusup not created'
);
const
socketToOmit
=
(
req
.
isSocket
)
?
req
.
socket
:
undefined
;
const
linkSupervisorToStudentEvent
=
sails
.
hooks
.
events
.
linkSupervisorToStudent
(
stuSup
.
supervisor
,
stuSup
.
student
);
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
supervisor
(
stuSup
.
supervisor
),
linkSupervisorToStudentEvent
,
socketToOmit
);
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
student
(
stuSup
.
student
),
linkSupervisorToStudentEvent
,
socketToOmit
);
return
stuSup
;
})
.
catch
((
err
)
=>
{
StuSup
.
findOne
({
student
:
req
.
param
(
'id_stu'
),
supervisor
:
req
.
param
(
'id_sup'
)
})
.
then
((
stuSup
)
=>
{
// It was already there!
if
(
stuSup
)
return
stuSup
;
else
throw
err
;
});
})
.
then
((
stuSup
)
=>
{
// update supervisor office if it is linked as therapist
Supervisor
.
findOne
({
id
:
req
.
param
(
'id_sup'
)})
.
then
((
sup
)
=>
{
if
(
sup
)
{
Student
.
findOne
({
id
:
req
.
param
(
'id_stu'
)})
.
then
((
stu
)
=>
{
if
(
stu
)
{
if
(
req
.
body
.
asTherapist
)
sup
.
office
=
stu
.
office
;
else
sup
.
office
=
null
;
delete
sup
.
password
;
sup
.
save
();
}
});
}
});
return
res
.
ok
();
})
.
catch
((
err
)
=>
{
return
res
.
serverError
(
"Error: "
+
err
);
});
},
/**
* Destroys a relation (drama queen) between the student and a given supervisor.
* It broadcasts the even unlinkSupervisorFromStudent to both the student room
* and the supervisor room.
* @param {request} {} (with studentId and supervisorId as url parameters)
* @param {response} {}
*/
unlink_supervisor
:
function
(
req
,
res
)
{
StuSup
.
findOne
({
student
:
req
.
param
(
'id_stu'
),
supervisor
:
req
.
param
(
'id_sup'
)
})
.
then
((
stuSup
)
=>
{
if
(
!
stuSup
)
throw
new
Error
(
"student and supervisor are not linked"
);
stuSup
.
destroy
();
const
socketToOmit
=
req
.
isSocket
?
req
.
socket
:
undefined
;
const
unlinkSupervisorFromStudentEvent
=
sails
.
hooks
.
events
.
unlinkSupervisorFromStudent
(
stuSup
.
student
,
stuSup
.
supervisor
);
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
supervisor
(
stuSup
.
supervisor
),
unlinkSupervisorFromStudentEvent
,
socketToOmit
);
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
student
(
stuSup
.
student
),
unlinkSupervisorFromStudentEvent
,
socketToOmit
);
return
res
.
ok
();
})
.
catch
((
err
)
=>
{
res
.
serverError
(
"Error unliking student: "
+
err
);
});
},
/**
* Get all methods from a given student (with their instructions)
* @param {request} req {} (with studentId as url parameter)
* @param {response} res
* [
* {
* id: methodId,
* student: studentId,
* name: 'Method Name',
* description: 'Method description',
* registration: null,
* notes: 'Method notes',
* last_ins: instructionId // Last instruccion executed,
* instructions: [
* {
* id: instructionId,
* name: 'Instruction Name',
* objective: 'Instruction Objective',
* status: 'instruction-status',
* begin: '2015-07-14T07:23:03.000Z',
* end: '2015-07-14T07:28:03.000Z',
* method: methodId
* },
* ...
* ]
* },
* ...
* ]
*/
methods
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
Method
.
find
({
student
:
params
.
id_stu
})
.
populate
(
'instructions'
)
.
exec
(
function
(
err
,
methods
)
{
if
(
err
)
return
res
.
json
(
500
,
{
error
:
err
});
if
(
!
methods
||
methods
.
length
==
0
)
return
res
.
json
([]);
// Return an empty array
if
(
methods
)
{
var
l_met
=
[];
async
.
eachSeries
(
methods
,
function
(
m
,
callback1
)
{
var
l_ins
=
[];
var
last_ins
=
null
;
var
last_time
=
0
;
sails
.
log
.
debug
(
'Loop methods: '
+
m
.
name
);
async
.
eachSeries
(
m
.
instructions
,
function
(
ins
,
callback2
)
{
sails
.
log
.
debug
(
'Loop instructions: '
+
ins
.
name
);
Instruction
.
findOne
({
id
:
ins
.
id
})
.
populate
(
'workingSessions'
,
{
sort
:
'begin DESC'
})
.
exec
(
function
(
err
,
instruction
)
{
if
(
err
)
{
sails
.
log
.
debug
(
'Error in method '
+
m
.
name
);
}
if
(
!
instruction
||
!
instruction
.
workingSessions
||
instruction
.
workingSessions
.
length
==
0
)
{
sails
.
log
.
debug
(
'No working sessions found for instruction '
+
instruction
.
id
);
}
else
{
var
last
=
instruction
.
workingSessions
.
length
-
1
;
instruction
.
begin
=
instruction
.
workingSessions
[
last
].
end
;
instruction
.
end
=
instruction
.
workingSessions
[
0
].
end
;
if
(
instruction
.
end
>
last_time
)
{
last_ins
=
instruction
.
id
;
last_time
=
instruction
.
end
;
}
}
// Add instruction to list (with or without tries)
l_ins
.
push
(
instruction
);
callback2
();
});
// Finish function when each callback is done
// Optionaly it can be passed and err parameter
},
function
(
err
)
{
if
(
err
)
{
// One of the iterations produced an error.
// All processing will now stop.
return
res
.
json
(
500
,
{
error
:
'Error looping in tries: '
+
err
});
}
m
.
instructions
=
l_ins
;
m
.
last_ins
=
last_ins
;
l_met
.
push
(
m
);
callback1
();
});
// Finish function when each callback is done
// Optionaly it can be passed and err parameter
},
function
(
err
)
{
if
(
err
)
{
// One of the iterations produced an error.
// All processing will now stop.
return
res
.
json
(
500
,
{
error
:
'Error looping in method instructions: '
+
err
});
}
else
{
// All end ok
//Assing the built list
return
res
.
json
(
l_met
);
}
});
}
});
},
// read action
// get tries of the last working session
//
lasttries
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
//Student.findOne({ id: params.id_stu }).populate('workingSessions')
VStuLastInstruction
.
findOne
({
where
:
{
student
:
params
.
id_stu
}
})
.
exec
(
function
(
err
,
ws
)
{
if
(
err
)
{
sails
.
log
.
debug
(
'Finding student working sessions: '
+
err
);
return
res
.
json
(
500
,
{
error
:
'No student last working session found'
});
}
if
(
!
ws
||
ws
.
length
==
0
)
{
return
res
.
json
([]);
// Return an empty array of last tries
}
if
(
ws
)
{
// Find the tries of this working session populating actions
sails
.
log
.
debug
(
'Find WS '
+
JSON
.
stringify
(
ws
));
Try
.
find
({
workingSession
:
ws
.
workingSession
})
.
populate
(
'actions'
,
{
//supervisor: null //recovering supervisor actions, too
})
.
exec
(
function
(
err
,
tries
)
{
if
(
err
)
{
return
res
.
json
(
500
,
{
error
:
'No student last tries found'
});
}
if
(
!
tries
||
tries
.
length
==
0
)
{
return
res
.
json
([]);
// Return an empty array of tries
}
if
(
tries
)
{
// A list for one element: The last working session
var
l_ws
=
[];
l_ws
.
push
({
'id'
:
ws
.
workingSession
,
'student'
:
ws
.
student
,
'begin'
:
ws
.
ws_begin
,
'end'
:
ws
.
ws_end
,
'description'
:
ws
.
ws_description
,
'tries'
:
tries
});
return
res
.
json
(
l_ws
);
}
});
}
});
},
/**
* Return all tries from a student
* @param {request} req {} (width studentId as url parameter)
* @param {response} res
* {
"methods": [
{
"student": 24,
"id": 1,
"name": "Test Method",
"description": null,
"registration": null,
"notes": null
"instructions": [
{
"id": 1,
"name": "Test Instruction",
"objective": null,
"status": "started",
"begin": null,
"end": null,
"method": 1,
"working_sessions": [
{
"id": 3,
"begin": "2016-09-09T08:26:24.500Z",
"end": "2016-08-28T23:36:35.000Z",
"current": null,
"description": "",
"supervisor": 23,
"instruction": 1
"tries": [
{
"actions": [],
"id": 1,
"begin": "2016-08-28T23:36:35.474Z",
"end": "2016-08-28T23:36:44.000Z",
"result": null,
"description": null,
"workingSession": 3
},
{
"actions": [],
"id": 2,
"begin": "2016-08-28T23:36:44.050Z",
"end": "2016-08-29T01:36:51.710Z",
"result": "SUCCESS",
"description": null,
"workingSession": 3
},
{
"actions": [],
"id": 3,
"begin": "2016-08-28T23:36:51.942Z",
"end": "2016-08-28T23:36:53.000Z",
"result": "DISCARDED",
"description": null,
"workingSession": 3
},
{
"actions": [],
"id": 4,
"begin": "2016-08-28T23:36:53.877Z",
"end": "2016-08-28T23:37:13.000Z",
"result": "SPONTANEOUS SUCCESS",
"description": null,
"workingSession": 3
}
]
}
}
]
}
]
}
*/
tries
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
return
res
.
badRequest
(
"Student not defined"
);
Student
.
tries
(
req
.
params
.
id_stu
,
function
(
err
,
l_met
)
{
if
(
err
)
return
res
.
serverError
(
err
);
return
res
.
ok
(
l_met
);
});
},
/**
* Get all pictos from a given student
* @param {request} req {} (with studentId as url parameter)
* @param {response} res
* [
* {
* id: student-picto ID,
* picto: {
* id: pictoId,
* uri: 'uri/to/picto.png',
* category: pictoCategoryId,
* source: 1 @TODO Other sources
* owner: supervisorId or null
* },
* expression: {
* id: expressionId,
* lang: 'es-es',
* text: 'Picto Expression',
* picto: pictoId
* },
* attributes: { @see StuPicto.getValidAttributes() }
* },
* ...
* ]
*/
pictos
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
}
sails
.
log
.
debug
(
'Pictos requested for student '
+
req
.
params
.
id_stu
);
Student
.
pictos
(
req
.
params
.
id_stu
,
function
(
err
,
pictos
)
{
if
(
err
)
return
res
.
serverError
(
"Error obtaining pictos: "
+
err
);
return
res
.
ok
(
pictos
);
});
},
//
// Returns all working sessions for the given student
//
ws
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
{
return
res
.
json
(
500
,
{
error
:
'No student defined'
});
}
sails
.
log
.
debug
(
'Working Sessions requested for student '
+
req
.
params
.
id_stu
);
Student
.
findOne
(
req
.
params
.
id_stu
)
.
populate
(
'workingSessions'
)
.
exec
(
function
(
err
,
stu
)
{
if
(
err
)
return
res
.
json
(
500
,
{
error
:
err
});
if
(
!
stu
||
!
stu
.
workingSessions
||
stu
.
workingSessions
.
length
==
0
)
return
res
.
json
([]);
// Return an empty array
else
return
res
.
json
(
stu
.
workingSessions
);
});
},
/**
* Add an existing picto to the student's collection
* @param {request} req (with studentId and pictoId as url parameter)
* {
* attributes: { @see StuPicto.getValidAttributes() }
* }
* @param {response} res
* {
* id: stuPictoId (association betweet student and picto)
* 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
();
Student
.
findOne
({
id
:
params
.
id_stu
})
.
then
((
student
)
=>
{
if
(
!
student
)
{
sails
.
log
.
error
(
`Student
${
params
.
id_stu
}
not found`
);
throw
new
Error
(
"Student not found"
);
}
return
[
Picto
.
findOne
({
id
:
params
.
id_picto
})
.
populate
(
'expressions'
,
{
lang
:
student
.
lang
})
,
student
];
})
.
spread
((
picto
,
student
)
=>
{
if
(
!
picto
)
{
sails
.
log
.
error
(
`Picto
${
params
.
id_picto
}
not found`
);
throw
new
Error
(
"Picto not found"
);
}
return
[
StuPicto
.
create
({
student
:
student
.
id
,
picto
:
picto
.
id
,
attributes
:
params
.
attributes
})
,
picto
];
})
.
spread
((
stuPicto
,
picto
)
=>
{
if
(
!
stuPicto
)
throw
new
Error
(
"stu_picto not created"
);
sails
.
log
.
debug
(
"->>"
+
JSON
.
stringify
(
picto
));
return
res
.
ok
({
id
:
stuPicto
.
id
,
student
:
params
.
id_stu
,
attributes
:
stuPicto
.
attributes
,
picto
:
picto
,
expression
:
picto
.
expressions
[
0
]
});
})
.
catch
(
err
=>
res
.
serverError
(
"Error adding picto: "
+
err
));
},
/**
* Removes an existing picto to the student's collection
* Warning: yout must send the **stuPictoId** as url parameter, not the **pictoId**.
* You can obtain this ID from the student picto collection.
* @param {request} req {} (with studentId and stuPictoId as url parameters)
* @param {response} res {}
*/
delete_picto
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
StuPicto
.
destroy
({
id
:
params
.
id_stuPicto
})
.
then
(
destroyed
=>
{
if
(
!
destroyed
)
throw
new
Error
();
return
res
.
ok
();
})
.
catch
(
err
=>
{
return
res
.
serverError
(
'Not removed picto for student: '
+
err
);
});
},
// update action
// update picto atributes for a studentPicto
//
update_picto
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
console
.
log
(
'Updating attributes for picto student '
+
JSON
.
stringify
(
params
));
console
.
log
(
JSON
.
stringify
(
params
));
query
=
params
.
id_stuPicto
?
{
id
:
params
.
id_stuPicto
}
:
{
id_stu
:
params
.
id_stu
,
id_pic
:
params
.
id_pic
}
StuPicto
.
update
(
query
,
{
attributes
:
params
.
attributes
})
.
then
(
updated
=>
{
if
(
!
updated
)
throw
new
Error
(
"error on update"
);
console
.
log
(
'Updated attributes for picto student:'
+
JSON
.
stringify
(
updated
[
0
]));
// return res.json(updated[0]);
return
res
.
ok
({
id
:
updated
[
0
].
id
,
// id of stu_picto
attributes
:
updated
[
0
].
attributes
,
// picto attributes for student
picto
:
{
id
:
updated
[
0
].
picto
// picto information
}
});
})
.
catch
(
err
=>
{
return
res
.
serverError
(
'Unable to update picto for student: '
+
err
);
});
},
// update action
// update picto atributes for a studentPicto
//
update_legend
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
var
query
=
'update stu_picto'
+
' set attributes=json_set(attributes, \'$.legend\',\''
+
params
.
legend_value
+
'\')'
+
' where id_stu='
+
params
.
id_stu
;
console
.
log
(
'Updating legend for student '
+
params
.
id_stu
+
" collection to "
+
params
.
legend_value
+
": "
+
query
);
StuPicto
.
query
(
query
,
function
(
err
,
result
)
{
if
(
err
)
throw
new
Error
(
"error on update"
);
else
{
console
.
log
(
'Updated attributes for picto student:'
+
params
.
id_stu
);
return
res
.
ok
({
id
:
params
.
id_stu
,
legend_value
:
params
.
legend_value
,
// picto attributes for student
});
}
});
},
/**
* Updates the student profile image
* @param {request} req
* file: [The image binary data]
* {
* "id": "student ID"
* }
* @param {response} res {}
*/
upload
:
function
(
req
,
res
)
{
var
fs
=
require
(
'fs'
);
var
path
=
require
(
'path'
);
var
newAvatarFileName
;
var
newAvatarFileDescriptor
;
var
newAvatarDirectory
=
sails
.
config
.
pictogram
.
paths
.
studentAvatarDirectory
;
Student
.
findOne
({
id
:
req
.
body
.
id
}).
then
(
function
(
student
)
{
if
(
!
student
)
{
throw
new
Error
(
"Student not found"
);
}
newAvatarFileName
=
sails
.
config
.
pictogram
.
paths
.
getStudentAvatarFileName
(
student
.
id
);
req
.
file
(
'file'
).
upload
({
maxBytes
:
1000000
,
dirname
:
newAvatarDirectory
,
saveAs
:
newAvatarFileName
},
function
whenDone
(
error
,
uploadedFiles
)
{
if
(
error
||
(
uploadedFiles
.
length
===
0
))
{
throw
new
Error
(
"upload failed"
);
}
try
{
newAvatarFileDescriptor
=
uploadedFiles
[
0
].
fd
;
if
(
student
.
pic
!==
sails
.
config
.
pictogram
.
paths
.
defaultAvatarFileName
)
{
fs
.
unlinkSync
(
path
.
join
(
newAvatarDirectory
,
student
.
pic
));
}
student
.
pic
=
newAvatarFileName
;
student
.
save
(
function
(
updateStudentError
)
{
if
(
updateStudentError
)
{
throw
updateStudentError
;
}
res
.
ok
();
});
}
catch
(
updateAvatarError
)
{
fs
.
unlinkSync
(
newAvatarFileDescriptor
);
res
.
serverError
(
"Error when updating profile image in server"
);
}
});
})
.
catch
(
function
(
err
)
{
res
.
badRequest
(
"Could not find supervisor: "
+
err
);
});
},
// ***************************************************************
// WEBSOCKETS
// ***************************************************************
//
// Subscribe to websockets events
//
subscribe
:
function
(
req
,
res
)
{
var
action
=
req
.
param
(
'action'
);
var
attributes
=
req
.
param
(
'attributes'
);
attributes
.
ui
=
attributes
.
ui
?
attributes
.
ui
:
'PCB'
;
if
(
req
.
isSocket
)
{
sails
.
hooks
.
rooms
.
subscribeToRoom
(
sails
.
hooks
.
rooms
.
student
(
attributes
.
id_stu
),
req
.
socket
,
attributes
.
ui
);
}
res
.
ok
({
msg
:
"Subscribed to student "
});
},
//
// Unsubscribe to websockets events
//
unsubscribe
:
function
(
req
,
res
)
{
var
action
=
req
.
param
(
'action'
);
//var attributes = req.param('attributes');
if
(
req
.
isSocket
)
{
var
rooms
=
sails
.
sockets
.
socketRooms
(
req
.
socket
);
console
.
log
(
"Subscribed rooms in socket: "
+
JSON
.
stringify
(
rooms
));
// Leave all rooms
for
(
var
i
=
0
;
i
<
rooms
.
length
;
i
++
)
{
//sails.sockets.leave(req.socket, rooms[i]); MODIFICADO POR FERNANDO. SI NO, NO SE ACTUALIZA UPDATE_PEERS
sails
.
hooks
.
rooms
.
unsubscribeFromRoom
(
rooms
[
i
],
req
.
socket
);
sails
.
log
.
debug
(
"Unsusbscribe from room "
+
rooms
[
i
]);
}
res
.
json
({
msg
:
"Unsubscribed from all rooms"
});
}
},
//
// Logs a vocabulary action and broadcast to anyone subscribed to this student
vocabulary
:
function
(
req
,
res
)
{
var
action
=
req
.
param
(
'action'
);
var
attributes
=
req
.
param
(
'attributes'
);
var
roomName
=
'studentRoom'
+
attributes
.
id_stu
;
sails
.
log
.
debug
(
"Inside vocabulary"
);
if
(
req
.
isSocket
)
{
sails
.
log
.
debug
(
"Inside vocabulary - isSocket"
);
// Send to all sockets subscribed to this room except the own socket that sends the message
// Parameters: room, action, data to send, socket to avoid sending (the socket that send this)
sails
.
sockets
.
broadcast
(
roomName
,
'vocabulary'
,
{
"action"
:
action
,
"attributes"
:
attributes
},
req
.
socket
);
res
.
json
({
msg
:
"Vocabulary "
+
action
+
" action from student "
+
attributes
.
id_stu
});
}
},
/**
* Logs a TRY action and broadcast to anyone subscribed to this student
* @param {request} req
* {
* "action": <action> ("add", "delete", ...),
* "attributes": {
* "id_stu": <id_stu>,
* "timestamp": <timestamp_string_in_ISO_format> (e.g.: "2016-07-13 17:50:00.224+0200"),
* "picto": {...}
* }
* }
* @param {response} res {<action_created>}
*/
action
:
function
(
req
,
res
)
{
var
action
=
req
.
param
(
'action'
);
var
attributes
=
req
.
param
(
'attributes'
);
sails
.
log
.
debug
(
"Inside action. Student:"
+
attributes
.
id_stu
);
if
(
!
req
.
isSocket
)
{
sails
.
log
.
debug
(
"No socket request for action"
);
res
.
badRequest
()
}
else
{
sails
.
log
.
debug
(
"websockets - room "
+
sails
.
hooks
.
rooms
.
student
(
attributes
.
id_stu
));
// BROADCAST to everyone subscribed to this student
const
socketToOmit
=
(
req
.
isSocket
)
?
req
.
socket
:
undefined
;
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
student
(
attributes
.
id_stu
),
sails
.
hooks
.
events
.
actionPerformed
(
action
,
attributes
),
socketToOmit
);
var
sup
=
null
;
if
(
attributes
.
id_sup
)
sup
=
attributes
.
id_sup
;
var
desc
=
null
;
if
(
attributes
.
stu_picto
)
desc
=
attributes
.
stu_picto
;
// select, add and delete actions data
if
(
attributes
.
pictos
)
desc
=
attributes
.
pictos
;
// show action data
Action
.
create
({
type
:
action
,
timestamp
:
attributes
.
timestamp
,
// it comes already in ISO format
supervisor
:
sup
,
student
:
attributes
.
id_stu
,
description
:
desc
})
.
then
(
function
(
created
)
{
res
.
json
({
action
:
created
});
})
.
fail
(
function
(
err
)
{
sails
.
log
.
error
(
err
.
details
);
res
.
serverError
(
err
.
details
);
});
}
},
//
// Logs a config action and broadcast to anyone subscribed to this student
config
:
function
(
req
,
res
)
{
var
action
=
req
.
param
(
'action'
);
var
attributes
=
req
.
param
(
'attributes'
);
sails
.
log
.
debug
(
"Inside config"
);
if
(
req
.
isSocket
)
{
const
socketToOmit
=
(
req
.
isSocket
)
?
req
.
socket
:
undefined
;
sails
.
hooks
.
events
.
broadcastEvent
(
sails
.
hooks
.
rooms
.
student
(
attributes
.
id_stu
),
'action'
,
{
"action"
:
action
,
"attributes"
:
attributes
},
socketToOmit
);
}
else
{
res
.
ok
({
msg
:
"Config "
+
action
+
" action from student "
+
attributes
.
id_stu
});
}
},
//
// Stores in the action table a bulk of actions loaded from of the
// recorded action log
//
actions_batch
:
function
(
req
,
res
)
{
var
params
=
req
.
allParams
();
var
count
=
0
;
sails
.
log
.
debug
(
"Actions_batch request"
);
if
(
!
params
.
actions
)
return
res
.
json
(
400
,
{
'error'
:
"no actions"
});
// We loop through the actions and store them in the database
async
.
forEach
(
params
.
actions
,
function
(
action
,
cb
)
{
sails
.
log
.
debug
(
"Actions batch is recording action: "
+
JSON
.
stringify
(
action
));
var
id_sup
=
null
;
if
(
action
.
attributes
.
id_sup
)
id_sup
=
action
.
attributes
.
sup
;
var
id_stu
=
null
;
if
(
action
.
attributes
.
id_stu
)
id_stu
=
action
.
attributes
.
id_stu
;
var
desc
=
null
;
if
(
action
.
attributes
.
picto
)
desc
=
action
.
attributes
.
picto
;
// select, add and delete actions data
if
(
action
.
attributes
.
pictos
)
desc
=
action
.
attributes
.
pictos
;
// show action data
Action
.
create
({
type
:
action
.
action
,
timestamp
:
action
.
attributes
.
timestamp
,
supervisor
:
id_sup
,
student
:
id_stu
,
description
:
desc
})
.
exec
(
function
(
err
,
created
)
{
if
(
err
)
{
console
.
log
(
err
.
details
);
sails
.
log
.
error
(
err
.
details
);
return
cb
(
err
);
}
else
if
(
created
)
count
++
;
cb
();
});
},
function
(
err
)
{
// function called when loop is done
if
(
err
)
{
console
.
log
(
err
.
details
);
sails
.
log
.
error
(
err
.
details
);
return
res
.
json
({
'error'
:
err
.
details
});
}
else
return
res
.
json
({
'result'
:
'Ok'
,
'total'
:
count
});
});
},
//
// Returns the last instruction for the student
//
last_instruction
:
function
(
req
,
res
)
{
if
(
!
req
.
params
.
id_stu
)
return
res
.
json
(
400
,
{
err
:
'id_stu parameter is missing'
});
VStuLastInstruction
.
find
({
id_stu
:
req
.
params
.
id_stu
})
.
exec
(
function
(
err
,
found
)
{
if
(
err
)
return
res
.
json
(
500
,
err
);
if
(
!
found
)
return
res
.
json
({});
return
res
.
json
(
found
);
});
}
};
sails/src/assets/scripts/modules/student/controllers/pictoconfig.js
View file @
5413736a
...
...
@@ -3,7 +3,7 @@
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
dashboardControllers
.
controller
(
'PictoConfigCtrl'
,
function
(
$scope
,
$modalInstance
,
$http
,
config
,
studentPicto
,
sup
,
stu
)
{
dashboardControllers
.
controller
(
'PictoConfigCtrl'
,
function
(
$
window
,
$
scope
,
$modalInstance
,
$http
,
config
,
studentPicto
,
sup
,
stu
)
{
// Last parameter passed from collections.js
// Basic data passed from the main window
...
...
@@ -34,7 +34,7 @@ dashboardControllers.controller('PictoConfigCtrl', function ($scope, $modalInsta
console
.
log
(
"Expression changed: "
+
JSON
.
stringify
(
data
)
+
": "
+
$scope
.
studentPicto
.
expression
.
text
);
$scope
.
studentPicto
.
attributes
.
expression
=
$scope
.
studentPicto
.
expression
.
text
;
// Close the modal instance
$modalInstance
.
close
(
$scope
.
studentPicto
.
expression
.
text
);
//
$modalInstance.close($scope.studentPicto.expression.text);
// Notifcar cambio
io
.
socket
.
post
(
'/stu/vocabulary'
,
{
...
...
@@ -70,7 +70,7 @@ dashboardControllers.controller('PictoConfigCtrl', function ($scope, $modalInsta
console
.
log
(
"Properties updated"
);
// Close the modal instance
$modalInstance
.
close
(
$scope
.
expression
);
//
$modalInstance.close($scope.expression);
// /////////////////////////////////////////////////////////////
// Websocket: Emit vocabulary update action
...
...
@@ -92,6 +92,21 @@ dashboardControllers.controller('PictoConfigCtrl', function ($scope, $modalInsta
});
};
$scope
.
update_legend
=
function
(){
console
.
log
(
"Legend: "
+
$scope
.
studentPicto
.
attributes
.
legend
+
" to be modified"
);
$http
.
put
(
config
.
backend
+
'/stu/'
+
$scope
.
stu
.
id
+
'/legend/'
+
$scope
.
studentPicto
.
attributes
.
legend
)
.
success
(
function
(
data
,
status
,
headers
,
config
)
{
console
.
log
(
"Legend updated"
);
// Close the modal instance
$modalInstance
.
close
(
$scope
.
expression
);
$window
.
location
.
reload
();
});
};
$scope
.
close
=
function
()
{
// Lo que se devuelve a collection
$modalInstance
.
dismiss
(
'cancel'
);
...
...
sails/src/assets/scripts/modules/student/views/pictoconfig.html
View file @
5413736a
...
...
@@ -62,12 +62,12 @@
<hr>
<div
class=
"row"
>
<div
class=
"col-md-8"
>
<input
type=
"checkbox"
>
<input
type=
"checkbox"
ng-model=
"update_all_legend"
ng-init=
"update_all_legend=false"
>
<span
translate
>
legend_apply_all
</span>
</div>
<div
class=
"col-md-4"
>
<div
class=
"form-group text-center"
>
<button
type=
"submit"
class=
"btn btn-primary ng-scope"
ng-click=
"
"
disabled
translate
>
apply
</button>
<button
type=
"submit"
class=
"btn btn-primary ng-scope"
ng-click=
"
update_all_legend ? update_legend() : close()"
translate
>
apply
</button>
</div>
</div>
</div>
...
...
sails/src/config/policies.js
View file @
5413736a
...
...
@@ -96,6 +96,7 @@ module.exports.policies = {
ws
:
[
'tokenAuth'
],
update
:
[
'tokenAuth'
],
update_picto
:
[
'tokenAuth'
,
'isSupervisorOfStudent'
],
update_legend
:
[
'tokenAuth'
],
login
:
true
,
create
:
[
'tokenAuth'
],
upload
:
[
'tokenAuth'
],
...
...
sails/src/config/routes.js
View file @
5413736a
...
...
@@ -81,6 +81,7 @@ module.exports.routes = {
'GET /stu/:id_stu/ws'
:
'StudentController.ws'
,
'PUT /stu/:id_stu'
:
'StudentController.update'
,
'PUT /stu/:id_stu/picto/:id_stuPicto'
:
'StudentController.update_picto'
,
'PUT /stu/:id_stu/legend/:legend_value'
:
'StudentController.update_legend'
,
'PUT /stu/:id_stu/picto'
:
'StudentController.update_picto'
,
'POST /stu/login'
:
'StudentController.login'
,
'POST /stu'
:
'StudentController.create'
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment