Skip to content

Commit

Permalink
Merge branch 'appsembler/amc/ginkgo/unginkgo' into appsembler/amc/fea…
Browse files Browse the repository at this point in the history
…ture/hawthorn_merge.2
  • Loading branch information
OmarIthawi committed Oct 28, 2018
2 parents 7ad437b + b0260ef commit 145ada3
Show file tree
Hide file tree
Showing 117 changed files with 39,459 additions and 89 deletions.
17 changes: 17 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
Appsembler Changes
------------------

As part of separating out our custom features from the main edx-platform branch, we've created our own environment. We now have an "aws_appsembler" setting. It can be used as follows:

e.g. bringing up a shell in production:
```
python manage.py lms --settings=aws_appsembler shell
```

we also have custom paver commands for the devstack:
```
paver devstack_appsembler lms
```



This is the main edX platform which consists of LMS and Studio.


Expand Down
7 changes: 7 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies:
override:
- npm install

- pip install -U pip
- pip install setuptools

# Mirror what paver install_prereqs does.
Expand All @@ -20,7 +21,11 @@ dependencies:
# from scratch again.
- pip install --exists-action w -r requirements/edx/testing.txt

# Try to fix SSL handshake problems when running tests
- pip install ndg-httpsclient==0.3.0 pyasn1

- pip install coveralls==1.0
- pip install codecov requests

# Output the installed python packages to the console to help
# with troubleshooting any issues with python requirements.
Expand All @@ -39,6 +44,7 @@ test:
parallel: true

post:
- paver coverage
- mkdir -p $CIRCLE_TEST_REPORTS/junit
# Copy the junit results up to be consumed by circleci,
# but only do this if there actually are results.
Expand All @@ -55,3 +61,4 @@ test:
# If you have not set up set up coveralls then the following statement will
# print a message but not affect the pass/fail status of the build.
- if [ -z $COVERALLS_REPO_TOKEN ]; then echo "Coveralls token not defined."; else coveralls; fi
- codecov
6 changes: 3 additions & 3 deletions cms/djangoapps/contentstore/api/views/course_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def get(self, request, course_key):
)
if get_bool_param(request, 'certificates', all_requested):
response.update(
certificates=self._certificates_validation(course)
certificates=self._certificates_validation(course, request)
)
if get_bool_param(request, 'updates', all_requested):
response.update(
Expand Down Expand Up @@ -136,8 +136,8 @@ def _grades_validation(self, course):
sum_of_weights=sum_of_weights,
)

def _certificates_validation(self, course):
is_activated, certificates = CertificateManager.is_activated(course)
def _certificates_validation(self, course, request):
is_activated, certificates = CertificateManager.is_activated(request=request, course=course)
return dict(
is_activated=is_activated,
has_certificate=len(certificates) > 0,
Expand Down
1 change: 1 addition & 0 deletions cms/djangoapps/contentstore/courseware_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ class CourseAboutSearchIndexer(object):
AboutInfo("org", AboutInfo.PROPERTY, AboutInfo.FROM_COURSE_PROPERTY),
AboutInfo("modes", AboutInfo.PROPERTY, AboutInfo.FROM_COURSE_MODE),
AboutInfo("language", AboutInfo.PROPERTY, AboutInfo.FROM_COURSE_PROPERTY),
AboutInfo("catalog_visibility", AboutInfo.PROPERTY, AboutInfo.FROM_COURSE_PROPERTY),
]

@classmethod
Expand Down
67 changes: 45 additions & 22 deletions cms/djangoapps/contentstore/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""

import logging
import re
from datetime import datetime

from django.conf import settings
Expand All @@ -14,6 +15,7 @@

from django_comment_common.models import assign_default_role
from django_comment_common.utils import seed_permissions_roles
from openedx.core.djangoapps.appsembler.sites.utils import get_lms_link_from_course_key
from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
from student import auth
from student.models import CourseEnrollment
Expand Down Expand Up @@ -104,48 +106,69 @@ def get_lms_link_for_item(location, preview=False):
"""
assert isinstance(location, UsageKey)

# checks LMS_BASE value in site configuration for the given course_org_filter(org)
# if not found returns settings.LMS_BASE
lms_base = SiteConfiguration.get_value_for_org(
location.org,
"LMS_BASE",
settings.LMS_BASE
)

if lms_base is None:
if settings.LMS_BASE is None:
return None

if preview:
# checks PREVIEW_LMS_BASE value in site configuration for the given course_org_filter(org)
# if not found returns settings.FEATURES.get('PREVIEW_LMS_BASE')
lms_base = SiteConfiguration.get_value_for_org(
location.org,
"PREVIEW_LMS_BASE",
settings.FEATURES.get('PREVIEW_LMS_BASE')
)
lms_base = settings.FEATURES.get('PREVIEW_LMS_BASE')
else:
lms_base = settings.LMS_BASE

return u"//{lms_base}/courses/{course_key}/jump_to/{location}".format(
lms_base=lms_base,
lms_base=get_lms_link_from_course_key(lms_base, location.course_key),
course_key=text_type(location.course_key),
location=text_type(location),
)


def get_lms_link_for_about_page(course_key):
"""
Returns the url to the course about page from the location tuple.
"""

assert isinstance(course_key, CourseKey)

if settings.FEATURES.get('ENABLE_MKTG_SITE', False):
if not hasattr(settings, 'MKTG_URLS'):
log.exception("ENABLE_MKTG_SITE is True, but MKTG_URLS is not defined.")
return None

marketing_urls = settings.MKTG_URLS

# Root will be "https://www.edx.org". The complete URL will still not be exactly correct,
# but redirects exist from www.edx.org to get to the Drupal course about page URL.
about_base = marketing_urls.get('ROOT', None)

if about_base is None:
log.exception('There is no ROOT defined in MKTG_URLS')
return None

# Strip off https:// (or http://) to be consistent with the formatting of LMS_BASE.
about_base = re.sub(r"^https?://", "", about_base)

elif settings.LMS_BASE is not None:
about_base = settings.LMS_BASE
else:
return None

return u"//{about_base_url}/courses/{course_key}/about".format(
about_base_url=get_lms_link_from_course_key(about_base, course_key),
course_key=course_key.to_deprecated_string()
)


# pylint: disable=invalid-name
def get_lms_link_for_certificate_web_view(user_id, course_key, mode):
"""
Returns the url to the certificate web view.
"""
assert isinstance(course_key, CourseKey)

# checks LMS_BASE value in SiteConfiguration against course_org_filter if not found returns settings.LMS_BASE
lms_base = SiteConfiguration.get_value_for_org(course_key.org, "LMS_BASE", settings.LMS_BASE)

if lms_base is None:
if settings.LMS_BASE is None:
return None

return u"//{certificate_web_base}/certificates/user/{user_id}/course/{course_id}?preview={mode}".format(
certificate_web_base=lms_base,
certificate_web_base=get_lms_link_from_course_key(settings.LMS_BASE, course_key),
user_id=user_id,
course_id=unicode(course_key),
mode=mode
Expand Down
14 changes: 11 additions & 3 deletions cms/djangoapps/contentstore/views/certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
from xmodule.modulestore import EdxJSONEncoder
from xmodule.modulestore.django import modulestore

from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers

CERTIFICATE_SCHEMA_VERSION = 1
CERTIFICATE_MINIMUM_ID = 100

Expand Down Expand Up @@ -158,14 +160,20 @@ def validate(certificate_data):
raise CertificateValidationError(_("must have name of the certificate"))

@staticmethod
def is_activated(course):
def is_activated(request, course):
"""
Returns whether certificates are activated for the given course,
along with the certificates.
"""
is_active = False
certificates = None
if settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False):

current_organization = request.user.organizations.first()
if configuration_helpers.get_value_for_org(
current_organization.name,
'CERTIFICATES_HTML_VIEW',
False
):
certificates = CertificateManager.get_certificates(course)
# we are assuming only one certificate in certificates collection.
for certificate in certificates:
Expand Down Expand Up @@ -409,7 +417,7 @@ def certificates_list_handler(request, course_key_string):
else:
certificate_web_view_url = None

is_active, certificates = CertificateManager.is_activated(course)
is_active, certificates = CertificateManager.is_activated(request, course)

return render_to_response('certificates.html', {
'context_course': course,
Expand Down
19 changes: 15 additions & 4 deletions cms/djangoapps/models/settings/course_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class CourseMetadata(object):
]

@classmethod
def filtered_list(cls):
def filtered_list(cls, org=None):
"""
Filter fields based on feature flag, i.e. enabled, disabled.
"""
Expand Down Expand Up @@ -113,6 +113,17 @@ def filtered_list(cls):
if not XBlockStudioConfigurationFlag.is_enabled():
filtered_list.append('allow_unsupported_xblocks')

# Appsembler specific, we don't display the field if the site doesn't
# belong to a MSFT LP
# if org and not get_value_for_org(
# org,
# "CUSTOMER_IS_MICROSOFT_LEARNING_PARTNER",
# settings.APPSEMBLER_FEATURES.get(
# "CUSTOMER_IS_MICROSOFT_LEARNING_PARTNER"
# )
# ):
# filtered_list.append('is_microsoft_course')

return filtered_list

@classmethod
Expand All @@ -124,7 +135,7 @@ def fetch(cls, descriptor):
result = {}
metadata = cls.fetch_all(descriptor)
for key, value in metadata.iteritems():
if key in cls.filtered_list():
if key in cls.filtered_list(org=descriptor.org):
continue
result[key] = value
return result
Expand Down Expand Up @@ -159,7 +170,7 @@ def update_from_json(cls, descriptor, jsondict, user, filter_tabs=True):
Ensures none of the fields are in the blacklist.
"""
filtered_list = cls.filtered_list()
filtered_list = cls.filtered_list(org=descriptor.org)
# Don't filter on the tab attribute if filter_tabs is False.
if not filter_tabs:
filtered_list.remove("tabs")
Expand Down Expand Up @@ -195,7 +206,7 @@ def validate_and_update_from_json(cls, descriptor, jsondict, user, filter_tabs=T
errors: list of error objects
result: the updated course metadata or None if error
"""
filtered_list = cls.filtered_list()
filtered_list = cls.filtered_list(org=descriptor.org)
if not filter_tabs:
filtered_list.remove("tabs")

Expand Down
107 changes: 107 additions & 0 deletions cms/envs/amc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from .aws import *
import dj_database_url

from django.utils.translation import ugettext_lazy as _

APPSEMBLER_AMC_API_BASE = AUTH_TOKENS.get('APPSEMBLER_AMC_API_BASE')
APPSEMBLER_FIRST_LOGIN_API = '/logged_into_edx'

APPSEMBLER_SECRET_KEY = AUTH_TOKENS.get("APPSEMBLER_SECRET_KEY")

INSTALLED_APPS += (
'openedx.core.djangoapps.appsembler.sites',
'openedx.core.djangoapps.appsembler.html_certificates',
)

APPSEMBLER_FEATURES = ENV_TOKENS.get('APPSEMBLER_FEATURES', {})

GOOGLE_ANALYTICS_APP_ID = AUTH_TOKENS.get('GOOGLE_ANALYTICS_APP_ID')
HUBSPOT_API_KEY = AUTH_TOKENS.get('HUBSPOT_API_KEY')
HUBSPOT_PORTAL_ID = AUTH_TOKENS.get('HUBSPOT_PORTAL_ID')
MIXPANEL_APP_ID = AUTH_TOKENS.get('MIXPANEL_APP_ID')

DEFAULT_TEMPLATE_ENGINE['OPTIONS']['context_processors'] += (
'openedx.core.djangoapps.appsembler.intercom_integration.context_processors.intercom',
'openedx.core.djangoapps.appsembler.analytics.context_processors.google_analytics',
'openedx.core.djangoapps.appsembler.analytics.context_processors.hubspot',
'openedx.core.djangoapps.appsembler.analytics.context_processors.mixpanel',
)

MANDRILL_API_KEY = AUTH_TOKENS.get("MANDRILL_API_KEY")

AMC_APP_URL = ENV_TOKENS.get('AMC_APP_URL')

if MANDRILL_API_KEY:
EMAIL_BACKEND = ENV_TOKENS.get('EMAIL_BACKEND', 'anymail.backends.mandrill.MandrillBackend')
ANYMAIL = {
"MANDRILL_API_KEY": MANDRILL_API_KEY,
}
INSTALLED_APPS += ("anymail",)

INTERCOM_APP_ID = AUTH_TOKENS.get("INTERCOM_APP_ID")
INTERCOM_APP_SECRET = AUTH_TOKENS.get("INTERCOM_APP_SECRET")

FEATURES['ENABLE_COURSEWARE_INDEX'] = True
FEATURES['ENABLE_LIBRARY_INDEX'] = True

SEARCH_ENGINE = "search.elastic.ElasticSearchEngine"
ELASTIC_FIELD_MAPPINGS = {
"start_date": {
"type": "date"
}
}

# SENTRY
SENTRY_DSN = AUTH_TOKENS.get('SENTRY_DSN', False)

if SENTRY_DSN:
# Set your DSN value
RAVEN_CONFIG = {
'environment': FEATURES['ENVIRONMENT'], # This should be moved somewhere more sensible
'tags': {
'app': 'edxapp',
'service': 'cms'
},
'dsn': SENTRY_DSN,
}

INSTALLED_APPS += ('raven.contrib.django.raven_compat',)

INSTALLED_APPS += (
'hijack',
'compat',
'hijack_admin',
)
MIDDLEWARE_CLASSES += (
'organizations.middleware.OrganizationMiddleware',
)

SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'

if FEATURES.get("ENABLE_TIERS_APP", True):
TIERS_ORGANIZATION_MODEL = 'organizations.Organization'
TIERS_EXPIRED_REDIRECT_URL = ENV_TOKENS.get('TIERS_EXPIRED_REDIRECT_URL', None)
TIERS_ORGANIZATION_TIER_GETTER_NAME = 'get_tier_for_org'

TIERS_DATABASE_URL = AUTH_TOKENS.get('TIERS_DATABASE_URL')
DATABASES['tiers'] = dj_database_url.parse(TIERS_DATABASE_URL)
DATABASE_ROUTERS += ['openedx.core.djangoapps.appsembler.sites.routers.TiersDbRouter']

MIDDLEWARE_CLASSES += (
'tiers.middleware.TierMiddleware',
)
INSTALLED_APPS += (
'tiers',
)

XQUEUE_WAITTIME_BETWEEN_REQUESTS = 5

CLONE_COURSE_FOR_NEW_SIGNUPS = False
HIJACK_ALLOW_GET_REQUESTS = True
HIJACK_LOGOUT_REDIRECT_URL = '/admin/auth/user'

DEFAULT_COURSE_MODE_SLUG = ENV_TOKENS.get('EDXAPP_DEFAULT_COURSE_MODE_SLUG', 'audit')
DEFAULT_MODE_NAME_FROM_SLUG = _(DEFAULT_COURSE_MODE_SLUG.capitalize())

CUSTOM_DOMAINS_REDIRECT_CACHE_TIMEOUT = None # The length of time we cache Redirect model data
CUSTOM_DOMAINS_REDIRECT_CACHE_KEY_PREFIX = 'custom_domains_redirects'
Loading

0 comments on commit 145ada3

Please sign in to comment.