refactor: pyupgrade in content_libraries and contentserver apps (#26892)
This commit is contained in:
@@ -51,7 +51,6 @@ from lxml import etree
|
||||
from opaque_keys.edx.keys import LearningContextKey
|
||||
from opaque_keys.edx.locator import BundleDefinitionLocator, LibraryLocatorV2, LibraryUsageLocatorV2
|
||||
from organizations.models import Organization
|
||||
import six
|
||||
from xblock.core import XBlock
|
||||
from xblock.exceptions import XBlockNotFoundError
|
||||
|
||||
@@ -552,10 +551,10 @@ def update_library(
|
||||
"slug": ref.slug,
|
||||
}
|
||||
if title is not None:
|
||||
assert isinstance(title, six.string_types)
|
||||
assert isinstance(title, str)
|
||||
fields["title"] = title
|
||||
if description is not None:
|
||||
assert isinstance(description, six.string_types)
|
||||
assert isinstance(description, str)
|
||||
fields["description"] = description
|
||||
update_bundle(ref.bundle_uuid, **fields)
|
||||
CONTENT_LIBRARY_UPDATED.send(sender=None, library_key=ref.library_key)
|
||||
@@ -704,7 +703,7 @@ def set_library_block_olx(usage_key, new_olx_str):
|
||||
# Verify that the OLX parses, at least as generic XML:
|
||||
node = etree.fromstring(new_olx_str)
|
||||
if node.tag != block_type:
|
||||
raise ValueError("Invalid root tag in OLX, expected {}".format(block_type))
|
||||
raise ValueError(f"Invalid root tag in OLX, expected {block_type}")
|
||||
# Write the new XML/OLX file into the library bundle's draft
|
||||
draft = get_or_create_bundle_draft(metadata.def_key.bundle_uuid, DRAFT_NAME)
|
||||
write_draft_file(draft.uuid, metadata.def_key.olx_path, new_olx_str.encode('utf-8'))
|
||||
@@ -734,7 +733,7 @@ def create_library_block(library_key, block_type, definition_id):
|
||||
total_blocks = len(lib_bundle.get_top_level_usages())
|
||||
if total_blocks + 1 > settings.MAX_BLOCKS_PER_CONTENT_LIBRARY:
|
||||
raise BlockLimitReachedError(
|
||||
_(u"Library cannot have more than {} XBlocks").format(settings.MAX_BLOCKS_PER_CONTENT_LIBRARY)
|
||||
_("Library cannot have more than {} XBlocks").format(settings.MAX_BLOCKS_PER_CONTENT_LIBRARY)
|
||||
)
|
||||
# Make sure the proposed ID will be valid:
|
||||
validate_unicode_slug(definition_id)
|
||||
@@ -749,10 +748,10 @@ def create_library_block(library_key, block_type, definition_id):
|
||||
)
|
||||
library_context = get_learning_context_impl(usage_key)
|
||||
if library_context.definition_for_usage(usage_key) is not None:
|
||||
raise LibraryBlockAlreadyExists("An XBlock with ID '{}' already exists".format(new_usage_id))
|
||||
raise LibraryBlockAlreadyExists(f"An XBlock with ID '{new_usage_id}' already exists")
|
||||
|
||||
new_definition_xml = '<{}/>'.format(block_type) # xss-lint: disable=python-wrap-html
|
||||
path = "{}/{}/definition.xml".format(block_type, definition_id)
|
||||
new_definition_xml = f'<{block_type}/>' # xss-lint: disable=python-wrap-html
|
||||
path = f"{block_type}/{definition_id}/definition.xml"
|
||||
# Write the new XML/OLX file into the library bundle's draft
|
||||
draft = get_or_create_bundle_draft(ref.bundle_uuid, DRAFT_NAME)
|
||||
write_draft_file(draft.uuid, path, new_definition_xml.encode('utf-8'))
|
||||
@@ -871,7 +870,7 @@ def add_library_block_static_asset_file(usage_key, file_name, file_content):
|
||||
video_block = UsageKey.from_string("lb:VideoTeam:python-intro:video:1")
|
||||
add_library_block_static_asset_file(video_block, "subtitles-en.srt", subtitles.encode('utf-8'))
|
||||
"""
|
||||
assert isinstance(file_content, six.binary_type)
|
||||
assert isinstance(file_content, bytes)
|
||||
def_key, lib_bundle = _lookup_usage_key(usage_key)
|
||||
if file_name != file_name.strip().strip('/'):
|
||||
raise InvalidNameError("file name cannot start/end with / or whitespace.")
|
||||
@@ -953,7 +952,7 @@ def get_bundle_links(library_key):
|
||||
links = blockstore_cache.get_bundle_draft_direct_links_cached(ref.bundle_uuid, DRAFT_NAME)
|
||||
results = []
|
||||
# To be able to quickly get the library ID from the bundle ID for links which point to other libraries, build a map:
|
||||
bundle_uuids = set(link_data.bundle_uuid for link_data in links.values())
|
||||
bundle_uuids = {link_data.bundle_uuid for link_data in links.values()}
|
||||
libraries_linked = {
|
||||
lib.bundle_uuid: lib
|
||||
for lib in ContentLibrary.objects.select_related('org').filter(bundle_uuid__in=bundle_uuids)
|
||||
|
||||
@@ -23,7 +23,7 @@ class ContentLibrariesConfig(AppConfig):
|
||||
PluginURLs.CONFIG: {
|
||||
ProjectType.CMS: {
|
||||
# The namespace to provide to django's urls.include.
|
||||
PluginURLs.NAMESPACE: u'content_libraries',
|
||||
PluginURLs.NAMESPACE: 'content_libraries',
|
||||
},
|
||||
},
|
||||
PluginSettings.CONFIG: {
|
||||
|
||||
@@ -130,7 +130,7 @@ class SearchIndexerBase(ABC):
|
||||
text_search_normalised = text_search.translate(text_search.maketrans('', '', RESERVED_CHARACTERS + '"'))
|
||||
text_search_normalised = text_search_normalised.replace('-', ' ')
|
||||
# Wrap with asterix to enable partial matches
|
||||
text_search_normalised = "*{}*".format(text_search_normalised)
|
||||
text_search_normalised = f"*{text_search_normalised}*"
|
||||
terms = [
|
||||
{
|
||||
'terms': {
|
||||
|
||||
@@ -92,7 +92,7 @@ def usage_for_child_include(parent_usage, parent_definition, parsed_include):
|
||||
)
|
||||
|
||||
|
||||
class LibraryBundle(object):
|
||||
class LibraryBundle:
|
||||
"""
|
||||
Wrapper around a Content Library Blockstore bundle that contains OLX.
|
||||
"""
|
||||
@@ -149,7 +149,7 @@ class LibraryBundle(object):
|
||||
version_arg = {"draft_name": self.draft_name}
|
||||
else:
|
||||
version_arg = {"bundle_version": get_bundle_version_number(self.bundle_uuid)}
|
||||
olx_path = "{}/{}/definition.xml".format(usage_key.block_type, usage_key.usage_id)
|
||||
olx_path = f"{usage_key.block_type}/{usage_key.usage_id}/definition.xml"
|
||||
try:
|
||||
get_bundle_file_metadata_with_cache(self.bundle_uuid, olx_path, **version_arg)
|
||||
return BundleDefinitionLocator(self.bundle_uuid, usage_key.block_type, olx_path, **version_arg)
|
||||
@@ -209,7 +209,7 @@ class LibraryBundle(object):
|
||||
try:
|
||||
xml_node = xml_for_definition(def_key)
|
||||
except: # pylint:disable=bare-except
|
||||
log.exception("Unable to load definition {}".format(def_key))
|
||||
log.exception(f"Unable to load definition {def_key}")
|
||||
return
|
||||
|
||||
for child in xml_node:
|
||||
@@ -220,7 +220,7 @@ class LibraryBundle(object):
|
||||
child_usage = usage_for_child_include(usage_key, def_key, parsed_include)
|
||||
child_def_key = definition_for_include(parsed_include, def_key)
|
||||
except BundleFormatException:
|
||||
log.exception("Unable to parse a child of {}".format(def_key))
|
||||
log.exception(f"Unable to parse a child of {def_key}")
|
||||
continue
|
||||
usages_found[child_usage] = child_def_key
|
||||
add_definitions_children(child_usage, child_def_key)
|
||||
@@ -314,8 +314,8 @@ class LibraryBundle(object):
|
||||
new_links = set(get_bundle_direct_links_with_cache(self.bundle_uuid, draft_name=self.draft_name).items())
|
||||
has_unpublished_changes = new_links != old_links
|
||||
|
||||
published_file_paths = set(f.path for f in get_bundle_files_cached(self.bundle_uuid))
|
||||
draft_file_paths = set(f.path for f in draft_files)
|
||||
published_file_paths = {f.path for f in get_bundle_files_cached(self.bundle_uuid)}
|
||||
draft_file_paths = {f.path for f in draft_files}
|
||||
for file_path in published_file_paths:
|
||||
if file_path not in draft_file_paths:
|
||||
has_unpublished_changes = True
|
||||
|
||||
@@ -27,7 +27,7 @@ class LibraryContextImpl(LearningContext):
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(LibraryContextImpl, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(**kwargs)
|
||||
self.use_draft = kwargs.get('use_draft', None)
|
||||
|
||||
def can_edit_block(self, user, usage_key):
|
||||
|
||||
@@ -31,8 +31,8 @@ class Command(BaseCommand):
|
||||
./manage.py reindex_content_library --clear-all - clear all libraries indexes
|
||||
"""
|
||||
help = dedent(__doc__)
|
||||
CONFIRMATION_PROMPT_CLEAR = u"This will clear all indexed libraries from elasticsearch. Do you want to continue?"
|
||||
CONFIRMATION_PROMPT_ALL = u"Reindexing all libraries might be a time consuming operation. Do you want to continue?"
|
||||
CONFIRMATION_PROMPT_CLEAR = "This will clear all indexed libraries from elasticsearch. Do you want to continue?"
|
||||
CONFIRMATION_PROMPT_ALL = "Reindexing all libraries might be a time consuming operation. Do you want to continue?"
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.23 on 2019-08-28 20:27
|
||||
|
||||
|
||||
@@ -51,6 +50,6 @@ class Migration(migrations.Migration):
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='contentlibrary',
|
||||
unique_together=set([('org', 'slug')]),
|
||||
unique_together={('org', 'slug')},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.26 on 2019-12-11 19:20
|
||||
|
||||
|
||||
@@ -40,6 +39,6 @@ class Migration(migrations.Migration):
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='contentlibrarypermission',
|
||||
unique_together=set([('library', 'user'), ('library', 'group')]),
|
||||
unique_together={('library', 'user'), ('library', 'group')},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -12,7 +12,6 @@ from openedx.core.djangoapps.content_libraries.constants import (
|
||||
ALL_RIGHTS_RESERVED,
|
||||
)
|
||||
from organizations.models import Organization # lint-amnesty, pylint: disable=wrong-import-order
|
||||
import six # lint-amnesty, pylint: disable=wrong-import-order
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
@@ -84,7 +83,7 @@ class ContentLibrary(models.Model):
|
||||
return LibraryLocatorV2(org=self.org.short_name, slug=self.slug)
|
||||
|
||||
def __str__(self):
|
||||
return "ContentLibrary ({})".format(six.text_type(self.library_key))
|
||||
return "ContentLibrary ({})".format(str(self.library_key))
|
||||
|
||||
|
||||
class ContentLibraryPermission(models.Model):
|
||||
@@ -127,4 +126,4 @@ class ContentLibraryPermission(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
who = self.user.username if self.user else self.group.name
|
||||
return "ContentLibraryPermission ({} for {})".format(self.access_level, who)
|
||||
return f"ContentLibraryPermission ({self.access_level} for {who})"
|
||||
|
||||
@@ -176,7 +176,7 @@ class LibraryXBlockStaticFileSerializer(serializers.Serializer):
|
||||
"""
|
||||
Generate the serialized representation of this static asset file.
|
||||
"""
|
||||
result = super(LibraryXBlockStaticFileSerializer, self).to_representation(instance) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
result = super().to_representation(instance)
|
||||
# Make sure the URL is one that will work from the user's browser,
|
||||
# not one that only works from within a docker container:
|
||||
result['url'] = blockstore_api.force_browser_url(result['url'])
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for Blockstore-based Content Libraries
|
||||
"""
|
||||
@@ -6,10 +5,10 @@ from contextlib import contextmanager
|
||||
from io import BytesIO
|
||||
from urllib.parse import urlencode
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.test.utils import override_settings
|
||||
from mock import patch
|
||||
from organizations.models import Organization
|
||||
from rest_framework.test import APITestCase, APIClient
|
||||
from search.search_engine_base import SearchEngine
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for Blockstore-based Content Libraries
|
||||
"""
|
||||
from uuid import UUID
|
||||
from unittest.mock import patch
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Group
|
||||
from django.test.utils import override_settings
|
||||
from mock import patch
|
||||
from organizations.models import Organization
|
||||
|
||||
from openedx.core.djangoapps.content_libraries.libraries_index import LibraryBlockIndexer, ContentLibraryIndexer
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"""
|
||||
Testing indexing of blockstore based content libraries
|
||||
"""
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management import call_command
|
||||
from django.test.utils import override_settings
|
||||
from mock import patch
|
||||
from opaque_keys.edx.locator import LibraryLocatorV2, LibraryUsageLocatorV2
|
||||
from search.search_engine_base import SearchEngine
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Test the Blockstore-based XBlock runtime and content libraries together.
|
||||
"""
|
||||
@@ -29,13 +28,13 @@ from common.djangoapps.student.tests.factories import UserFactory
|
||||
from xmodule.unit_block import UnitBlock
|
||||
|
||||
|
||||
class ContentLibraryContentTestMixin(object):
|
||||
class ContentLibraryContentTestMixin:
|
||||
"""
|
||||
Mixin for content library tests that creates two students and a library.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(ContentLibraryContentTestMixin, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
# Create a couple students that the tests can use
|
||||
cls.student_a = UserFactory.create(username="Alice", email="alice@example.com", password="edx")
|
||||
cls.student_b = UserFactory.create(username="Bob", email="bob@example.com", password="edx")
|
||||
@@ -398,7 +397,7 @@ class ContentLibraryXBlockUserStateTest(ContentLibraryContentTestMixin, TestCase
|
||||
client = APIClient()
|
||||
client.login(username=self.student_a.username, password='edx')
|
||||
student_view_result = client.get(URL_BLOCK_RENDER_VIEW.format(block_key=block_id, view_name='student_view'))
|
||||
problem_key = "input_{}_2_1".format(block_id)
|
||||
problem_key = f"input_{block_id}_2_1"
|
||||
assert problem_key in student_view_result.data['content']
|
||||
|
||||
# And submit a wrong answer:
|
||||
@@ -488,7 +487,7 @@ class ContentLibraryXBlockCompletionTest(ContentLibraryContentTestMixin, Complet
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(ContentLibraryXBlockCompletionTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
# Enable the completion waffle flag for these tests
|
||||
self.override_waffle_switch(True)
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for static asset files in Blockstore-based Content Libraries
|
||||
"""
|
||||
@@ -11,17 +10,17 @@ from openedx.core.djangoapps.content_libraries.tests.base import ContentLibrarie
|
||||
# Binary data representing an SVG image file
|
||||
SVG_DATA = """<svg xmlns="http://www.w3.org/2000/svg" height="30" width="100">
|
||||
<text x="0" y="15" fill="red">SVG is 🔥</text>
|
||||
</svg>""".encode('utf-8')
|
||||
</svg>""".encode()
|
||||
|
||||
# part of an .srt transcript file
|
||||
TRANSCRIPT_DATA = """1
|
||||
TRANSCRIPT_DATA = b"""1
|
||||
00:00:00,260 --> 00:00:01,510
|
||||
Welcome to edX.
|
||||
|
||||
2
|
||||
00:00:01,510 --> 00:00:04,480
|
||||
I'm Anant Agarwal, I'm the president of edX,
|
||||
""".encode('utf-8')
|
||||
"""
|
||||
|
||||
|
||||
class ContentLibrariesStaticAssetsTest(ContentLibrariesRestApiTest):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Block for testing variously scoped XBlock fields.
|
||||
"""
|
||||
|
||||
@@ -167,7 +167,7 @@ class LibraryRootView(APIView):
|
||||
ensure_organization(org_name)
|
||||
except InvalidOrganizationException:
|
||||
raise ValidationError( # lint-amnesty, pylint: disable=raise-missing-from
|
||||
detail={"org": "No such organization '{}' found.".format(org_name)}
|
||||
detail={"org": f"No such organization '{org_name}' found."}
|
||||
)
|
||||
org = Organization.objects.get(short_name=org_name)
|
||||
try:
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
"""
|
||||
Helper functions for caching course assets.
|
||||
"""
|
||||
|
||||
|
||||
import six
|
||||
|
||||
from django.core.cache import caches
|
||||
from django.core.cache.backends.base import InvalidCacheBackendError
|
||||
from opaque_keys import InvalidKeyError
|
||||
@@ -23,14 +19,14 @@ def set_cached_content(content):
|
||||
"""
|
||||
Stores the given piece of content in the cache, using its location as the key.
|
||||
"""
|
||||
CONTENT_CACHE.set(six.text_type(content.location).encode("utf-8"), content, version=STATIC_CONTENT_VERSION)
|
||||
CONTENT_CACHE.set(str(content.location).encode("utf-8"), content, version=STATIC_CONTENT_VERSION)
|
||||
|
||||
|
||||
def get_cached_content(location):
|
||||
"""
|
||||
Retrieves the given piece of content by its location if cached.
|
||||
"""
|
||||
return CONTENT_CACHE.get(six.text_type(location).encode("utf-8"), version=STATIC_CONTENT_VERSION)
|
||||
return CONTENT_CACHE.get(str(location).encode("utf-8"), version=STATIC_CONTENT_VERSION)
|
||||
|
||||
|
||||
def del_cached_content(location):
|
||||
@@ -42,7 +38,7 @@ def del_cached_content(location):
|
||||
"""
|
||||
def location_str(loc):
|
||||
"""Force the location to a Unicode string."""
|
||||
return six.text_type(loc).encode("utf-8")
|
||||
return str(loc).encode("utf-8")
|
||||
|
||||
locations = [location_str(location)]
|
||||
try:
|
||||
|
||||
@@ -6,7 +6,6 @@ Middleware to serve assets.
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
import six
|
||||
from django.http import (
|
||||
HttpResponse,
|
||||
HttpResponseBadRequest,
|
||||
@@ -18,7 +17,6 @@ from django.http import (
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.locator import AssetLocator
|
||||
from six import text_type
|
||||
|
||||
from openedx.core.djangoapps.header_control import force_header_for_response
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
@@ -41,7 +39,7 @@ except ImportError:
|
||||
# TODO: Soon as we have a reasonable way to serialize/deserialize AssetKeys, we need
|
||||
# to change this file so instead of using course_id_partial, we're just using asset keys
|
||||
|
||||
HTTP_DATE_FORMAT = u"%a, %d %b %Y %H:%M:%S GMT"
|
||||
HTTP_DATE_FORMAT = "%a, %d %b %Y %H:%M:%S GMT"
|
||||
|
||||
|
||||
class StaticContentServer(MiddlewareMixin):
|
||||
@@ -144,19 +142,19 @@ class StaticContentServer(MiddlewareMixin):
|
||||
except ValueError as exception:
|
||||
# If the header field is syntactically invalid it should be ignored.
|
||||
log.exception(
|
||||
u"%s in Range header: %s for content: %s",
|
||||
text_type(exception), header_value, six.text_type(loc)
|
||||
"%s in Range header: %s for content: %s",
|
||||
str(exception), header_value, str(loc)
|
||||
)
|
||||
else:
|
||||
if unit != 'bytes':
|
||||
# Only accept ranges in bytes
|
||||
log.warning(u"Unknown unit in Range header: %s for content: %s", header_value, text_type(loc))
|
||||
log.warning("Unknown unit in Range header: %s for content: %s", header_value, str(loc))
|
||||
elif len(ranges) > 1:
|
||||
# According to Http/1.1 spec content for multiple ranges should be sent as a multipart message.
|
||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16
|
||||
# But we send back the full content.
|
||||
log.warning(
|
||||
u"More than 1 ranges in Range header: %s for content: %s", header_value, text_type(loc)
|
||||
"More than 1 ranges in Range header: %s for content: %s", header_value, str(loc)
|
||||
)
|
||||
else:
|
||||
first, last = ranges[0]
|
||||
@@ -164,7 +162,7 @@ class StaticContentServer(MiddlewareMixin):
|
||||
if 0 <= first <= last < content.length:
|
||||
# If the byte range is satisfiable
|
||||
response = HttpResponse(content.stream_data_in_range(first, last))
|
||||
response['Content-Range'] = u'bytes {first}-{last}/{length}'.format(
|
||||
response['Content-Range'] = 'bytes {first}-{last}/{length}'.format(
|
||||
first=first, last=last, length=content.length
|
||||
)
|
||||
response['Content-Length'] = str(last - first + 1)
|
||||
@@ -174,8 +172,8 @@ class StaticContentServer(MiddlewareMixin):
|
||||
newrelic.agent.add_custom_parameter('contentserver.ranged', True)
|
||||
else:
|
||||
log.warning(
|
||||
u"Cannot satisfy ranges in Range header: %s for content: %s",
|
||||
header_value, text_type(loc)
|
||||
"Cannot satisfy ranges in Range header: %s for content: %s",
|
||||
header_value, str(loc)
|
||||
)
|
||||
return HttpResponse(status=416) # Requested Range Not Satisfiable
|
||||
|
||||
@@ -219,7 +217,7 @@ class StaticContentServer(MiddlewareMixin):
|
||||
newrelic.agent.add_custom_parameter('contentserver.cacheable', True)
|
||||
|
||||
response['Expires'] = StaticContentServer.get_expiration_value(datetime.datetime.utcnow(), cache_ttl)
|
||||
response['Cache-Control'] = u"public, max-age={ttl}, s-maxage={ttl}".format(ttl=cache_ttl)
|
||||
response['Cache-Control'] = "public, max-age={ttl}, s-maxage={ttl}".format(ttl=cache_ttl)
|
||||
elif is_locked:
|
||||
if newrelic:
|
||||
newrelic.agent.add_custom_parameter('contentserver.cacheable', False)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#pylint: skip-file
|
||||
|
||||
|
||||
@@ -20,7 +19,7 @@ class Migration(migrations.Migration):
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
|
||||
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
|
||||
('cache_ttl', models.PositiveIntegerField(default=0, help_text=u'The time, in seconds, to report that a course asset is allowed to be cached for.')),
|
||||
('cache_ttl', models.PositiveIntegerField(default=0, help_text='The time, in seconds, to report that a course asset is allowed to be cached for.')),
|
||||
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
@@ -20,7 +17,7 @@ class Migration(migrations.Migration):
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
|
||||
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
|
||||
('cdn_user_agents', models.TextField(default=u'Amazon CloudFront', help_text=u'A newline-separated list of user agents that should be considered CDNs.')),
|
||||
('cdn_user_agents', models.TextField(default='Amazon CloudFront', help_text='A newline-separated list of user agents that should be considered CDNs.')),
|
||||
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
"""
|
||||
Models for contentserver
|
||||
"""
|
||||
|
||||
|
||||
import six
|
||||
|
||||
from config_models.models import ConfigurationModel
|
||||
from django.db.models.fields import PositiveIntegerField, TextField
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CourseAssetCacheTtlConfig(ConfigurationModel):
|
||||
"""
|
||||
Configuration for the TTL of course assets.
|
||||
@@ -18,12 +12,12 @@ class CourseAssetCacheTtlConfig(ConfigurationModel):
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
app_label = 'contentserver'
|
||||
|
||||
cache_ttl = PositiveIntegerField(
|
||||
default=0,
|
||||
help_text=u"The time, in seconds, to report that a course asset is allowed to be cached for."
|
||||
help_text="The time, in seconds, to report that a course asset is allowed to be cached for."
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -32,13 +26,12 @@ class CourseAssetCacheTtlConfig(ConfigurationModel):
|
||||
return cls.current().cache_ttl
|
||||
|
||||
def __repr__(self):
|
||||
return '<CourseAssetCacheTtlConfig(cache_ttl={})>'.format(self.get_cache_ttl())
|
||||
return f'<CourseAssetCacheTtlConfig(cache_ttl={self.get_cache_ttl()})>'
|
||||
|
||||
def __str__(self):
|
||||
return six.text_type(repr(self))
|
||||
return str(repr(self))
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class CdnUserAgentsConfig(ConfigurationModel):
|
||||
"""
|
||||
Configuration for the user agents we expect to see from CDNs.
|
||||
@@ -46,12 +39,12 @@ class CdnUserAgentsConfig(ConfigurationModel):
|
||||
.. no_pii:
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
app_label = 'contentserver'
|
||||
|
||||
cdn_user_agents = TextField(
|
||||
default=u'Amazon CloudFront',
|
||||
help_text=u"A newline-separated list of user agents that should be considered CDNs."
|
||||
default='Amazon CloudFront',
|
||||
help_text="A newline-separated list of user agents that should be considered CDNs."
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -60,7 +53,7 @@ class CdnUserAgentsConfig(ConfigurationModel):
|
||||
return cls.current().cdn_user_agents
|
||||
|
||||
def __repr__(self):
|
||||
return '<WhitelistedCdnConfig(cdn_user_agents={})>'.format(self.get_cdn_user_agents())
|
||||
return f'<WhitelistedCdnConfig(cdn_user_agents={self.get_cdn_user_agents()})>'
|
||||
|
||||
def __str__(self):
|
||||
return six.text_type(repr(self))
|
||||
return str(repr(self))
|
||||
|
||||
@@ -6,17 +6,16 @@ Tests for StaticContentServer
|
||||
import copy
|
||||
|
||||
import datetime
|
||||
import ddt
|
||||
import logging # lint-amnesty, pylint: disable=wrong-import-order
|
||||
import six
|
||||
import unittest # lint-amnesty, pylint: disable=wrong-import-order
|
||||
from uuid import uuid4 # lint-amnesty, pylint: disable=wrong-import-order
|
||||
import logging
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from uuid import uuid4
|
||||
|
||||
import ddt
|
||||
from django.conf import settings
|
||||
from django.test import RequestFactory
|
||||
from django.test.client import Client
|
||||
from django.test.utils import override_settings
|
||||
from mock import patch
|
||||
|
||||
from xmodule.contentstore.django import contentstore
|
||||
from xmodule.contentstore.content import StaticContent, VERSIONED_ASSETS_PREFIX
|
||||
@@ -62,7 +61,7 @@ def get_old_style_versioned_asset_url(asset_path):
|
||||
try:
|
||||
locator = StaticContent.get_location_from_path(asset_path)
|
||||
content = AssetManager.find(locator, as_stream=True)
|
||||
return u'{}/{}{}'.format(VERSIONED_ASSETS_PREFIX, content.content_digest, asset_path)
|
||||
return f'{VERSIONED_ASSETS_PREFIX}/{content.content_digest}{asset_path}'
|
||||
except (InvalidKeyError, ItemNotFoundError):
|
||||
pass
|
||||
|
||||
@@ -78,7 +77,7 @@ class ContentStoreToyCourseTest(SharedModuleStoreTestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(ContentStoreToyCourseTest, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
|
||||
cls.contentstore = contentstore()
|
||||
cls.modulestore = modulestore()
|
||||
@@ -92,14 +91,14 @@ class ContentStoreToyCourseTest(SharedModuleStoreTestCase):
|
||||
|
||||
# A locked asset
|
||||
cls.locked_asset = cls.course_key.make_asset_key('asset', 'sample_static.html')
|
||||
cls.url_locked = six.text_type(cls.locked_asset)
|
||||
cls.url_locked = str(cls.locked_asset)
|
||||
cls.url_locked_versioned = get_versioned_asset_url(cls.url_locked)
|
||||
cls.url_locked_versioned_old_style = get_old_style_versioned_asset_url(cls.url_locked)
|
||||
cls.contentstore.set_attr(cls.locked_asset, 'locked', True)
|
||||
|
||||
# An unlocked asset
|
||||
cls.unlocked_asset = cls.course_key.make_asset_key('asset', 'another_static.txt')
|
||||
cls.url_unlocked = six.text_type(cls.unlocked_asset)
|
||||
cls.url_unlocked = str(cls.unlocked_asset)
|
||||
cls.url_unlocked_versioned = get_versioned_asset_url(cls.url_unlocked)
|
||||
cls.url_unlocked_versioned_old_style = get_old_style_versioned_asset_url(cls.url_unlocked)
|
||||
cls.length_unlocked = cls.contentstore.get_attr(cls.unlocked_asset, 'length')
|
||||
@@ -108,7 +107,7 @@ class ContentStoreToyCourseTest(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Create user and login.
|
||||
"""
|
||||
super(ContentStoreToyCourseTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.staff_usr = AdminFactory.create()
|
||||
self.non_staff_usr = UserFactory.create()
|
||||
|
||||
@@ -220,8 +219,8 @@ class ContentStoreToyCourseTest(SharedModuleStoreTestCase):
|
||||
assert resp.status_code == 206
|
||||
# HTTP_206_PARTIAL_CONTENT
|
||||
assert resp['Content-Range'] ==\
|
||||
u'bytes {first}-{last}/{length}'.format(first=0, last=(self.length_unlocked - 1),
|
||||
length=self.length_unlocked)
|
||||
'bytes {first}-{last}/{length}'.format(first=0, last=(self.length_unlocked - 1),
|
||||
length=self.length_unlocked)
|
||||
assert resp['Content-Length'] == str(self.length_unlocked)
|
||||
|
||||
def test_range_request_partial_file(self):
|
||||
@@ -237,10 +236,10 @@ class ContentStoreToyCourseTest(SharedModuleStoreTestCase):
|
||||
|
||||
assert resp.status_code == 206
|
||||
# HTTP_206_PARTIAL_CONTENT
|
||||
assert resp['Content-Range'] == u'bytes {first}-{last}/{length}'.format(first=first_byte,
|
||||
last=last_byte,
|
||||
length=self.length_unlocked)
|
||||
assert resp['Content-Length'] == str(((last_byte - first_byte) + 1))
|
||||
assert resp['Content-Range'] == 'bytes {first}-{last}/{length}'.format(first=first_byte,
|
||||
last=last_byte,
|
||||
length=self.length_unlocked)
|
||||
assert resp['Content-Length'] == str((last_byte - first_byte) + 1)
|
||||
|
||||
def test_range_request_multiple_ranges(self):
|
||||
"""
|
||||
@@ -413,7 +412,7 @@ class ParseRangeHeaderTestCase(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(ParseRangeHeaderTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.content_length = 10000
|
||||
|
||||
def test_bytes_unit(self):
|
||||
|
||||
Reference in New Issue
Block a user