Coverage for visailu/publish.py: 100.00%
45 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-05 19:49:28 +00:00
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-05 19:49:28 +00:00
1"""Publish a valid YAML model as application specific JSON.
3Target format is a naive 10 question array with 4 options each array:
4[
5 {
6 "id": 1,
7 "question": "A",
8 "options": [
9 { "answer": "1", "isCorrect": false },
10 { "answer": "2", "isCorrect": false },
11 { "answer": "3", "isCorrect": true },
12 { "answer": "4", "isCorrect": false }
13 ]
14 },
15 ...
16 {
17 "id": 10,
18 "question": "J",
19 "options": [
20 { "answer": "a", "isCorrect": false },
21 { "answer": "b", "isCorrect": false },
22 { "answer": "c", "isCorrect": true },
23 { "answer": "d", "isCorrect": false }
24 ]
25 },
26]
27"""
29import json
30import pathlib
31from typing import Any, Union, no_type_check
33from visailu import (
34 OUT_QUESTION_COUNT,
35 OUT_ANSWERS_COUNT,
36 log,
37)
38from visailu.validate import validate_path
40AnswerExportType = list[dict[str, Union[str, bool]]]
41QuestionExportType = dict[str, Union[int, str, AnswerExportType]]
42QuizExportType = list[QuestionExportType]
45@no_type_check
46def etl(data: Any) -> QuizExportType:
47 """Extract, load, and transform the data."""
48 id_export = 1
49 quiz_export: QuestionExportType = []
50 questions = data['questions']
51 num_questions = len(questions)
52 if num_questions < OUT_QUESTION_COUNT or OUT_QUESTION_COUNT < num_questions:
53 problem = 'too few' if num_questions < OUT_QUESTION_COUNT else 'too many'
54 log.warning(f'model with {problem} questions {num_questions} instead of 10')
55 for entry in questions:
56 question_export = {
57 'id': id_export,
58 'question': entry['question'],
59 'options': [],
60 }
61 answers = entry['answers']
62 num_answers = len(answers)
63 if num_answers < OUT_ANSWERS_COUNT or OUT_ANSWERS_COUNT < num_answers:
64 problem = 'too few' if num_answers < OUT_ANSWERS_COUNT else 'too many'
65 log.warning(f'model with {problem} answers {num_answers} instead of 4 at question {id_export}')
66 for option in answers:
67 question_export['options'].append({'answer': option['answer'], 'isCorrect': option['rating']})
68 quiz_export.append(question_export)
69 id_export += 1
70 if id_export > OUT_QUESTION_COUNT:
71 break
73 if len(quiz_export) < OUT_QUESTION_COUNT:
74 log.warning(f'quiz with too few questions {len(quiz_export)} instead of 10')
76 return quiz_export
79@no_type_check
80def publish_path(path: str, options=None) -> tuple[int, str, Any]:
81 """Drive the model publication."""
82 code, message, data = validate_path(path)
83 if code != 0:
84 return code, message, data
86 quiz = etl(data)
87 build_path = pathlib.Path('build')
88 build_path.mkdir(parents=True, exist_ok=True)
89 target_path = build_path / (pathlib.Path(path).stem + '.json')
90 with target_path.open('wt', encoding='utf-8') as handle:
91 json.dump(quiz, handle, indent=2)
93 return 0, f'published quiz data at {target_path} (from model at {path})', quiz