|
16 | 16 | XML_TEMPLATE_PATH = ROOT / 'build' / 'template.xml' |
17 | 17 |
|
18 | 18 |
|
19 | | -# Очистка папки вывода |
20 | | -if OUTPUT_PATH.exists(): |
21 | | - for file in OUTPUT_PATH.iterdir(): |
22 | | - if file.is_dir(): |
23 | | - shutil.rmtree(file) |
24 | | - else: |
25 | | - file.unlink() |
26 | | -else: |
27 | | - OUTPUT_PATH.mkdir(parents=True, exist_ok=True) |
28 | | - |
29 | | - |
30 | | -# Получение base64 от zip-архива всех файлов проекта |
31 | | -sources = [*SOURCE_PATH.glob('**/*.py')] |
32 | | - |
33 | | -bundle_tempfile = tempfile.NamedTemporaryFile(delete=False) |
34 | | - |
35 | | -with zipfile.ZipFile(bundle_tempfile.name, 'w', zipfile.ZIP_DEFLATED) as bundle_file: |
36 | | - for file in sources: |
37 | | - bundle_file.write(file, file.relative_to(SOURCE_PATH)) |
38 | | - |
39 | | -with open(bundle_tempfile.name, 'rb') as f: |
40 | | - bundle_base64 = base64.b64encode(f.read()).decode('ascii') |
41 | | - |
42 | | -bundle_tempfile.close() |
43 | | -os.unlink(bundle_tempfile.name) |
44 | | - |
45 | | - |
46 | | -# Загрузка xml-шаблона вопроса и создание записи об zip-архиве |
47 | | -with XML_TEMPLATE_PATH.open('r', encoding='utf-8') as xml_file: |
48 | | - xml_parser = xml.XMLParser(strip_cdata=False) |
49 | | - xml_template = xml.parse(xml_file, xml_parser) |
50 | | - |
51 | | -xml_template.xpath('//file')[0].text = bundle_base64 |
52 | | - |
53 | | - |
54 | | -# Класс извлечения узла аргументов из конструктора класса и именя вопроса |
| 19 | +# Класс извлечения узла аргументов из конструктора класса и имени задачи |
55 | 20 | class InternalQuestionDataExtractor(ast.NodeVisitor): |
56 | 21 | def visit_FunctionDef(self, node): |
57 | 22 | if node.name != '__init__': |
@@ -96,58 +61,98 @@ def extract(self, node: ast.AST) -> tuple[str | None, ast.arguments | None]: |
96 | 61 | return self.class_name, self.arguments_node, self.question_name |
97 | 62 |
|
98 | 63 |
|
99 | | -# Шаблоны кода, внедряемого в xml-файл |
100 | | -parameters_code_template = r'''import sys |
| 64 | +if __name__ == '__main__': |
| 65 | + # Очистка папки вывода |
| 66 | + if OUTPUT_PATH.exists(): |
| 67 | + for file in OUTPUT_PATH.iterdir(): |
| 68 | + if file.is_dir(): |
| 69 | + shutil.rmtree(file) |
| 70 | + else: |
| 71 | + file.unlink() |
| 72 | + else: |
| 73 | + OUTPUT_PATH.mkdir(parents=True, exist_ok=True) |
| 74 | + |
| 75 | + |
| 76 | + # Получение base64 от zip-архива всех файлов проекта |
| 77 | + sources = [*SOURCE_PATH.glob('**/*.py')] |
| 78 | + |
| 79 | + bundle_tempfile = tempfile.NamedTemporaryFile(delete=False) |
| 80 | + |
| 81 | + with zipfile.ZipFile(bundle_tempfile.name, 'w', zipfile.ZIP_DEFLATED) as bundle_file: |
| 82 | + for file in sources: |
| 83 | + bundle_file.write(file, file.relative_to(SOURCE_PATH)) |
| 84 | + |
| 85 | + with open(bundle_tempfile.name, 'rb') as f: |
| 86 | + bundle_base64 = base64.b64encode(f.read()).decode('ascii') |
| 87 | + |
| 88 | + bundle_tempfile.close() |
| 89 | + os.unlink(bundle_tempfile.name) |
| 90 | + |
| 91 | + |
| 92 | + # Загрузка xml-шаблона задачи и создание записи об zip-архиве |
| 93 | + with XML_TEMPLATE_PATH.open('r', encoding='utf-8') as xml_file: |
| 94 | + xml_parser = xml.XMLParser(strip_cdata=False) |
| 95 | + xml_template = xml.parse(xml_file, xml_parser) |
| 96 | + |
| 97 | + xml_template.xpath('//file')[0].text = bundle_base64 |
| 98 | + |
| 99 | + |
| 100 | + # Шаблоны кода, внедряемого в xml-файл |
| 101 | + parameters_code_template = r'''import sys |
101 | 102 | sys.path.insert(0, 'bundle.zip') |
102 | 103 | from prog_questions import {class_name} |
103 | 104 |
|
104 | 105 | question = {constructor_code} |
105 | 106 | print(question.getTemplateParameters()) |
106 | | -''' |
| 107 | + ''' |
107 | 108 |
|
108 | | -code_template = r'''import sys |
| 109 | + code_template = r'''import sys |
109 | 110 | sys.path.insert(0, 'bundle.zip') |
110 | 111 | from prog_questions import {class_name} |
111 | 112 |
|
112 | 113 | question = {class_name}.initWithParameters("""{{{{ PARAMETERS | e('py') }}}}""") |
113 | | -print(question.test("""{{{{ STUDENT_ANSWER | e('py') }}}}""")) |
114 | | -''' |
| 114 | +print(question.runTest("""{{{{ STUDENT_ANSWER | e('py') }}}}""")) |
| 115 | + ''' |
| 116 | + |
115 | 117 |
|
| 118 | + # Проверка для всех файлов проекта |
| 119 | + for file in sources: |
| 120 | + # Получение информации о классе задачи из файла |
| 121 | + question_class, question_arguments, question_name = QuestionDataExtractor().extract(ast.parse(file.read_text(encoding='utf-8'))) |
116 | 122 |
|
117 | | -# Проверка для всех файлов проекта |
118 | | -for file in sources: |
119 | | - # Получение информации о классе вопроса из файла |
120 | | - question_class, question_arguments, question_name = QuestionDataExtractor().extract(ast.parse(file.read_text(encoding='utf-8'))) |
| 123 | + # Если в файле нет класса задачи - пропускаем |
| 124 | + if question_class is None: |
| 125 | + continue |
121 | 126 |
|
122 | | - # Если в файле нет класса вопроса - пропускаем |
123 | | - if question_class is None: |
124 | | - continue |
| 127 | + # Конвертация узла arguments в массив keyword |
| 128 | + keywords = [ast.keyword(arg='seed', value=ast.Constant(value=Ellipsis))] |
125 | 129 |
|
126 | | - # Конвертация узла arguments в массив keyword |
127 | | - keywords = [] |
| 130 | + if question_arguments is not None: |
| 131 | + for kw_name, kw_value in zip(question_arguments.kwonlyargs, question_arguments.kw_defaults): |
| 132 | + if kw_name.arg == 'seed': |
| 133 | + continue |
128 | 134 |
|
129 | | - if question_arguments is not None: |
130 | | - for kw_name, kw_value in zip(question_arguments.kwonlyargs, question_arguments.kw_defaults): |
131 | | - if kw_name.arg == 'seed': |
132 | | - continue |
| 135 | + kw_name.annotation = None |
| 136 | + keywords.append(ast.keyword(arg=kw_name, value=kw_value)) |
133 | 137 |
|
134 | | - kw_name.annotation = None |
135 | | - keywords.append(ast.keyword(arg=kw_name, value=kw_value)) |
| 138 | + # Создание куска кода с вызовом initTemplate со стандартными параметрами (полученными из кода конструктора) |
| 139 | + call_node = ast.Call(func=ast.Attribute(value=ast.Name(id=question_class), attr='initTemplate'), args=[], keywords=keywords) |
| 140 | + constructor_code = astor.to_source(call_node).rstrip() |
136 | 141 |
|
137 | | - # Создание куска кода с вызовом initTemplate со стандартными параметрами (полученными из кода конструктора) |
138 | | - call_node = ast.Call(func=ast.Attribute(value=ast.Name(id=question_class), attr='initTemplate'), args=[], keywords=keywords) |
139 | | - constructor_code = astor.to_source(call_node).rstrip() |
| 142 | + # Подстановка в шаблоны кода |
| 143 | + parameters_code = parameters_code_template.format(class_name=question_class, constructor_code=constructor_code).lstrip() |
| 144 | + code = code_template.format(class_name=question_class).lstrip() |
140 | 145 |
|
141 | | - # Подстановка в шаблоны кода |
142 | | - parameters_code = parameters_code_template.format(class_name=question_class, constructor_code=constructor_code).lstrip() |
143 | | - code = code_template.format(class_name=question_class).lstrip() |
| 146 | + # Модификация xml-шаблона |
| 147 | + xml_template.xpath('//question/name/text')[0].text = xml.CDATA(question_name) |
| 148 | + xml_template.xpath('//templateparams')[0].text = xml.CDATA(parameters_code) |
| 149 | + xml_template.xpath('//template')[0].text = xml.CDATA(code) |
144 | 150 |
|
145 | | - # Модификация xml-шаблона |
146 | | - xml_template.xpath('//question/name/text')[0].text = xml.CDATA(question_name) |
147 | | - xml_template.xpath('//templateparams')[0].text = xml.CDATA(parameters_code) |
148 | | - xml_template.xpath('//template')[0].text = xml.CDATA(code) |
| 151 | + # Запись в файл и вывод в консоль |
| 152 | + xml_output_path = OUTPUT_PATH / f'{question_class}.xml' |
| 153 | + xml_template.write(xml_output_path, xml_declaration=True, encoding='utf-8') |
149 | 154 |
|
150 | | - # Запись в файл и вывод в консоль |
151 | | - xml_output_path = OUTPUT_PATH / f'{question_class}.xml' |
152 | | - xml_template.write(xml_output_path, xml_declaration=True, encoding='utf-8') |
153 | | - print(xml_output_path) |
| 155 | + # Вывод информации о собранных задачах |
| 156 | + print("Задачи успешно собраны:") |
| 157 | + for built_file in OUTPUT_PATH.glob('*.xml'): |
| 158 | + print(built_file.name) |
0 commit comments