Skip to content

Commit

Permalink
Merge branch 'master' into kenclary/TNL-10912
Browse files Browse the repository at this point in the history
  • Loading branch information
kenclary authored Aug 14, 2023
2 parents b453e48 + a5912b7 commit af07ab7
Show file tree
Hide file tree
Showing 540 changed files with 8,910 additions and 6,510 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"func-names": "off",
"indent": ["error", 4],
"react/jsx-indent": ["error", 4],
"react/jsx-indent-props": ["error", 4],
"new-cap": "off",
"no-else-return": "off",
"no-shadow": "error",
Expand Down Expand Up @@ -70,7 +71,6 @@
"prefer-rest-params": "off",
"prefer-template": "off",
"radix": "off",
"react/jsx-indent-props": ["error", 4],
"react/prop-types": "off",
"vars-on-top": "off"
}
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ compile-requirements: pre-requirements $(COMMON_CONSTRAINTS_TXT) ## Re-compile *
@# Bootstrapping: Rebuild pip and pip-tools first, and then install them
@# so that if there are any failures we'll know now, rather than the next
@# time someone tries to use the outputs.
sed '/^django-simple-history==/d' requirements/common_constraints.txt > requirements/common_constraints.tmp
mv requirements/common_constraints.tmp requirements/common_constraints.txt
pip-compile -v --allow-unsafe ${COMPILE_OPTS} -o requirements/pip.txt requirements/pip.in
pip install -r requirements/pip.txt

Expand Down
20 changes: 18 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ platform. Functionally, the edx-platform repository provides two services:
* CMS (Content Management Service), which powers Open edX Studio, the platform's learning content authoring environment; and
* LMS (Learning Management Service), which delivers learning content.

Installation
************
Getting Started
***************

Installing and running an Open edX instance is not simple. We strongly
recommend that you use a service provider to run the software for you. They
Expand Down Expand Up @@ -122,6 +122,13 @@ Contributions are welcome! The first step is to submit a signed
information – it also contains guidelines for how to maintain high code
quality, which will make your contribution more likely to be accepted.

New features are accepted. Discussing your new ideas with the maintainers
before you write code will also increase the chances that your work is accepted.

Code of Conduct
***************

Please read the `Community Code of Conduct`_ for interacting with this repository.

Reporting Security Issues
*************************
Expand All @@ -131,3 +138,12 @@ security@edx.org.

.. _individual contributor agreement: https://openedx.org/cla
.. _CONTRIBUTING: https://github.com/openedx/.github/blob/master/CONTRIBUTING.md
.. _Community Code of Conduct: https://openedx.org/code-of-conduct/

People
******

The current maintainers of this repository can be found on `Backstage`_.

.. _Backstage: https://backstage.openedx.org/catalog/default/component/edx-platform

16 changes: 16 additions & 0 deletions catalog-info.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# This file records information about this repo. Its use is described in OEP-55:
# https://open-edx-proposals.readthedocs.io/en/latest/processes/oep-0055-proc-project-maintainers.html

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: 'edx-platform'
description: "The monolith at the center of the Open edX platform"
links:
- url: "https://docs.openedx.org"
title: "Documentation"
icon: "Web"
spec:
owner: group:arch-bom
type: 'service'
lifecycle: 'production'
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""A Command to Copy or uncopy V1 Content Libraries entires to be stored as v2 content libraries."""

import logging
import csv
from textwrap import dedent

from django.core.management import BaseCommand, CommandError
Expand Down Expand Up @@ -28,15 +29,13 @@ class Command(BaseCommand):
and -- file followed by the path for a list of libraries from a file.
Example usage:
$ ./manage.py cms copy_libraries_from_v1_to_v2 'collection_uuid' --all
$ ./manage.py cms copy_libraries_from_v1_to_v2 'collection_uuid' --all --uncopy
$ ./manage.py cms copy_libraries_from_v1_to_v2 'collection_uuid 'library-v1:edX+DemoX+Better_Library'
$ ./manage.py cms copy_libraries_from_v1_to_v2 'collection_uuid 'library-v1:edX+DemoX+Better_Library' --uncopy
$ ./manage.py cms copy_libraries_from_v1_to_v2
library-v1:edX+DemoX+Demo_Library' 'library-v1:edX+DemoX+Better_Library' -c 'collection_uuid'
$ ./manage.py cms copy_libraries_from_v1_to_v2 --all --uncopy
$ ./manage.py cms copy_libraries_from_v1_to_v2 'library-v1:edX+DemoX+Better_Library' --uncopy
$ ./manage.py cms copy_libraries_from_v1_to_v2
'11111111-2111-4111-8111-111111111111'
'./list_of--library-locators- --file
'11111111-2111-4111-8111-111111111111'
'./list_of--library-locators.csv --all
Note:
This Command Also produces an "output file" which contains the mapping of locators and the status of the copy.
Expand All @@ -49,17 +48,18 @@ def add_arguments(self, parser):
"""arguements for command"""

parser.add_argument(
'-collection_uuid',
'-c',
nargs=1,
'collection_uuid',
type=str,
help='the uuid for the collection to create the content library in.'
)
parser.add_argument(
'library_ids',
nargs='*',
help='a space-seperated list of v1 library ids to copy'
'output_csv',
type=str,
nargs='?',
default=None,
help='a file path to write the tasks output to. Without this the result is simply logged.'
)

parser.add_argument(
'--all',
action='store_true',
Expand All @@ -72,12 +72,11 @@ def add_arguments(self, parser):
dest='uncopy',
help='Delete libraries specified'
)

parser.add_argument(
'output_csv',
nargs='?',
default=None,
help='a file path to write the tasks output to. Without this the result is simply logged.'
'library_ids',
nargs='*',
default=[],
help='a space-seperated list of v1 library ids to copy'
)

def _parse_library_key(self, raw_value):
Expand All @@ -90,10 +89,6 @@ def _parse_library_key(self, raw_value):

def handle(self, *args, **options): # lint-amnesty, pylint: disable=unused-argument
"""Parse args and generate tasks for copying content."""
print(options)

if (not options['library_ids'] and not options['all']) or (options['library_ids'] and options['all']):
raise CommandError("copy_libraries_from_v1_to_v2 requires one or more <library_id>s or the --all flag.")

if (not options['library_ids'] and not options['all']) or (options['library_ids'] and options['all']):
raise CommandError("copy_libraries_from_v1_to_v2 requires one or more <library_id>s or the --all flag.")
Expand All @@ -110,16 +105,17 @@ def handle(self, *args, **options): # lint-amnesty, pylint: disable=unused-argu
v1_library_keys = list(map(self._parse_library_key, options['library_ids']))

create_library_task_group = group([
delete_v2_library_from_v1_library.s(str(v1_library_key), options['collection_uuid'][0])
delete_v2_library_from_v1_library.s(str(v1_library_key), options['collection_uuid'])
if options['uncopy']
else create_v2_library_from_v1_library.s(str(v1_library_key), options['collection_uuid'][0])
else create_v2_library_from_v1_library.s(str(v1_library_key), options['collection_uuid'])
for v1_library_key in v1_library_keys
])

group_result = create_library_task_group.apply_async().get()
if options['output_csv']:
with open(options['output_csv'][0], 'w', encoding='utf-8', newline='') as output_writer:
output_writer.writerow("v1_library_id", "v2_library_id", "status", "error_msg")
with open(options['output_csv'], 'w', encoding='utf-8', newline='') as file:
output_writer = csv.writer(file)
output_writer.writerow(["v1_library_id", "v2_library_id", "status", "error_msg"])
for result in group_result:
output_writer.write(result.keys())
output_writer.writerow(result.values())
log.info(group_result)
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"""
A Command which, given a mapping of V1 to V2 Libraries,
edits all xblocks in courses which refer to the v1 library to point to the v2 library.
"""

import logging
import csv

from django.core.management import BaseCommand, CommandError
from celery import group

from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from cms.djangoapps.contentstore.tasks import (
replace_all_library_source_blocks_ids_for_course,
validate_all_library_source_blocks_ids_for_course,
undo_all_library_source_blocks_ids_for_course
)

log = logging.getLogger(__name__)


class Command(BaseCommand):
"""
Example usage:
$ ./manage.py cms replace_v1_lib_refs_with_v2_in_courses '/path/to/library_mappings.csv'
$ ./manage.py cms replace_v1_lib_refs_with_v2_in_courses '/path/to/library_mappings.csv' --validate
$ ./manage.py cms replace_v1_lib_refs_with_v2_in_courses '/path/to/library_mappings.csv' --undo
"""
def add_arguments(self, parser):
parser.add_argument('file_path', type=str, help='Path to the CSV file.')
parser.add_argument('--validate', action='store_true', help='Validate previous runs of the command')
parser.add_argument('--undo', action='store_true', help='Validate previous runs of the command')

def replace_all_library_source_blocks_ids(self, v1_to_v2_lib_map):
"""A method to replace 'source_library_id' in all relevant blocks."""

courses = CourseOverview.get_all_courses()

# Use Celery to distribute the workload
tasks = group(
replace_all_library_source_blocks_ids_for_course.s(
course,
v1_to_v2_lib_map
)
for course in courses
)
results = tasks.apply_async()

for result in results.get():
if isinstance(result, Exception):
# Handle the task failure here
log.error("Task failed with error: %s", str(result))
continue
log.info(
"Completed replacing all v1 library source ids with v2 library source ids"
)

def validate(self, v1_to_v2_lib_map):
""" Validate that replace_all_library_source_blocks_ids was successful"""
courses = CourseOverview.get_all_courses()
tasks = group(validate_all_library_source_blocks_ids_for_course.s(course, v1_to_v2_lib_map) for course in courses) # lint-amnesty, pylint: disable=line-too-long
results = tasks.apply_async()

validation = set()
for result in results.get():
if isinstance(result, Exception):
# Handle the task failure here
log.error("Task failed with error: %s", str(result))
continue
else:
validation.update(result)

if validation.issubset(v1_to_v2_lib_map.values()):
log.info("Validation: All values in the input map are present in courses.")
else:
log.info(
"Validation Failed: There are unmapped v1 libraries."
)

def undo(self, v1_to_v2_lib_map):
""" undo the changes made by replace_all_library_source_blocks_ids"""
courses = CourseOverview.get_all_courses()

# Use Celery to distribute the workload
tasks = group(undo_all_library_source_blocks_ids_for_course.s(course, v1_to_v2_lib_map) for course in courses)
results = tasks.apply_async()

for result in results.get():
if isinstance(result, Exception):
# Handle the task failure here
log.error("Task failed with error: %s", str(result))
continue
log.info("Completed replacing all v2 library source ids with v1 library source ids. Undo Complete")

def handle(self, *args, **kwargs):
""" Parse arguments and begin command"""
file_path = kwargs['file_path']
v1_to_v2_lib_map = {}
try:
with open(file_path, 'r', encoding='utf-8') as csvfile:

if not file_path.endswith('.csv'):
raise CommandError('Invalid file format. Only CSV files are supported.')

csv_reader = csv.reader(csvfile)

for row in csv_reader:
if len(row) >= 2:
key = row[0].strip()
value = row[1].strip()
v1_to_v2_lib_map[key] = value

print("Data successfully imported as dictionary:")

except FileNotFoundError:
log.error("File not found at '%s'.", {file_path})
except Exception as e: # lint-amnesty, pylint: disable=broad-except
log.error("An error occurred: %s", {str(e)})

if kwargs['validate']:
self.validate(v1_to_v2_lib_map)
if kwargs['undo']:
self.undo(v1_to_v2_lib_map)
else:
self.replace_all_library_source_blocks_ids(v1_to_v2_lib_map)
Loading

0 comments on commit af07ab7

Please sign in to comment.