Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-notes-api
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-notes-api
Commits
aced6994
Commit
aced6994
authored
Dec 25, 2014
by
Oleg Marshev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use models in views.
parent
c38270d5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
103 additions
and
34 deletions
+103
-34
notesapi/v1/migrations/0001_initial.py
+34
-0
notesapi/v1/migrations/__init__.py
+0
-0
notesapi/v1/models.py
+10
-15
notesapi/v1/tests/test_models.py
+5
-5
notesapi/v1/views.py
+54
-14
No files found.
notesapi/v1/migrations/0001_initial.py
0 → 100644
View file @
aced6994
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
import
notesapi.v1.models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
]
operations
=
[
migrations
.
CreateModel
(
name
=
'Note'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
(
'user_id'
,
models
.
CharField
(
max_length
=
255
)),
(
'course_id'
,
notesapi
.
v1
.
models
.
CourseKeyField
(
max_length
=
255
)),
(
'usage_id'
,
notesapi
.
v1
.
models
.
UsageKeyField
(
max_length
=
255
)),
(
'text'
,
models
.
TextField
(
default
=
b
''
)),
(
'quote'
,
models
.
TextField
(
default
=
b
''
)),
(
'range_start'
,
models
.
CharField
(
max_length
=
2048
)),
(
'range_start_offset'
,
models
.
IntegerField
()),
(
'range_end'
,
models
.
CharField
(
max_length
=
2048
)),
(
'range_end_offset'
,
models
.
IntegerField
()),
(
'created'
,
models
.
DateTimeField
(
auto_now_add
=
True
)),
(
'updated'
,
models
.
DateTimeField
(
auto_now
=
True
)),
],
options
=
{
},
bases
=
(
models
.
Model
,),
),
]
notesapi/v1/migrations/__init__.py
0 → 100644
View file @
aced6994
notesapi/v1/models.py
View file @
aced6994
...
@@ -128,32 +128,27 @@ class Note(models.Model):
...
@@ -128,32 +128,27 @@ class Note(models.Model):
updated
=
models
.
DateTimeField
(
auto_now
=
True
)
updated
=
models
.
DateTimeField
(
auto_now
=
True
)
def
clean
(
self
,
json_body
):
def
clean
(
self
,
note
):
"""
"""
Clean the note object or raises a ValidationError.
Clean the note object or raises a ValidationError.
"""
"""
if
json_body
is
None
:
if
note
is
None
:
raise
ValidationError
(
'Note must have a body.'
)
raise
ValidationError
(
'Note must have a body.'
)
try
:
if
type
(
note
)
is
not
dict
:
body
=
json
.
loads
(
json_body
)
raise
ValidationError
(
'Note must be a dictionary.'
)
except
(
ValueError
,
TypeError
)
as
error
:
raise
ValidationError
(
'Note must have a valid json.'
)
if
not
type
(
body
)
is
dict
:
raise
ValidationError
(
'Note body must be a dictionary.'
)
self
.
text
=
body
.
get
(
'text'
,
''
)
self
.
text
=
note
.
get
(
'text'
,
''
)
self
.
quote
=
body
.
get
(
'quote'
,
''
)
self
.
quote
=
note
.
get
(
'quote'
,
''
)
try
:
try
:
self
.
course_id
=
CourseLocator
.
from_string
(
body
[
'course_id'
])
self
.
course_id
=
CourseLocator
.
from_string
(
note
[
'course_id'
])
self
.
usage_id
=
BlockUsageLocator
.
from_string
(
body
[
'usage_id'
])
self
.
usage_id
=
BlockUsageLocator
.
from_string
(
note
[
'usage_id'
])
self
.
user_id
=
body
[
'user'
]
self
.
user_id
=
note
[
'user'
]
except
KeyError
as
error
:
except
KeyError
as
error
:
raise
ValidationError
(
'Note must have a course_id and usage_id and user_id.'
)
raise
ValidationError
(
'Note must have a course_id and usage_id and user_id.'
)
ranges
=
body
.
get
(
'ranges'
)
ranges
=
note
.
get
(
'ranges'
)
if
ranges
is
None
or
len
(
ranges
)
!=
1
:
if
ranges
is
None
or
len
(
ranges
)
!=
1
:
raise
ValidationError
(
'Note must contain exactly one range.'
)
raise
ValidationError
(
'Note must contain exactly one range.'
)
...
...
notesapi/v1/tests/test_models.py
View file @
aced6994
...
@@ -25,7 +25,7 @@ class NoteTest(TestCase):
...
@@ -25,7 +25,7 @@ class NoteTest(TestCase):
def
test_clean_valid_note
(
self
):
def
test_clean_valid_note
(
self
):
note
=
Note
()
note
=
Note
()
note
.
clean
(
json
.
dumps
(
self
.
note
)
)
note
.
clean
(
self
.
note
)
self
.
note
.
update
({
self
.
note
.
update
({
'id'
:
None
,
'id'
:
None
,
...
@@ -47,19 +47,19 @@ class NoteTest(TestCase):
...
@@ -47,19 +47,19 @@ class NoteTest(TestCase):
payload
.
pop
(
field
)
payload
.
pop
(
field
)
with
self
.
assertRaises
(
ValidationError
):
with
self
.
assertRaises
(
ValidationError
):
note
.
clean
(
json
.
dumps
(
payload
)
)
note
.
clean
(
payload
)
def
test_clean_many_ranges
(
self
):
def
test_clean_many_ranges
(
self
):
note
=
Note
()
note
=
Note
()
with
self
.
assertRaises
(
ValidationError
):
with
self
.
assertRaises
(
ValidationError
):
note
.
clean
(
json
.
dumps
(
{
note
.
clean
({
'text'
:
'foo'
,
'text'
:
'foo'
,
'quote'
:
'bar'
,
'quote'
:
'bar'
,
'ranges'
:
[{}
for
i
in
range
(
10
)]
# Too many ranges.
'ranges'
:
[{}
for
i
in
range
(
10
)]
# Too many ranges.
})
)
})
def
test_save
(
self
):
def
test_save
(
self
):
note
=
Note
()
note
=
Note
()
note
.
clean
(
json
.
dumps
(
self
.
note
)
)
note
.
clean
(
self
.
note
)
note
.
save
()
note
.
save
()
notesapi/v1/views.py
View file @
aced6994
import
logging
from
django.conf
import
settings
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.core.exceptions
import
ValidationError
from
rest_framework
import
status
from
rest_framework
import
status
from
rest_framework.response
import
Response
from
rest_framework.response
import
Response
from
rest_framework.views
import
APIView
from
rest_framework.views
import
APIView
from
annotator.annotation
import
Annotation
from
annotator.annotation
import
Annotation
from
notesapi.v1.models
import
Note
CREATE_FILTER_FIELDS
=
(
'updated'
,
'created'
,
'consumer'
,
'id'
)
CREATE_FILTER_FIELDS
=
(
'updated'
,
'created'
,
'consumer'
,
'id'
)
UPDATE_FILTER_FIELDS
=
(
'updated'
,
'created'
,
'user'
,
'consumer'
)
UPDATE_FILTER_FIELDS
=
(
'updated'
,
'created'
,
'user'
,
'consumer'
)
log
=
logging
.
getLogger
(
__name__
)
class
AnnotationSearchView
(
APIView
):
class
AnnotationSearchView
(
APIView
):
"""
"""
...
@@ -75,6 +81,16 @@ class AnnotationListView(APIView):
...
@@ -75,6 +81,16 @@ class AnnotationListView(APIView):
if
len
(
filtered_payload
)
==
0
:
if
len
(
filtered_payload
)
==
0
:
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
)
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
)
note
=
Note
()
try
:
note
.
clean
(
filtered_payload
)
except
ValidationError
as
error
:
log
.
debug
(
error
)
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
)
note
.
save
()
annotation
=
Annotation
(
filtered_payload
)
annotation
=
Annotation
(
filtered_payload
)
annotation
.
save
(
refresh
=
True
)
annotation
.
save
(
refresh
=
True
)
...
@@ -106,34 +122,58 @@ class AnnotationDetailView(APIView):
...
@@ -106,34 +122,58 @@ class AnnotationDetailView(APIView):
"""
"""
Update an existing annotation.
Update an existing annotation.
"""
"""
annotation_id
=
self
.
kwargs
.
get
(
'annotation_id'
)
note_id
=
self
.
kwargs
.
get
(
'annotation_id'
)
annotation
=
Annotation
.
fetch
(
annotation_id
)
if
not
annotation
:
try
:
note
=
Note
.
objects
.
get
(
id
=
note_id
)
except
Note
.
DoesNotExist
:
return
Response
(
'Annotation not found! No update performed.'
,
status
=
status
.
HTTP_404_NOT_FOUND
)
return
Response
(
'Annotation not found! No update performed.'
,
status
=
status
.
HTTP_404_NOT_FOUND
)
if
self
.
request
.
DATA
is
not
None
:
es_note
=
Annotation
.
fetch
(
note_id
)
updated
=
_filter_input
(
self
.
request
.
DATA
,
UPDATE_FILTER_FIELDS
)
updated
[
'id'
]
=
annotation_id
# use id from URL, regardless of what arrives in JSON payload.
annotation
.
update
(
updated
)
if
not
es_note
:
return
Response
(
'Annotation not found! No update performed.'
,
status
=
status
.
HTTP_404_NOT_FOUND
)
refresh
=
self
.
kwargs
.
get
(
'refresh'
)
!=
'false'
if
note
.
user_id
!=
es_note
[
'user_id'
]:
annotation
.
save
(
refresh
=
refresh
)
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
)
return
Response
(
annotation
)
filtered_payload
=
_filter_input
(
self
.
request
.
DATA
,
UPDATE_FILTER_FIELDS
)
# use id from URL, regardless of what arrives in JSON payload.
filtered_payload
[
'id'
]
=
note_id
es_note
.
update
(
updated
)
try
:
note
.
clean
(
filtered_payload
)
except
ValidationError
as
e
:
log
.
debug
(
e
)
return
Response
(
status
=
status
.
HTTP_400_BAD_REQUEST
)
note
.
save
()
refresh
=
self
.
kwargs
.
get
(
'refresh'
)
!=
'false'
es_note
.
save
(
refresh
=
refresh
)
return
Response
(
es_note
)
def
delete
(
self
,
*
args
,
**
kwargs
):
# pylint: disable=unused-argument
def
delete
(
self
,
*
args
,
**
kwargs
):
# pylint: disable=unused-argument
"""
"""
Delete an annotation.
Delete an annotation.
"""
"""
annotation_id
=
self
.
kwargs
.
get
(
'annotation_id'
)
note_id
=
self
.
kwargs
.
get
(
'annotation_id'
)
annotation
=
Annotation
.
fetch
(
annotation_id
)
if
not
annotation
:
try
:
note
=
Note
.
objects
.
get
(
id
=
note_id
)
except
Note
.
DoesNotExist
:
return
Response
(
'Annotation not found! No update performed.'
,
status
=
status
.
HTTP_404_NOT_FOUND
)
es_note
=
Annotation
.
fetch
(
note_id
)
if
not
es_note
:
return
Response
(
'Annotation not found! No delete performed.'
,
status
=
status
.
HTTP_404_NOT_FOUND
)
return
Response
(
'Annotation not found! No delete performed.'
,
status
=
status
.
HTTP_404_NOT_FOUND
)
annotation
.
delete
()
note
.
delete
()
es_note
.
delete
()
# Annotation deleted successfully.
# Annotation deleted successfully.
return
Response
(
status
=
status
.
HTTP_204_NO_CONTENT
)
return
Response
(
status
=
status
.
HTTP_204_NO_CONTENT
)
...
...
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