Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Kontr 2.0
Portal API Backend
Commits
c0a95638
Verified
Commit
c0a95638
authored
Oct 06, 2018
by
Peter Stanko
Browse files
Clients endpoint updated for type selection
parent
6b8f2b86
Changes
7
Hide whitespace changes
Inline
Side-by-side
management/data/data_test.py
View file @
c0a95638
...
@@ -43,10 +43,12 @@ def init_test_data(app: Flask, db: SQLAlchemy):
...
@@ -43,10 +43,12 @@ def init_test_data(app: Flask, db: SQLAlchemy):
tc1_student
=
factory
.
scaffold_role
(
role_type
=
'student'
,
course
=
test_course1
)
tc1_student
=
factory
.
scaffold_role
(
role_type
=
'student'
,
course
=
test_course1
)
tc1_teacher
=
factory
.
scaffold_role
(
role_type
=
'teacher'
,
course
=
test_course1
)
tc1_teacher
=
factory
.
scaffold_role
(
role_type
=
'teacher'
,
course
=
test_course1
)
tc1_owner
=
factory
.
scaffold_role
(
role_type
=
'owner'
,
course
=
test_course1
)
tc1_owner
=
factory
.
scaffold_role
(
role_type
=
'owner'
,
course
=
test_course1
)
tc1_workers
=
factory
.
scaffold_role
(
role_type
=
'worker'
,
course
=
test_course1
)
tc2_student
=
factory
.
scaffold_role
(
role_type
=
'student'
,
course
=
test_course2
)
tc2_student
=
factory
.
scaffold_role
(
role_type
=
'student'
,
course
=
test_course2
)
tc2_teacher
=
factory
.
scaffold_role
(
role_type
=
'teacher'
,
course
=
test_course2
)
tc2_teacher
=
factory
.
scaffold_role
(
role_type
=
'teacher'
,
course
=
test_course2
)
tc2_owner
=
factory
.
scaffold_role
(
role_type
=
'owner'
,
course
=
test_course2
)
tc2_owner
=
factory
.
scaffold_role
(
role_type
=
'owner'
,
course
=
test_course2
)
tc2_workers
=
factory
.
scaffold_role
(
role_type
=
'worker'
,
course
=
test_course2
)
# relationships
# relationships
teacher_student
=
teacher1
teacher_student
=
teacher1
...
@@ -111,6 +113,8 @@ def init_test_data(app: Flask, db: SQLAlchemy):
...
@@ -111,6 +113,8 @@ def init_test_data(app: Flask, db: SQLAlchemy):
processing
=
factory
.
create_worker
(
name
=
'processing'
,
url
=
"bar/url"
)
processing
=
factory
.
create_worker
(
name
=
'processing'
,
url
=
"bar/url"
)
processing
.
secrets
.
append
(
Secret
(
'processing_secret'
,
"processing_secret"
,
processing
.
secrets
.
append
(
Secret
(
'processing_secret'
,
"processing_secret"
,
time
.
current_time
()
+
timedelta
(
hours
=
1
)))
time
.
current_time
()
+
timedelta
(
hours
=
1
)))
executor
.
roles
.
append
(
tc1_workers
)
processing
.
roles
.
append
(
tc2_workers
)
db
.
session
.
add_all
([
executor
,
processing
])
db
.
session
.
add_all
([
executor
,
processing
])
# Commit to the DB
# Commit to the DB
db
.
session
.
commit
()
db
.
session
.
commit
()
portal/database/models.py
View file @
c0a95638
...
@@ -310,6 +310,13 @@ class User(EntityBase, Client):
...
@@ -310,6 +310,13 @@ class User(EntityBase, Client):
return
hash
(
self
.
id
)
return
hash
(
self
.
id
)
def
_get_class_based_on_client_type
(
client_type
):
klass
=
Client
if
client_type
is
not
None
:
klass
=
User
if
client_type
==
ClientType
.
USER
else
Worker
return
klass
class
Course
(
db
.
Model
,
EntityBase
,
NamedMixin
):
class
Course
(
db
.
Model
,
EntityBase
,
NamedMixin
):
"""Course model
"""Course model
Attributes:
Attributes:
...
@@ -392,11 +399,13 @@ class Course(db.Model, EntityBase, NamedMixin):
...
@@ -392,11 +399,13 @@ class Course(db.Model, EntityBase, NamedMixin):
.
join
(
User
.
roles
).
filter
((
Role
.
id
==
role
.
id
)
&
(
Role
.
course
==
self
))
\
.
join
(
User
.
roles
).
filter
((
Role
.
id
==
role
.
id
)
&
(
Role
.
course
==
self
))
\
.
all
()
.
all
()
def
get_clients_filtered
(
self
,
groups
:
List
[
'Group'
],
roles
:
List
[
'Role'
]):
def
get_clients_filtered
(
self
,
groups
:
List
[
'Group'
]
=
None
,
roles
:
List
[
'Role'
]
=
None
,
client_type
=
None
):
"""Gets all users in the course who are members of at least one of
"""Gets all users in the course who are members of at least one of
the provided groups and have at least one of the specified roles.
the provided groups and have at least one of the specified roles.
Args:
Args:
client_type:
groups(List['Group']): A list of groups. A user must be part of at least one to
groups(List['Group']): A list of groups. A user must be part of at least one to
be included in the result.
be included in the result.
roles(List['Role']): A list of roles. A user must be part of at
roles(List['Role']): A list of roles. A user must be part of at
...
@@ -406,13 +415,17 @@ class Course(db.Model, EntityBase, NamedMixin):
...
@@ -406,13 +415,17 @@ class Course(db.Model, EntityBase, NamedMixin):
(are in at least one group AND have at least one role).
(are in at least one group AND have at least one role).
"""
"""
query
=
Client
.
query
.
join
(
Client
.
roles
).
filter
(
Role
.
course_id
==
self
.
id
)
klass
=
_get_class_based_on_client_type
(
client_type
)
query
=
klass
.
query
.
join
(
klass
.
roles
)
if
client_type
is
not
None
:
query
=
query
.
filter
(
klass
.
type
==
client_type
)
query
=
query
.
filter
(
Role
.
course_id
==
self
.
id
)
if
roles
:
if
roles
:
role_ids
=
[
role
.
id
for
role
in
roles
]
role_ids
=
[
role
.
id
for
role
in
roles
]
query
=
query
.
filter
(
Role
.
id
.
in_
(
role_ids
))
query
=
query
.
filter
(
Role
.
id
.
in_
(
role_ids
))
if
groups
:
if
groups
and
client_type
==
ClientType
.
USER
:
group_ids
=
[
group
.
id
for
group
in
groups
]
group_ids
=
[
group
.
id
for
group
in
groups
]
query
=
query
.
join
(
Client
.
groups
).
filter
(
Group
.
id
.
in_
(
group_ids
)).
filter
(
query
=
query
.
join
(
User
.
groups
).
filter
(
Group
.
id
.
in_
(
group_ids
)).
filter
(
Group
.
course_id
==
self
.
id
)
Group
.
course_id
==
self
.
id
)
return
query
.
all
()
return
query
.
all
()
...
...
portal/rest/courses.py
View file @
c0a95638
...
@@ -3,6 +3,7 @@ from flask_jwt_extended import jwt_required
...
@@ -3,6 +3,7 @@ from flask_jwt_extended import jwt_required
from
flask_restplus
import
Namespace
from
flask_restplus
import
Namespace
from
portal
import
logger
from
portal
import
logger
from
portal.database.models
import
ClientType
from
portal.rest
import
rest_helpers
from
portal.rest
import
rest_helpers
from
portal.rest.custom_resource
import
CustomResource
from
portal.rest.custom_resource
import
CustomResource
from
portal.rest.schemas
import
SCHEMAS
from
portal.rest.schemas
import
SCHEMAS
...
@@ -138,17 +139,63 @@ class CourseImport(CustomResource):
...
@@ -138,17 +139,63 @@ class CourseImport(CustomResource):
return
SCHEMAS
.
dump
(
'course'
,
copied_course
)
return
SCHEMAS
.
dump
(
'course'
,
copied_course
)
def
extract_client_type
():
client_type
=
request
.
args
.
get
(
'type'
)
if
client_type
is
not
None
:
if
client_type
==
'worker'
:
client_type
=
ClientType
.
WORKER
elif
client_type
==
'user'
:
client_type
=
ClientType
.
USER
else
:
client_type
=
None
return
client_type
@
courses_namespace
.
route
(
'/<string:cid>/clients'
)
@
courses_namespace
.
route
(
'/<string:cid>/clients'
)
@
courses_namespace
.
param
(
'cid'
,
'Course id'
)
@
courses_namespace
.
param
(
'cid'
,
'Course id'
)
@
courses_namespace
.
response
(
404
,
'Course not found'
)
@
courses_namespace
.
response
(
404
,
'Course not found'
)
class
CourseClients
(
CustomResource
):
class
CourseClients
(
CustomResource
):
@
jwt_required
@
jwt_required
# @courses_namespace.response(200, 'Users in the course', model=users_schema)
# @courses_namespace.response(200, 'Users in the course', model=users_schema)
@
courses_namespace
.
response
(
403
,
'Not allowed to see
user
s in the course'
)
@
courses_namespace
.
response
(
403
,
'Not allowed to see
client
s in the course'
)
def
get
(
self
,
cid
):
def
get
(
self
,
cid
):
course
=
self
.
find
.
course
(
cid
)
course
=
self
.
find
.
course
(
cid
)
self
.
permissions
(
course
=
course
).
require
.
permissions
([
'view_course_full'
])
self
.
permissions
(
course
=
course
).
require
.
permissions
([
'view_course_full'
])
group_ids
=
request
.
args
.
getlist
(
'group'
)
group_ids
=
request
.
args
.
getlist
(
'group'
)
role_ids
=
request
.
args
.
getlist
(
'role'
)
role_ids
=
request
.
args
.
getlist
(
'role'
)
users
=
self
.
rest
.
courses
(
course
).
get_clients_filtered
(
group_ids
,
role_ids
)
client_type
=
extract_client_type
()
return
SCHEMAS
.
dump
(
'users'
,
users
)
clients
=
self
.
rest
.
courses
(
course
).
get_clients_filtered
(
group_ids
,
role_ids
,
client_type
=
client_type
)
return
SCHEMAS
.
dump
(
'clients'
,
clients
)
@
courses_namespace
.
route
(
'/<string:cid>/users'
)
@
courses_namespace
.
param
(
'cid'
,
'Course id'
)
@
courses_namespace
.
response
(
404
,
'Course not found'
)
class
CourseUsers
(
CustomResource
):
@
jwt_required
# @courses_namespace.response(200, 'Users in the course', model=users_schema)
@
courses_namespace
.
response
(
403
,
'Not allowed to see users in the course'
)
def
get
(
self
,
cid
):
course
=
self
.
find
.
course
(
cid
)
self
.
permissions
(
course
=
course
).
require
.
permissions
([
'view_course_full'
])
group_ids
=
request
.
args
.
getlist
(
'group'
)
role_ids
=
request
.
args
.
getlist
(
'role'
)
users
=
self
.
rest
.
courses
(
course
).
get_clients_filtered
(
group_ids
,
role_ids
,
client_type
=
ClientType
.
USER
)
return
SCHEMAS
.
dump
(
'users'
,
users
)
@
courses_namespace
.
route
(
'/<string:cid>/workers'
)
@
courses_namespace
.
param
(
'cid'
,
'Course id'
)
@
courses_namespace
.
response
(
404
,
'Course not found'
)
class
CourseWorkers
(
CustomResource
):
@
jwt_required
# @courses_namespace.response(200, 'Users in the course', model=users_schema)
@
courses_namespace
.
response
(
403
,
'Not allowed to see workers in the course'
)
def
get
(
self
,
cid
):
course
=
self
.
find
.
course
(
cid
)
self
.
permissions
(
course
=
course
).
require
.
permissions
([
'view_course_full'
])
group_ids
=
request
.
args
.
getlist
(
'group'
)
role_ids
=
request
.
args
.
getlist
(
'role'
)
workers
=
self
.
rest
.
courses
(
course
).
get_clients_filtered
(
group_ids
,
role_ids
,
client_type
=
ClientType
.
WORKER
)
return
SCHEMAS
.
dump
(
'workers'
,
workers
)
portal/rest/schemas.py
View file @
c0a95638
...
@@ -382,6 +382,7 @@ class Schemas:
...
@@ -382,6 +382,7 @@ class Schemas:
PARAMS
=
{
PARAMS
=
{
'user_reduced'
:
(
*
ALWAYS_ALLOWED
,
'username'
,
'uco'
,
'email'
,
'name'
),
'user_reduced'
:
(
*
ALWAYS_ALLOWED
,
'username'
,
'uco'
,
'email'
,
'name'
),
'users'
:
(
*
ALWAYS_ALLOWED
,
'username'
,
'uco'
,
'email'
),
'users'
:
(
*
ALWAYS_ALLOWED
,
'username'
,
'uco'
,
'email'
),
'clients'
:
(
*
ALWAYS_ALLOWED
,
'codename'
,
'type'
),
'submissions'
:
(
*
ALWAYS_ALLOWED
,
'course'
,
'state'
,
'project'
,
'scheduled_for'
,
'user'
),
'submissions'
:
(
*
ALWAYS_ALLOWED
,
'course'
,
'state'
,
'project'
,
'scheduled_for'
,
'user'
),
'submission_state'
:
(
*
ALWAYS_ALLOWED
,
'state'
,
'parameters'
,
'scheduled_for'
),
'submission_state'
:
(
*
ALWAYS_ALLOWED
,
'state'
,
'parameters'
,
'scheduled_for'
),
'roles'
:
ENT_W_COURSE
,
'roles'
:
ENT_W_COURSE
,
...
@@ -391,8 +392,11 @@ class Schemas:
...
@@ -391,8 +392,11 @@ class Schemas:
'courses'
:
(
*
CODENAME_W_DESC
,),
'courses'
:
(
*
CODENAME_W_DESC
,),
'config_reduced'
:
(
*
ALWAYS_ALLOWED
,
'project'
,
'submissions_allowed_from'
,
'config_reduced'
:
(
*
ALWAYS_ALLOWED
,
'project'
,
'submissions_allowed_from'
,
'submissions_allowed_to'
,
'file_whitelist'
),
'submissions_allowed_to'
,
'file_whitelist'
),
'secrets'
:
(
*
ALWAYS_ALLOWED
,
'name'
,
'expires_at'
,
'client.id'
,
'client.type'
,
'client.name'
,
'client.codename'
),
'secrets'
:
(
'secret'
:
(
*
ALWAYS_ALLOWED
,
'name'
,
'expires_at'
,
'client.id'
,
'client.type'
,
'client.name'
,
'client.codename'
)
*
ALWAYS_ALLOWED
,
'name'
,
'expires_at'
,
'client.id'
,
'client.type'
,
'client.name'
,
'client.codename'
),
'secret'
:
(
*
ALWAYS_ALLOWED
,
'name'
,
'expires_at'
,
'client.id'
,
'client.type'
,
'client.name'
,
'client.codename'
)
}
}
def
__get_schema
(
self
,
schema_klass
,
select_params
=
None
,
only
=
None
,
strict
=
True
,
**
kwargs
):
def
__get_schema
(
self
,
schema_klass
,
select_params
=
None
,
only
=
None
,
strict
=
True
,
**
kwargs
):
...
@@ -414,6 +418,14 @@ class Schemas:
...
@@ -414,6 +418,14 @@ class Schemas:
def
users
(
self
,
**
kwargs
):
def
users
(
self
,
**
kwargs
):
return
self
.
__get_schema
(
UserSchema
,
many
=
True
,
**
kwargs
)
return
self
.
__get_schema
(
UserSchema
,
many
=
True
,
**
kwargs
)
@
fn_name
def
client
(
self
,
**
kwargs
):
return
self
.
__get_schema
(
ClientSchema
,
**
kwargs
)
@
fn_name
def
clients
(
self
,
**
kwargs
):
return
self
.
__get_schema
(
ClientSchema
,
many
=
True
,
**
kwargs
)
@
fn_name
@
fn_name
def
user_reduced
(
self
,
**
kwargs
):
def
user_reduced
(
self
,
**
kwargs
):
return
self
.
__get_schema
(
UserSchema
,
**
kwargs
)
return
self
.
__get_schema
(
UserSchema
,
**
kwargs
)
...
...
portal/service/courses.py
View file @
c0a95638
...
@@ -76,7 +76,7 @@ class CourseService(GeneralService):
...
@@ -76,7 +76,7 @@ class CourseService(GeneralService):
log
.
info
(
f
"[UPDATE] Notes access token
{
self
.
course
.
log_name
}
:
{
token
}
"
)
log
.
info
(
f
"[UPDATE] Notes access token
{
self
.
course
.
log_name
}
:
{
token
}
"
)
return
self
.
course
return
self
.
course
def
get_clients_filtered
(
self
,
groups
:
List
[
str
],
roles
:
List
[
str
])
->
List
[
User
]:
def
get_clients_filtered
(
self
,
groups
:
List
[
str
],
roles
:
List
[
str
]
,
client_type
=
None
)
->
List
[
User
]:
"""Get all users for course filtered
"""Get all users for course filtered
Args:
Args:
groups(list): Group names list
groups(list): Group names list
...
@@ -85,4 +85,5 @@ class CourseService(GeneralService):
...
@@ -85,4 +85,5 @@ class CourseService(GeneralService):
"""
"""
groups_entities
=
Group
.
query
.
filter
(
Group
.
id
.
in_
(
groups
)).
all
()
if
groups
else
None
groups_entities
=
Group
.
query
.
filter
(
Group
.
id
.
in_
(
groups
)).
all
()
if
groups
else
None
roles_entities
=
Role
.
query
.
filter
(
Role
.
id
.
in_
(
roles
)).
all
()
if
roles
else
None
roles_entities
=
Role
.
query
.
filter
(
Role
.
id
.
in_
(
roles
)).
all
()
if
roles
else
None
return
self
.
course
.
get_clients_filtered
(
groups_entities
,
roles_entities
)
return
self
.
course
.
get_clients_filtered
(
groups_entities
,
roles_entities
,
client_type
=
client_type
)
tests/database/test_db.py
View file @
c0a95638
...
@@ -869,6 +869,7 @@ def test_user_roles_for_course(session):
...
@@ -869,6 +869,7 @@ def test_user_roles_for_course(session):
assert
len
(
cpp_roles
)
==
2
assert
len
(
cpp_roles
)
==
2
assert
teacher_cpp
in
cpp_roles
assert
teacher_cpp
in
cpp_roles
assert
lect_cpp
in
cpp_roles
assert
lect_cpp
in
cpp_roles
assert
len
(
course
.
get_clients_filtered
(
client_type
=
ClientType
.
USER
))
==
1
java_roles
=
user
.
get_roles_in_course
(
course
=
c_java
)
java_roles
=
user
.
get_roles_in_course
(
course
=
c_java
)
assert
len
(
java_roles
)
==
1
assert
len
(
java_roles
)
==
1
...
...
tests/rest/test_course.py
View file @
c0a95638
...
@@ -79,19 +79,37 @@ def test_read(client):
...
@@ -79,19 +79,37 @@ def test_read(client):
rest_tools
.
assert_course
(
c
,
course
)
rest_tools
.
assert_course
(
c
,
course
)
def
test_read_course_
user
s
(
client
):
def
test_read_course_
client
s
(
client
):
course
=
Course
.
query
.
filter_by
(
codename
=
"testcourse2"
).
first
()
course
=
Course
.
query
.
filter_by
(
codename
=
"testcourse2"
).
first
()
response
=
rest_tools
.
make_request
(
client
,
f
"/courses/
{
course
.
id
}
/clients"
)
response
=
rest_tools
.
make_request
(
client
,
f
"/courses/
{
course
.
id
}
/clients"
)
assert_response
(
response
,
200
)
assert_response
(
response
,
200
)
users
=
rest_tools
.
extract_data
(
response
)
users
=
rest_tools
.
extract_data
(
response
)
course_users
=
set
()
course_users
=
set
()
for
role
in
course
.
roles
:
for
role
in
course
.
roles
:
course_users
.
update
(
role
.
clients
)
course_users
.
update
(
[
c
.
id
for
c
in
role
.
clients
]
)
assert
users
assert
users
assert
len
(
users
)
==
4
assert
len
(
users
)
==
5
assert
len
(
course_users
)
==
len
(
users
)
assert
len
(
course_users
)
==
len
(
users
)
def
test_read_course_client_type_workers
(
client
):
course
=
Course
.
query
.
filter_by
(
codename
=
"testcourse1"
).
first
()
response
=
rest_tools
.
make_request
(
client
,
f
"/courses/
{
course
.
id
}
/clients?type=worker"
)
assert_response
(
response
,
200
)
workers
=
rest_tools
.
extract_data
(
response
)
assert
workers
assert
len
(
workers
)
==
1
def
test_read_course_client_type_users
(
rest_service
,
client
):
course
=
Course
.
query
.
filter_by
(
codename
=
"testcourse1"
).
first
()
response
=
rest_tools
.
make_request
(
client
,
f
"/courses/
{
course
.
id
}
/clients?type=user"
)
assert_response
(
response
,
200
)
users
=
rest_tools
.
extract_data
(
response
)
assert
users
assert
len
(
users
)
==
5
def
test_update
(
client
):
def
test_update
(
client
):
c
=
Course
.
query
.
filter_by
(
codename
=
"testcourse1"
).
first
()
c
=
Course
.
query
.
filter_by
(
codename
=
"testcourse1"
).
first
()
request_dict
=
dict
(
request_dict
=
dict
(
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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