refactor: Ran pyupgrade on openedx/core/djangoapps
Ran pyupgrade on openedx/core/djangoapps/{xblock, xmodule_django, zendesk_proxy}
This commit is contained in:
@@ -16,7 +16,6 @@ from django.utils.translation import ugettext as _
|
||||
from opaque_keys.edx.keys import UsageKeyV2
|
||||
from opaque_keys.edx.locator import BundleDefinitionLocator
|
||||
from rest_framework.exceptions import NotFound
|
||||
import six
|
||||
from xblock.core import XBlock
|
||||
from xblock.exceptions import NoSuchViewError
|
||||
|
||||
@@ -48,7 +47,7 @@ def get_runtime_system():
|
||||
# and XBlock field data in general [does not distinguish between setting initial values during parsing and changing
|
||||
# values at runtime due to user interaction], and how it interacts with BlockstoreFieldData. Keeping the caches
|
||||
# local to each thread completely avoids this problem.)
|
||||
cache_name = '_system_{}'.format(threading.get_ident())
|
||||
cache_name = f'_system_{threading.get_ident()}'
|
||||
if not hasattr(get_runtime_system, cache_name):
|
||||
params = dict(
|
||||
handler_url=get_handler_url,
|
||||
@@ -77,7 +76,7 @@ def load_block(usage_key, user):
|
||||
if user is not None and not context_impl.can_view_block(user, usage_key):
|
||||
# We do not know if the block was not found or if the user doesn't have
|
||||
# permission, but we want to return the same result in either case:
|
||||
raise NotFound("XBlock {} does not exist, or you don't have permission to view it.".format(usage_key))
|
||||
raise NotFound(f"XBlock {usage_key} does not exist, or you don't have permission to view it.")
|
||||
|
||||
# TODO: load field overrides from the context
|
||||
# e.g. a course might specify that all 'problem' XBlocks have 'max_attempts'
|
||||
@@ -106,7 +105,7 @@ def get_block_metadata(block, includes=()):
|
||||
children in other bundles.
|
||||
"""
|
||||
data = {
|
||||
"block_id": six.text_type(block.scope_ids.usage_id),
|
||||
"block_id": str(block.scope_ids.usage_id),
|
||||
"block_type": block.scope_ids.block_type,
|
||||
"display_name": get_block_display_name(block),
|
||||
}
|
||||
@@ -174,7 +173,7 @@ def get_block_display_name(block_or_key):
|
||||
def_key = resolve_definition(block_or_key)
|
||||
use_draft = get_xblock_app_config().get_learning_context_params().get('use_draft')
|
||||
cache = BundleCache(def_key.bundle_uuid, draft_name=use_draft)
|
||||
cache_key = ('block_display_name', six.text_type(def_key))
|
||||
cache_key = ('block_display_name', str(def_key))
|
||||
display_name = cache.get(cache_key)
|
||||
if display_name is None:
|
||||
# Instead of loading the block, just load its XML and parse it
|
||||
@@ -236,7 +235,7 @@ def get_handler_url(usage_key, handler_name, user):
|
||||
|
||||
This view does not check/care if the XBlock actually exists.
|
||||
"""
|
||||
usage_key_str = six.text_type(usage_key)
|
||||
usage_key_str = str(usage_key)
|
||||
site_root_url = get_xblock_app_config().get_site_root_url()
|
||||
if not user: # lint-amnesty, pylint: disable=no-else-raise
|
||||
raise TypeError("Cannot get handler URLs without specifying a specific user ID.")
|
||||
|
||||
@@ -4,7 +4,7 @@ of content where learning happens.
|
||||
"""
|
||||
|
||||
|
||||
class LearningContext(object):
|
||||
class LearningContext:
|
||||
"""
|
||||
Abstract base class for learning context implementations.
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ def get_learning_context_impl(key):
|
||||
context_type = key.context_key.CANONICAL_NAMESPACE
|
||||
elif isinstance(key, OpaqueKey):
|
||||
# Maybe this is an older modulestore key etc.
|
||||
raise TypeError("Opaque key {} does not have a learning context.".format(key))
|
||||
raise TypeError(f"Opaque key {key} does not have a learning context.")
|
||||
else:
|
||||
raise TypeError("key '{}' is not an opaque key. You probably forgot [KeyType].from_string(...)".format(key))
|
||||
raise TypeError(f"key '{key}' is not an opaque key. You probably forgot [KeyType].from_string(...)")
|
||||
|
||||
try:
|
||||
return _learning_context_cache[context_type]
|
||||
|
||||
@@ -26,7 +26,7 @@ CHILDREN_INCLUDES = Sentinel('CHILDREN_INCLUDES') # Key for a pseudo-field that
|
||||
MAX_DEFINITIONS_LOADED = 100 # How many of the most recently used XBlocks' field data to keep in memory at max.
|
||||
|
||||
|
||||
class BlockInstanceUniqueKey(object):
|
||||
class BlockInstanceUniqueKey:
|
||||
"""
|
||||
An empty object used as a unique key for each XBlock instance, see
|
||||
get_weak_key_for_block() and BlockstoreFieldData._get_active_block(). Every
|
||||
@@ -70,7 +70,7 @@ def get_olx_hash_for_definition_key(def_key):
|
||||
for entry in files_list:
|
||||
if entry.path == def_key.olx_path:
|
||||
return entry.hash_digest
|
||||
raise NoSuchDefinition("Could not load OLX file for key {}".format(def_key))
|
||||
raise NoSuchDefinition(f"Could not load OLX file for key {def_key}")
|
||||
|
||||
|
||||
class BlockstoreFieldData(FieldData):
|
||||
@@ -103,7 +103,7 @@ class BlockstoreFieldData(FieldData):
|
||||
# (see _get_active_block()) and the value is an ActiveBlock object
|
||||
# (which holds olx_hash and changed_fields)
|
||||
self.active_blocks = WeakKeyDictionary()
|
||||
super(BlockstoreFieldData, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__()
|
||||
|
||||
def _getfield(self, block, name):
|
||||
"""
|
||||
@@ -134,7 +134,7 @@ class BlockstoreFieldData(FieldData):
|
||||
raise InvalidScopeError("BlockstoreFieldData only supports UserScope.NONE fields")
|
||||
if field.scope.block not in (BlockScope.DEFINITION, BlockScope.USAGE):
|
||||
raise InvalidScopeError(
|
||||
"BlockstoreFieldData does not support BlockScope.{} fields".format(field.scope.block)
|
||||
f"BlockstoreFieldData does not support BlockScope.{field.scope.block} fields"
|
||||
)
|
||||
# There is also BlockScope.TYPE but we don't need to support that;
|
||||
# it's mostly relevant as Scope.preferences(UserScope.ONE, BlockScope.TYPE)
|
||||
@@ -250,7 +250,7 @@ class BlockstoreFieldData(FieldData):
|
||||
as long as they're not in use.
|
||||
"""
|
||||
olx_hashes = set(self.loaded_definitions.keys())
|
||||
olx_hashes_needed = set(entry.olx_hash for entry in self.active_blocks.values())
|
||||
olx_hashes_needed = {entry.olx_hash for entry in self.active_blocks.values()}
|
||||
|
||||
olx_hashes_safe_to_delete = olx_hashes - olx_hashes_needed
|
||||
|
||||
@@ -278,7 +278,7 @@ class BlockstoreChildrenData(FieldData):
|
||||
"""
|
||||
# The data store that holds Scope.usage and Scope.definition data:
|
||||
self.authored_data_store = blockstore_field_data
|
||||
super(BlockstoreChildrenData, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__()
|
||||
|
||||
def _check_field(self, block, name): # pylint: disable=unused-argument
|
||||
"""
|
||||
|
||||
@@ -41,7 +41,7 @@ class BlockstoreXBlockRuntime(XBlockRuntime):
|
||||
"""
|
||||
def_id = self.id_reader.get_definition_id(usage_id)
|
||||
if def_id is None:
|
||||
raise ValueError("Definition not found for usage {}".format(usage_id))
|
||||
raise ValueError(f"Definition not found for usage {usage_id}")
|
||||
if not isinstance(def_id, BundleDefinitionLocator):
|
||||
raise TypeError("This runtime can only load blocks stored in Blockstore bundles.")
|
||||
try:
|
||||
|
||||
@@ -25,7 +25,7 @@ from xblock.runtime import KeyValueStore
|
||||
FIELD_DATA_TIMEOUT = None # keep in cache indefinitely, until cache needs pruning
|
||||
|
||||
|
||||
class NotFound(object):
|
||||
class NotFound:
|
||||
"""
|
||||
This class is a unique value that can be stored in a cache to indicate "not found"
|
||||
"""
|
||||
@@ -40,7 +40,7 @@ class EphemeralKeyValueStore(KeyValueStore):
|
||||
"""
|
||||
Expand the given XBlock key tuple to a format we can use as a key.
|
||||
"""
|
||||
return u"ephemeral-xblock:{}".format(repr(tuple(key)))
|
||||
return "ephemeral-xblock:{}".format(repr(tuple(key)))
|
||||
|
||||
@property
|
||||
def _cache(self):
|
||||
|
||||
@@ -37,11 +37,11 @@ class LmsBlockMixin(XBlockMixin):
|
||||
"""
|
||||
completion_service = self.runtime.service(self, 'completion')
|
||||
if completion_service is None: # lint-amnesty, pylint: disable=no-else-raise
|
||||
raise JsonHandlerError(500, u"No completion service found")
|
||||
raise JsonHandlerError(500, "No completion service found")
|
||||
elif not completion_service.completion_tracking_enabled():
|
||||
raise JsonHandlerError(404, u"Completion tracking is not enabled and API calls are unexpected")
|
||||
raise JsonHandlerError(404, "Completion tracking is not enabled and API calls are unexpected")
|
||||
if not completion_service.can_mark_block_complete_on_view(self):
|
||||
raise JsonHandlerError(400, u"Block not configured for completion on view.")
|
||||
raise JsonHandlerError(400, "Block not configured for completion on view.")
|
||||
self.runtime.publish(self, "completion", data)
|
||||
return {'result': 'ok'}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ def parse_xblock_include(include_node):
|
||||
# Where "source" and "usage" are optional.
|
||||
if include_node.tag != 'xblock-include':
|
||||
# xss-lint: disable=python-wrap-html
|
||||
raise BundleFormatException("Expected an <xblock-include /> XML node, but got <{}>".format(include_node.tag))
|
||||
raise BundleFormatException(f"Expected an <xblock-include /> XML node, but got <{include_node.tag}>")
|
||||
try:
|
||||
definition_path = include_node.attrib['definition']
|
||||
except KeyError:
|
||||
@@ -41,7 +41,7 @@ def parse_xblock_include(include_node):
|
||||
try:
|
||||
block_type, definition_id = definition_path.split("/")
|
||||
except ValueError:
|
||||
raise BundleFormatException("Invalid definition attribute: {}".format(definition_path)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise BundleFormatException(f"Invalid definition attribute: {definition_path}") # lint-amnesty, pylint: disable=raise-missing-from
|
||||
return XBlockInclude(link_id=link_id, block_type=block_type, definition_id=definition_id, usage_hint=usage_hint)
|
||||
|
||||
|
||||
@@ -69,18 +69,18 @@ def definition_for_include(parsed_include, parent_definition_key):
|
||||
try:
|
||||
link = links[parsed_include.link_id]
|
||||
except KeyError:
|
||||
raise BundleFormatException("Link not found: {}".format(parsed_include.link_id)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise BundleFormatException(f"Link not found: {parsed_include.link_id}") # lint-amnesty, pylint: disable=raise-missing-from
|
||||
return BundleDefinitionLocator(
|
||||
bundle_uuid=link.bundle_uuid,
|
||||
block_type=parsed_include.block_type,
|
||||
olx_path="{}/{}/definition.xml".format(parsed_include.block_type, parsed_include.definition_id),
|
||||
olx_path=f"{parsed_include.block_type}/{parsed_include.definition_id}/definition.xml",
|
||||
bundle_version=link.version,
|
||||
)
|
||||
else:
|
||||
return BundleDefinitionLocator(
|
||||
bundle_uuid=parent_definition_key.bundle_uuid,
|
||||
block_type=parsed_include.block_type,
|
||||
olx_path="{}/{}/definition.xml".format(parsed_include.block_type, parsed_include.definition_id),
|
||||
olx_path=f"{parsed_include.block_type}/{parsed_include.definition_id}/definition.xml",
|
||||
bundle_version=parent_definition_key.bundle_version,
|
||||
draft_name=parent_definition_key.draft_name,
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ Common base classes for all new XBlock runtimes.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from urllib.parse import urljoin # pylint: disable=import-error
|
||||
|
||||
import crum
|
||||
from completion.waffle import ENABLE_COMPLETION_TRACKING_SWITCH
|
||||
@@ -12,7 +13,6 @@ from django.contrib.auth import get_user_model
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.utils.lru_cache import lru_cache
|
||||
from eventtracking import tracker
|
||||
from six.moves.urllib.parse import urljoin # pylint: disable=import-error
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.exceptions import NoSuchServiceError
|
||||
from xblock.field_data import SplitFieldData
|
||||
@@ -72,7 +72,7 @@ class XBlockRuntime(RuntimeShim, Runtime):
|
||||
suppports_state_for_anonymous_users = True
|
||||
|
||||
def __init__(self, system, user):
|
||||
super(XBlockRuntime, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(
|
||||
id_reader=system.id_reader,
|
||||
mixins=(
|
||||
LmsBlockMixin, # Adds Non-deprecated LMS/Studio functionality
|
||||
@@ -212,7 +212,7 @@ class XBlockRuntime(RuntimeShim, Runtime):
|
||||
# be removed from here and from XBlock.runtime
|
||||
declaration = block.service_declaration(service_name)
|
||||
if declaration is None:
|
||||
raise NoSuchServiceError("Service {!r} was not requested.".format(service_name))
|
||||
raise NoSuchServiceError(f"Service {service_name!r} was not requested.")
|
||||
# Most common service is field-data so check that first:
|
||||
if service_name == "field-data":
|
||||
if block.scope_ids not in self.block_field_datas:
|
||||
@@ -233,7 +233,7 @@ class XBlockRuntime(RuntimeShim, Runtime):
|
||||
# Otherwise, fall back to the base implementation which loads services
|
||||
# defined in the constructor:
|
||||
if service is None:
|
||||
service = super(XBlockRuntime, self).service(block, service_name) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
service = super().service(block, service_name)
|
||||
return service
|
||||
|
||||
def _init_field_data_for_block(self, block):
|
||||
@@ -290,7 +290,7 @@ class XBlockRuntime(RuntimeShim, Runtime):
|
||||
# which create relative URLs (/static/studio/bundles/webpack-foo.js).
|
||||
# We want all resource URLs to be absolute, such as is done when
|
||||
# local_resource_url() is used.
|
||||
fragment = super(XBlockRuntime, self).render(block, view_name, context) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
fragment = super().render(block, view_name, context)
|
||||
needs_fix = False
|
||||
for resource in fragment.resources:
|
||||
if resource.kind == 'url' and resource.data.startswith('/'):
|
||||
@@ -360,7 +360,7 @@ class XBlockRuntime(RuntimeShim, Runtime):
|
||||
return None
|
||||
|
||||
|
||||
class XBlockRuntimeSystem(object):
|
||||
class XBlockRuntimeSystem:
|
||||
"""
|
||||
This class is essentially a factory for XBlockRuntimes. This is a
|
||||
long-lived object which provides the behavior specific to the application
|
||||
|
||||
@@ -17,14 +17,14 @@ from common.djangoapps.edxmako.shortcuts import render_to_string
|
||||
from common.djangoapps.student.models import anonymous_id_for_user
|
||||
|
||||
|
||||
class RuntimeShim(object):
|
||||
class RuntimeShim:
|
||||
"""
|
||||
All the old/deprecated APIs that our XBlock runtime(s) need to
|
||||
support are captured in this mixin.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RuntimeShim, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
self._active_block = None
|
||||
|
||||
def render(self, block, view_name, context=None):
|
||||
@@ -40,7 +40,7 @@ class RuntimeShim(object):
|
||||
old_active_block = self._active_block
|
||||
self._active_block = block
|
||||
try:
|
||||
return super(RuntimeShim, self).render(block, view_name, context) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().render(block, view_name, context)
|
||||
finally:
|
||||
# Reset the active view to what it was before entering this method
|
||||
self._active_block = old_active_block
|
||||
@@ -53,7 +53,7 @@ class RuntimeShim(object):
|
||||
old_active_block = self._active_block
|
||||
self._active_block = block
|
||||
try:
|
||||
return super(RuntimeShim, self).handle(block, handler_name, request, suffix) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().handle(block, handler_name, request, suffix)
|
||||
finally:
|
||||
# Reset the active view to what it was before entering this method
|
||||
self._active_block = old_active_block
|
||||
@@ -358,7 +358,7 @@ class RuntimeShim(object):
|
||||
Get the list of CSS classes that the wrapping <div> should have for the
|
||||
specified xblock or aside's view.
|
||||
"""
|
||||
css_classes = super(RuntimeShim, self)._css_classes_for(block, view) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
css_classes = super()._css_classes_for(block, view)
|
||||
# Many CSS styles for former XModules use
|
||||
# .xmodule_display.xmodule_VideoBlock
|
||||
# as their selector, so add those classes:
|
||||
@@ -366,11 +366,11 @@ class RuntimeShim(object):
|
||||
css_classes.append('xmodule_display')
|
||||
elif view == 'studio_view':
|
||||
css_classes.append('xmodule_edit')
|
||||
css_classes.append('xmodule_{}'.format(block.unmixed_class.__name__))
|
||||
css_classes.append(f'xmodule_{block.unmixed_class.__name__}')
|
||||
return css_classes
|
||||
|
||||
|
||||
class XBlockShim(object):
|
||||
class XBlockShim:
|
||||
"""
|
||||
Mixin added to XBlock classes in this runtime, to support
|
||||
older/XModule APIs
|
||||
|
||||
@@ -10,7 +10,6 @@ from uuid import uuid4
|
||||
|
||||
import crum
|
||||
from django.conf import settings
|
||||
from six import text_type # lint-amnesty, pylint: disable=unused-import
|
||||
|
||||
|
||||
def get_secure_token_for_xblock_handler(user_id, block_key_str, time_idx=0):
|
||||
|
||||
@@ -40,11 +40,11 @@ class NoneToEmptyQuerySet(models.query.QuerySet):
|
||||
direct = not field_object.auto_created or field_object.concrete
|
||||
if direct and hasattr(field_object, 'Empty'):
|
||||
for suffix in ('', '_exact'):
|
||||
key = '{}{}'.format(field_object.name, suffix)
|
||||
key = f'{field_object.name}{suffix}'
|
||||
if key in kwargs and kwargs[key] is None:
|
||||
kwargs[key] = field_object.Empty
|
||||
|
||||
return super(NoneToEmptyQuerySet, self)._filter_or_exclude(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super()._filter_or_exclude(*args, **kwargs)
|
||||
|
||||
|
||||
class OpaqueKeyField(opaque_keys.edx.django.models.OpaqueKeyField):
|
||||
@@ -61,7 +61,7 @@ class OpaqueKeyField(opaque_keys.edx.django.models.OpaqueKeyField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn("openedx.core.djangoapps.xmodule_django.models.OpaqueKeyField is deprecated. "
|
||||
"Please use opaque_keys.edx.django.models.OpaqueKeyField instead.", stacklevel=2)
|
||||
super(OpaqueKeyField, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class CourseKeyField(opaque_keys.edx.django.models.CourseKeyField):
|
||||
@@ -71,7 +71,7 @@ class CourseKeyField(opaque_keys.edx.django.models.CourseKeyField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn("openedx.core.djangoapps.xmodule_django.models.LocationKeyField is deprecated. "
|
||||
"Please use opaque_keys.edx.django.models.UsageKeyField instead.", stacklevel=2)
|
||||
super(CourseKeyField, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class UsageKeyField(opaque_keys.edx.django.models.UsageKeyField):
|
||||
@@ -81,7 +81,7 @@ class UsageKeyField(opaque_keys.edx.django.models.UsageKeyField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn("openedx.core.djangoapps.xmodule_django.models.UsageKeyField is deprecated. "
|
||||
"Please use opaque_keys.edx.django.models.UsageKeyField instead.", stacklevel=2)
|
||||
super(UsageKeyField, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class UsageKeyWithRunField(opaque_keys.edx.django.models.UsageKeyField):
|
||||
@@ -90,7 +90,7 @@ class UsageKeyWithRunField(opaque_keys.edx.django.models.UsageKeyField):
|
||||
missing `run` values, for old Mongo courses.
|
||||
"""
|
||||
def to_python(self, value):
|
||||
value = super(UsageKeyWithRunField, self).to_python(value) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
value = super().to_python(value)
|
||||
if value is not None and value.run is None:
|
||||
value = value.replace(course_key=modulestore().fill_in_run(value.course_key))
|
||||
return value
|
||||
@@ -103,4 +103,4 @@ class BlockTypeKeyField(opaque_keys.edx.django.models.BlockTypeKeyField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
warnings.warn("openedx.core.djangoapps.xmodule_django.models.BlockTypeKeyField is deprecated. "
|
||||
"Please use opaque_keys.edx.django.models.BlockTypeKeyField instead.", stacklevel=2)
|
||||
super(BlockTypeKeyField, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -19,24 +19,24 @@ class ZendeskProxyConfig(AppConfig):
|
||||
plugin_app = {
|
||||
PluginURLs.CONFIG: {
|
||||
ProjectType.CMS: {
|
||||
PluginURLs.NAMESPACE: u'',
|
||||
PluginURLs.NAMESPACE: '',
|
||||
PluginURLs.REGEX: r'^zendesk_proxy/',
|
||||
PluginURLs.RELATIVE_PATH: u'urls',
|
||||
PluginURLs.RELATIVE_PATH: 'urls',
|
||||
},
|
||||
ProjectType.LMS: {
|
||||
PluginURLs.NAMESPACE: u'',
|
||||
PluginURLs.NAMESPACE: '',
|
||||
PluginURLs.REGEX: r'^zendesk_proxy/',
|
||||
PluginURLs.RELATIVE_PATH: u'urls',
|
||||
PluginURLs.RELATIVE_PATH: 'urls',
|
||||
}
|
||||
},
|
||||
PluginSettings.CONFIG: {
|
||||
ProjectType.CMS: {
|
||||
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: u'settings.common'},
|
||||
SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: u'settings.production'},
|
||||
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: 'settings.common'},
|
||||
SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: 'settings.production'},
|
||||
},
|
||||
ProjectType.LMS: {
|
||||
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: u'settings.common'},
|
||||
SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: u'settings.production'},
|
||||
SettingsType.COMMON: {PluginSettings.RELATIVE_PATH: 'settings.common'},
|
||||
SettingsType.PRODUCTION: {PluginSettings.RELATIVE_PATH: 'settings.production'},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ Tests of Zendesk interaction utility functions
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
from django.test.utils import override_settings
|
||||
|
||||
import ddt
|
||||
from mock import MagicMock, patch
|
||||
from openedx.core.djangoapps.zendesk_proxy.utils import create_zendesk_ticket
|
||||
from openedx.core.lib.api.test_utils import ApiTestCase
|
||||
|
||||
@@ -28,7 +28,7 @@ class TestUtils(ApiTestCase): # lint-amnesty, pylint: disable=missing-class-doc
|
||||
'subject': 'Python Unit Test Help Request',
|
||||
'body': "Help! I'm trapped in a unit test factory and I can't get out!",
|
||||
}
|
||||
return super(TestUtils, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().setUp()
|
||||
|
||||
@override_settings(
|
||||
ZENDESK_URL=None,
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
from copy import deepcopy
|
||||
import json
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
import ddt
|
||||
from django.urls import reverse
|
||||
from django.test.utils import override_settings
|
||||
from mock import MagicMock, patch
|
||||
import six
|
||||
from six.moves import range
|
||||
|
||||
from openedx.core.djangoapps.zendesk_proxy.v0.views import ZENDESK_REQUESTS_PER_HOUR
|
||||
from openedx.core.lib.api.test_utils import ApiTestCase
|
||||
@@ -34,7 +32,7 @@ class ZendeskProxyTestCase(ApiTestCase):
|
||||
'message': "Help! I'm trapped in a unit test factory and I can't get out!",
|
||||
}
|
||||
}
|
||||
return super(ZendeskProxyTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().setUp()
|
||||
|
||||
def test_post(self):
|
||||
with patch('requests.post', return_value=MagicMock(status_code=201)) as mock_post:
|
||||
@@ -47,7 +45,7 @@ class ZendeskProxyTestCase(ApiTestCase):
|
||||
self.assertHttpCreated(response)
|
||||
(mock_args, mock_kwargs) = mock_post.call_args
|
||||
assert mock_args == ('https://www.superrealurlsthataredefinitelynotfake.com/api/v2/tickets.json',)
|
||||
six.assertCountEqual(self, mock_kwargs.keys(), ['headers', 'data'])
|
||||
self.assertCountEqual(mock_kwargs.keys(), ['headers', 'data'])
|
||||
assert mock_kwargs['headers'] == {
|
||||
'content-type': 'application/json', 'Authorization': 'Bearer abcdefghijklmnopqrstuvwxyz1234567890'
|
||||
}
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
|
||||
from copy import deepcopy
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import ddt
|
||||
from django.urls import reverse
|
||||
from django.test.utils import override_settings
|
||||
from mock import MagicMock, patch
|
||||
import six
|
||||
from six.moves import range
|
||||
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from openedx.core.djangoapps.zendesk_proxy.v1.views import ZendeskProxyThrottle
|
||||
@@ -45,7 +43,7 @@ class ZendeskProxyTestCase(ApiTestCase):
|
||||
}
|
||||
],
|
||||
}
|
||||
return super(ZendeskProxyTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().setUp()
|
||||
|
||||
@ddt.data(
|
||||
True, False
|
||||
@@ -67,7 +65,7 @@ class ZendeskProxyTestCase(ApiTestCase):
|
||||
self.assertHttpCreated(response)
|
||||
(mock_args, mock_kwargs) = mock_post.call_args
|
||||
assert mock_args == ('https://www.superrealurlsthataredefinitelynotfake.com/api/v2/tickets.json',)
|
||||
six.assertCountEqual(self, mock_kwargs.keys(), ['headers', 'data'])
|
||||
self.assertCountEqual(mock_kwargs.keys(), ['headers', 'data'])
|
||||
assert mock_kwargs['headers'] == {
|
||||
'content-type': 'application/json', 'Authorization': 'Bearer abcdefghijklmnopqrstuvwxyz1234567890'
|
||||
}
|
||||
|
||||
@@ -5,24 +5,24 @@ Utility functions for zendesk interaction.
|
||||
|
||||
import json
|
||||
import logging
|
||||
from urllib.parse import urljoin # pylint: disable=import-error
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from rest_framework import status
|
||||
from six.moves.urllib.parse import urljoin # pylint: disable=import-error
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _std_error_message(details, payload):
|
||||
"""Internal helper to standardize error message. This allows for simpler splunk alerts."""
|
||||
return u'zendesk_proxy action required\n{}\nNo ticket created for payload {}'.format(details, payload)
|
||||
return f'zendesk_proxy action required\n{details}\nNo ticket created for payload {payload}'
|
||||
|
||||
|
||||
def _get_request_headers():
|
||||
return {
|
||||
'content-type': 'application/json',
|
||||
'Authorization': u"Bearer {}".format(settings.ZENDESK_OAUTH_ACCESS_TOKEN),
|
||||
'Authorization': f"Bearer {settings.ZENDESK_OAUTH_ACCESS_TOKEN}",
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ def create_zendesk_ticket(
|
||||
group_id = settings.ZENDESK_GROUP_ID_MAPPING[group]
|
||||
data['ticket']['group_id'] = group_id
|
||||
else:
|
||||
msg = u"Group ID not found for group {}. Please update ZENDESK_GROUP_ID_MAPPING".format(group)
|
||||
msg = f"Group ID not found for group {group}. Please update ZENDESK_GROUP_ID_MAPPING"
|
||||
log.error(_std_error_message(msg, payload))
|
||||
return status.HTTP_400_BAD_REQUEST
|
||||
|
||||
@@ -84,11 +84,11 @@ def create_zendesk_ticket(
|
||||
|
||||
# Check for HTTP codes other than 201 (Created)
|
||||
if response.status_code == status.HTTP_201_CREATED:
|
||||
log.debug(u'Successfully created ticket for {}'.format(requester_email))
|
||||
log.debug(f'Successfully created ticket for {requester_email}')
|
||||
else:
|
||||
log.error(
|
||||
_std_error_message(
|
||||
u'Unexpected response: {} - {}'.format(response.status_code, response.content),
|
||||
f'Unexpected response: {response.status_code} - {response.content}',
|
||||
payload
|
||||
)
|
||||
)
|
||||
@@ -98,8 +98,8 @@ def create_zendesk_ticket(
|
||||
except (ValueError, KeyError):
|
||||
log.error(
|
||||
_std_error_message(
|
||||
u"Got an unexpected response from zendesk api. Can't"
|
||||
u" get the ticket number to add extra info. {}".format(additional_info),
|
||||
"Got an unexpected response from zendesk api. Can't"
|
||||
" get the ticket number to add extra info. {}".format(additional_info),
|
||||
response.content
|
||||
)
|
||||
)
|
||||
@@ -118,8 +118,8 @@ def post_additional_info_as_comment(ticket_id, additional_info):
|
||||
to management and not students.
|
||||
"""
|
||||
additional_info_string = (
|
||||
u"Additional information:\n\n" +
|
||||
u"\n".join(u"%s: %s" % (key, value) for (key, value) in additional_info.items() if value is not None)
|
||||
"Additional information:\n\n" +
|
||||
"\n".join(f"{key}: {value}" for (key, value) in additional_info.items() if value is not None)
|
||||
)
|
||||
|
||||
data = {
|
||||
@@ -131,16 +131,16 @@ def post_additional_info_as_comment(ticket_id, additional_info):
|
||||
}
|
||||
}
|
||||
|
||||
url = urljoin(settings.ZENDESK_URL, 'api/v2/tickets/{}.json'.format(ticket_id))
|
||||
url = urljoin(settings.ZENDESK_URL, f'api/v2/tickets/{ticket_id}.json')
|
||||
|
||||
try:
|
||||
response = requests.put(url, data=json.dumps(data), headers=_get_request_headers())
|
||||
if response.status_code == 200:
|
||||
log.debug(u'Successfully created comment for ticket {}'.format(ticket_id))
|
||||
log.debug(f'Successfully created comment for ticket {ticket_id}')
|
||||
else:
|
||||
log.error(
|
||||
_std_error_message(
|
||||
u'Unexpected response: {} - {}'.format(response.status_code, response.content),
|
||||
f'Unexpected response: {response.status_code} - {response.content}',
|
||||
data
|
||||
)
|
||||
)
|
||||
|
||||
@@ -18,8 +18,8 @@ class ZendeskProxyThrottle(SimpleRateThrottle):
|
||||
Custom throttle rates for this particular endpoint's use case.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.rate = '{}/hour'.format(ZENDESK_REQUESTS_PER_HOUR)
|
||||
super(ZendeskProxyThrottle, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
self.rate = f'{ZENDESK_REQUESTS_PER_HOUR}/hour'
|
||||
super().__init__()
|
||||
|
||||
def get_cache_key(self, request, view):
|
||||
"""
|
||||
|
||||
@@ -24,8 +24,8 @@ class ZendeskProxyThrottle(UserRateThrottle):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.rate = '{}/hour'.format(REQUESTS_PER_HOUR)
|
||||
super(ZendeskProxyThrottle, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
self.rate = f'{REQUESTS_PER_HOUR}/hour'
|
||||
super().__init__()
|
||||
|
||||
|
||||
class ZendeskPassthroughView(APIView):
|
||||
|
||||
Reference in New Issue
Block a user