Skip to content
This repository has been archived by the owner on Nov 15, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1546 from philanthropy-u/develop
Browse files Browse the repository at this point in the history
Release 3.6
  • Loading branch information
Saad Waqar authored May 15, 2020
2 parents 1930f53 + 195c8f3 commit c0aa335
Show file tree
Hide file tree
Showing 13 changed files with 218 additions and 24 deletions.
1 change: 1 addition & 0 deletions lms/djangoapps/onboarding/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@

NOT_INTERESTED_KEY = 'NT'
NOT_INTERESTED_VAL = "No Thanks, I'm Not Interested"
ORG_SEARCH_TERM_LENGTH = 2
4 changes: 4 additions & 0 deletions lms/djangoapps/onboarding/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from mailchimp_pipeline.signals.handlers import update_user_email_in_mailchimp
from nodebb.tasks import task_update_user_profile_on_nodebb
from oef.models import OrganizationOefUpdatePrompt
from lms.djangoapps.onboarding.constants import ORG_SEARCH_TERM_LENGTH
from lms.djangoapps.onboarding.models import (
Organization, OrganizationMetricUpdatePrompt, PartnerNetwork, OrganizationAdminHashKeys
)
Expand Down Expand Up @@ -7864,6 +7865,8 @@ def get_close_matching_orgs_with_suggestions(request, query):
data = {}

organizations = Organization.objects.filter(label__istartswith=query)
if len(query) == ORG_SEARCH_TERM_LENGTH:
organizations = organizations.filter(label__length=ORG_SEARCH_TERM_LENGTH)
for organization in organizations:
match_ratio = get_str_match_ratio(query.lower(), organization.label.lower())
is_suggestion = True if re.match(query, organization.label, re.I) else False
Expand Down Expand Up @@ -8022,6 +8025,7 @@ def serialize_partner_networks():

return data


def get_user_on_demand_courses(user):
"""
Return user on demand courses
Expand Down
1 change: 1 addition & 0 deletions lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3564,3 +3564,4 @@ def _make_locale_paths(settings):
# CDN LINK

CDN_LINK = "https://static.philanthropyu.org/"

10 changes: 10 additions & 0 deletions lms/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@
url(r'^marketplace/', include('openedx.features.marketplace.urls')),
url(r'^idea/', include('openedx.features.idea.urls')),

# temporary experiment urls block start.
url(r'^organizations/', TemplateView.as_view(template_name='main_django.html')),
url(r'^learners/', TemplateView.as_view(template_name='main_django.html')),
url(r'^organizational-capacity-assessment/',
TemplateView.as_view(template_name='main_django.html')),
url(r'^course-development/', TemplateView.as_view(template_name='main_django.html')),
url(r'^organization-onboarding/',
TemplateView.as_view(template_name='main_django.html')),
# temporary experiment urls block End.

# CloudSponge proxy url to lets the OAuth providers know, we are asking CloudSponge for address book
url(r'^auth/proxy/cloudsponge/$', TemplateView.as_view(template_name='features/smart_referral/auth_proxy.html'),
name='cloud_sponge_proxy'),
Expand Down
1 change: 1 addition & 0 deletions openedx/features/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from openedx.features.lookups import *
26 changes: 23 additions & 3 deletions openedx/features/course_card/helpers.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from datetime import datetime

import pytz
from logging import getLogger

import pytz
from crum import get_current_request
from custom_settings.models import CustomSettings

from course_action_state.models import CourseRerunState
from custom_settings.models import CustomSettings
from openedx.core.djangoapps.catalog.utils import get_programs
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.theming.helpers import get_current_request
from openedx.features.course_card.models import CourseCard

log = getLogger(__name__)


Expand Down Expand Up @@ -96,3 +99,20 @@ def get_course_cards_list():
course_card_ids = [cc.course_id for cc in cards_query_set]
courses_list = CourseOverview.objects.select_related('image_set').filter(id__in=course_card_ids)
return courses_list


def is_course_in_programs(course_id):
"""
Helper function to check if course is part of program
@param course_id: course key
@return: true if course is part of program otherwise false
"""
programs = get_programs(get_current_request().site)

for program in programs:
for program_course in program['courses']:
if program_course['course_runs']:
for course_rerun in program_course['course_runs']:
if course_rerun['key'] == str(course_id):
return True
return False
1 change: 1 addition & 0 deletions openedx/features/job_board/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
JOB_PARAM_QUERY_KEY = 'query'
JOB_PARAM_COUNTRY_KEY = 'country'
JOB_PARAM_CITY_KEY = 'city'
JOB_PARAM_TRUE_VALUE = '1'

JOB_TYPE_REMOTE_KEY = 'remote'
JOB_TYPE_ONSITE_KEY = 'onsite'
Expand Down
135 changes: 133 additions & 2 deletions openedx/features/job_board/tests/views/test_job_board.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
from ddt import data, ddt, unpack
from rest_framework import status
from w3lib.url import add_or_replace_parameters

from django.test import TestCase
from django.urls import reverse
from rest_framework import status

from openedx.core.djangolib.testing.philu_utils import configure_philu_theme
from openedx.features.job_board.constants import (
JOB_COMP_HOURLY_KEY,
JOB_COMP_SALARIED_KEY,
JOB_COMP_VOLUNTEER_KEY,
JOB_HOURS_FREELANCE_KEY,
JOB_HOURS_FULLTIME_KEY,
JOB_HOURS_PARTTIME_KEY,
JOB_PARAM_CITY_KEY,
JOB_PARAM_COUNTRY_KEY,
JOB_PARAM_QUERY_KEY,
JOB_PARAM_TRUE_VALUE,
JOB_TYPE_ONSITE_KEY,
JOB_TYPE_REMOTE_KEY
)
from openedx.features.job_board.models import Job
from openedx.features.job_board.tests.factories import JobFactory
from openedx.features.job_board.views import JobCreateView, JobListView


@ddt
class JobBoardViewTest(TestCase):

def setUp(self):
Expand All @@ -21,7 +39,7 @@ def setUpClass(cls):
def test_job_create_view(self):
response = self.client.get(reverse('job_create'))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(JobCreateView.fields, '__all__')
self.assertEqual(JobCreateView.form_class.Meta.fields, '__all__')

def test_create_view_post_successful(self):
self.client.post('/jobs/create/', {"function": "dummy function",
Expand Down Expand Up @@ -69,3 +87,116 @@ def test_job_list_view(self):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(JobListView.paginate_by, 10)
self.assertEqual(JobListView.ordering, ['-created'])

@unpack
@data(('', 10), ('?page=2', 4))
def test_job_list_view_pagination(self, page_query_param, job_list_size):
# Create multiple new jobs to test pagination.
JobFactory.create_batch(14)
response_second_page = self.client.get(reverse('job_list') + page_query_param)
self.assertEqual(response_second_page.status_code, status.HTTP_200_OK)
self.assertTrue('is_paginated' in response_second_page.context_data)
self.assertTrue(response_second_page.context_data['is_paginated'] is True)
self.assertTrue(len(response_second_page.context_data['job_list']) == job_list_size)

@data(JOB_TYPE_REMOTE_KEY, JOB_TYPE_ONSITE_KEY)
def test_job_list_view_filters_job_type(self, job_type):
# Create a new job with `type=job_type` to search for.
# And another job with `type="test"` to see it's not fetched.
JobFactory(type=job_type)
JobFactory(type="test")

query_params = {job_type: JOB_PARAM_TRUE_VALUE}
response = self.client.get(add_or_replace_parameters(reverse('job_list'), query_params))

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.context_data['search_fields'][job_type], JOB_PARAM_TRUE_VALUE)

# ensure that no other filter was applied
del [response.context_data['search_fields'][job_type]]
self.assertTrue(not any(response.context_data['search_fields'].values()))

self.assertTrue(response.context_data['filtered'], True)
self.assertTrue(len(response.context_data['job_list']) == 1)
self.assertEqual(response.context_data['job_list'].first().type, job_type)

@data(JOB_COMP_VOLUNTEER_KEY, JOB_COMP_HOURLY_KEY, JOB_COMP_SALARIED_KEY)
def test_job_list_view_filters_job_compensation(self, job_comp):
# Create a new job with `compensation=job_comp` to search for.
# And another job with `compensation="test"` to see it's not fetched.
JobFactory(compensation=job_comp)
JobFactory(compensation="test")

query_params = {job_comp: JOB_PARAM_TRUE_VALUE}
response = self.client.get(add_or_replace_parameters(reverse('job_list'), query_params))

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.context_data['search_fields'][job_comp], JOB_PARAM_TRUE_VALUE)

# ensure that no other filter was applied
del [response.context_data['search_fields'][job_comp]]
self.assertTrue(not any(response.context_data['search_fields'].values()))

self.assertTrue(response.context_data['filtered'], True)
self.assertTrue(len(response.context_data['job_list']) == 1)
self.assertEqual(response.context_data['job_list'].first().compensation, job_comp)

@data(JOB_HOURS_FULLTIME_KEY, JOB_HOURS_PARTTIME_KEY, JOB_HOURS_FREELANCE_KEY)
def test_job_list_view_filters_job_hours(self, job_hours):
# Create a new job with `hours=job_hours` to search for.
# And another job with `hours="test"` to see it's not fetched.
JobFactory(hours=job_hours)
JobFactory(hours="test")

query_params = {job_hours: JOB_PARAM_TRUE_VALUE}
response = self.client.get(add_or_replace_parameters(reverse('job_list'), query_params))

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.context_data['search_fields'][job_hours], JOB_PARAM_TRUE_VALUE)

# ensure that no other filter was applied
del [response.context_data['search_fields'][job_hours]]
self.assertTrue(not any(response.context_data['search_fields'].values()))

self.assertTrue(response.context_data['filtered'], True)
self.assertTrue(len(response.context_data['job_list']) == 1)
self.assertEqual(response.context_data['job_list'].first().hours, job_hours)

def test_job_list_view_filters_job_location(self):
# Create a new job with custom location to search for.
job = JobFactory(country='PK', city='Karachi')

query_params = {JOB_PARAM_CITY_KEY: job.city, JOB_PARAM_COUNTRY_KEY: job.country.name}
response = self.client.get(add_or_replace_parameters(reverse('job_list'), query_params))

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.context_data['search_fields']['country'], job.country.name)
self.assertTrue(response.context_data['search_fields']['city'], job.city)

# ensure that no other filter was applied
del [response.context_data['search_fields']['country']]
del [response.context_data['search_fields']['city']]
self.assertTrue(not any(response.context_data['search_fields'].values()))

self.assertTrue(response.context_data['filtered'], True)
self.assertTrue(len(response.context_data['job_list']) == 1)
self.assertTrue(response.context_data['job_list'][0].country.name == job.country.name)
self.assertTrue(response.context_data['job_list'][0].city == job.city)

def test_job_list_view_filters_job_query(self):
# Create a new job with custom title to search for.
job = JobFactory(title='custom_job')

query_params = {JOB_PARAM_QUERY_KEY: job.title}
response = self.client.get(add_or_replace_parameters(reverse('job_list'), query_params))

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.context_data['search_fields']['query'], job.title)

# ensure that no other filter was applied
del [response.context_data['search_fields']['query']]
self.assertTrue(not any(response.context_data['search_fields'].values()))

self.assertTrue(response.context_data['filtered'], True)
self.assertTrue(len(response.context_data['job_list']) == 1)
self.assertTrue(job.title in response.context_data['job_list'][0].title)
3 changes: 3 additions & 0 deletions openedx/features/lookups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

models.CharField.register_lookup(models.functions.Length, 'length')
10 changes: 5 additions & 5 deletions openedx/features/partners/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ def clean_registration_data(self, registration_data):
registration_data.update(self.cleaned_data)

def clean_country(self):
country = self.cleaned_data['country']
cleaned_country = next((code for code, label in COUNTRIES.items() if label.lower() == country.lower()), None)
if cleaned_country:
return cleaned_country
raise ValidationError(_('Please select country.'))
country_code = self.cleaned_data['country']
if country_code in COUNTRIES.keys():
return country_code
else:
raise ValidationError(_('The given country is invalid.') if country_code else _('Please select country.'))

username = UsernameField()
password = forms.CharField()
Expand Down
31 changes: 18 additions & 13 deletions openedx/features/partners/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from datetime import datetime
from logging import getLogger

import analytics
from django.conf import settings
from django.contrib.auth import authenticate, login
from django.contrib.auth.models import User
Expand All @@ -14,29 +13,29 @@
from django.utils.translation import get_language
from django.views.decorators.debug import sensitive_post_parameters
from django.views.decorators.http import require_http_methods
from edxmako.shortcuts import render_to_response
from eventtracking import tracker
from notification_prefs.views import enable_notifications
from pytz import UTC
from rest_framework import status

from openedx.core.djangoapps.user_api.views import RegistrationView
import analytics
from edxmako.shortcuts import render_to_response
from eventtracking import tracker
from lms.djangoapps.onboarding.models import EmailPreference, Organization, PartnerNetwork, UserExtendedProfile
from nodebb.helpers import update_nodebb_for_user_status, set_user_activation_status_on_nodebb
from nodebb.helpers import set_user_activation_status_on_nodebb, update_nodebb_for_user_status
from notification_prefs.views import enable_notifications
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.user_api.preferences import api as preferences_api
from openedx.core.djangoapps.user_api.views import RegistrationView
from openedx.core.djangoapps.user_authn.cookies import set_logged_in_cookies
from openedx.core.djangoapps.user_authn.views.register import REGISTER_USER, record_registration_attributions
from openedx.features.partners.helpers import auto_join_partner_community, get_partner_recommended_courses
from openedx.features.partners.models import PartnerUser
from openedx.features.student_account.helpers import save_user_utm_info
from openedx.features.student_account.helpers import save_user_utm_info, get_registration_countries
from philu_overrides.user_api.views import LoginSessionViewCustom
from rest_framework import status
from student.models import Registration, UserProfile
from student.views import password_change_request_handler

from . import constants as partner_constants
from .forms import PartnerAccountCreationForm
from .forms import PartnerResetPasswordForm
from .forms import PartnerAccountCreationForm, PartnerResetPasswordForm
from .helpers import import_form_using_slug, user_has_performance_access
from .models import Partner

Expand All @@ -47,8 +46,15 @@
def dashboard(request, slug):
partner = get_object_or_404(Partner, slug=slug)
courses = get_partner_recommended_courses(partner.slug, request.user)
return render_to_response('features/partners/dashboard.html', {'recommended_courses': courses,
'slug': partner.slug, 'partner': partner})
registration_countries = get_registration_countries()

context = {
'recommended_courses': courses,
'slug': partner.slug, 'partner': partner,
'registration_countries': registration_countries,
}

return render_to_response('features/partners/dashboard.html', context)


def performance_dashboard(request, slug):
Expand Down Expand Up @@ -359,4 +365,3 @@ def post(self, request, partner):
user=user.username, partner=partner.slug, exp=str(ex))
)
return response

8 changes: 8 additions & 0 deletions openedx/features/student_account/constants.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
NON_ACTIVE_COURSE_NOTIFICATION = 'The course you enrolled in, %s opened last week but you can start it right now. <a ' \
'class="click-here-link" href="%s">Click here</a> to get started.'

TOP_REGISTRATION_COUNTRIES = (
('NG', 'Nigeria'),
('IN', 'India'),
('PK', 'Pakistan'),
('KE', 'Kenya'),
('GH', 'Ghana'),
)
11 changes: 10 additions & 1 deletion openedx/features/student_account/helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import operator

from datetime import datetime
from pytz import utc
Expand All @@ -7,7 +8,7 @@

from mailchimp_pipeline.signals.handlers import task_send_account_activation_email

from constants import NON_ACTIVE_COURSE_NOTIFICATION
from constants import NON_ACTIVE_COURSE_NOTIFICATION, TOP_REGISTRATION_COUNTRIES
from student.models import CourseEnrollment
from courseware.models import StudentModule

Expand All @@ -16,6 +17,7 @@
from openedx.core.djangoapps.timed_notification.core import get_course_first_chapter_link
from openedx.core.lib.request_utils import safe_get_host

from lms.djangoapps.onboarding.helpers import COUNTRIES
from lms.djangoapps.onboarding.models import EmailPreference, Organization, UserExtendedProfile
from lms.djangoapps.philu_overrides.constants import ACTIVATION_ALERT_TYPE

Expand Down Expand Up @@ -163,3 +165,10 @@ def check_and_add_third_party_params(request, params):
params['provider'] = running_pipeline.get('backend')

params['access_token'] = running_pipeline['kwargs']['response']['access_token']


def get_registration_countries():
return {
'all_countries': sorted(COUNTRIES.items(), key=operator.itemgetter(1)),
'top_countries': TOP_REGISTRATION_COUNTRIES or []
}

0 comments on commit c0aa335

Please # to comment.