Skip to content

Commit

Permalink
feat: [AXM-1387] save block size to model
Browse files Browse the repository at this point in the history
  • Loading branch information
kyrylo-kh committed Feb 11, 2025
1 parent 643d2f0 commit 24c0f8a
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 5 deletions.
25 changes: 25 additions & 0 deletions openedx/features/offline_mode/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
Offline mode admin configuration.
"""
from django.contrib import admin

from .models import OfflineBlockSize


class OfflineBlockSizeAdmin(admin.ModelAdmin):
"""
OfflineBlockSize admin configuration.
"""
list_display = (
"course_id",
"block_location",
"size",
)
search_fields = (
"course_id",
"block_location",
)
list_filter = ("course_id",)


admin.site.register(OfflineBlockSize, OfflineBlockSizeAdmin)
24 changes: 24 additions & 0 deletions openedx/features/offline_mode/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.18 on 2025-02-10 16:20

from django.db import migrations, models
import opaque_keys.edx.django.models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='OfflineBlockSize',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('course_id', opaque_keys.edx.django.models.CourseKeyField(db_index=True, max_length=255)),
('block_location', opaque_keys.edx.django.models.UsageKeyField(max_length=255)),
('size', models.PositiveIntegerField(default=0)),
],
),
]
Empty file.
15 changes: 15 additions & 0 deletions openedx/features/offline_mode/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""
Offline mode models.
"""
from django.db import models
from opaque_keys.edx.django.models import CourseKeyField, UsageKeyField


class OfflineBlockSize(models.Model):
"""
Model to store the block size for offline content.
"""

course_id = CourseKeyField(max_length=255, db_index=True)
block_location = UsageKeyField(blank=False, max_length=255)
size = models.PositiveIntegerField(default=0)
23 changes: 22 additions & 1 deletion openedx/features/offline_mode/storage_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .assets_management import block_storage_path, clean_outdated_xblock_files
from .html_manipulator import HtmlManipulator
from .renderer import XBlockRenderer
from .models import OfflineBlockSize

User = get_user_model()
log = logging.getLogger(__name__)
Expand Down Expand Up @@ -61,7 +62,8 @@ def generate_offline_content(self):
tmp_dir = mkdtemp()
try:
self.save_xblock_html(tmp_dir)
self.create_zip_file(tmp_dir, base_path, f'{self.xblock.location.block_id}.zip')
file_size = self.create_zip_file(tmp_dir, base_path, f'{self.xblock.location.block_id}.zip')
self.save_block_size(file_size)
finally:
shutil.rmtree(tmp_dir, ignore_errors=True)

Expand All @@ -84,6 +86,8 @@ def create_zip_file(self, temp_dir, base_path, file_name):
temp_dir (str): The temporary directory path where the content is stored
base_path (str): The base path directory to save the zip file
file_name (str): The name of the zip file
Returns:
int: The file size.
"""
file_path = os.path.join(temp_dir, file_name)
with ZipFile(file_path, 'w') as zip_file:
Expand All @@ -93,11 +97,14 @@ def create_zip_file(self, temp_dir, base_path, file_name):
current_base_path=os.path.join(temp_dir, 'assets'),
current_path_in_zip='assets',
)

with open(file_path, 'rb') as buffered_zip:
content_file = ContentFile(buffered_zip.read())
self.storage.save(base_path + file_name, content_file)
log.info(f'Offline content for {file_name} has been generated.')

return os.path.getsize(file_path)

def add_files_to_zip_recursively(self, zip_file, current_base_path, current_path_in_zip):
"""
Recursively adds files to the zip file.
Expand All @@ -117,3 +124,17 @@ def add_files_to_zip_recursively(self, zip_file, current_base_path, current_path
except OSError:
log.error(f'Error while reading the directory: {current_base_path}')
return

def save_block_size(self, file_size):
"""
Saves the file size of the offline content in the related model.
Args:
file_size (int): The file size of the offline content
"""

OfflineBlockSize.objects.update_or_create(
course_id=self.xblock.course_id,
block_location=self.xblock.location,
defaults={"size": file_size}
)
13 changes: 10 additions & 3 deletions openedx/features/offline_mode/tests/test_storage_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ def test_render_block_html_data_http404(
@patch('openedx.features.offline_mode.storage_management.mkdtemp')
@patch('openedx.features.offline_mode.storage_management.clean_outdated_xblock_files')
@patch('openedx.features.offline_mode.storage_management.block_storage_path')
@patch('openedx.features.offline_mode.storage_management.OfflineContentGenerator.save_block_size')
def test_generate_offline_content_for_modified_xblock(
self,
save_block_size_mock: MagicMock,
block_storage_path_mock: MagicMock,
clean_outdated_xblock_files_mock: MagicMock,
mkdtemp_mock: MagicMock,
Expand All @@ -78,6 +80,8 @@ def test_generate_offline_content_for_modified_xblock(
) -> None:
xblock_mock = Mock()
html_data_mock = 'html_markup_data_mock'
file_size_mock = 1024
create_zip_file_mock.return_value = file_size_mock

OfflineContentGenerator(xblock_mock, html_data_mock).generate_offline_content()

Expand All @@ -90,6 +94,7 @@ def test_generate_offline_content_for_modified_xblock(
block_storage_path_mock.return_value,
f'{xblock_mock.location.block_id}.zip'
)
save_block_size_mock.assert_called_once_with(file_size_mock)
shutil_rmtree_mock.assert_called_once_with(mkdtemp_mock.return_value, ignore_errors=True)

@patch('openedx.features.offline_mode.storage_management.os.path.join')
Expand All @@ -115,6 +120,7 @@ def test_save_xblock_html(
html_manipulator_mock.return_value.process_html.return_value
)

@patch('os.path.getsize', return_value=2048)
@patch('openedx.features.offline_mode.storage_management.log.info')
@patch('openedx.features.offline_mode.storage_management.ContentFile')
@patch('openedx.features.offline_mode.storage_management.open')
Expand All @@ -129,17 +135,18 @@ def test_create_zip_file(
open_context_manager_mock: MagicMock,
content_file_mock: MagicMock,
log_info_mock: MagicMock,
getsize_mock: MagicMock,
) -> None:
xblock_mock = Mock()
html_data_mock = 'html_markup_data_mock'
temp_dir_mock = 'temp_dir_mock'
base_path_mock = 'base_path_mock'
file_name_mock = 'file_name_mock'

OfflineContentGenerator(xblock_mock, html_data_mock).create_zip_file(
temp_dir_mock, base_path_mock, file_name_mock
)
generator = OfflineContentGenerator(xblock_mock, html_data_mock)
file_size = generator.create_zip_file(temp_dir_mock, base_path_mock, file_name_mock)

self.assertEqual(file_size, 2048)
zip_file_context_manager.assert_called_once_with(os.path.join(temp_dir_mock, file_name_mock), 'w')
zip_file_context_manager.return_value.__enter__.return_value.write.assert_called_once_with(
os.path.join(temp_dir_mock, 'index.html'), 'index.html'
Expand Down
2 changes: 1 addition & 1 deletion openedx/features/offline_mode/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Offline mode utils.
"""
import logging

Expand Down

0 comments on commit 24c0f8a

Please sign in to comment.