Commit 31af5249 by Clinton Blackburn

Restructured Project

parent ac9be333
[run] [run]
data_file = ../.coverage data_file = .coverage
omit = analytics_dashboard/settings* omit = analytics_dashboard/settings*
*wsgi.py *wsgi.py
...@@ -39,7 +39,7 @@ Thumbs.db ...@@ -39,7 +39,7 @@ Thumbs.db
Desktop.ini Desktop.ini
.idea/ .idea/
analytics_dashboard/assets/ assets/
node_modules/ node_modules/
analytics_dashboard/static/bower_components/ analytics_dashboard/static/bower_components/
analytics_dashboard/static/dist/ analytics_dashboard/static/dist/
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
ROOT = $(shell echo "$$PWD") ROOT = $(shell echo "$$PWD")
COVERAGE = $(ROOT)/build/coverage COVERAGE = $(ROOT)/build/coverage
PACKAGES = analytics_dashboard courses django_rjs help
NUM_PROCESSES = 2 NUM_PROCESSES = 2
NODE_BIN=./node_modules/.bin NODE_BIN=./node_modules/.bin
...@@ -28,19 +27,17 @@ test.acceptance: develop ...@@ -28,19 +27,17 @@ test.acceptance: develop
pip install -q -r edx-analytics-data-api/requirements/base.txt pip install -q -r edx-analytics-data-api/requirements/base.txt
migrate: migrate:
cd analytics_dashboard && ./manage.py migrate python manage.py migrate
clean: clean:
find . -name '*.pyc' -delete find . -name '*.pyc' -delete
coverage erase coverage erase
test_python: clean test_python: clean
cd analytics_dashboard && ./manage.py test --settings=analytics_dashboard.settings.test --with-ignore-docstrings \ python manage.py test analytics_dashboard --settings=analytics_dashboard.settings.test --with-coverage \
--exclude-dir=analytics_dashboard/settings --exclude-dir=analytics_dashboard/migrations --with-coverage \ --cover-package=analytics_dashboard --exclude-dir=analytics_dashboard/settings --exclude-dir=analytics_dashboard/migrations \
--cover-inclusive --cover-branches --cover-html --cover-html-dir=$(COVERAGE)/html/ \ --cover-branches --cover-html --cover-html-dir=$(COVERAGE)/html/ --with-ignore-docstrings \
--cover-xml --cover-xml-file=$(COVERAGE)/coverage.xml \ --cover-xml --cover-xml-file=$(COVERAGE)/coverage.xml --exclude=core/admin
$(foreach package,$(PACKAGES),--cover-package=$(package)) \
$(PACKAGES)
accept: accept:
nosetests -v acceptance_tests --processes=$(NUM_PROCESSES) --process-timeout=120 --exclude-dir=acceptance_tests/course_validation nosetests -v acceptance_tests --processes=$(NUM_PROCESSES) --process-timeout=120 --exclude-dir=acceptance_tests/course_validation
...@@ -49,11 +46,8 @@ course_validation: ...@@ -49,11 +46,8 @@ course_validation:
python -m acceptance_tests.course_validation.generate_report python -m acceptance_tests.course_validation.generate_report
quality: quality:
pep8 analytics_dashboard pep8 acceptance_tests analytics_dashboard
cd analytics_dashboard && pylint --rcfile=../pylintrc $(PACKAGES) PYTHONPATH=".:./analytics_dashboard:$PYTHONPATH" pylint --rcfile=pylintrc acceptance_tests analytics_dashboard
# Ignore module level docstrings and all test files
#cd analytics_dashboard && pep257 --ignore=D100,D203 --match='(?!test).*py' $(PACKAGES)
validate_python: test.requirements test_python quality validate_python: test.requirements test_python quality
...@@ -65,14 +59,14 @@ validate_js: requirements.js ...@@ -65,14 +59,14 @@ validate_js: requirements.js
validate: validate_python validate_js validate: validate_python validate_js
demo: demo:
cd analytics_dashboard && ./manage.py switch show_engagement_forum_activity on --create python manage.py switch show_engagement_forum_activity on --create
cd analytics_dashboard && ./manage.py switch display_verified_enrollment on --create python manage.py switch display_verified_enrollment on --create
compile_translations: compile_translations:
cd analytics_dashboard && i18n_tool generate -v cd analytics_dashboard && i18n_tool generate -v
extract_translations: extract_translations:
cd analytics_dashboard && DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} PYTHONPATH=".:$PYTHONPATH" i18n_tool extract -v cd analytics_dashboard && i18n_tool extract -v
dummy_translations: dummy_translations:
cd analytics_dashboard && i18n_tool dummy -v cd analytics_dashboard && i18n_tool dummy -v
...@@ -86,5 +80,5 @@ update_translations: pull_translations generate_fake_translations ...@@ -86,5 +80,5 @@ update_translations: pull_translations generate_fake_translations
static: static:
$(NODE_BIN)/r.js -o build.js $(NODE_BIN)/r.js -o build.js
cd analytics_dashboard && ./manage.py collectstatic --noinput python manage.py collectstatic --noinput
cd analytics_dashboard && ./manage.py compress python manage.py compress
...@@ -39,7 +39,8 @@ if ENABLE_OAUTH_TESTS and not (LMS_HOSTNAME and LMS_USERNAME and LMS_PASSWORD): ...@@ -39,7 +39,8 @@ if ENABLE_OAUTH_TESTS and not (LMS_HOSTNAME and LMS_USERNAME and LMS_PASSWORD):
TEST_COURSE_ID = os.environ.get('TEST_COURSE_ID', u'edX/DemoX/Demo_Course') TEST_COURSE_ID = os.environ.get('TEST_COURSE_ID', u'edX/DemoX/Demo_Course')
TEST_PROBLEM_ID = os.environ.get('TEST_PROBLEM_ID', u'i4x://edX/DemoX.1/problem/05d289c5ad3d47d48a77622c4a81ec36') TEST_PROBLEM_ID = os.environ.get('TEST_PROBLEM_ID', u'i4x://edX/DemoX.1/problem/05d289c5ad3d47d48a77622c4a81ec36')
TEST_PROBLEM_PART_ID = os.environ.get('TEST_PROBLEM_PART_ID', u'i4x-edX-DemoX_1-problem-05c289c5ad3d47d48a77622c4a81ec33_2_1') TEST_PROBLEM_PART_ID = os.environ.get('TEST_PROBLEM_PART_ID',
u'i4x-edX-DemoX_1-problem-05c289c5ad3d47d48a77622c4a81ec33_2_1')
DOC_BASE_URL = os.environ.get('DOC_BASE_URL', 'http://edx-insights.readthedocs.org/en/latest') DOC_BASE_URL = os.environ.get('DOC_BASE_URL', 'http://edx-insights.readthedocs.org/en/latest')
......
...@@ -30,7 +30,7 @@ class CourseReporter(object): ...@@ -30,7 +30,7 @@ class CourseReporter(object):
try: try:
self.course.enrollment() self.course.enrollment()
return True return True
except ClientError as e: except ClientError:
return False return False
def has_enrollment_geography(self): def has_enrollment_geography(self):
......
...@@ -30,11 +30,10 @@ from acceptance_tests.course_validation.course_reporter import CourseReporter, A ...@@ -30,11 +30,10 @@ from acceptance_tests.course_validation.course_reporter import CourseReporter, A
NUM_PROCESSES = 8 NUM_PROCESSES = 8
TIMESTAMP = datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M-%S') TIMESTAMP = datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M-%S')
logger = logging.getLogger(__name__)
def _setup_logging(): def _setup_logging():
global logger, fh, ch, formatter
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
# Log all debug and higher to files # Log all debug and higher to files
...@@ -171,14 +170,14 @@ def main(): ...@@ -171,14 +170,14 @@ def main():
try: try:
p = Pool(NUM_PROCESSES, pool_init, [reports, api_client, http_client.cookies]) p = Pool(NUM_PROCESSES, pool_init, [reports, api_client, http_client.cookies])
p.map(check_course, courses) p.map(check_course, courses)
except Exception as e: except Exception as e: # pylint: disable=broad-except
logger.error('Validation failed to finish: %s', e) logger.error('Validation failed to finish: %s', e)
# Write the data to an external file # Write the data to an external file
write_csv(reports) write_csv(reports)
end = time.time() end = time.time()
logger.info('Finished in {} seconds.'.format(end - start)) logger.info('Finished in %d seconds.', end - start)
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -9,7 +9,7 @@ import edx_api_client ...@@ -9,7 +9,7 @@ import edx_api_client
from acceptance_tests import API_SERVER_URL, API_AUTH_TOKEN, DASHBOARD_FEEDBACK_EMAIL, SUPPORT_URL, LMS_USERNAME, \ from acceptance_tests import API_SERVER_URL, API_AUTH_TOKEN, DASHBOARD_FEEDBACK_EMAIL, SUPPORT_URL, LMS_USERNAME, \
LMS_PASSWORD, DASHBOARD_SERVER_URL, ENABLE_AUTO_AUTH, DOC_BASE_URL, COURSE_API_URL, COURSE_API_KEY, \ LMS_PASSWORD, DASHBOARD_SERVER_URL, ENABLE_AUTO_AUTH, DOC_BASE_URL, COURSE_API_URL, COURSE_API_KEY, \
ENABLE_COURSE_API ENABLE_COURSE_API
from pages import LMSLoginPage from acceptance_tests.pages import LMSLoginPage
MAX_SUMMARY_POINT_VALUE_LENGTH = 13 MAX_SUMMARY_POINT_VALUE_LENGTH = 13
...@@ -371,7 +371,7 @@ class CourseDemographicsPageTestsMixin(CoursePageTestsMixin): ...@@ -371,7 +371,7 @@ class CourseDemographicsPageTestsMixin(CoursePageTestsMixin):
self._test_table_row(self.demographic_data[i], columns, sum_count) self._test_table_row(self.demographic_data[i], columns, sum_count)
def _test_table_row(self, datum, column, sum_count): def _test_table_row(self, datum, column, sum_count):
return NotImplementedError raise NotImplementedError
def _test_data_information_message(self): def _test_data_information_message(self):
element = self.page.q(css='div.data-information-message') element = self.page.q(css='div.data-information-message')
......
...@@ -9,6 +9,7 @@ from acceptance_tests import DASHBOARD_SERVER_URL, BASIC_AUTH_PASSWORD, BASIC_AU ...@@ -9,6 +9,7 @@ from acceptance_tests import DASHBOARD_SERVER_URL, BASIC_AUTH_PASSWORD, BASIC_AU
TEST_COURSE_ID, TEST_PROBLEM_ID, TEST_PROBLEM_PART_ID TEST_COURSE_ID, TEST_PROBLEM_ID, TEST_PROBLEM_PART_ID
# pylint: disable=abstract-method
class DashboardPage(PageObject): class DashboardPage(PageObject):
path = None path = None
basic_auth_username = None basic_auth_username = None
......
from unittest import skipUnless from unittest import skipUnless
from unittest import skip
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
from acceptance_tests import ENABLE_OAUTH_TESTS from acceptance_tests import ENABLE_OAUTH_TESTS
from acceptance_tests.mixins import LoginMixin
from acceptance_tests.pages import LoginPage
from mixins import LoginMixin
from pages import LoginPage
from unittest import skip
@skipUnless(ENABLE_OAUTH_TESTS, 'OAuth tests are not enabled.') @skipUnless(ENABLE_OAUTH_TESTS, 'OAuth tests are not enabled.')
class OAuth2FlowTests(LoginMixin, WebAppTest): class OAuth2FlowTests(LoginMixin, WebAppTest):
......
...@@ -7,6 +7,7 @@ from acceptance_tests.pages import CourseHomePage ...@@ -7,6 +7,7 @@ from acceptance_tests.pages import CourseHomePage
_multiprocess_can_split_ = True _multiprocess_can_split_ = True
# pylint: disable=abstract-method
class CourseHomeTests(CoursePageTestsMixin, WebAppTest): class CourseHomeTests(CoursePageTestsMixin, WebAppTest):
def setUp(self): def setUp(self):
super(CourseHomeTests, self).setUp() super(CourseHomeTests, self).setUp()
...@@ -25,7 +26,7 @@ class CourseHomeTests(CoursePageTestsMixin, WebAppTest): ...@@ -25,7 +26,7 @@ class CourseHomeTests(CoursePageTestsMixin, WebAppTest):
""" """
Generates a URL path from the specified view name. Generates a URL path from the specified view name.
""" """
return '/' + view.replace('_', '/')\ return '/' + view.replace('_', '/') \
.replace('courses:', 'courses/{}/'.format(self.page.course_id)) \ .replace('courses:', 'courses/{}/'.format(self.page.course_id)) \
.replace(':', '/') + '/' .replace(':', '/') + '/'
......
...@@ -2,8 +2,8 @@ import datetime ...@@ -2,8 +2,8 @@ import datetime
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
from analyticsclient.constants import activity_type as at from analyticsclient.constants import activity_type as at
from acceptance_tests import ENABLE_FORUM_POSTS
from acceptance_tests import ENABLE_FORUM_POSTS
from acceptance_tests.mixins import CoursePageTestsMixin from acceptance_tests.mixins import CoursePageTestsMixin
from acceptance_tests.pages import CourseEngagementContentPage from acceptance_tests.pages import CourseEngagementContentPage
...@@ -48,7 +48,8 @@ class CourseEngagementTests(CoursePageTestsMixin, WebAppTest): ...@@ -48,7 +48,8 @@ class CourseEngagementTests(CoursePageTestsMixin, WebAppTest):
activity_types = [at.ANY, at.ATTEMPTED_PROBLEM, at.PLAYED_VIDEO] activity_types = [at.ANY, at.ATTEMPTED_PROBLEM, at.PLAYED_VIDEO]
expected_tooltips = { expected_tooltips = {
at.ANY: u'Students who visited at least one page in the course content.', at.ANY: u'Students who visited at least one page in the course content.',
at.ATTEMPTED_PROBLEM: u'Students who submitted an answer for a standard problem. Not all problem types are included.', at.ATTEMPTED_PROBLEM: u'Students who submitted an answer for a standard problem. '
u'Not all problem types are included.',
at.PLAYED_VIDEO: u'Students who played one or more videos.' at.PLAYED_VIDEO: u'Students who played one or more videos.'
} }
for activity_type in activity_types: for activity_type in activity_types:
......
import datetime import datetime
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
from analyticsclient.constants import demographic, UNKNOWN_COUNTRY_CODE, enrollment_modes from analyticsclient.constants import demographic, UNKNOWN_COUNTRY_CODE, enrollment_modes
from acceptance_tests import ENABLE_ENROLLMENT_MODES from acceptance_tests import ENABLE_ENROLLMENT_MODES
from acceptance_tests.mixins import CoursePageTestsMixin from acceptance_tests.mixins import CoursePageTestsMixin
from acceptance_tests.pages import CourseEnrollmentActivityPage, CourseEnrollmentGeographyPage from acceptance_tests.pages import CourseEnrollmentActivityPage, CourseEnrollmentGeographyPage
...@@ -25,9 +25,9 @@ class CourseEnrollmentActivityTests(CoursePageTestsMixin, WebAppTest): ...@@ -25,9 +25,9 @@ class CourseEnrollmentActivityTests(CoursePageTestsMixin, WebAppTest):
""" """
end_date = datetime.datetime.utcnow() end_date = datetime.datetime.utcnow()
end_date_string = end_date.strftime(self.analytics_api_client.DATE_FORMAT) end_date_string = end_date.strftime(self.analytics_api_client.DATE_FORMAT)
demographic = 'mode' if ENABLE_ENROLLMENT_MODES else None _demographic = 'mode' if ENABLE_ENROLLMENT_MODES else None
return self.course.enrollment(demographic, start_date=None, end_date=end_date_string) return self.course.enrollment(_demographic, start_date=None, end_date=end_date_string)
def test_page(self): def test_page(self):
super(CourseEnrollmentActivityTests, self).test_page() super(CourseEnrollmentActivityTests, self).test_page()
...@@ -73,7 +73,8 @@ class CourseEnrollmentActivityTests(CoursePageTestsMixin, WebAppTest): ...@@ -73,7 +73,8 @@ class CourseEnrollmentActivityTests(CoursePageTestsMixin, WebAppTest):
# Verify the total enrollment change metric tile. # Verify the total enrollment change metric tile.
i = 7 i = 7
enrollment = enrollment - enrollment_data[-(i + 1)]['count'] enrollment = enrollment - enrollment_data[-(i + 1)]['count']
tooltip = u'The difference between the number of students enrolled at the end of the day yesterday and one week before.' tooltip = u'The difference between the number of students enrolled at the end of the day ' \
u'yesterday and one week before.'
self.assertMetricTileValid('enrollment_change_last_%s_days' % i, enrollment, tooltip) self.assertMetricTileValid('enrollment_change_last_%s_days' % i, enrollment, tooltip)
if ENABLE_ENROLLMENT_MODES: if ENABLE_ENROLLMENT_MODES:
...@@ -86,7 +87,8 @@ class CourseEnrollmentActivityTests(CoursePageTestsMixin, WebAppTest): ...@@ -86,7 +87,8 @@ class CourseEnrollmentActivityTests(CoursePageTestsMixin, WebAppTest):
self.assertMetricTileValid('verified_enrollment', verified_enrollment, tooltip) self.assertMetricTileValid('verified_enrollment', verified_enrollment, tooltip)
verified_enrollment = verified_enrollment - enrollment_data[-(i + 1)][enrollment_modes.VERIFIED] verified_enrollment = verified_enrollment - enrollment_data[-(i + 1)][enrollment_modes.VERIFIED]
tooltip = u'The difference between the number of students pursuing verified certificates at the end of the day yesterday and one week before.' tooltip = u'The difference between the number of students pursuing verified certificates at the ' \
u'end of the day yesterday and one week before.'
self.assertMetricTileValid('verified_change_last_%s_days' % i, verified_enrollment, tooltip) self.assertMetricTileValid('verified_change_last_%s_days' % i, verified_enrollment, tooltip)
# Verify *something* rendered where the graph should be. We cannot easily verify what rendered # Verify *something* rendered where the graph should be. We cannot easily verify what rendered
......
import datetime import datetime
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
import analyticsclient.constants.education_level as EDUCATION_LEVEL import analyticsclient.constants.education_level as EDUCATION_LEVEL
from analyticsclient.constants import demographic from analyticsclient.constants import demographic
import analyticsclient.constants.gender as GENDER import analyticsclient.constants.gender as GENDER
from acceptance_tests.mixins import CourseDemographicsPageTestsMixin from acceptance_tests.mixins import CourseDemographicsPageTestsMixin
from acceptance_tests.pages import CourseEnrollmentDemographicsAgePage, CourseEnrollmentDemographicsEducationPage, \ from acceptance_tests.pages import CourseEnrollmentDemographicsAgePage, CourseEnrollmentDemographicsEducationPage, \
CourseEnrollmentDemographicsGenderPage CourseEnrollmentDemographicsGenderPage
...@@ -148,7 +148,6 @@ class CourseEnrollmentDemographicsGenderTests(CourseDemographicsPageTestsMixin, ...@@ -148,7 +148,6 @@ class CourseEnrollmentDemographicsGenderTests(CourseDemographicsPageTestsMixin,
class CourseEnrollmentDemographicsEducationTests(CourseDemographicsPageTestsMixin, WebAppTest): class CourseEnrollmentDemographicsEducationTests(CourseDemographicsPageTestsMixin, WebAppTest):
EDUCATION_NAMES = { EDUCATION_NAMES = {
EDUCATION_LEVEL.NONE: 'None', EDUCATION_LEVEL.NONE: 'None',
EDUCATION_LEVEL.OTHER: 'Other', EDUCATION_LEVEL.OTHER: 'Other',
...@@ -186,17 +185,21 @@ class CourseEnrollmentDemographicsEducationTests(CourseDemographicsPageTestsMixi ...@@ -186,17 +185,21 @@ class CourseEnrollmentDemographicsEducationTests(CourseDemographicsPageTestsMixi
{ {
'levels': ['primary', 'junior_secondary', 'secondary'], 'levels': ['primary', 'junior_secondary', 'secondary'],
'stat_type': 'education_high_school_or_less_enrollment', 'stat_type': 'education_high_school_or_less_enrollment',
'tooltip': 'The percentage of students who selected Secondary/high school, Junior secondary/junior high/middle school, or Elementary/primary school as their highest level of education completed.' 'tooltip': 'The percentage of students who selected Secondary/high school, Junior secondary/junior '
'high/middle school, or Elementary/primary school as their highest level of '
'education completed.'
}, },
{ {
'levels': ['associates', 'bachelors'], 'levels': ['associates', 'bachelors'],
'stat_type': 'education_college_enrollment', 'stat_type': 'education_college_enrollment',
'tooltip': "The percentage of students who selected Bachelor's degree or Associate degree as their highest level of education completed." 'tooltip': "The percentage of students who selected Bachelor's degree or Associate degree as their "
"highest level of education completed."
}, },
{ {
'levels': ['masters', 'doctorate'], 'levels': ['masters', 'doctorate'],
'stat_type': 'education_advanced_enrollment', 'stat_type': 'education_advanced_enrollment',
'tooltip': "The percentage of students who selected Doctorate or Master's or professional degree as their highest level of education completed." 'tooltip': "The percentage of students who selected Doctorate or Master's or professional degree as "
"their highest level of education completed."
} }
] ]
......
...@@ -2,7 +2,7 @@ from bok_choy.web_app_test import WebAppTest ...@@ -2,7 +2,7 @@ from bok_choy.web_app_test import WebAppTest
from acceptance_tests import TEST_COURSE_ID from acceptance_tests import TEST_COURSE_ID
from acceptance_tests.mixins import AnalyticsDashboardWebAppTestMixin from acceptance_tests.mixins import AnalyticsDashboardWebAppTestMixin
from pages import CourseIndexPage from acceptance_tests.pages import CourseIndexPage
_multiprocess_can_split_ = True _multiprocess_can_split_ = True
......
...@@ -10,7 +10,6 @@ _multiprocess_can_split_ = True ...@@ -10,7 +10,6 @@ _multiprocess_can_split_ = True
class CoursePerformanceAnswerDistributionTests(CoursePageTestsMixin, WebAppTest): class CoursePerformanceAnswerDistributionTests(CoursePageTestsMixin, WebAppTest):
help_path = 'performance/index.html' help_path = 'performance/index.html'
def setUp(self): def setUp(self):
...@@ -21,7 +20,6 @@ class CoursePerformanceAnswerDistributionTests(CoursePageTestsMixin, WebAppTest) ...@@ -21,7 +20,6 @@ class CoursePerformanceAnswerDistributionTests(CoursePageTestsMixin, WebAppTest)
data = [i for i in api_response if i['part_id'] == self.page.part_id] data = [i for i in api_response if i['part_id'] == self.page.part_id]
self.answer_distribution = sorted(data, key=lambda a: a['count'], reverse=True) self.answer_distribution = sorted(data, key=lambda a: a['count'], reverse=True)
def _get_data_update_message(self): def _get_data_update_message(self):
current_data = self.answer_distribution[0] current_data = self.answer_distribution[0]
last_updated = datetime.datetime.strptime(current_data['created'], self.api_datetime_format) last_updated = datetime.datetime.strptime(current_data['created'], self.api_datetime_format)
......
from unittest import skipUnless from unittest import skipUnless
from bok_choy.web_app_test import WebAppTest from bok_choy.web_app_test import WebAppTest
from acceptance_tests import PLATFORM_NAME, APPLICATION_NAME, SUPPORT_URL, ENABLE_ERROR_PAGE_TESTS from acceptance_tests import PLATFORM_NAME, APPLICATION_NAME, SUPPORT_URL, ENABLE_ERROR_PAGE_TESTS
from pages import ServerErrorPage, NotFoundErrorPage, AccessDeniedErrorPage, AuthErrorPage from acceptance_tests.pages import ServerErrorPage, NotFoundErrorPage, AccessDeniedErrorPage, AuthErrorPage
@skipUnless(ENABLE_ERROR_PAGE_TESTS, 'Error page tests are not enabled.') @skipUnless(ENABLE_ERROR_PAGE_TESTS, 'Error page tests are not enabled.')
......
...@@ -2,7 +2,7 @@ from bok_choy.web_app_test import WebAppTest ...@@ -2,7 +2,7 @@ from bok_choy.web_app_test import WebAppTest
from acceptance_tests.mixins import LoginMixin, LogoutMixin, FooterLegalMixin, PageTestMixin from acceptance_tests.mixins import LoginMixin, LogoutMixin, FooterLegalMixin, PageTestMixin
from acceptance_tests import OPEN_SOURCE_URL, RESEARCH_URL, SUPPORT_URL, SHOW_LANDING_RESEARCH from acceptance_tests import OPEN_SOURCE_URL, RESEARCH_URL, SUPPORT_URL, SHOW_LANDING_RESEARCH
from pages import LandingPage from acceptance_tests.pages import LandingPage
_multiprocess_can_split_ = False _multiprocess_can_split_ = False
......
default_app_config = 'core.apps.AnalyticsDashboardConfig'
default_app_config = 'analytics_dashboard.apps.AnalyticsDashboardConfig'
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin
from analytics_dashboard.models import User
from core.models import User
class AnalyticsDashboardUserAdmin(UserAdmin): class AnalyticsDashboardUserAdmin(UserAdmin):
......
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from analytics_dashboard.utils import delete_auto_auth_users
from core.utils import delete_auto_auth_users
User = get_user_model() User = get_user_model()
......
...@@ -32,6 +32,7 @@ class Migration(migrations.Migration): ...@@ -32,6 +32,7 @@ class Migration(migrations.Migration):
('user_permissions', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions')), ('user_permissions', models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions')),
], ],
options={ options={
'db_table': 'analytics_dashboard_user',
'get_latest_by': 'date_joined', 'get_latest_by': 'date_joined',
}, },
bases=(models.Model,), bases=(models.Model,),
......
...@@ -15,3 +15,4 @@ class User(AbstractUser): ...@@ -15,3 +15,4 @@ class User(AbstractUser):
class Meta(object): class Meta(object):
get_latest_by = 'date_joined' get_latest_by = 'date_joined'
db_table = 'analytics_dashboard_user' # Legacy table name
...@@ -10,7 +10,7 @@ DUMMY_AUTHORIZED_COURSE = 'dummy/course/id' ...@@ -10,7 +10,7 @@ DUMMY_AUTHORIZED_COURSE = 'dummy/course/id'
class EdXOpenIdConnectTests(OpenIdConnectTestMixin, OAuth2Test): class EdXOpenIdConnectTests(OpenIdConnectTestMixin, OAuth2Test):
backend_path = 'analytics_dashboard.backends.EdXOpenIdConnect' backend_path = 'core.backends.EdXOpenIdConnect'
issuer = settings.SOCIAL_AUTH_EDX_OIDC_URL_ROOT issuer = settings.SOCIAL_AUTH_EDX_OIDC_URL_ROOT
expected_username = 'test_user' expected_username = 'test_user'
......
from django.test import TestCase from django.test import TestCase
from django_dynamic_fixture import G from django_dynamic_fixture import G
from lang_pref_middleware.tests import LangPrefMiddlewareTestCaseMixin from lang_pref_middleware.tests import LangPrefMiddlewareTestCaseMixin
from analytics_dashboard.middleware import LanguagePreferenceMiddleware
from analytics_dashboard.models import User from core.middleware import LanguagePreferenceMiddleware
from core.models import User
class TestUserLanguagePreferenceMiddleware(LangPrefMiddlewareTestCaseMixin, TestCase): class TestUserLanguagePreferenceMiddleware(LangPrefMiddlewareTestCaseMixin, TestCase):
......
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.test import TestCase from django.test import TestCase
from django_dynamic_fixture import G from django_dynamic_fixture import G
from analytics_dashboard.pipeline import get_user_if_exists
from core.pipeline import get_user_if_exists
User = get_user_model() User = get_user_model()
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
from django.template import Template, Context, TemplateSyntaxError from django.template import Template, Context, TemplateSyntaxError
from django.test import TestCase from django.test import TestCase
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from analytics_dashboard.templatetags.dashboard_extras import format_course_key
from analytics_dashboard.templatetags import dashboard_extras from core.templatetags.dashboard_extras import format_course_key
from core.templatetags import dashboard_extras
class DashboardExtraTests(TestCase): class DashboardExtraTests(TestCase):
......
...@@ -5,7 +5,7 @@ from django.contrib.auth import get_user_model ...@@ -5,7 +5,7 @@ from django.contrib.auth import get_user_model
from django.test.utils import override_settings from django.test.utils import override_settings
from django.test import TestCase from django.test import TestCase
from analytics_dashboard.utils import delete_auto_auth_users from core.utils import delete_auto_auth_users
User = get_user_model() User = get_user_model()
......
...@@ -17,7 +17,7 @@ from analyticsclient.exceptions import ClientError ...@@ -17,7 +17,7 @@ from analyticsclient.exceptions import ClientError
from social.exceptions import AuthException from social.exceptions import AuthException
from social.utils import parse_qs from social.utils import parse_qs
from analytics_dashboard.backends import EdXOpenIdConnect from core.backends import EdXOpenIdConnect
from courses.permissions import set_user_course_permissions, user_can_view_course, get_user_course_permissions from courses.permissions import set_user_course_permissions, user_can_view_course, get_user_course_permissions
......
...@@ -10,10 +10,10 @@ from django.http import HttpResponse, Http404 ...@@ -10,10 +10,10 @@ from django.http import HttpResponse, Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from django.views.generic import View, TemplateView from django.views.generic import View, TemplateView
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
from analyticsclient.client import Client from analyticsclient.client import Client
from analyticsclient.exceptions import ClientError from analyticsclient.exceptions import ClientError
from courses import permissions
from analytics_dashboard.courses import permissions
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -114,7 +114,7 @@ class LandingView(TemplateView): ...@@ -114,7 +114,7 @@ class LandingView(TemplateView):
""" """
Displays a public landing page when users first come to the site. Displays a public landing page when users first come to the site.
""" """
template_name = "analytics_dashboard/landing.html" template_name = "core/landing.html"
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
""" Non logged in users will be directed to the landing page. """ """ Non logged in users will be directed to the landing page. """
......
...@@ -7,7 +7,7 @@ from django.dispatch import receiver ...@@ -7,7 +7,7 @@ from django.dispatch import receiver
from social.apps.django_app import load_strategy from social.apps.django_app import load_strategy
from analytics_dashboard.backends import EdXOpenIdConnect from core.backends import EdXOpenIdConnect
from courses.exceptions import UserNotAssociatedWithBackendError, InvalidAccessTokenError, \ from courses.exceptions import UserNotAssociatedWithBackendError, InvalidAccessTokenError, \
PermissionsRetrievalFailedError PermissionsRetrievalFailedError
......
...@@ -90,7 +90,7 @@ class PermissionsTests(TestCase): ...@@ -90,7 +90,7 @@ class PermissionsTests(TestCase):
mock_refresh.assert_called_with(self.user) mock_refresh.assert_called_with(self.user)
self.assertFalse(permissions.user_can_view_course(self.user, self.course_id)) self.assertFalse(permissions.user_can_view_course(self.user, self.course_id))
@mock.patch('analytics_dashboard.backends.EdXOpenIdConnect.get_json', @mock.patch('core.backends.EdXOpenIdConnect.get_json',
mock.Mock(return_value={'staff_courses': ['edX/DemoX/Demo_Course']})) mock.Mock(return_value={'staff_courses': ['edX/DemoX/Demo_Course']}))
def test_refresh_user_course_permissions(self): def test_refresh_user_course_permissions(self):
""" """
...@@ -129,7 +129,7 @@ class PermissionsTests(TestCase): ...@@ -129,7 +129,7 @@ class PermissionsTests(TestCase):
# Sanity check: verify the user can view the course # Sanity check: verify the user can view the course
self.assertTrue(permissions.user_can_view_course(self.user, self.course_id)) self.assertTrue(permissions.user_can_view_course(self.user, self.course_id))
@mock.patch('analytics_dashboard.backends.EdXOpenIdConnect.get_json', mock.Mock(return_value={})) @mock.patch('core.backends.EdXOpenIdConnect.get_json', mock.Mock(return_value={}))
def test_refresh_user_course_permissions_with_missing_permissions(self): def test_refresh_user_course_permissions_with_missing_permissions(self):
""" """
If the authorization backend fails to return course permission data, a warning should be logged and the users If the authorization backend fails to return course permission data, a warning should be logged and the users
...@@ -175,5 +175,5 @@ class PermissionsTests(TestCase): ...@@ -175,5 +175,5 @@ class PermissionsTests(TestCase):
# Raise a PermissionsError if the backend is unavailable. # Raise a PermissionsError if the backend is unavailable.
G(UserSocialAuth, user=self.user, provider='edx-oidc', extra_data={'access_token': '1234'}) G(UserSocialAuth, user=self.user, provider='edx-oidc', extra_data={'access_token': '1234'})
with mock.patch('analytics_dashboard.backends.EdXOpenIdConnect.get_json', side_effect=Exception): with mock.patch('core.backends.EdXOpenIdConnect.get_json', side_effect=Exception):
self.assertRaises(PermissionsRetrievalFailedError, permissions.get_user_course_permissions, self.user) self.assertRaises(PermissionsRetrievalFailedError, permissions.get_user_course_permissions, self.user)
...@@ -10,7 +10,7 @@ from django.conf import settings ...@@ -10,7 +10,7 @@ from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from analyticsclient.exceptions import NotFoundError from analyticsclient.exceptions import NotFoundError
from analytics_dashboard.tests.test_views import RedirectTestCaseMixin, UserTestCaseMixin from core.tests.test_views import RedirectTestCaseMixin, UserTestCaseMixin
from courses.permissions import set_user_course_permissions, revoke_user_course_permissions from courses.permissions import set_user_course_permissions, revoke_user_course_permissions
from courses.tests import SwitchMixin from courses.tests import SwitchMixin
from courses.tests.utils import set_empty_permissions, get_mock_api_enrollment_data, mock_course_name from courses.tests.utils import set_empty_permissions, get_mock_api_enrollment_data, mock_course_name
......
...@@ -64,7 +64,7 @@ TIME_ZONE = 'America/New_York' ...@@ -64,7 +64,7 @@ TIME_ZONE = 'America/New_York'
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'en-us'
LOCALE_PATHS = ( LOCALE_PATHS = (
join(SITE_ROOT, 'conf', 'locale'), join(DJANGO_ROOT, 'conf', 'locale'),
) )
# See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id # See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
...@@ -79,13 +79,13 @@ USE_L10N = True ...@@ -79,13 +79,13 @@ USE_L10N = True
# See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz # See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
USE_TZ = True USE_TZ = True
FORMAT_MODULE_PATH = 'analytics_dashboard.formats' FORMAT_MODULE_PATH = 'core.formats'
########## END GENERAL CONFIGURATION ########## END GENERAL CONFIGURATION
########## MEDIA CONFIGURATION ########## MEDIA CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
MEDIA_ROOT = normpath(join(SITE_ROOT, 'media')) MEDIA_ROOT = normpath(join(DJANGO_ROOT, 'media'))
# See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url
MEDIA_URL = '/media/' MEDIA_URL = '/media/'
...@@ -101,7 +101,7 @@ STATIC_URL = '/static/' ...@@ -101,7 +101,7 @@ STATIC_URL = '/static/'
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS
STATICFILES_DIRS = ( STATICFILES_DIRS = (
normpath(join(SITE_ROOT, 'static')), normpath(join(DJANGO_ROOT, 'static')),
) )
# See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders
...@@ -136,7 +136,7 @@ ALLOWED_HOSTS = [] ...@@ -136,7 +136,7 @@ ALLOWED_HOSTS = []
########## FIXTURE CONFIGURATION ########## FIXTURE CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS # See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS
FIXTURE_DIRS = ( FIXTURE_DIRS = (
normpath(join(SITE_ROOT, 'fixtures')), normpath(join(DJANGO_ROOT, 'fixtures')),
) )
########## END FIXTURE CONFIGURATION ########## END FIXTURE CONFIGURATION
...@@ -152,7 +152,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( ...@@ -152,7 +152,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.tz', 'django.core.context_processors.tz',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
'django.core.context_processors.request', 'django.core.context_processors.request',
'analytics_dashboard.context_processors.common', 'core.context_processors.common',
) )
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders
...@@ -163,11 +163,11 @@ TEMPLATE_LOADERS = ( ...@@ -163,11 +163,11 @@ TEMPLATE_LOADERS = (
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
TEMPLATE_DIRS = ( TEMPLATE_DIRS = (
normpath(join(SITE_ROOT, 'templates')), normpath(join(DJANGO_ROOT, 'templates')),
) )
ALLOWED_INCLUDE_ROOTS = ( ALLOWED_INCLUDE_ROOTS = (
normpath(join(SITE_ROOT, 'templates')), normpath(join(DJANGO_ROOT, 'templates')),
) )
########## END TEMPLATE CONFIGURATION ########## END TEMPLATE CONFIGURATION
...@@ -184,7 +184,7 @@ MIDDLEWARE_CLASSES = ( ...@@ -184,7 +184,7 @@ MIDDLEWARE_CLASSES = (
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'waffle.middleware.WaffleMiddleware', 'waffle.middleware.WaffleMiddleware',
'analytics_dashboard.middleware.LanguagePreferenceMiddleware', 'core.middleware.LanguagePreferenceMiddleware',
'courses.middleware.CourseMiddleware', 'courses.middleware.CourseMiddleware',
'courses.middleware.CoursePermissionsExceptionMiddleware', 'courses.middleware.CoursePermissionsExceptionMiddleware',
'social.apps.django_app.middleware.SocialAuthExceptionMiddleware', 'social.apps.django_app.middleware.SocialAuthExceptionMiddleware',
...@@ -222,7 +222,7 @@ DJANGO_APPS = ( ...@@ -222,7 +222,7 @@ DJANGO_APPS = (
# Apps specific for this project go here. # Apps specific for this project go here.
LOCAL_APPS = ( LOCAL_APPS = (
'analytics_dashboard', 'core',
'courses', 'courses',
'django_rjs', 'django_rjs',
'help', 'help',
...@@ -318,13 +318,13 @@ LMS_COURSE_SHORTCUT_BASE_URL = None ...@@ -318,13 +318,13 @@ LMS_COURSE_SHORTCUT_BASE_URL = None
DATE_FORMAT = 'F d, Y' DATE_FORMAT = 'F d, Y'
########## AUTHENTICATION ########## AUTHENTICATION
AUTH_USER_MODEL = 'analytics_dashboard.User' AUTH_USER_MODEL = 'core.User'
INSTALLED_APPS += ('social.apps.django_app.default',) INSTALLED_APPS += ('social.apps.django_app.default',)
# Allow authentication via edX OAuth2/OpenID Connect # Allow authentication via edX OAuth2/OpenID Connect
AUTHENTICATION_BACKENDS = ( AUTHENTICATION_BACKENDS = (
'analytics_dashboard.backends.EdXOpenIdConnect', 'core.backends.EdXOpenIdConnect',
'django.contrib.auth.backends.ModelBackend', 'django.contrib.auth.backends.ModelBackend',
) )
...@@ -342,7 +342,7 @@ SOCIAL_AUTH_PIPELINE = ( ...@@ -342,7 +342,7 @@ SOCIAL_AUTH_PIPELINE = (
# By default python-social-auth will simply create a new user/username if the username # By default python-social-auth will simply create a new user/username if the username
# from the provider conflicts with an existing username in this system. This custom pipeline function # from the provider conflicts with an existing username in this system. This custom pipeline function
# loads existing users instead of creating new ones. # loads existing users instead of creating new ones.
'analytics_dashboard.pipeline.get_user_if_exists', 'core.pipeline.get_user_if_exists',
'social.pipeline.user.get_username', 'social.pipeline.user.get_username',
'social.pipeline.user.create_user', 'social.pipeline.user.create_user',
'social.pipeline.social_auth.associate_user', 'social.pipeline.social_auth.associate_user',
...@@ -397,7 +397,7 @@ RJS_OPTIMIZATION_ENABLED = False ...@@ -397,7 +397,7 @@ RJS_OPTIMIZATION_ENABLED = False
########## DOCS/HELP CONFIGURATION ########## DOCS/HELP CONFIGURATION
DOCS_ROOT = join(dirname(SITE_ROOT), 'docs') DOCS_ROOT = join(SITE_ROOT, 'docs')
# Load the docs config into memory when the server starts # Load the docs config into memory when the server starts
with open(join(DOCS_ROOT, "config.ini")) as config_file: with open(join(DOCS_ROOT, "config.ini")) as config_file:
......
...@@ -7,13 +7,13 @@ from django.contrib import admin ...@@ -7,13 +7,13 @@ from django.contrib import admin
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
from django.views.generic import RedirectView from django.views.generic import RedirectView
from analytics_dashboard import views from analytics_dashboard.core import views
admin.autodiscover() admin.autodiscover()
js_info_dict = { js_info_dict = {
'packages': ('analytics_dashboard', 'courses',), 'packages': ('core', 'courses',),
} }
urlpatterns = patterns( urlpatterns = patterns(
......
...@@ -17,7 +17,7 @@ import os ...@@ -17,7 +17,7 @@ import os
from os.path import abspath, dirname from os.path import abspath, dirname
from sys import path from sys import path
SITE_ROOT = dirname(dirname(abspath(__file__))) SITE_ROOT = dirname(abspath(__file__))
path.append(SITE_ROOT) path.append(SITE_ROOT)
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks # We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
......
...@@ -30,7 +30,7 @@ echo "Starting Analytics Data API Server..." ...@@ -30,7 +30,7 @@ echo "Starting Analytics Data API Server..."
./edx-analytics-data-api/manage.py runserver 9001 --noreload & ./edx-analytics-data-api/manage.py runserver 9001 --noreload &
echo "Starting Analytics Dashboard Server..." echo "Starting Analytics Dashboard Server..."
./analytics_dashboard/manage.py runserver 9000 --noreload & ./manage.py runserver 9000 --noreload &
echo "Running acceptance tests..." echo "Running acceptance tests..."
make accept -e NUM_PROCESSES=1 make accept -e NUM_PROCESSES=1
......
[pep8] [pep8]
max_line_length=120 max_line_length=120
exclude=settings,migrations,analytics_dashboard/static,bower_components,analytics_dashboard/analytics_dashboard/wsgi.py exclude=settings,migrations,analytics_dashboard/static,bower_components,analytics_dashboard/wsgi.py
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment