|
13 | 13 | from django.core.files import File |
14 | 14 | from django.core.files.storage import default_storage as storage |
15 | 15 | from le_utils.constants import exercises |
| 16 | +from le_utils.constants import format_presets |
16 | 17 | from PIL import Image |
17 | 18 |
|
18 | 19 | from contentcuration import models |
@@ -136,6 +137,67 @@ def add_file_to_write(self, filepath, content): |
136 | 137 | f.write(content) |
137 | 138 | self.files_to_write.append(full_path) |
138 | 139 |
|
| 140 | + def _write_raw_perseus_assets(self, assessment_item, images_dir): |
| 141 | + """Write a raw Perseus item's image and graphie assets into ``images_dir`` |
| 142 | + and return the sorted list of package-relative paths written. |
| 143 | +
|
| 144 | + Graphie files are stored as a single delimited blob and are split into |
| 145 | + their ``.svg`` and ``-data.json`` parts here. |
| 146 | + """ |
| 147 | + # For raw perseus JSON questions, the files must be |
| 148 | + # specified in advance. |
| 149 | + |
| 150 | + # Files have been prefetched when the assessment item was |
| 151 | + # queried, so take advantage of that. |
| 152 | + files = sorted(assessment_item.files.all(), key=lambda x: x.checksum) |
| 153 | + image_files = filter( |
| 154 | + lambda x: x.preset_id == format_presets.EXERCISE_IMAGE, files |
| 155 | + ) |
| 156 | + graphie_files = filter( |
| 157 | + lambda x: x.preset_id == format_presets.EXERCISE_GRAPHIE, files |
| 158 | + ) |
| 159 | + written_paths = [] |
| 160 | + for image in image_files: |
| 161 | + image_name = "{}/{}.{}".format( |
| 162 | + images_dir, image.checksum, image.file_format_id |
| 163 | + ) |
| 164 | + with storage.open( |
| 165 | + models.generate_object_storage_name(image.checksum, str(image)), |
| 166 | + "rb", |
| 167 | + ) as content: |
| 168 | + self.add_file_to_write(image_name, content.read()) |
| 169 | + written_paths.append(image_name) |
| 170 | + |
| 171 | + for image in graphie_files: |
| 172 | + svg_name = "{}/{}.svg".format(images_dir, image.original_filename) |
| 173 | + json_name = "{}/{}-data.json".format(images_dir, image.original_filename) |
| 174 | + with storage.open( |
| 175 | + models.generate_object_storage_name(image.checksum, str(image)), |
| 176 | + "rb", |
| 177 | + ) as content: |
| 178 | + content = content.read() |
| 179 | + # in Python 3, delimiter needs to be in bytes format |
| 180 | + content = content.split(exercises.GRAPHIE_DELIMITER.encode("ascii")) |
| 181 | + if len(content) != 2: |
| 182 | + raise ValueError( |
| 183 | + f"Graphie file '{image.original_filename}' " |
| 184 | + f"missing delimiter {exercises.GRAPHIE_DELIMITER!r}" |
| 185 | + ) |
| 186 | + self.add_file_to_write(svg_name, content[0]) |
| 187 | + self.add_file_to_write(json_name, content[1]) |
| 188 | + written_paths.append(svg_name) |
| 189 | + written_paths.append(json_name) |
| 190 | + |
| 191 | + return sorted(written_paths) |
| 192 | + |
| 193 | + def _rewrite_content_storage_refs(self, raw_data, images_dir): |
| 194 | + """Rewrite a raw item's content-storage references to the packaged |
| 195 | + ``images_dir`` (under the ``IMG_PLACEHOLDER`` the renderer resolves).""" |
| 196 | + return raw_data.replace( |
| 197 | + exercises.CONTENT_STORAGE_PLACEHOLDER, |
| 198 | + f"{exercises.IMG_PLACEHOLDER}/{images_dir}", |
| 199 | + ) |
| 200 | + |
139 | 201 | def _add_original_image(self, checksum, filename, new_file_path): |
140 | 202 | """Extract original image handling""" |
141 | 203 | with storage.open( |
@@ -314,15 +376,13 @@ def process_assessment_item(self, assessment_item): |
314 | 376 | processed_answers = self._process_answers(assessment_item) |
315 | 377 | processed_hints = self._process_hints(assessment_item) |
316 | 378 |
|
317 | | - new_file_path = self.get_image_file_path() |
318 | | - new_image_path = f"{exercises.IMG_PLACEHOLDER}/{new_file_path}" |
319 | 379 | context = { |
320 | 380 | "question": question, |
321 | 381 | "question_images": question_images, |
322 | 382 | "answers": processed_answers, |
323 | 383 | "multiple_select": assessment_item.type == exercises.MULTIPLE_SELECTION, |
324 | | - "raw_data": assessment_item.raw_data.replace( |
325 | | - exercises.CONTENT_STORAGE_PLACEHOLDER, new_image_path |
| 384 | + "raw_data": self._rewrite_content_storage_refs( |
| 385 | + assessment_item.raw_data, self.get_image_file_path() |
326 | 386 | ), |
327 | 387 | "hints": processed_hints, |
328 | 388 | "randomize": assessment_item.randomize, |
|
0 commit comments