Skip to content

Commit

Permalink
feat: refresh_xblock_skills and event handlers for openedx-events (#138)
Browse files Browse the repository at this point in the history
This commit combines changes implemented in following commits:
- cc1889b
- ec2a581

The changes were reverted as part of commit
a999401 as the upstream MR in
openedx-events: openedx/openedx-events#143 was
not merged. It is now merged and the dependencies are udpated.
  • Loading branch information
navinkarkera authored Jan 6, 2023
1 parent b4228a7 commit 2922df7
Show file tree
Hide file tree
Showing 21 changed files with 984 additions and 27 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ Change Log
Unreleased

[1.33.0] - 2023-01-06
---------------------
* https://github.com/openedx/openedx-events/pull/143 merged, so adding back
changes reverted in version 1.32.1
* Added refresh_xblock_skills command to update skills for xblocks.
* Added handlers for openedx-events: XBLOCK_DELETED, XBLOCK_PUBLISHED and XBLOCK_PUBLISHED.

[1.32.3] - 2023-01-05
---------------------
* Added log for EMSI client access token and raising error for error status.
Expand Down
2 changes: 2 additions & 0 deletions requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ django-solo
pytz
edx-rest-api-client
edx-django-utils
edx-opaque-keys
celery
algoliasearch
django-ses
beautifulsoup4
django-choices
django-filter
openedx-events
2 changes: 1 addition & 1 deletion requirements/ci.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ charset-normalizer==2.1.1
# via requests
codecov==2.1.12
# via -r requirements/ci.in
coverage==7.0.1
coverage==7.0.2
# via codecov
distlib==0.3.6
# via virtualenv
Expand Down
21 changes: 19 additions & 2 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ astroid==2.3.3
attrs==22.2.0
# via
# -r requirements/test.txt
# openedx-events
# pytest
beautifulsoup4==4.11.1
# via -r requirements/test.txt
Expand Down Expand Up @@ -78,7 +79,7 @@ code-annotations==1.3.0
# via -r requirements/test.txt
codecov==2.1.12
# via -r requirements/ci.txt
coverage[toml]==7.0.1
coverage[toml]==7.0.2
# via
# -r requirements/ci.txt
# -r requirements/test.txt
Expand All @@ -105,6 +106,7 @@ django==3.2.16
# djangorestframework
# edx-django-utils
# edx-i18n-tools
# openedx-events
django-choices==1.7.2
# via -r requirements/test.txt
django-crum==0.7.9
Expand Down Expand Up @@ -135,6 +137,10 @@ edx-lint==1.5.2
# via
# -c requirements/constraints.txt
# -r requirements/dev.in
edx-opaque-keys[django]==2.3.0
# via
# -r requirements/test.txt
# openedx-events
edx-rest-api-client==5.5.0
# via -r requirements/test.txt
exceptiongroup==1.1.0
Expand All @@ -147,6 +153,10 @@ faker==15.3.4
# via
# -r requirements/test.txt
# factory-boy
fastavro==1.7.0
# via
# -r requirements/test.txt
# openedx-events
filelock==3.9.0
# via
# -r requirements/ci.txt
Expand Down Expand Up @@ -193,6 +203,8 @@ newrelic==8.5.0
# via
# -r requirements/test.txt
# edx-django-utils
openedx-events==4.1.0
# via -r requirements/test.txt
packaging==22.0
# via
# -r requirements/ci.txt
Expand Down Expand Up @@ -242,7 +254,7 @@ pycparser==2.21
# via
# -r requirements/test.txt
# cffi
pydocstyle==6.1.1
pydocstyle==6.2.1
# via -r requirements/dev.in
pygments==2.14.0
# via diff-cover
Expand All @@ -264,6 +276,10 @@ pylint-plugin-utils==0.7
# via
# pylint-celery
# pylint-django
pymongo==3.13.0
# via
# -r requirements/test.txt
# edx-opaque-keys
pynacl==1.5.0
# via
# -r requirements/test.txt
Expand Down Expand Up @@ -341,6 +357,7 @@ stevedore==4.1.1
# -r requirements/test.txt
# code-annotations
# edx-django-utils
# edx-opaque-keys
testfixtures==7.0.4
# via -r requirements/test.txt
text-unidecode==1.3
Expand Down
4 changes: 2 additions & 2 deletions requirements/pip-tools.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#
# This file is autogenerated by pip-compile with python 3.8
# To update, run:
# This file is autogenerated by pip-compile with Python 3.8
# by the following command:
#
# make upgrade
#
Expand Down
10 changes: 4 additions & 6 deletions requirements/pip.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
#
# This file is autogenerated by pip-compile with python 3.8
# To update, run:
# This file is autogenerated by pip-compile with Python 3.8
# by the following command:
#
# make upgrade
#
wheel==0.38.4
# via -r requirements/pip.in

# The following packages are considered to be unsafe in a requirements file:
pip==22.3.1
# via -r requirements/pip.in
setuptools==65.6.3
# via -r requirements/pip.in
wheel==0.38.4
# via -r requirements/pip.in
18 changes: 16 additions & 2 deletions requirements/test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ amqp==2.6.1
asgiref==3.6.0
# via django
attrs==22.2.0
# via pytest
# via
# openedx-events
# pytest
beautifulsoup4==4.11.1
# via -r requirements/base.in
billiard==3.6.4.0
Expand All @@ -40,7 +42,7 @@ click==8.1.3
# edx-django-utils
code-annotations==1.3.0
# via -r requirements/test.in
coverage[toml]==7.0.1
coverage[toml]==7.0.2
# via pytest-cov
ddt==1.6.0
# via -r requirements/test.in
Expand All @@ -55,6 +57,7 @@ ddt==1.6.0
# django-solo
# djangorestframework
# edx-django-utils
# openedx-events
django-choices==1.7.2
# via -r requirements/base.in
django-crum==0.7.9
Expand All @@ -75,6 +78,10 @@ edx-django-utils==5.2.0
# via
# -r requirements/base.in
# edx-rest-api-client
edx-opaque-keys[django]==2.3.0
# via
# -r requirements/base.in
# openedx-events
edx-rest-api-client==5.5.0
# via -r requirements/base.in
exceptiongroup==1.1.0
Expand All @@ -85,6 +92,8 @@ faker==15.3.4
# via
# -r requirements/test.in
# factory-boy
fastavro==1.7.0
# via openedx-events
idna==3.4
# via requests
iniconfig==1.1.1
Expand All @@ -103,6 +112,8 @@ mock==5.0.0
# via -r requirements/test.in
newrelic==8.5.0
# via edx-django-utils
openedx-events==4.1.0
# via -r requirements/base.in
packaging==22.0
# via pytest
pbr==5.11.0
Expand All @@ -115,6 +126,8 @@ pycparser==2.21
# via cffi
pyjwt==2.6.0
# via edx-rest-api-client
pymongo==3.13.0
# via edx-opaque-keys
pynacl==1.5.0
# via edx-django-utils
pytest==7.2.0
Expand Down Expand Up @@ -164,6 +177,7 @@ stevedore==4.1.1
# via
# code-annotations
# edx-django-utils
# edx-opaque-keys
testfixtures==7.0.4
# via -r requirements/test.in
text-unidecode==1.3
Expand Down
2 changes: 1 addition & 1 deletion taxonomy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
# 2. MINOR version when you add functionality in a backwards compatible manner, and
# 3. PATCH version when you make backwards compatible bug fixes.
# More details can be found at https://semver.org/
__version__ = '1.32.3'
__version__ = '1.33.0'

default_app_config = 'taxonomy.apps.TaxonomyConfig' # pylint: disable=invalid-name
6 changes: 6 additions & 0 deletions taxonomy/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ class ProgramMetadataNotFoundError(Exception):
"""


class XBlockMetadataNotFoundError(Exception):
"""
Exception to raise when metadata was not found for an XBlock.
"""


class InvalidCommandOptionsError(Exception):
"""
Exception to raise when incorrect command options are provided.
Expand Down
137 changes: 137 additions & 0 deletions taxonomy/management/commands/refresh_xblock_skills.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
"""
Management command for refreshing the skills associated with xblocks.
"""

import logging

from django.core.management.base import BaseCommand
from django.utils.translation import gettext as _
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey

from taxonomy import utils
from taxonomy.choices import ProductTypes
from taxonomy.exceptions import InvalidCommandOptionsError, XBlockMetadataNotFoundError
from taxonomy.models import RefreshXBlockSkillsConfig
from taxonomy.providers.utils import get_course_metadata_provider, get_xblock_metadata_provider

LOGGER = logging.getLogger(__name__)


class Command(BaseCommand):
"""
Command to refresh skills associated with the XBlocks.
Example usage:
$ ./manage.py refresh_xblock_skills --xblock 'xblock-usage-key1' --xblock 'xblock-usage-key2' --commit
$ # To refresh all xblock skills under given courses.
$ ./manage.py refresh_xblock_skills --course 'course-v1:edX+DemoX+1' --course 'course-v1:edX+DemoY+1' --commit
$ # args-from-database means command line arguments will be picked from the database.
$ ./manage.py refresh_xblock_skills --args-from-database
$ # To update all xblocks in all the courses
$ ./manage.py refresh_xblock_skills --all --commit
"""
help = 'Refreshes the skills associated with XBlocks.'
product_type = ProductTypes.XBlock

def add_arguments(self, parser):
"""
Add arguments to the command parser.
"""
parser.add_argument(
'--course',
metavar=_('COURSE_KEY'),
action='append',
help=_('Update skills for XBlocks under given course keys. For eg. course-v1:edX+DemoX.1+2014'),
default=[],
)
parser.add_argument(
'--xblock',
metavar=_('USAGE_KEY'),
action='append',
help=_('Update skills for given Xblock usage keys.'),
default=[],
)
parser.add_argument(
'--args-from-database',
action='store_true',
help=_('Use arguments from the RefreshXBlockSkillsConfig model instead of the command line.'),
)
parser.add_argument(
'--all',
action='store_true',
help=_('Create xblock skill mapping for all xblocks in all the courses.'),
)
parser.add_argument(
'--commit',
action='store_true',
default=False,
help=_('Commits the skills to storage.')
)

def get_args_from_database(self):
"""
Return an options dictionary from the current RefreshXBlockSkillsConfig model.
"""
config = RefreshXBlockSkillsConfig.get_solo()
argv = config.arguments.split()
parser = self.create_parser('manage.py', 'refresh_xblock_skills')
return parser.parse_args(argv).__dict__

@staticmethod
def is_valid_key(key, key_cls, key_cls_str):
"""
Validates usage and course keys.
"""
try:
key_cls.from_string(key)
return True
except InvalidKeyError:
LOGGER.error('[TAXONOMY] Invalid %s: [%s]', key_cls_str, key)
return False

def handle(self, *args, **options):
"""
Entry point for management command execution.
"""
if not (options['args_from_database'] or options['all'] or options['course'] or options['xblock']):
raise InvalidCommandOptionsError(
'Either course, xblock, args_from_database or all argument must be provided.',
)

if options['args_from_database']:
options = self.get_args_from_database()

if options['course'] and options['xblock']:
raise InvalidCommandOptionsError('Either course or xblock argument should be provided and not both.')

LOGGER.info('[TAXONOMY] Refresh XBlock Skills. Options: [%s]', options)

courses = []
xblocks_from_args = []
xblock_provider = get_xblock_metadata_provider()
if options['all']:
courses = get_course_metadata_provider().get_all_courses()
elif options['course']:
courses = [{"key": course} for course in options['course']]
elif options['xblock']:
valid_usage_keys = set(key for key in options['xblock'] if self.is_valid_key(key, UsageKey, "UsageKey"))
xblocks_from_args = xblock_provider.get_xblocks(xblock_ids=list(valid_usage_keys))
if not xblocks_from_args:
raise XBlockMetadataNotFoundError(
'No xblock metadata was found for following xblocks. {}'.format(options['xblock'])
)
else:
raise InvalidCommandOptionsError('Either course, xblock or --all argument must be provided.')

for course in courses:
course_key = course.get("key")
if self.is_valid_key(course_key, CourseKey, "CourseKey"):
xblocks = xblock_provider.get_all_xblocks_in_course(course_key)
LOGGER.info('[TAXONOMY] Refresh xblocks skills process started for course: {course_key}.')
utils.refresh_product_skills(xblocks, options['commit'], self.product_type)

if xblocks_from_args:
LOGGER.info('[TAXONOMY] Refresh XBlock skills process started for xblocks: [%s]', options['xblock'])
utils.refresh_product_skills(xblocks_from_args, options['commit'], self.product_type)
Loading

0 comments on commit 2922df7

Please sign in to comment.