Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
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-platform
Commits
649ccb53
Commit
649ccb53
authored
Jun 11, 2014
by
Zia Fazal
Committed by
Jonathan Piacenti
Aug 20, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
cousemodulecompletion implentation
switch to APIView to support url conventions merged with master
parent
8c411041
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
396 additions
and
5 deletions
+396
-5
lms/djangoapps/api_manager/courses/serializers.py
+15
-0
lms/djangoapps/api_manager/courses/tests.py
+97
-0
lms/djangoapps/api_manager/courses/urls.py
+3
-0
lms/djangoapps/api_manager/courses/views.py
+100
-2
lms/djangoapps/api_manager/migrations/0010_auto__add_coursemodulecompletion.py
+143
-0
lms/djangoapps/api_manager/models.py
+11
-0
lms/djangoapps/api_manager/permissions.py
+27
-3
No files found.
lms/djangoapps/api_manager/courses/serializers.py
0 → 100644
View file @
649ccb53
""" Django REST Framework Serializers """
from
api_manager.models
import
CourseModuleCompletion
from
rest_framework
import
serializers
class
CourseModuleCompletionSerializer
(
serializers
.
ModelSerializer
):
""" Serializer for CourseModuleCompletion model interactions """
user_id
=
serializers
.
Field
(
source
=
'user.id'
)
class
Meta
:
""" Serializer/field specification """
model
=
CourseModuleCompletion
fields
=
(
'id'
,
'user_id'
,
'course_id'
,
'content_id'
,
'created'
,
'modified'
)
read_only
=
(
'id'
,
'created'
)
lms/djangoapps/api_manager/courses/tests.py
View file @
649ccb53
...
...
@@ -1132,3 +1132,100 @@ class CoursesApiTests(TestCase):
response
=
self
.
do_get
(
'{}?enrolled={}&type={}'
.
format
(
test_uri_users
,
'true'
,
'project'
))
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
),
1
)
def
test_coursemodulecompletions_detail_delete
(
self
):
data
=
{
'email'
:
'test@example.com'
,
'username'
:
'test_user'
,
'password'
:
'test_pass'
,
'first_name'
:
'John'
,
'last_name'
:
'Doe'
}
response
=
self
.
do_post
(
self
.
base_users_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
created_user_id
=
response
.
data
[
'id'
]
detail_uri
=
'{}/{}/completions/{}/{}'
.
format
(
self
.
base_courses_uri
,
self
.
course
.
id
,
self
.
course_content
.
id
,
created_user_id
)
response
=
self
.
do_post
(
detail_uri
,
{})
self
.
assertEqual
(
response
.
status_code
,
201
)
coursemodulecomp_id
=
response
.
data
[
'id'
]
self
.
assertGreater
(
coursemodulecomp_id
,
0
)
self
.
assertEqual
(
response
.
data
[
'user_id'
],
created_user_id
)
self
.
assertEqual
(
response
.
data
[
'course_id'
],
self
.
course
.
id
)
self
.
assertEqual
(
response
.
data
[
'content_id'
],
self
.
course_content
.
id
)
self
.
assertIsNotNone
(
response
.
data
[
'created'
])
self
.
assertIsNotNone
(
response
.
data
[
'modified'
])
# test to create course completion with same attributes
response
=
self
.
do_post
(
detail_uri
,
{})
self
.
assertEqual
(
response
.
status_code
,
409
)
# test for delete
response
=
self
.
do_delete
(
detail_uri
)
self
.
assertEqual
(
response
.
status_code
,
204
)
response
=
self
.
do_get
(
'{}/{}/completions?user_id={}&content_id={}'
.
format
(
self
.
base_courses_uri
,
self
.
course
.
id
,
created_user_id
,
self
.
course_content
.
id
))
self
.
assertEqual
(
response
.
status_code
,
404
)
#test deletion of non existing course module completion
non_existing_uri
=
'{}/{}/completions/{}/{}'
.
format
(
self
.
base_courses_uri
,
self
.
course
.
id
,
self
.
course_content
.
id
,
'3323432'
)
response
=
self
.
do_delete
(
non_existing_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
def
test_coursemodulecompletions_filters
(
self
):
completion_uri
=
'{}/{}/completions/'
.
format
(
self
.
base_courses_uri
,
self
.
course
.
id
)
for
i
in
xrange
(
1
,
3
):
data
=
{
'email'
:
'test{}@example.com'
.
format
(
i
),
'username'
:
'test_user{}'
.
format
(
i
),
'password'
:
'test_pass'
,
'first_name'
:
'John{}'
.
format
(
i
),
'last_name'
:
'Doe{}'
.
format
(
i
)
}
response
=
self
.
do_post
(
self
.
base_users_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
created_user_id
=
response
.
data
[
'id'
]
for
i
in
xrange
(
1
,
26
):
content_id
=
self
.
course_content
.
id
+
str
(
i
)
response
=
self
.
do_post
(
'{}{}/{}'
.
format
(
completion_uri
,
content_id
,
created_user_id
),
{})
self
.
assertEqual
(
response
.
status_code
,
201
)
#filter course module completion by user
user_filter_uri
=
'{}?user_id={}&page_size=10&page=3'
.
format
(
completion_uri
,
created_user_id
)
response
=
self
.
do_get
(
user_filter_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'count'
],
25
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
5
)
self
.
assertEqual
(
response
.
data
[
'num_pages'
],
3
)
#filter course module completion by multiple user ids
user_filter_uri
=
'{}?user_id={}'
.
format
(
completion_uri
,
str
(
created_user_id
)
+
',3,4'
)
response
=
self
.
do_get
(
user_filter_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'count'
],
25
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
20
)
self
.
assertEqual
(
response
.
data
[
'num_pages'
],
2
)
#filter course module completion by user who has not completed any course module
user_filter_uri
=
'{}?user_id={}'
.
format
(
completion_uri
,
1
)
response
=
self
.
do_get
(
user_filter_uri
)
self
.
assertEqual
(
response
.
status_code
,
404
)
#filter course module completion by course_id
course_filter_uri
=
'{}?course_id={}&page_size=10'
.
format
(
completion_uri
,
self
.
course
.
id
)
response
=
self
.
do_get
(
course_filter_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'count'
],
25
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
10
)
#filter course module completion by content_id
content_filter_uri
=
'{}?content_id={}'
.
format
(
completion_uri
,
self
.
course_content
.
id
+
str
(
1
))
response
=
self
.
do_get
(
content_filter_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'count'
],
1
)
self
.
assertEqual
(
len
(
response
.
data
[
'results'
]),
1
)
lms/djangoapps/api_manager/courses/urls.py
View file @
649ccb53
...
...
@@ -26,6 +26,9 @@ urlpatterns = patterns(
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/static_tabs/*$'
,
courses_views
.
CoursesStaticTabsList
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/users/(?P<user_id>[0-9]+)$'
,
courses_views
.
CoursesUsersDetail
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/users/*$'
,
courses_views
.
CoursesUsersList
.
as_view
()),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/completions/*$'
,
courses_views
.
CourseModuleCompletionList
.
as_view
(),
name
=
'completion-list'
),
url
(
r'^(?P<course_id>[^/]+/[^/]+/[^/]+)/completions/(?P<content_id>[a-zA-Z0-9/_:]+)/(?P<user_id>[0-9]+)$'
,
courses_views
.
CourseModuleCompletionDetail
.
as_view
(),
name
=
'completion-detail'
),
)
urlpatterns
=
format_suffix_patterns
(
urlpatterns
)
lms/djangoapps/api_manager/courses/views.py
View file @
649ccb53
...
...
@@ -8,11 +8,15 @@ from StringIO import StringIO
from
django.contrib.auth.models
import
Group
,
User
from
django.core.exceptions
import
ObjectDoesNotExist
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.conf
import
settings
from
django.http
import
Http404
from
rest_framework
import
status
from
rest_framework.response
import
Response
from
api_manager.models
import
CourseGroupRelationship
,
CourseContentGroupRelationship
,
GroupProfile
from
api_manager.models
import
CourseGroupRelationship
,
CourseContentGroupRelationship
,
GroupProfile
,
\
CourseModuleCompletion
from
api_manager.users.serializers
import
UserSerializer
from
courseware
import
module_render
from
courseware.courses
import
get_course
,
get_course_about_section
,
get_course_info_section
...
...
@@ -21,7 +25,8 @@ from courseware.views import get_static_tab_contents
from
student.models
import
CourseEnrollment
,
CourseEnrollmentAllowed
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore
import
Location
,
InvalidLocationError
from
api_manager.permissions
import
SecureAPIView
from
api_manager.permissions
import
SecureAPIView
,
SecureListAPIView
from
.serializers
import
CourseModuleCompletionSerializer
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -1055,3 +1060,96 @@ class CourseContentUsersList(SecureAPIView):
serializer
=
UserSerializer
(
queryset
,
many
=
True
)
return
Response
(
serializer
.
data
)
# pylint: disable=E1101
class
CourseModuleCompletionList
(
SecureListAPIView
):
"""
### The CourseModuleCompletionList allows clients to view user's course module completion entities
to monitor a user's progression throughout the duration of a course,
- URI: ```/api/courses/{course_id}/completions```
- GET: Returns a JSON representation of the course, content and user and timestamps
- GET Example:
{
"count":"1",
"num_pages": "1",
"previous": null
"next": null
"results": [
{
"id": 2,
"user_id": "3",
"course_id": "32fgdf",
"content_id": "324dfgd",
"created": "2014-06-10T13:14:49.878Z",
"modified": "2014-06-10T13:14:49.914Z"
}
]
}
Filters can also be applied
```/api/courses/{course_id}/completions/?user_id={user_id}```
```/api/courses/{course_id}/completions/?content_id={content_id}```
```/api/courses/{course_id}/completions/?user_id={user_id}&content_id={content_id}```
### Use Cases/Notes:
* Use GET operation to retrieve list of course completions by user
* Use GET operation to verify user has completed specific course module
"""
serializer_class
=
CourseModuleCompletionSerializer
def
get_queryset
(
self
):
"""
GET /api/courses/{course_id}/completions/
"""
user_ids
=
self
.
request
.
QUERY_PARAMS
.
get
(
'user_id'
,
None
)
content_id
=
self
.
request
.
QUERY_PARAMS
.
get
(
'content_id'
,
None
)
course_id
=
self
.
kwargs
[
'course_id'
]
queryset
=
CourseModuleCompletion
.
objects
.
filter
(
course_id
=
course_id
)
upper_bound
=
getattr
(
settings
,
'API_LOOKUP_UPPER_BOUND'
,
100
)
if
user_ids
:
if
','
in
user_ids
:
user_ids
=
user_ids
.
split
(
","
)[:
upper_bound
]
queryset
=
queryset
.
filter
(
user__in
=
user_ids
)
if
content_id
:
queryset
=
queryset
.
filter
(
content_id
=
content_id
)
if
not
queryset
.
exists
()
and
(
user_ids
or
content_id
):
raise
Http404
return
queryset
class
CourseModuleCompletionDetail
(
SecureAPIView
):
"""
### The CourseModuleCompletionDetail view allows clients to interact with a
specific Course-Content completion entity
- URI: ```/api/courses/{course_id}/completions/{content_id}/{user_id}```
- POST: Creates a Course-Module completion entity
- DELETE: Removes an existing Course-Module completion entity from the system
### Use Cases/Notes:
* Use this operation to save or remove Course-Module completion entity
"""
def
post
(
self
,
request
,
course_id
,
content_id
,
user_id
):
"""
POST /api/courses/{course_id}/completions/{content_id}/{user_id}
"""
completion
,
created
=
CourseModuleCompletion
.
objects
.
get_or_create
(
user_id
=
user_id
,
course_id
=
course_id
,
content_id
=
content_id
)
serializer
=
CourseModuleCompletionSerializer
(
completion
)
if
created
:
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_201_CREATED
)
# pylint: disable=E1101
else
:
return
Response
({
'message'
:
_
(
'Resource already exists'
)},
status
=
status
.
HTTP_409_CONFLICT
)
def
delete
(
self
,
request
,
course_id
,
content_id
,
user_id
):
"""
DELETE /api/courses/{course_id}/completions/{content_id}/{user_id}
"""
try
:
completion
=
CourseModuleCompletion
.
objects
.
get
(
user_id
=
user_id
,
course_id
=
course_id
,
content_id
=
content_id
)
completion
.
delete
()
except
ObjectDoesNotExist
:
raise
Http404
response_data
=
{
'uri'
:
_generate_base_uri
(
request
)}
return
Response
(
response_data
,
status
=
status
.
HTTP_204_NO_CONTENT
)
lms/djangoapps/api_manager/migrations/0010_auto__add_coursemodulecompletion.py
0 → 100644
View file @
649ccb53
# -*- coding: utf-8 -*-
import
datetime
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
django.db
import
models
class
Migration
(
SchemaMigration
):
def
forwards
(
self
,
orm
):
# Adding model 'CourseModuleCompletion'
db
.
create_table
(
'api_manager_coursemodulecompletion'
,
(
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'created'
,
self
.
gf
(
'model_utils.fields.AutoCreatedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'modified'
,
self
.
gf
(
'model_utils.fields.AutoLastModifiedField'
)(
default
=
datetime
.
datetime
.
now
)),
(
'user'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
related_name
=
'course_completions'
,
to
=
orm
[
'auth.User'
])),
(
'course_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
,
db_index
=
True
)),
(
'content_id'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
255
,
db_index
=
True
)),
))
db
.
send_create_signal
(
'api_manager'
,
[
'CourseModuleCompletion'
])
def
backwards
(
self
,
orm
):
# Deleting model 'CourseModuleCompletion'
db
.
delete_table
(
'api_manager_coursemodulecompletion'
)
models
=
{
'api_manager.coursecontentgrouprelationship'
:
{
'Meta'
:
{
'unique_together'
:
"(('course_id', 'content_id', 'group_profile'),)"
,
'object_name'
:
'CourseContentGroupRelationship'
},
'content_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'group_profile'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['api_manager.GroupProfile']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.coursegrouprelationship'
:
{
'Meta'
:
{
'object_name'
:
'CourseGroupRelationship'
},
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'group'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['auth.Group']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.coursemodulecompletion'
:
{
'Meta'
:
{
'object_name'
:
'CourseModuleCompletion'
},
'content_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'course_id'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'db_index'
:
'True'
}),
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'user'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'course_completions'"
,
'to'
:
"orm['auth.User']"
})
},
'api_manager.groupprofile'
:
{
'Meta'
:
{
'object_name'
:
'GroupProfile'
,
'db_table'
:
"'auth_groupprofile'"
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'data'
:
(
'django.db.models.fields.TextField'
,
[],
{
'blank'
:
'True'
}),
'group'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'unique'
:
'True'
}),
'group_type'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'32'
,
'null'
:
'True'
,
'db_index'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.grouprelationship'
:
{
'Meta'
:
{
'object_name'
:
'GroupRelationship'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'group'
:
(
'django.db.models.fields.related.OneToOneField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'unique'
:
'True'
,
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'parent_group'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'default'
:
'0'
,
'related_name'
:
"'child_groups'"
,
'null'
:
'True'
,
'blank'
:
'True'
,
'to'
:
"orm['api_manager.GroupRelationship']"
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
})
},
'api_manager.linkedgrouprelationship'
:
{
'Meta'
:
{
'object_name'
:
'LinkedGroupRelationship'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'from_group_relationship'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'from_group_relationships'"
,
'to'
:
"orm['api_manager.GroupRelationship']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'record_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'to_group_relationship'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"'to_group_relationships'"
,
'to'
:
"orm['api_manager.GroupRelationship']"
})
},
'api_manager.organization'
:
{
'Meta'
:
{
'object_name'
:
'Organization'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
}),
'users'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'related_name'
:
"'organizations'"
,
'symmetrical'
:
'False'
,
'to'
:
"orm['auth.User']"
}),
'workgroups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'related_name'
:
"'organizations'"
,
'symmetrical'
:
'False'
,
'to'
:
"orm['projects.Workgroup']"
})
},
'auth.group'
:
{
'Meta'
:
{
'object_name'
:
'Group'
},
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'80'
}),
'permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
})
},
'auth.permission'
:
{
'Meta'
:
{
'ordering'
:
"('content_type__app_label', 'content_type__model', 'codename')"
,
'unique_together'
:
"(('content_type', 'codename'),)"
,
'object_name'
:
'Permission'
},
'codename'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'to'
:
"orm['contenttypes.ContentType']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'50'
})
},
'auth.user'
:
{
'Meta'
:
{
'object_name'
:
'User'
},
'date_joined'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'email'
:
(
'django.db.models.fields.EmailField'
,
[],
{
'max_length'
:
'75'
,
'blank'
:
'True'
}),
'first_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'groups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Group']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'is_active'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'True'
}),
'is_staff'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'is_superuser'
:
(
'django.db.models.fields.BooleanField'
,
[],
{
'default'
:
'False'
}),
'last_login'
:
(
'django.db.models.fields.DateTimeField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'last_name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'30'
,
'blank'
:
'True'
}),
'password'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'128'
}),
'user_permissions'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'to'
:
"orm['auth.Permission']"
,
'symmetrical'
:
'False'
,
'blank'
:
'True'
}),
'username'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'30'
})
},
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
},
'projects.workgroup'
:
{
'Meta'
:
{
'object_name'
:
'Workgroup'
},
'created'
:
(
'model_utils.fields.AutoCreatedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'groups'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'related_name'
:
"'workgroups'"
,
'symmetrical'
:
'False'
,
'to'
:
"orm['auth.Group']"
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'modified'
:
(
'model_utils.fields.AutoLastModifiedField'
,
[],
{
'default'
:
'datetime.datetime.now'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'255'
,
'null'
:
'True'
,
'blank'
:
'True'
}),
'users'
:
(
'django.db.models.fields.related.ManyToManyField'
,
[],
{
'related_name'
:
"'workgroups'"
,
'symmetrical'
:
'False'
,
'to'
:
"orm['auth.User']"
})
}
}
complete_apps
=
[
'api_manager'
]
\ No newline at end of file
lms/djangoapps/api_manager/models.py
View file @
649ccb53
...
...
@@ -138,3 +138,14 @@ class Organization(TimeStampedModel):
name
=
models
.
CharField
(
max_length
=
255
)
workgroups
=
models
.
ManyToManyField
(
Workgroup
,
related_name
=
"organizations"
)
users
=
models
.
ManyToManyField
(
User
,
related_name
=
"organizations"
)
class
CourseModuleCompletion
(
TimeStampedModel
):
"""
The CourseModuleCompletion model contains user, course, module information
to monitor a user's progression throughout the duration of a course,
we need to observe and record completions of the individual course modules.
"""
user
=
models
.
ForeignKey
(
User
,
db_index
=
True
,
related_name
=
"course_completions"
)
course_id
=
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)
content_id
=
models
.
CharField
(
max_length
=
255
,
db_index
=
True
)
lms/djangoapps/api_manager/permissions.py
View file @
649ccb53
...
...
@@ -7,6 +7,7 @@ from api_manager.utils import get_client_ip_address, address_exists_in_network
from
rest_framework
import
permissions
,
generics
,
filters
,
pagination
,
serializers
from
rest_framework.views
import
APIView
log
=
logging
.
getLogger
(
__name__
)
...
...
@@ -87,10 +88,11 @@ class IdsInFilterBackend(filters.BaseFilterBackend):
Parse querystring to get ids and the filter the queryset
Max of 100 values are allowed for performance reasons
"""
upper_bound
=
getattr
(
settings
,
'API_LOOKUP_UPPER_BOUND'
,
100
)
ids
=
request
.
QUERY_PARAMS
.
get
(
'ids'
)
if
ids
:
if
','
in
ids
:
ids
=
ids
.
split
(
","
)[:
100
]
ids
=
ids
.
split
(
","
)[:
upper_bound
]
return
queryset
.
filter
(
id__in
=
ids
)
return
queryset
...
...
@@ -106,13 +108,35 @@ class SecureAPIView(APIView):
permission_classes
=
(
ApiKeyHeaderPermission
,
)
class
SecureListAPIView
(
generics
.
ListAPIView
):
class
PermissionMixin
(
object
):
"""
Inherited from ListAPIView
Mixin to set custom permission_classes
"""
permission_classes
=
(
ApiKeyHeaderPermission
,
IPAddressRestrictedPermission
)
class
FilterBackendMixin
(
object
):
"""
Mixin to set custom filter_backends
"""
filter_backends
=
(
filters
.
DjangoFilterBackend
,
IdsInFilterBackend
,)
class
PaginationMixin
(
object
):
"""
Mixin to set custom pagination support
"""
pagination_serializer_class
=
CustomPaginationSerializer
paginate_by
=
getattr
(
settings
,
'API_PAGE_SIZE'
,
20
)
paginate_by_param
=
'page_size'
max_paginate_by
=
100
class
SecureListAPIView
(
PermissionMixin
,
FilterBackendMixin
,
PaginationMixin
,
generics
.
ListAPIView
):
"""
Inherited from ListAPIView
"""
pass
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