Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
pvk-tool
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
13
Issues
13
List
Boards
Labels
Service Desk
Milestones
Merge Requests
2
Merge Requests
2
Operations
Operations
Incidents
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
amiv
pvk-tool
Commits
fe18248b
Commit
fe18248b
authored
Mar 27, 2018
by
Alexander Dietmüller
Committed by
adietmue
Mar 28, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Backend: Introduce 'assistants' resource.
parent
5653a29a
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
119 additions
and
38 deletions
+119
-38
Backend/backend/settings.py
Backend/backend/settings.py
+32
-15
Backend/backend/validation.py
Backend/backend/validation.py
+23
-5
Backend/tests/test_payments.py
Backend/tests/test_payments.py
+1
-3
Backend/tests/test_resources.py
Backend/tests/test_resources.py
+11
-2
Backend/tests/test_validation.py
Backend/tests/test_validation.py
+52
-13
No files found.
Backend/backend/settings.py
View file @
fe18248b
...
...
@@ -47,6 +47,8 @@ COURSE_PRICE = 1000
# ISO 8601 time format instead of rfc1123
DATE_FORMAT
=
"%Y-%m-%dT%H:%M:%SZ"
# Email Format
EMAIL_REGEX
=
'^.+@.+$'
# More Feedback when creating something: Return all fields
BANDWIDTH_SAVER
=
False
...
...
@@ -84,6 +86,28 @@ STANDARD_ERRORS = [400, 401, 403, 404, 405, 406, 409, 410, 412, 422, 428]
# Resources
DOMAIN
=
{
'assistants'
:
{
'user_methods'
:
[
'GET'
],
'schema'
:
{
'name'
:
{
'type'
:
'string'
,
'maxlength'
:
100
,
'required'
:
True
,
'nullable'
:
False
,
'empty'
:
False
,
},
'email'
:
{
'type'
:
'string'
,
'maxlength'
:
100
,
'regex'
:
EMAIL_REGEX
,
'required'
:
True
,
'unique'
:
True
,
'nullable'
:
True
,
}
}
},
'lectures'
:
{
'user_methods'
:
[
'GET'
],
...
...
@@ -109,18 +133,6 @@ DOMAIN = {
'max'
:
3
,
'required'
:
True
},
'assistants'
:
{
# List of nethz of assistants
'type'
:
'list'
,
'schema'
:
{
'type'
:
'string'
,
'maxlength'
:
10
,
'empty'
:
False
,
'nullable'
:
False
,
},
# TODO: Not the same nethz twice (use new nocopies validator)
# TODO: Is nethz enough here?
}
},
},
...
...
@@ -140,9 +152,13 @@ DOMAIN = {
'required'
:
True
,
},
'assistant'
:
{
'type'
:
'string'
,
# TODO: Assistant needs to exist for lecture
# TODO: assistant timeslot
'type'
:
'objectid'
,
'data_relation'
:
{
'resource'
:
'assistants'
,
'field'
:
'_id'
,
'embeddable'
:
True
,
'unique_assistant_booking'
:
True
,
},
},
'signup'
:
TIMESPAN_SCHEMA
,
...
...
@@ -152,6 +168,7 @@ DOMAIN = {
'schema'
:
TIMESPAN_SCHEMA
,
'no_time_overlap'
:
True
,
'unique_room_booking'
:
True
,
'unique_assistant_booking'
:
True
,
},
'room'
:
{
'type'
:
'string'
,
...
...
Backend/backend/validation.py
View file @
fe18248b
...
...
@@ -35,7 +35,6 @@ class APIValidator(Validator):
Furthermore, the user must be a member to use his nethz.
Only admins can sign up someone else.
"""
print
(
get_user
())
if
enabled
and
not
is_admin
():
if
value
!=
get_user
().
get
(
'nethz'
):
self
.
_error
(
field
,
...
...
@@ -99,13 +98,13 @@ class APIValidator(Validator):
if
enabled
and
(
value
.
get
(
'start'
)
>
value
.
get
(
'end'
)):
self
.
_error
(
field
,
'start time must be earlier then end time.'
)
def
_validate_unique_room_booking
(
self
,
enabled
,
field
,
_
):
def
_validate_unique_room_booking
(
self
,
enabled
,
field
,
value
):
"""A room can not be used at the same time by several courses."""
#
Get new room and timespans for current course
#
Compatibility with both room and datetime fields for POST and PATCH
room
=
self
.
_get_field
(
'room'
)
timespans
=
self
.
_get_field
(
'datetimes'
)
# Get _id of current course
(needed for path)
and filter to ignore it
# Get _id of current course and filter to ignore it
course_id
=
self
.
_get_field
(
'_id'
)
id_filter
=
{
'_id'
:
{
'$ne'
:
course_id
}}
if
course_id
else
{}
...
...
@@ -119,7 +118,26 @@ class APIValidator(Validator):
if
has_overlap
(
*
timespans
,
*
other_timespans
):
self
.
_error
(
field
,
"the room '%s' is already occupied by "
"another course at the same time"
)
"another course at the same time"
%
value
)
def
_validate_unique_assistant_booking
(
self
,
enabled
,
field
,
value
):
"""An assistant can not hold several courses simultaneously."""
# See room booking comments
assistant
=
self
.
_get_field
(
'assistant'
)
timespans
=
self
.
_get_field
(
'datetimes'
)
course_id
=
self
.
_get_field
(
'_id'
)
id_filter
=
{
'_id'
:
{
'$ne'
:
course_id
}}
if
course_id
else
{}
if
enabled
and
timespans
and
assistant
:
course_db
=
current_app
.
data
.
driver
.
db
[
'courses'
]
courses
=
course_db
.
find
({
'assistant'
:
assistant
,
**
id_filter
})
other_timespans
=
chain
.
from_iterable
(
course
[
'datetimes'
]
for
course
in
courses
)
if
has_overlap
(
*
timespans
,
*
other_timespans
):
self
.
_error
(
field
,
"the assistant '%s' is giving "
"another course at the same time"
%
value
)
def
_validate_no_time_overlap
(
self
,
enabled
,
field
,
value
):
"""Multiple timeslots of the same course must not overlap."""
...
...
Backend/tests/test_payments.py
View file @
fe18248b
...
...
@@ -10,7 +10,6 @@ def base_data(app):
'title'
:
'Awesome Lecture'
,
'department'
:
'itet'
,
'year'
:
3
,
'assistants'
:
[
'pablo'
,
'pablone'
],
}
lecture
=
app
.
client
.
post
(
'lectures'
,
data
=
lecture_data
,
...
...
@@ -18,8 +17,7 @@ def base_data(app):
course
=
{
'lecture'
:
lecture
[
'_id'
],
'assistant'
:
'anon'
,
'room'
:
'ETZ f 6'
,
'room'
:
'ETZ F 6'
,
'spots'
:
20
,
'signup'
:
{
'start'
:
'2021-05-01T10:00:00Z'
,
...
...
Backend/tests/test_resources.py
View file @
fe18248b
...
...
@@ -12,15 +12,22 @@ def test_create(app):
'title'
:
"Awesome Lecture"
,
'department'
:
"itet"
,
'year'
:
3
,
'assistants'
:
[
'pablo'
,
'pablone'
],
}
lecture_response
=
app
.
client
.
post
(
'lectures'
,
data
=
lecture
,
assert_status
=
201
)
assistant
=
{
'name'
:
"Pablo Pablone"
,
'email'
:
"pablop@ethz.ch"
,
}
assistant_response
=
app
.
client
.
post
(
'assistants'
,
data
=
assistant
,
assert_status
=
201
)
course
=
{
'lecture'
:
lecture_response
[
'_id'
],
'assistant'
:
'pablo'
,
'assistant'
:
assistant_response
[
'_id'
]
,
'room'
:
'ETZ E 6'
,
'spots'
:
30
,
'signup'
:
{
...
...
@@ -53,6 +60,8 @@ def test_create(app):
data
=
signup
,
assert_status
=
201
)
# Payment is tested separately
def
test_no_double_signup
(
app
):
"""Users can signup for several courses, but not for any course twice."""
...
...
Backend/tests/test_validation.py
View file @
fe18248b
...
...
@@ -12,7 +12,6 @@ def lecture(app):
'title'
:
"Time and Space"
,
'department'
:
"itet"
,
'year'
:
2
,
'assistants'
:
[
'pablo'
],
}
return
app
.
client
.
post
(
'lectures'
,
data
=
lecture
,
...
...
@@ -23,7 +22,6 @@ def test_start_time_before_end(app, lecture):
"""Test that any start time must come before end time."""
correct
=
{
'lecture'
:
lecture
,
'assistant'
:
'pablo'
,
'spots'
:
10
,
'datetimes'
:
[{
'start'
:
'2019-01-09T10:00:00Z'
,
...
...
@@ -33,7 +31,6 @@ def test_start_time_before_end(app, lecture):
wrong
=
{
'lecture'
:
lecture
,
'assistant'
:
'pablo'
,
'spots'
:
10
,
'datetimes'
:
[{
'start'
:
'2019-02-09T13:00:00Z'
,
...
...
@@ -53,7 +50,6 @@ def courses(app, lecture):
same_time
=
{
'lecture'
:
lecture
,
'assistant'
:
'pablo'
,
'spots'
:
10
,
'datetimes'
:
[{
'start'
:
'2019-01-09T10:00:00Z'
,
...
...
@@ -145,7 +141,6 @@ def test_no_double_booking_of_room(app, lecture):
room
=
'LEE E 12'
first
=
{
'lecture'
:
lecture
,
'assistant'
:
'pablo'
,
'room'
:
room
,
'spots'
:
10
,
'datetimes'
:
[{
...
...
@@ -156,7 +151,6 @@ def test_no_double_booking_of_room(app, lecture):
second
=
{
'lecture'
:
lecture
,
'assistant'
:
'pablo'
,
'room'
:
room
,
'spots'
:
20
,
'datetimes'
:
[{
...
...
@@ -174,14 +168,13 @@ def test_no_double_booking_of_room(app, lecture):
# Posting only one is ok, but the second one will fail
app
.
client
.
post
(
'courses'
,
data
=
first
,
assert_status
=
201
)
print
(
"NOW"
)
app
.
client
.
post
(
'courses'
,
data
=
second
,
assert_status
=
422
)
# Posting the second course with a different room is ok
second
[
'room'
]
=
'other %s'
%
room
response
=
app
.
client
.
post
(
'courses'
,
data
=
second
,
assert_status
=
201
)
# Patching
the time or room without overlap is ok:
# Patching
without overlap is ok
url
=
'courses/%s'
%
response
[
'_id'
]
separate_room
=
{
'room'
:
'another different %s'
%
room
}
response
=
app
.
client
.
patch
(
url
,
data
=
separate_room
,
...
...
@@ -189,12 +182,62 @@ def test_no_double_booking_of_room(app, lecture):
assert_status
=
200
)
def
test_unique_assistant
(
app
,
lecture
):
"""Test that an assistant cannot be set for overlapping courses."""
with
app
.
admin
():
# Create dummy assistants
assistant
=
str
(
app
.
data
.
driver
.
db
[
'assistants'
].
insert
({}))
other_assistant
=
str
(
app
.
data
.
driver
.
db
[
'assistants'
].
insert
({}))
another_assistant
=
str
(
app
.
data
.
driver
.
db
[
'assistants'
].
insert
({}))
first
=
{
'lecture'
:
lecture
,
'assistant'
:
assistant
,
'spots'
:
10
,
'datetimes'
:
[{
'start'
:
'2018-12-06T10:00:00Z'
,
'end'
:
'2018-12-06T13:00:00Z'
,
}],
}
second
=
{
'lecture'
:
lecture
,
'assistant'
:
assistant
,
'spots'
:
20
,
'datetimes'
:
[{
# Contains the first course
'start'
:
'2018-12-06T9:00:00Z'
,
'end'
:
'2018-12-06T15:00:00Z'
,
},
{
'start'
:
'2018-12-10T13:00:00Z'
,
'end'
:
'2018-12-10T14:00:00Z'
,
},
{
'start'
:
'2018-12-10T15:00:00Z'
,
'end'
:
'2018-12-10T16:00:00Z'
,
}],
}
# Posting only one is ok, but the second one will fail
app
.
client
.
post
(
'courses'
,
data
=
first
,
assert_status
=
201
)
app
.
client
.
post
(
'courses'
,
data
=
second
,
assert_status
=
422
)
# Posting the second course with a different assistant is ok
second
[
'assistant'
]
=
other_assistant
response
=
app
.
client
.
post
(
'courses'
,
data
=
second
,
assert_status
=
201
)
# Patching without overlap is ok
url
=
'courses/%s'
%
response
[
'_id'
]
separate_room
=
{
'assistant'
:
another_assistant
}
response
=
app
.
client
.
patch
(
url
,
data
=
separate_room
,
headers
=
{
'If-Match'
:
response
[
'_etag'
]},
assert_status
=
200
)
@
pytest
.
fixture
def
patch_courses
(
app
,
lecture
):
"""Courses that nearly overlap."""
first
=
{
'lecture'
:
lecture
,
'assistant'
:
'pablo'
,
'room'
:
'ETZ E 1'
,
'spots'
:
10
,
'datetimes'
:
[{
...
...
@@ -205,7 +248,6 @@ def patch_courses(app, lecture):
# Second course has same room but different time
second
=
{
'lecture'
:
lecture
,
'assistant'
:
'pablo'
,
'room'
:
'ETZ E 1'
,
'spots'
:
10
,
'datetimes'
:
[{
...
...
@@ -216,7 +258,6 @@ def patch_courses(app, lecture):
# Third course has different room but same time
third
=
{
'lecture'
:
lecture
,
'assistant'
:
'pablo'
,
'room'
:
'ETZ E 2'
,
'spots'
:
10
,
'datetimes'
:
[{
...
...
@@ -227,7 +268,6 @@ def patch_courses(app, lecture):
# Control course has different room and time
control
=
{
'lecture'
:
lecture
,
'assistant'
:
'pablo'
,
'room'
:
'ETZ E 3'
,
'spots'
:
10
,
'datetimes'
:
[{
...
...
@@ -306,7 +346,6 @@ def test_patch_self_overlap(app, lecture):
}
data
=
{
'lecture'
:
lecture
,
'assistant'
:
'pablo'
,
'room'
:
'someroom'
,
'spots'
:
10
,
'datetimes'
:
[
time_1
,
time_2
],
...
...
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