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
c338b751
Commit
c338b751
authored
Jul 18, 2017
by
Diana Huang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Consolidate access checks for prereqs and surveys.
parent
27cd3d19
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
81 additions
and
83 deletions
+81
-83
common/djangoapps/student/views.py
+0
-1
lms/djangoapps/courseware/access.py
+9
-6
lms/djangoapps/courseware/access_response.py
+2
-2
lms/djangoapps/courseware/courses.py
+23
-5
lms/djangoapps/courseware/exceptions.py
+9
-1
lms/djangoapps/courseware/tests/test_access.py
+6
-7
lms/djangoapps/courseware/views/index.py
+0
-2
lms/djangoapps/courseware/views/views.py
+2
-34
lms/djangoapps/mobile_api/decorators.py
+4
-0
lms/djangoapps/mobile_api/tests/test_milestones.py
+2
-2
lms/djangoapps/mobile_api/users/tests.py
+2
-2
lms/djangoapps/survey/tests/test_utils.py
+6
-6
lms/djangoapps/survey/utils.py
+16
-15
No files found.
common/djangoapps/student/views.py
View file @
c338b751
...
...
@@ -734,7 +734,6 @@ def dashboard(request):
show_courseware_links_for
=
frozenset
(
enrollment
.
course_id
for
enrollment
in
course_enrollments
if
has_access
(
request
.
user
,
'load'
,
enrollment
.
course_overview
)
and
has_access
(
request
.
user
,
'view_courseware_with_prerequisites'
,
enrollment
.
course_overview
)
)
# Find programs associated with course runs being displayed. This information
...
...
lms/djangoapps/courseware/access.py
View file @
c338b751
...
...
@@ -21,7 +21,11 @@ from django.utils.timezone import UTC
from
opaque_keys.edx.keys
import
CourseKey
,
UsageKey
from
xblock.core
import
XBlock
from
courseware.access_response
import
MilestoneError
,
MobileAvailabilityError
,
VisibilityError
from
courseware.access_response
import
(
MilestoneAccessError
,
MobileAvailabilityError
,
VisibilityError
,
)
from
courseware.access_utils
import
(
ACCESS_DENIED
,
ACCESS_GRANTED
,
...
...
@@ -309,7 +313,8 @@ def _has_access_course(user, action, courselike):
"""
response
=
(
_visible_to_nonstaff_users
(
courselike
)
and
check_course_open_for_learner
(
user
,
courselike
)
check_course_open_for_learner
(
user
,
courselike
)
and
_can_view_courseware_with_prerequisites
(
user
,
courselike
)
)
return
(
...
...
@@ -355,8 +360,6 @@ def _has_access_course(user, action, courselike):
checkers
=
{
'load'
:
can_load
,
'view_courseware_with_prerequisites'
:
lambda
:
_can_view_courseware_with_prerequisites
(
user
,
courselike
),
'load_mobile'
:
lambda
:
can_load
()
and
_can_load_course_on_mobile
(
user
,
courselike
),
'enroll'
:
can_enroll
,
'see_exists'
:
see_exists
,
...
...
@@ -770,7 +773,7 @@ def _has_fulfilled_all_milestones(user, course_id):
course_id: ID of the course to check
user_id: ID of the user to check
"""
return
MilestoneError
()
if
any_unfulfilled_milestones
(
course_id
,
user
.
id
)
else
ACCESS_GRANTED
return
Milestone
Access
Error
()
if
any_unfulfilled_milestones
(
course_id
,
user
.
id
)
else
ACCESS_GRANTED
def
_has_fulfilled_prerequisites
(
user
,
course_id
):
...
...
@@ -782,7 +785,7 @@ def _has_fulfilled_prerequisites(user, course_id):
user: user to check
course_id: ID of the course to check
"""
return
MilestoneError
()
if
get_pre_requisite_courses_not_completed
(
user
,
course_id
)
else
ACCESS_GRANTED
return
Milestone
Access
Error
()
if
get_pre_requisite_courses_not_completed
(
user
,
course_id
)
else
ACCESS_GRANTED
def
_has_catalog_visibility
(
course
,
visibility_type
):
...
...
lms/djangoapps/courseware/access_response.py
View file @
c338b751
...
...
@@ -105,7 +105,7 @@ class StartDateError(AccessError):
super
(
StartDateError
,
self
)
.
__init__
(
error_code
,
developer_message
,
user_message
)
class
MilestoneError
(
AccessError
):
class
Milestone
Access
Error
(
AccessError
):
"""
Access denied because the user has unfulfilled milestones
"""
...
...
@@ -113,7 +113,7 @@ class MilestoneError(AccessError):
error_code
=
"unfulfilled_milestones"
developer_message
=
"User has unfulfilled milestones"
user_message
=
_
(
"You have unfulfilled milestones"
)
super
(
MilestoneError
,
self
)
.
__init__
(
error_code
,
developer_message
,
user_message
)
super
(
Milestone
Access
Error
,
self
)
.
__init__
(
error_code
,
developer_message
,
user_message
)
class
VisibilityError
(
AccessError
):
...
...
lms/djangoapps/courseware/courses.py
View file @
c338b751
...
...
@@ -9,7 +9,7 @@ from datetime import datetime
import
branding
import
pytz
from
courseware.access
import
has_access
from
courseware.access_response
import
StartDateError
from
courseware.access_response
import
StartDateError
,
MilestoneAccessError
from
courseware.date_summary
import
(
CourseEndDate
,
CourseStartDate
,
...
...
@@ -32,6 +32,7 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
from
path
import
Path
as
path
from
static_replace
import
replace_static_urls
from
student.models
import
CourseEnrollment
from
survey.utils
import
is_survey_required_and_unanswered
from
util.date_utils
import
strftime_localized
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
...
...
@@ -72,7 +73,7 @@ def get_course_by_id(course_key, depth=0):
raise
Http404
(
"Course not found: {}."
.
format
(
unicode
(
course_key
)))
def
get_course_with_access
(
user
,
action
,
course_key
,
depth
=
0
,
check_if_enrolled
=
False
):
def
get_course_with_access
(
user
,
action
,
course_key
,
depth
=
0
,
check_if_enrolled
=
False
,
check_survey_complete
=
True
):
"""
Given a course_key, look up the corresponding course descriptor,
check that the user has the access to perform the specified action
...
...
@@ -84,9 +85,14 @@ def get_course_with_access(user, action, course_key, depth=0, check_if_enrolled=
check_if_enrolled: If true, additionally verifies that the user is either enrolled in the course
or has staff access.
check_survey_complete: If true, additionally verifies that the user has either completed the course survey
or has staff access.
Note: We do not want to continually add these optional booleans. Ideally,
these special cases could not only be handled inside has_access, but could
be plugged in as additional callback checks for different actions.
"""
course
=
get_course_by_id
(
course_key
,
depth
)
check_course_access
(
course
,
user
,
action
,
check_if_enrolled
)
check_course_access
(
course
,
user
,
action
,
check_if_enrolled
,
check_survey_complete
)
return
course
...
...
@@ -109,12 +115,13 @@ def get_course_overview_with_access(user, action, course_key, check_if_enrolled=
return
course_overview
def
check_course_access
(
course
,
user
,
action
,
check_if_enrolled
=
False
):
def
check_course_access
(
course
,
user
,
action
,
check_if_enrolled
=
False
,
check_survey_complete
=
True
):
"""
Check that the user has the access to perform the specified action
on the course (CourseDescriptor|CourseOverview).
check_if_enrolled: If true, additionally verifies that the user is enrolled.
check_survey_complete: If true, additionally verifies that the user has completed the survey.
"""
# Allow staff full access to the course even if not enrolled
if
has_access
(
user
,
'staff'
,
course
.
id
):
...
...
@@ -130,7 +137,13 @@ def check_course_access(course, user, action, check_if_enrolled=False):
raise
CourseAccessRedirect
(
'{dashboard_url}?{params}'
.
format
(
dashboard_url
=
reverse
(
'dashboard'
),
params
=
params
.
urlencode
()
))
),
access_response
)
# Redirect if the user must answer a survey before entering the course.
if
isinstance
(
access_response
,
MilestoneAccessError
):
raise
CourseAccessRedirect
(
'{dashboard_url}'
.
format
(
dashboard_url
=
reverse
(
'dashboard'
),
),
access_response
)
# Deliberately return a non-specific error message to avoid
# leaking info about access control settings
...
...
@@ -141,6 +154,11 @@ def check_course_access(course, user, action, check_if_enrolled=False):
if
not
CourseEnrollment
.
is_enrolled
(
user
,
course
.
id
):
raise
CourseAccessRedirect
(
reverse
(
'about_course'
,
args
=
[
unicode
(
course
.
id
)]))
# Redirect if the user must answer a survey before entering the course.
if
check_survey_complete
and
action
==
'load'
:
if
is_survey_required_and_unanswered
(
user
,
course
):
raise
CourseAccessRedirect
(
reverse
(
'course_survey'
,
args
=
[
unicode
(
course
.
id
)]))
def
can_self_enroll_in_course
(
course_key
):
"""
...
...
lms/djangoapps/courseware/exceptions.py
View file @
c338b751
...
...
@@ -15,5 +15,13 @@ class Redirect(Exception):
class
CourseAccessRedirect
(
Redirect
):
"""
Redirect raised when user does not have access to a course.
Arguments:
url (string): The redirect url.
access_error (AccessErro): The AccessError that caused the redirect.
The AccessError contains messages for developers and users explaining why
the user was denied access. These strings can then be exposed to the user.
"""
pass
def
__init__
(
self
,
url
,
access_error
=
None
):
super
(
CourseAccessRedirect
,
self
)
.
__init__
(
url
)
self
.
access_error
=
access_error
lms/djangoapps/courseware/tests/test_access.py
View file @
c338b751
...
...
@@ -595,16 +595,16 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
# user should not be able to load course even if enrolled
CourseEnrollmentFactory
(
user
=
user
,
course_id
=
course
.
id
)
response
=
access
.
_has_access_course
(
user
,
'
view_courseware_with_prerequisites
'
,
course
)
response
=
access
.
_has_access_course
(
user
,
'
load
'
,
course
)
self
.
assertFalse
(
response
)
self
.
assertIsInstance
(
response
,
access_response
.
MilestoneError
)
self
.
assertIsInstance
(
response
,
access_response
.
Milestone
Access
Error
)
# Staff can always access course
staff
=
StaffFactory
.
create
(
course_key
=
course
.
id
)
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'
view_courseware_with_prerequisites
'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
staff
,
'
load
'
,
course
))
# User should be able access after completing required course
fulfill_course_milestone
(
pre_requisite_course
.
id
,
user
)
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'
view_courseware_with_prerequisites
'
,
course
))
self
.
assertTrue
(
access
.
_has_access_course
(
user
,
'
load
'
,
course
))
@ddt.data
(
(
True
,
True
,
True
),
...
...
@@ -615,8 +615,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
"""
Test course access on mobile for staff and students.
"""
descriptor
=
Mock
(
id
=
self
.
course
.
id
,
user_partitions
=
[])
descriptor
.
_class_tags
=
{}
descriptor
=
CourseFactory
()
descriptor
.
visible_to_staff_only
=
False
descriptor
.
mobile_available
=
mobile_available
...
...
@@ -773,7 +772,7 @@ class CourseOverviewAccessTestCase(ModuleStoreTestCase):
PREREQUISITES_TEST_DATA
=
list
(
itertools
.
product
(
[
'user_normal'
,
'user_completed_pre_requisite'
,
'user_staff'
,
'user_anonymous'
],
[
'
view_courseware_with_prerequisites
'
],
[
'
load
'
],
[
'course_default'
,
'course_with_pre_requisite'
,
'course_with_pre_requisites'
],
))
...
...
lms/djangoapps/courseware/views/index.py
View file @
c338b751
...
...
@@ -52,7 +52,6 @@ from ..model_data import FieldDataCache
from
..module_render
import
get_module_for_descriptor
,
toc_for_course
from
.views
import
(
CourseTabView
,
check_access_to_course
,
check_and_get_upgrade_link
,
get_cosmetic_verified_display_price
)
...
...
@@ -136,7 +135,6 @@ class CoursewareIndex(View):
"""
Render the index page.
"""
check_access_to_course
(
request
,
self
.
course
)
self
.
_redirect_if_needed_to_pay_for_course
()
self
.
_prefetch_and_bind_course
(
request
)
...
...
lms/djangoapps/courseware/views/views.py
View file @
c338b751
...
...
@@ -9,7 +9,6 @@ from datetime import datetime, timedelta
import
analytics
import
shoppingcart
import
survey.utils
import
survey.views
import
waffle
from
certificates
import
api
as
certs_api
...
...
@@ -91,7 +90,6 @@ from openedx.features.enterprise_support.api import data_sharing_consent_require
from
rest_framework
import
status
from
shoppingcart.utils
import
is_shopping_cart_enabled
from
student.models
import
CourseEnrollment
,
UserTestGroup
from
survey.utils
import
must_answer_survey
from
util.cache
import
cache
,
cache_if_anonymous
from
util.db
import
outer_atomic
from
util.milestones_helpers
import
get_prerequisite_courses_display
...
...
@@ -278,10 +276,6 @@ def course_info(request, course_id):
if
not
user_is_enrolled
and
not
can_self_enroll_in_course
(
course_key
):
return
redirect
(
reverse
(
'dashboard'
))
# TODO: LEARNER-1865: Handle prereqs and course survey in new Course Home.
# Redirect the user if they are not yet allowed to view this course
check_access_to_course
(
request
,
course
)
# LEARNER-170: Entrance exam is handled by new Course Outline. (DONE)
# If the user needs to take an entrance exam to access this course, then we'll need
# to send them to that specific course module before allowing them into other areas
...
...
@@ -424,9 +418,6 @@ class CourseTabView(EdxFragmentView):
with
modulestore
()
.
bulk_operations
(
course_key
):
course
=
get_course_with_access
(
request
.
user
,
'load'
,
course_key
)
try
:
# Verify that the user has access to the course
check_access_to_course
(
request
,
course
)
# Show warnings if the user has limited access
self
.
register_user_access_warning_messages
(
request
,
course_key
)
...
...
@@ -739,8 +730,7 @@ def course_about(request, course_id):
show_courseware_link
=
bool
(
(
has_access
(
request
.
user
,
'load'
,
course
)
and
has_access
(
request
.
user
,
'view_courseware_with_prerequisites'
,
course
)
has_access
(
request
.
user
,
'load'
,
course
)
)
or
settings
.
FEATURES
.
get
(
'ENABLE_LMS_MIGRATION'
)
)
...
...
@@ -921,9 +911,6 @@ def _progress(request, course_key, student_id):
# NOTE: To make sure impersonation by instructor works, use
# student instead of request.user in the rest of the function.
# Redirect the user if they are not yet allowed to view this course
check_access_to_course
(
request
,
course
)
# The pre-fetching of groups is done to make auth checks not require an
# additional DB lookup (this kills the Progress page in particular).
student
=
User
.
objects
.
prefetch_related
(
"groups"
)
.
get
(
id
=
student
.
id
)
...
...
@@ -1311,7 +1298,7 @@ def course_survey(request, course_id):
"""
course_key
=
CourseKey
.
from_string
(
course_id
)
course
=
get_course_with_access
(
request
.
user
,
'load'
,
course_key
)
course
=
get_course_with_access
(
request
.
user
,
'load'
,
course_key
,
check_survey_complete
=
False
)
redirect_url
=
reverse
(
course_home_url_name
(
course
.
id
),
args
=
[
course_id
])
...
...
@@ -1721,22 +1708,3 @@ def get_financial_aid_courses(user):
)
return
financial_aid_courses
def
check_access_to_course
(
request
,
course
):
"""
Raises Redirect exceptions if the user does not have course access.
"""
# TODO: LEARNER-1865: Handle prereqs in new Course Home.
# Redirect to the dashboard if not all prerequisites have been met
if
not
has_access
(
request
.
user
,
'view_courseware_with_prerequisites'
,
course
):
log
.
info
(
u'User
%
d tried to view course
%
s '
u'without fulfilling prerequisites'
,
request
.
user
.
id
,
unicode
(
course
.
id
))
raise
CourseAccessRedirect
(
reverse
(
'dashboard'
))
# TODO: LEARNER-1865: Handle course surveys in new Course Home.
# Redirect if the user must answer a survey before entering the course.
if
must_answer_survey
(
course
,
request
.
user
):
raise
CourseAccessRedirect
(
reverse
(
'course_survey'
,
args
=
[
unicode
(
course
.
id
)]))
lms/djangoapps/mobile_api/decorators.py
View file @
c338b751
...
...
@@ -42,6 +42,10 @@ def mobile_course_access(depth=0):
except
CoursewareAccessException
as
error
:
return
Response
(
data
=
error
.
to_json
(),
status
=
status
.
HTTP_404_NOT_FOUND
)
except
CourseAccessRedirect
as
error
:
# If the redirect contains information about the triggering AccessError,
# return the information contained in the AccessError.
if
error
.
access_error
is
not
None
:
return
Response
(
data
=
error
.
access_error
.
to_json
(),
status
=
status
.
HTTP_404_NOT_FOUND
)
# Raise a 404 if the user does not have course access
raise
Http404
return
func
(
self
,
request
,
course
=
course
,
*
args
,
**
kwargs
)
...
...
lms/djangoapps/mobile_api/tests/test_milestones.py
View file @
c338b751
...
...
@@ -4,7 +4,7 @@ Milestone related tests for the mobile_api
from
django.conf
import
settings
from
mock
import
patch
from
courseware.access_response
import
MilestoneError
from
courseware.access_response
import
Milestone
Access
Error
from
courseware.tests.test_entrance_exam
import
add_entrance_exam_milestone
,
answer_entrance_exam_problem
from
openedx.core.djangolib.testing.utils
import
get_mock_request
from
util.milestones_helpers
import
add_prerequisite_course
,
fulfill_course_milestone
...
...
@@ -136,4 +136,4 @@ class MobileAPIMilestonesMixin(object):
self
.
api_response
()
else
:
response
=
self
.
api_response
(
expected_response_code
=
404
)
self
.
assertEqual
(
response
.
data
,
MilestoneError
()
.
to_json
())
self
.
assertEqual
(
response
.
data
,
Milestone
Access
Error
()
.
to_json
())
lms/djangoapps/mobile_api/users/tests.py
View file @
c338b751
...
...
@@ -18,7 +18,7 @@ from certificates.api import generate_user_certificates
from
certificates.models
import
CertificateStatuses
from
certificates.tests.factories
import
GeneratedCertificateFactory
from
course_modes.models
import
CourseMode
from
courseware.access_response
import
MilestoneError
,
StartDateError
,
VisibilityError
from
courseware.access_response
import
Milestone
Access
Error
,
StartDateError
,
VisibilityError
from
lms.djangoapps.grades.tests.utils
import
mock_passing_grade
from
mobile_api.testutils
import
(
MobileAPITestCase
,
...
...
@@ -155,7 +155,7 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
]
expected_error_codes
=
[
MilestoneError
()
.
error_code
,
# 'unfulfilled_milestones'
Milestone
Access
Error
()
.
error_code
,
# 'unfulfilled_milestones'
StartDateError
(
self
.
NEXT_WEEK
)
.
error_code
,
# 'course_not_started'
VisibilityError
()
.
error_code
,
# 'not_visible_to_user'
None
,
...
...
lms/djangoapps/survey/tests/test_utils.py
View file @
c338b751
...
...
@@ -8,7 +8,7 @@ from django.contrib.auth.models import User
from
django.test.client
import
Client
from
survey.models
import
SurveyForm
from
survey.utils
import
is_survey_required_for_course
,
must_answer_survey
from
survey.utils
import
is_survey_required_for_course
,
is_survey_required_and_unanswered
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
...
...
@@ -89,28 +89,28 @@ class SurveyModelsTests(ModuleStoreTestCase):
"""
Assert that a new course which has a required survey but user has not answered it yet
"""
self
.
assertTrue
(
must_answer_survey
(
self
.
course
,
self
.
student
))
self
.
assertTrue
(
is_survey_required_and_unanswered
(
self
.
student
,
self
.
course
))
temp_course
=
CourseFactory
.
create
(
course_survey_required
=
False
)
self
.
assertFalse
(
must_answer_survey
(
temp_course
,
self
.
student
))
self
.
assertFalse
(
is_survey_required_and_unanswered
(
self
.
student
,
temp_course
))
temp_course
=
CourseFactory
.
create
(
course_survey_required
=
True
,
course_survey_name
=
"NonExisting"
)
self
.
assertFalse
(
must_answer_survey
(
temp_course
,
self
.
student
))
self
.
assertFalse
(
is_survey_required_and_unanswered
(
self
.
student
,
temp_course
))
def
test_user_has_answered_required_survey
(
self
):
"""
Assert that a new course which has a required survey and user has answers for it
"""
self
.
survey
.
save_user_answers
(
self
.
student
,
self
.
student_answers
,
None
)
self
.
assertFalse
(
must_answer_survey
(
self
.
course
,
self
.
student
))
self
.
assertFalse
(
is_survey_required_and_unanswered
(
self
.
student
,
self
.
course
))
def
test_staff_must_answer_survey
(
self
):
"""
Assert that someone with staff level permissions does not have to answer the survey
"""
self
.
assertFalse
(
must_answer_survey
(
self
.
course
,
self
.
staff
))
self
.
assertFalse
(
is_survey_required_and_unanswered
(
self
.
staff
,
self
.
course
))
lms/djangoapps/survey/utils.py
View file @
c338b751
"""
Helper methods for Surveys
Utilities for determining whether or not a survey needs to be completed.
"""
from
courseware.access
import
has_access
from
survey.models
import
Survey
Answer
,
SurveyForm
from
survey.models
import
Survey
Form
,
SurveyAnswer
def
is_survey_required_for_course
(
course_descriptor
):
...
...
@@ -11,17 +10,19 @@ def is_survey_required_for_course(course_descriptor):
Returns whether a Survey is required for this course
"""
# check to see that the Survey name has been defined in the CourseDescriptor
# and that the specified Survey exists
# Check to see that the survey is required in the CourseDescriptor.
if
not
getattr
(
course_descriptor
,
'course_survey_required'
,
False
):
return
False
return
course_descriptor
.
course_survey_required
and
\
SurveyForm
.
get
(
course_descriptor
.
course_survey_name
,
throw_if_not_found
=
False
)
# Check that the specified Survey for the course exists.
return
SurveyForm
.
get
(
course_descriptor
.
course_survey_name
,
throw_if_not_found
=
False
)
def
must_answer_survey
(
course_descriptor
,
use
r
):
def
is_survey_required_and_unanswered
(
user
,
course_descripto
r
):
"""
Returns whether a user
needs to answer a required survey
Returns whether a user
is required to answer the survey and has yet to do so.
"""
if
not
is_survey_required_for_course
(
course_descriptor
):
return
False
...
...
@@ -29,13 +30,13 @@ def must_answer_survey(course_descriptor, user):
if
user
.
is_anonymous
():
return
False
# this will throw exception if not found, but a non existing survey name will
# be trapped in the above is_survey_required_for_course() method
survey
=
SurveyForm
.
get
(
course_descriptor
.
course_survey_name
)
# course staff do not need to answer survey
has_staff_access
=
has_access
(
user
,
'staff'
,
course_descriptor
)
if
has_staff_access
:
return
False
# survey is required and it exists, let's see if user has answered the survey
# course staff do not need to answer survey
survey
=
SurveyForm
.
get
(
course_descriptor
.
course_survey_name
)
answered_survey
=
SurveyAnswer
.
do_survey_answers_exist
(
survey
,
user
)
return
not
answered_survey
and
not
has_staff_access
if
not
answered_survey
:
return
True
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