Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions openedx_learning/apps/authoring/backup_restore/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"""
import zipfile

from openedx_learning.apps.authoring.publishing.api import get_learning_package_by_key

from .toml import TOMLLearningPackageFile

TOML_PACKAGE_NAME = "package.toml"
Expand All @@ -11,9 +13,12 @@
def create_zip_file(lp_key: str, path: str) -> None:
"""
Creates a zip file with a toml file so far (WIP)

Can throw a NotFoundError at get_learning_package_by_key
"""
toml_file = TOMLLearningPackageFile()
toml_file.create(lp_key)
learning_package = get_learning_package_by_key(lp_key)
toml_file = TOMLLearningPackageFile(learning_package)
toml_file.create()
Comment thread
dwong2708 marked this conversation as resolved.
toml_content: str = toml_file.get()
with zipfile.ZipFile(path, "w", compression=zipfile.ZIP_DEFLATED) as zipf:
# Add the TOML string as a file in the ZIP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
"""
import logging

from django.core.management import CommandError
from django.core.management.base import BaseCommand

from openedx_learning.apps.authoring.backup_restore.api import create_zip_file
from openedx_learning.apps.authoring.publishing.api import LearningPackage

logger = logging.getLogger(__name__)

Expand All @@ -23,15 +25,21 @@ def add_arguments(self, parser):
def handle(self, *args, **options):
lp_key = options['lp_key']
file_name = options['file_name']
if not file_name.endswith(".zip"):
raise CommandError("Output file name must end with .zip")
try:
create_zip_file(lp_key, file_name)
message = f'{lp_key} written to {file_name}'
self.stdout.write(self.style.SUCCESS(message))
except Exception as e: # pylint: disable=broad-exception-caught
message = f"Error creating zip file: error {e}"
self.stderr.write(self.style.ERROR(message))
except LearningPackage.DoesNotExist as exc:
message = f"Learning package with key {lp_key} not found"
logger.exception(message)
Comment thread
dwong2708 marked this conversation as resolved.
raise CommandError(message) from exc
except Exception as e:
message = f"Failed to export learning package '{lp_key}': {e}"
logger.exception(
"Failed to create zip file %s (learning‑package key %s)",
file_name,
lp_key,
)
raise CommandError(message) from e
45 changes: 36 additions & 9 deletions openedx_learning/apps/authoring/backup_restore/toml.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,65 @@
from tomlkit import comment, document, dumps, nl, table
from tomlkit.items import Table

from openedx_learning.apps.authoring.publishing.models.learning_package import LearningPackage


class TOMLLearningPackageFile():
"""
Class to create a .toml file of a learning package (WIP)
Class to create a .toml representation of a LearningPackage instance.

This class builds a structured TOML document using `tomlkit` with metadata and fields
extracted from a `LearningPackage` object. The output can later be saved to a file or used elsewhere.
"""

def __init__(self):
def __init__(self, learning_package: LearningPackage):
self.doc = document()
self.learning_package = learning_package

def _create_header(self) -> None:
"""
Adds a comment with the current datetime to indicate when the export occurred.
This helps with traceability and file versioning.
"""
self.doc.add(comment(f"Datetime of the export: {datetime.now()}"))
self.doc.add(nl())

def _create_table(self, params: Dict[str, Any]) -> Table:
"""
Builds a TOML table section from a dictionary of key-value pairs.

Args:
params (Dict[str, Any]): A dictionary containing keys and values to include in the TOML table.

Returns:
Table: A TOML table populated with the provided keys and values.
"""
section = table()
for key, value in params.items():
section.add(key, value)
return section

def create(self, lp_key: str) -> None:
def create(self) -> None:
"""
Process the toml file
Populates the TOML document with a header and a table containing
metadata from the LearningPackage instance.

This method must be called before calling `get()`, otherwise the document will be empty.
"""
self._create_header()
section = self._create_table({
"title": "",
"key": lp_key,
"description": "",
"created": "",
"updated": ""
"title": self.learning_package.title,
"key": self.learning_package.key,
"description": self.learning_package.description,
"created": self.learning_package.created,
"updated": self.learning_package.updated
})
self.doc.add("learning_package", section)

def get(self) -> str:
"""
Returns:
str: The string representation of the generated TOML document.
Ensure `create()` has been called beforehand to get meaningful output.
"""
return dumps(self.doc)