mitodl/mitxpro

Course/Program platform should not be nullable

mbertrand opened this issue · 15 comments

Every course and program should have a Platform assigned to it. The schema should be changed to reflect this:

    platform = models.ForeignKey(
        Platform, on_delete=models.PROTECT, null=False, blank=False
    )

Some clarifications are needed before doing this.

Context:

  • Initially, the plan was to add Partner instead of Platform. But for the sake of consistency within multiple apps in mit-open we decided to add the Platform field instead. This was done as part of #2698.
  • The field was left nullable/blankable because we did not have partners for all the courses and thought the platform would only be set for external courses and not internal.

Problem: If we are to make it non nullable/blankable we need to decide how we will populate the existing data for courses and programs. Possible problems are:

  • How many Platform entries are to be created in the system before we do this change?
  • How do we decide which Course/Program to associate with which Platform?

A possible Solution: We can create a default platform in xPRO named MITxPRO and in the migration we set all the courses and programs to it by default. Later on, we can change the platform manually where needed. I believe manual intervention will be required for external courses only.

Ferdi commented

@arslanashraf7 how many courses/program do we have where platform is not set ?

@Ferdi I looked at the data and here are the stats:

On Production:

Total Course Courses Without Platform Total Programs Courses Without Program
154 77 26 19

Details (readable_id, live, is_external) about courses and programs with missing platforms can be seen below:

Courses list without platfrom
[
   {'readable_id': 'course-v1:xPRO+SysEngxB2', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+LASERxE4B', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+LASERxE3B', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+LASERxE2B', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+LASERxE1B', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO-ADGM+YDL2', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO-ADGM+YDL3', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO-ADGM-FDL1', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO-ADGM+FDL2', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO-ADGM+FDL3', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO-LASER-SL-Course2', 'live': True, 'is_external': True},
   {'readable_id': 'course-v1:xPRO-LASER-SL-Course3', 'live': True, 'is_external': True},
   {'readable_id': 'course-v1:xPRO-LASER-SL-Course4', 'live': True, 'is_external': True},
   {'readable_id': 'course-v1:xPRO+CDAT2_Boeing', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+CDAT3_Boeing', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+CDAT1_Boeing', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+AMxB', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+AMx_CROWN', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+AMxF+R1', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+ADETxB', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+ADSTxB', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+ADVTxB', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+DECA_Boeing', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+DSCA_Boeing', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+DVCA_Boeing', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+SysEngx1_Raytheon', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+PCCx3+R1', 'live': False, 'is_external': True},
   {'readable_id': 'course-v1:xPRO+QCFx1TouchEdu', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+PCCYx2+R1', 'live': False, 'is_external': True},
   {'readable_id': 'course-v1:xPRO+PCCYx1+R1', 'live': False, 'is_external': True},
   {'readable_id': 'course-v1:xPRO+DSx', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+AMxDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+MLx2DEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+SysEng1xDEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+LASERE4xDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+DSxDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+LASERx4DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+ENxDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+HNxDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+QCFx1DEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+LASERx3DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+LASERE3xDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+MLx1DEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+SysEng3xDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+SysEng2xDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+LASERx2DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+GNxDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+QCRx1DEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+SysEng4xDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+QCFx2DEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+QCRx2DEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:MITxPRO+LASERxE1DEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+LASERE2xDEMO+DEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+LASER1xDEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+PCCx2+R1', 'live': False, 'is_external': True},
   {'readable_id': 'course-v1:xPRO+AMx_CROWN_FUNDAMENTALS', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+NHx', 'live': True, 'is_external': False},
   {'readable_id': 'v1-bootcamp-innovation-leadership', 'live': True, 'is_external': True},
   {'readable_id': 'course-v1:xPRO+MBDTx', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:DEMO+EngLeadxDEMO', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:MITxPRO+LASERMC+R1', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+MLx1+R0', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+GNx', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+SPOC_MIT_PE', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+PCCx+R1+Course2_DO_NOT_USE', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+PCCx+R1+Course3_DO_NOT_USE', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+PCCYx-DONOTUSE', 'live': False, 'is_external': True},
   {'readable_id': 'program-v1:xPRO+PCDEx+Course2', 'live': False, 'is_external': True},
   {'readable_id': 'course-v1:xPRO+PCDGx+Course2', 'live': False, 'is_external': True},
   {'readable_id': 'course-v1:xPRO+PCTPMx+Course3', 'live': False, 'is_external': True},
   {'readable_id': 'course-v1:xPRO+ENxSE', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+LASERxE1_FreddieMac', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO-WHU+TTLS', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO-WHU+TTLS+R0', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+SysEngxNAV', 'live': False, 'is_external': False},
   {'readable_id': 'v1-bootcamp-venture', 'live': True, 'is_external': True},
   {'readable_id': 'course-v1:xPRO+PCCx1+R1', 'live': False, 'is_external': True}
]
Programs list without platfrom
[
   {'readable_id': 'program-v1:xPRO-SL+LASERx-SL', 'live': True, 'is_external': False},
   {'readable_id': 'program-v1:xPRO-ADGM+FDL', 'live': True, 'is_external': False},
   {'readable_id': 'program-v1:xPRO-ADGM+YDL', 'live': True, 'is_external': False},
   {'readable_id': 'program-v1:xPRO+CB_Boeing', 'live': True, 'is_external': False},
   {'readable_id': 'program-v1:xPRO+LASERx', 'live': True, 'is_external': False},
   {'readable_id': 'program-v1:xPRO+SysEngxB', 'live': False, 'is_external': False},
   {'readable_id': 'program-v1:xPRO+LASERxB', 'live': False, 'is_external': False},
   {'readable_id': 'program-v1:xPRO+LASERxEB',  'live': False, 'is_external': False},
   {'readable_id': 'program-v1:xPRO+PCCx+R1', 'live': False,  'is_external': False},
   {'readable_id': 'program-v1:xPRO+PCCxW+R1',  'live': False,  'is_external': True},
   {'readable_id': 'program-v1:xPRO+PCCYx', 'live': False, 'is_external': True},
   {'readable_id': 'program-v1:xPRO+TIAPx+R1',  'live': True,  'is_external': True},
   {'readable_id': 'program-v1:xPRO+PCDEx', 'live': False, 'is_external': True},
   {'readable_id': 'program-v1:xPRO+PCCFTx', 'live': False, 'is_external': True},
   {'readable_id': 'program-v1:xPRO+PCTPMx', 'live': False, 'is_external': True},
   {'readable_id': 'program-v1:xPRO+LASER-SL',  'live': True,  'is_external': False},
   {'readable_id': 'program-v1:xPRO+PCGDx', 'live': True, 'is_external': True},
   {'readable_id': 'program-v1:xPRO+DONOTUSE',  'live': False,  'is_external': False},
   {'readable_id': 'program-v1:xPRO+CDAT_Boeing',  'live': True,  'is_external': False}
]

On RC:

Total Course Courses Without Platform Total Programs Courses Without Program
55 65 16 15
Courses list without platfrom
[
   {'readable_id': 'course-v1:xPRO+SysEngxB1',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+SysEngxB3',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+SysEngxB2',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+SysEngxB4',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+DgtlLearn1',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+DgtlLearn2',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+DgtlLearn3',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+AnlgLearn1',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+AnlgLearn2',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+AnlgLearn3',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+LASERx4', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+LASERx3', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+LASERx2', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+LASERx1', 'live': True, 'is_external': False},
   {'readable_id': 'SysEngxB3', 'live': False, 'is_external': False},
   {'readable_id': 'SysEngBx2', 'live': False, 'is_external': False},
   {'readable_id': 'SysEngBx4', 'live': False, 'is_external': False},
   {'readable_id': 'SysEngBx3', 'live': False, 'is_external': False},
   {'readable_id': 'TestX1', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:TESTx+hidden2x',  'live': False,  'is_external': False},
   {'readable_id': 'course-v1:TESTx+hidden1x',  'live': False,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+TestCourse1',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+TestCourse2',  'live': True,  'is_external': False},
   {'readable_id': 'ATCx', 'live': False, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+Bonx', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+Bonx+SPOC',  'live': False,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+external1+R1',  'live': True,  'is_external': True},
   {'readable_id': 'course-v1:xPRO+external2+R1',  'live': True,  'is_external': True},
   {'readable_id': 'course-v1:xPRO+external3+R1',  'live': True,  'is_external': True},
   {'readable_id': 'course-v1:arslan_external_1',  'live': True,  'is_external': True},
   {'readable_id': 'ATP_external_2', 'live': True, 'is_external': True},
   {'readable_id': 'arslan_test_internal_externl_1',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+BONx2', 'live': True, 'is_external': False},
   {'readable_id': 'course_without_cms_page',  'live': True,  'is_external': False},
   {'readable_id': 'AMxB', 'live': True, 'is_external': False},
   {'readable_id': 'AMxBIntro', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:MITx+Ali-Test-Course',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:MITx+101', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO-ARSLAN2', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:edx+BlockchainBasics',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+ExtCourse',  'live': True,  'is_external': True},
   {'readable_id': 'brian_test_external_page_2',  'live': False,  'is_external': True},
   {'readable_id': 'DSx', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+ENx', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+MLx1+R0', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:MAPLE+MAPLE', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:xPRO+EngWeb', 'live': True, 'is_external': True},
   {'readable_id': 'QCFx2', 'live': True, 'is_external': False},
   {'readable_id': 'RCHNTC', 'live': True, 'is_external': False},
   {'readable_id': 'course-v1:edX+DemoX+Demo_Course',  'live': True,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+NotLive1',  'live': False,  'is_external': False},
   {'readable_id': 'course-v1:xPRO+QuantCompx1',  'live': True,  'is_external': False},
   {'readable_id': 'external-readable-id', 'live': True, 'is_external': True},
   {'readable_id': 'TC3', 'live': True, 'is_external': False}
]
Programs list without platfrom
[
   {'readable_id': 'program-v1:ARSLAN+TestProgram',  'live': True,  'is_external': False},
   {'readable_id': 'program-v1:xPRO+LASERx', 'live': True, 'is_external': False},
   {'readable_id': 'program-v1:MITxPro+SysEngxB',  'live': True,  'is_external': False},
   {'readable_id': 'program-v1:xPRO+SysEngx',  'live': True,  'is_external': False},
   {'readable_id': 'program-v1:xPRO+DgtlLearn',  'live': True,  'is_external': False},
   {'readable_id': 'program-v1:xPRO+AnlgLearn',  'live': True,  'is_external': False},
   {'readable_id': 'program-v1:xPRO+SysEngBx',  'live': True,  'is_external': False},
   {'readable_id': 'TestX', 'live': True, 'is_external': False},
   {'readable_id': 'program-v1:TESTx+SysEngBx',  'live': False,  'is_external': False},
   {'readable_id': 'program-v1:xPRO+TestProgram',  'live': True,  'is_external': False},
   {'readable_id': 'program-v1:xPRO+Bonx', 'live': True, 'is_external': False},
   {'readable_id': 'program-v1:xPRO+externalprogram',  'live': True,  'is_external': True},
   {'readable_id': 'ATP_external_1', 'live': True, 'is_external': True},
   {'readable_id': 'test_internal_program_external',  'live': True,  'is_external': False},
   {'readable_id': 'progam_without_cms_page',  'live': True,  'is_external': False}
]

Let me know if you require further details.

Ferdi commented

Thanks Arslan, why don't I see these courses here https://xpro.mit.edu/api/courses/?

Thanks Arslan, why don't I see these courses here https://xpro.mit.edu/api/courses/?

That would be because the /api/courses only returns those courses that have:

  1. live=True (This is from Django)
  2. coursepage.live=True (This is CMS page for the course)
  3. externalcoursepage.live=True (Again, from CMS but for external courses)

*Edit
In the list that I provided above, I didn't filter the data based on any of these conditions because we will need a platform for all of those irrespective of the above conditions.

Ferdi commented

ok, should we fix the data before making the field non-nullable or after. Most of them seem to be external = false, so we can set the platform to mitxpro.

ok, should we fix the data before making the field non-nullable or after. Most of them seem to be external = false, so we can set the platform to mitxpro.

We can do both ways.

  • If we know what platform we want for all the remaining courses and programs, we can automatically fix it in the Django migration that we will generate to make this field non-nullable.
  • If not, Then we need to fix the data before we make it non-nullable.
Ferdi commented

it seems like external courses/programs need to be fixed before. Internal ones (platform = mitxpro) can be done during migration

it seems like external courses/programs need to be fixed before. Internal ones (platform = mitxpro) can be done during migration

I agree. I suppose the course team would fix that data? Or I can but I will require details about the external courses. In any case, we can put up the PR as soon as the data is fixed.

Ferdi commented

Can we get a list of the external courses/programs for @cachob to look at ?

Can we get a list of the external courses/programs for @cachob to look at ?

Sure.

@cachob Please take a look below for the list of external courses and programs without the platform. For ease, I've convertedthe lists to easily clickable links.

Production:

Here is the list of external courses without a platform

https://xpro.mit.edu/admin/courses/course/154/change/
https://xpro.mit.edu/admin/courses/course/155/change/
https://xpro.mit.edu/admin/courses/course/156/change/
https://xpro.mit.edu/admin/courses/course/131/change/
https://xpro.mit.edu/admin/courses/course/129/change/
https://xpro.mit.edu/admin/courses/course/151/change/
https://xpro.mit.edu/admin/courses/course/133/change/
https://xpro.mit.edu/admin/courses/course/176/change/
https://xpro.mit.edu/admin/courses/course/168/change/
https://xpro.mit.edu/admin/courses/course/172/change/
https://xpro.mit.edu/admin/courses/course/173/change/
https://xpro.mit.edu/admin/courses/course/175/change/
https://xpro.mit.edu/admin/courses/course/136/change/
https://xpro.mit.edu/admin/courses/course/141/change/

Here is the list of external programs without a platform

https://xpro.mit.edu/admin/courses/program/20/change/
https://xpro.mit.edu/admin/courses/program/21/change/
https://xpro.mit.edu/admin/courses/program/22/change/
https://xpro.mit.edu/admin/courses/program/23/change/
https://xpro.mit.edu/admin/courses/program/24/change/
https://xpro.mit.edu/admin/courses/program/25/change/
https://xpro.mit.edu/admin/courses/program/27/change/


RC: (Changing this doesn't matter much since this is just test data but in case you would like to take a look)

Here is the list of external courses without a platform

https://rc.xpro.mit.edu/admin/courses/course/43/change/
https://rc.xpro.mit.edu/admin/courses/course/45/change/
https://rc.xpro.mit.edu/admin/courses/course/44/change/
https://rc.xpro.mit.edu/admin/courses/course/49/change/
https://rc.xpro.mit.edu/admin/courses/course/51/change/
https://rc.xpro.mit.edu/admin/courses/course/48/change/
https://rc.xpro.mit.edu/admin/courses/course/47/change/
https://rc.xpro.mit.edu/admin/courses/course/42/change/
https://rc.xpro.mit.edu/admin/courses/course/46/change/

Here is the list of external programs without a platform

https://rc.xpro.mit.edu/admin/courses/program/13/change/
https://rc.xpro.mit.edu/admin/courses/program/14/change/

cachob commented

@arslanashraf7 - I noticed that a lot of the courses are the dummy courses that we had to create to correctly display the amount of courses for a program in the catalog. The others are also not set as live. I will clean up and update both courses and programs on the lists as needed, and set the default value as xPRO if applicable

@arslanashraf7 - I noticed that a lot of the courses are the dummy courses that we had to create to correctly display the amount of courses for a program in the catalog. The others are also not set as live. I will clean up and update both courses and programs on the lists as needed, and set the default value as xPRO if applicable

@cachob Thanks. Let us know when you are done.

cachob commented

@arslanashraf7 - production data is all set

Done via #2786. This has been deployed on production. The data migration for this was successful and there are no more courses or programs without a platform.