From 087250cff7df75c4275a14c7d56d8139d2bb19f7 Mon Sep 17 00:00:00 2001
From: Amit <43564590+amitvadhel@users.noreply.github.com>
Date: Tue, 11 Jun 2019 19:47:52 +0530
Subject: [PATCH] =?UTF-8?q?INCR-250:=20Make=20compatible=20with=20Python?=
 =?UTF-8?q?=203.x=20without=20breaking=20Python=202.7=E2=80=A6=20(#20534)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* INCR-250: Make compatible with Python 3.x without breaking Python 2.7 support --> openedx/core/djangoapps/programs

* INCR-250: Disable pylint warning and replace _f placeholder with actual name

* INCR-250: pylint format correction and fix over length line limit
---
 openedx/core/djangoapps/programs/__init__.py |  4 ++-
 openedx/core/djangoapps/programs/admin.py    |  2 ++
 openedx/core/djangoapps/programs/apps.py     |  2 ++
 openedx/core/djangoapps/programs/models.py   |  2 ++
 openedx/core/djangoapps/programs/signals.py  |  2 ++
 openedx/core/djangoapps/programs/utils.py    | 27 ++++++++++++--------
 6 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/openedx/core/djangoapps/programs/__init__.py b/openedx/core/djangoapps/programs/__init__.py
index 380a63c022b..5a4ef836a81 100644
--- a/openedx/core/djangoapps/programs/__init__.py
+++ b/openedx/core/djangoapps/programs/__init__.py
@@ -8,7 +8,9 @@ if and only if the service is deployed in the Open edX installation.
 To ensure maximum separation of concerns, and a minimum of interdependencies,
 this package should be kept small, thin, and stateless.
 """
-from openedx.core.djangoapps.waffle_utils import (WaffleSwitch, WaffleSwitchNamespace)
+from __future__ import absolute_import
+
+from openedx.core.djangoapps.waffle_utils import WaffleSwitch, WaffleSwitchNamespace
 
 PROGRAMS_WAFFLE_SWITCH_NAMESPACE = WaffleSwitchNamespace(name='programs')
 
diff --git a/openedx/core/djangoapps/programs/admin.py b/openedx/core/djangoapps/programs/admin.py
index 64da72dca3b..44b95b707b1 100644
--- a/openedx/core/djangoapps/programs/admin.py
+++ b/openedx/core/djangoapps/programs/admin.py
@@ -1,6 +1,8 @@
 """
 django admin pages for program support models
 """
+from __future__ import absolute_import
+
 from config_models.admin import ConfigurationModelAdmin
 from django.contrib import admin
 
diff --git a/openedx/core/djangoapps/programs/apps.py b/openedx/core/djangoapps/programs/apps.py
index e6d87eab18a..936e94f621a 100644
--- a/openedx/core/djangoapps/programs/apps.py
+++ b/openedx/core/djangoapps/programs/apps.py
@@ -1,6 +1,8 @@
 """
 Programs Configuration
 """
+from __future__ import absolute_import
+
 from django.apps import AppConfig
 
 
diff --git a/openedx/core/djangoapps/programs/models.py b/openedx/core/djangoapps/programs/models.py
index 89b2dd77c83..0cf5c0bb198 100644
--- a/openedx/core/djangoapps/programs/models.py
+++ b/openedx/core/djangoapps/programs/models.py
@@ -1,5 +1,7 @@
 """Models providing Programs support for the LMS and Studio."""
 
+from __future__ import absolute_import
+
 from config_models.models import ConfigurationModel
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
diff --git a/openedx/core/djangoapps/programs/signals.py b/openedx/core/djangoapps/programs/signals.py
index 6f18239fd7a..1d8351b4eac 100644
--- a/openedx/core/djangoapps/programs/signals.py
+++ b/openedx/core/djangoapps/programs/signals.py
@@ -1,6 +1,8 @@
 """
 This module contains signals / handlers related to programs.
 """
+from __future__ import absolute_import
+
 import logging
 
 from django.dispatch import receiver
diff --git a/openedx/core/djangoapps/programs/utils.py b/openedx/core/djangoapps/programs/utils.py
index 119d2450108..38d22b86cdd 100644
--- a/openedx/core/djangoapps/programs/utils.py
+++ b/openedx/core/djangoapps/programs/utils.py
@@ -1,12 +1,15 @@
 # -*- coding: utf-8 -*-
 """Helper functions for working with Programs."""
+from __future__ import absolute_import
+
 import datetime
 import logging
 from collections import defaultdict
 from copy import deepcopy
 from itertools import chain
-from urlparse import urljoin, urlparse, urlunparse
 
+import six
+from six.moves.urllib.parse import urljoin, urlparse, urlunparse  # pylint: disable=import-error
 from dateutil.parser import parse
 from django.conf import settings
 from django.contrib.auth import get_user_model
@@ -25,7 +28,7 @@ from lms.djangoapps.certificates.models import GeneratedCertificate
 from lms.djangoapps.commerce.utils import EcommerceService
 from lms.djangoapps.courseware.access import has_access
 from lms.djangoapps.grades.api import CourseGradeFactory
-from openedx.core.djangoapps.catalog.utils import get_programs, get_fulfillable_course_runs_for_entitlement
+from openedx.core.djangoapps.catalog.utils import get_fulfillable_course_runs_for_entitlement, get_programs
 from openedx.core.djangoapps.certificates.api import available_date_for_certificate
 from openedx.core.djangoapps.commerce.utils import ecommerce_api_client
 from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
@@ -95,7 +98,7 @@ class ProgramProgressMeter(object):
         self.course_run_ids = []
         for enrollment in self.enrollments:
             # enrollment.course_id is really a CourseKey (╯ಠ_ಠ)╯︵ ┻━┻
-            enrollment_id = unicode(enrollment.course_id)
+            enrollment_id = six.text_type(enrollment.course_id)
             mode = enrollment.mode
             if mode == CourseMode.NO_ID_PROFESSIONAL_MODE:
                 mode = CourseMode.PROFESSIONAL
@@ -140,7 +143,7 @@ class ProgramProgressMeter(object):
                             program_list.append(program)
 
         # Sort programs by title for consistent presentation.
-        for program_list in inverted_programs.itervalues():
+        for program_list in six.itervalues(inverted_programs):
             program_list.sort(key=lambda p: p['title'])
 
         return inverted_programs
@@ -326,14 +329,16 @@ class ProgramProgressMeter(object):
                 if modes_match and certificate_api.is_passing_status(certificate.status):
                     course_overview = CourseOverview.get_from_id(key)
                     available_date = available_date_for_certificate(course_overview, certificate)
-                    earliest_course_run_date = min(filter(None, [available_date, earliest_course_run_date]))
+                    earliest_course_run_date = min(
+                        [date for date in [available_date, earliest_course_run_date] if date]
+                    )
 
             # If we're missing a cert for a course, the program isn't completed and we should just bail now
             if earliest_course_run_date is None:
                 return None
 
             # Keep the catalog course date if it's the latest one
-            program_available_date = max(filter(None, [earliest_course_run_date, program_available_date]))
+            program_available_date = max([date for date in [earliest_course_run_date, program_available_date] if date])
 
         return program_available_date
 
@@ -427,7 +432,7 @@ class ProgramProgressMeter(object):
         completed_runs, failed_runs = [], []
         for certificate in course_run_certificates:
             course_data = {
-                'course_run_id': unicode(certificate['course_key']),
+                'course_run_id': six.text_type(certificate['course_key']),
                 'type': self._certificate_mode_translation(certificate['type']),
             }
 
@@ -592,7 +597,7 @@ class ProgramDataExtender(object):
         # Here we check the entitlements' expired_at_datetime property rather than filter by the expired_at attribute
         # to ensure that the expiration status is as up to date as possible
         entitlements = [e for e in entitlements if not e.expired_at_datetime]
-        courses_with_entitlements = set(unicode(entitlement.course_uuid) for entitlement in entitlements)
+        courses_with_entitlements = set(six.text_type(entitlement.course_uuid) for entitlement in entitlements)
         return [course for course in courses if course['uuid'] not in courses_with_entitlements]
 
     def _filter_out_courses_with_enrollments(self, courses):
@@ -610,10 +615,10 @@ class ProgramDataExtender(object):
             is_active=True,
             mode__in=self.data['applicable_seat_types']
         )
-        course_runs_with_enrollments = set(unicode(enrollment.course_id) for enrollment in enrollments)
+        course_runs_with_enrollments = set(six.text_type(enrollment.course_id) for enrollment in enrollments)
         courses_without_enrollments = []
         for course in courses:
-            if all(unicode(run['key']) not in course_runs_with_enrollments for run in course['course_runs']):
+            if all(six.text_type(run['key']) not in course_runs_with_enrollments for run in course['course_runs']):
                 courses_without_enrollments.append(course)
 
         return courses_without_enrollments
@@ -805,7 +810,7 @@ class ProgramMarketingDataExtender(ProgramDataExtender):
             self.data['instructor_ordering'] = []
 
         sorted_instructor_names = [
-            ' '.join(filter(None, (instructor['given_name'], instructor['family_name'])))
+            ' '.join([name for name in (instructor['given_name'], instructor['family_name']) if name])
             for instructor in self.data['instructor_ordering']
         ]
         instructors_to_be_sorted = [
-- 
GitLab