-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
342 lines (270 loc) · 11.9 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# Importando a biblioteca 'sys' para receber os pa-
# râmetros via linha de comando
import sys
# Importando biblioteca de datas
from datetime import datetime
# Importando biblioteca para fazer parsing dos
# argumentos.
import argparse
# Importação da biblioteca de interface com o SO
import os
# Importação de biblioteca de expressões regulares
import re
# Importando biblioteca para utilização de JSON
import json
# Importando subprocesso para injetar comandos no
# terminal do Sistema Operacional
import subprocess
# Regex de nomes de arquivos que devem ser incluídos
including_names = []
# Definição do header do arquivo latex do jeito que
# costumo usar
header = r'''\documentclass{article}
\usepackage[top=20mm]{geometry}
\usepackage[utf8]{inputenc}
\usepackage[brazil]{babel}
\usepackage{xcolor}
\usepackage{float}
\usepackage{graphicx}
\usepackage{color}
\usepackage{multirow}
\usepackage{hyperref}
\usepackage{caption}
\usepackage{longtable}
\usepackage{minted}
\usepackage{url}
\usepackage{tikz}
\usemintedstyle{manni}
\DeclareCaptionType{script}
\newcommand{\leg}[1]{
\begin{center}
\textbf{Fonte: } #1.
\end{center}
\vspace{0.5cm}
}
\begin{document}
'''
# Definição do rodapé do documento
footer = '\n' + r'''\end{document}'''
# Definição do header de cada bloco de código
code_header = r'''
\begin{minted}[linenos]{'''
# Definição do rodapé de cada bloco de código
code_footer = r'''
\end{minted}
'''
def remove_prints_octave_matlab ( filename: str ):
# abrindo arquivo a ser analisado
file = open(filename + '.m', 'r')
# arquivo temporário para armazenar versão
# modificada do arquivo original
file_cache = open(filename + '_tmpcache.m', 'w')
# string para template de saída
output = ''
for line in file:
# adiciona à string de saída toda linha que
# não contém o comando print
if 'print(' not in line:
output += line
# escreve no arquivo provisório de saída
file_cache.write(output)
def search ( extension: str, data ):
"""
Busca no arquivo 'data' passado por parâmetros,
pelo nome da linguagem que contém a extensão pas-
sada.
"""
for entry in data:
try:
if extension in entry['extensions']: return(entry["name"])
except KeyError: pass
return None
def find_language_name ( extension: str ) :
"""
Procura o nome da linguagem a partir da extensão do
arquivo passado por parâmetro.
"""
# Abre json com as linguagens e suas extensões
languages = json.load(open('lang.json', 'r'))['languages']
# Returna o resultado da busca pela extensão
return search(extension, languages)
def filter_files ( lista ) :
"""
Função para filtrar arquivos a partir de uma lista
passada por parâmetros. O retorno é uma lista de no-
mes de arquivos que contém apenas nomes com exten-
sões contidas na estrutura 'including_names'
"""
# Vetor de nomes a ser retornado
final = []
# Itera sobre os nomes incluídos
for i in including_names:
# Compila a expressão regular correspondente
regex = re.compile(i)
# Itera sobre os caminhos da lista passada
for l in lista:
# Se o arquivo contém a extensão atual,
# adiciona ao vetor de nomes a ser retor-
# nado.
if regex.match(l):
final.append(l)
return final
def generate_latex_code ( template: str ):
"""
Gera um arquivo latex com o template passado por parametro
e salva na pasta saída
"""
# Abre o arquivo onde será gravado
file_out = open('saida/saida' + datetime.timestamp(datetime.now()).__str__() + '.tex' , 'w')
# Escreve o arquivo
file_out.write(template)
def execute ( parser ):
"""
Função que faz o carregamento dos arquivos e gera o template
para gerar no final, o arquivo latex básico.
"""
# Array que será carregado com os caminhos para os arquivos
# que irão ser carregados.
files_to_use = []
# Itera sobre uma pasta, buscando nome das subpastas e nome
# dos arquivos para que os mesmos sejam filtrados pela fun-
# ção 'filter_files()'.
for (dirpath, dirnames, filenames) in os.walk('exercicios'):
# Caso haja arquivos, verifica se os mesmos compreendem
# arquivos com as extensões desejadas, e se sim, adicio-
# nam os mesmos (com o caminho completo) ao array de ar-
# quivos.
if len(filenames) > 0:
for file in filter_files(filenames):
files_to_use.append(dirpath.__str__() + '/' + file)
# Inicia o template com o header do documento
template = header
# Abre o arquivo com os dados do cabeçalho
file = open('assets/inicio.tex', 'r')
# Adiciona cada uma das linhas do cabeçalho no
# template
for line in file:
template += line
template += '\n'
files_to_use.sort()
# Variável que irá armazenar a questão atual para que
# possamos gerar uma seção com seu número no latex:
# - questao_atual = 1 irá gerar um látex com a seção
# \section{Exercício 1}
questao_atual = 0
# Itera sobre os arquivos do array de arquivos filtrados
for file_path in files_to_use:
# Abre o arquivo corresposnde com função de leitura
file_in = open(file_path, 'r')
# Tenta uma busca pelo número da questão/exercício
# no nome do arquivo, exemplo:
# - arquivo 'pasta_1/pasta_2/ex1/ex1.m' tem número
# de exercício = 1
numero_exercicio = re.search(r'(?<=\/[a-zA-Z]{2})([0-9]+)(?=\.m)', file_path)
# Se a busca obtiver sucesso atualiza a variavel de
# controle 'questao_atual' e agrega ao template o có-
# digo para gerar a seção correspondente e a legenda
# no latex.
#
# Se a busca não obtiver sucesso, uma segunda busca
# irá ser testada. Essa tentativa representa os exer-
# cícios com tópicos/letras:
# - Nº1 letra a, nº2 letra b etc.
# variavel para armazenar a questao atual
questao_atual = 0
if numero_exercicio:
# Atualiza a questão atual
questao_atual = numero_exercicio.group(0)
# Adiciona seção correspondente ao template
template += '\n' + r'''\section*{Exercício ''' + questao_atual + '}\n'
# Adiciona legenda no bloco atual do template
template += '\n' + r'''\captionof{script}{Script para execução do exercício ''' + questao_atual + r'''}\vspace{0.2cm}''' + '\n'
else:
# Tenta encontrar o número do exercício com o se-
# guinte padrão:
# - arquivo 'pasta_1/pasta_2/ex1/ex1_a.m' tem número
# de exercício = 1
numero_exercicio = re.search(r'(?<=\/[a-zA-Z]{2})([0-9]{1})(?=\_[a-z]\.m)', file_path)
# Se a busca for bem sucedida:
if numero_exercicio:
# Compara se o número encontrado é diferente da
# questão atual
if numero_exercicio.group(0) != questao_atual:
# Atualiza a questão atual com o novo número
questao_atual = numero_exercicio.group(0)
# Gera uma seção nova com o nome da questão atual
template += '\n\n' + r'''\section*{Exercício ''' + questao_atual + '}\n'
# Tenta uma busca pela letra/tópico do exercício:
# - arquivo 'pasta_1/pasta_2/ex1/ex1_a.m' tem letra
# de exercício = a
letra_exercicio = re.search(r'(?<=\/[a-zA-Z]{2}[0-9]{1}\_)([a-z]+)(?=\.m)', file_path)
# Caso a busca seja bem sucedida
if letra_exercicio:
# Gera uma subseção para o tópico/letra encontra-
# dos na busca
template += r'''\subsection*{Letra ''' + letra_exercicio.group(0) + '}\n'
# Adiciona a legenda do código no bloco atual do template
template += '\n' + r'''\captionof{script}{Script para execução do exercício ''' + questao_atual + r''' letra ''' + letra_exercicio.group(0) + r'''}\vspace{0.2cm}''' + '\n'
# Busca a extensão do arquivo.
extensao = re.search(r'(\.[^.]+)$', file_path)
# Caso a busca seja bem sucedida.
if extensao:
# Adiciona ao template o cabeçalho de bloco de código e a lin-
# guagem buscada a partir da extensão do arquivo.
template += code_header + find_language_name(extensao.group(0)) + '}\n'
# Para cada linha do arquivo contendo o código do arquivo
# adiciona a mesma ao template,
for line in file_in:
template += line
# Adiciona o rodapé de código no template
template += code_footer
# Adiciona a fonte do código como autores
template += '\n' + r'''\leg{autores}''' + '\n'
if parser.parse_args().o or parser.parse_args().x:
# Adiciona subseção para saída do script
template += '\n' + r'''\subsection*{Saída do exercício ''' + questao_atual + '}\n'
# Adiciona abertura de verbatim para saída do comando
template += '\n' + r'''\begin{small}''' + '\n' + r'''\begin{verbatim}''' + '\n'
if parser.parse_args().x:
if extensao.group(0) == '.m':
try:
# removendo print() do arquivo .m (dispara uma exceção no python)
script_execution_output = remove_prints_octave_matlab('exercicios/ex' + questao_atual + '/ex' + questao_atual)
# adiciona saída do programa gerada a partir da execução do script no terminal
# do sistema operacional
template += str(subprocess.check_output([ 'octave', 'exercicios/ex' + questao_atual + '/ex' + questao_atual + '_tmpcache.m' ], universal_newlines=True))
# remove arquivo de cache
subprocess.check_output([ 'rm', 'exercicios/ex' + questao_atual + '/ex' + questao_atual + '_tmpcache.m' ])
except:
print('[ warning ] : Problema na execução do script exercicios/ex' + questao_atual + '/ex' + questao_atual + '.m')
if parser.parse_args().o or parser.parse_args().x:
# Adiciona fechamento de verbatim para saída do comando
template += r'''\end{verbatim}''' + '\n' + r'''\end{small}''' + '\n'
# Adiciona a fonte da saída como autores
template += '\n' + r'''\leg{autores}''' + '\n'
# Adiciona o rodapé do documento ao template
template += footer
# Gera e salva o código latex do template gerado anteriormente
generate_latex_code(template)
def main():
"""
Cria um parser de argumentos para receber as extensões de ar-
quivos a ser processadas, e chama a função para gerar o latex.
"""
# Cria o parser de argumentos
parser = argparse.ArgumentParser(description='Gerador de relatórios')
# Adiciona um tipo de argumento para receber as extensões.
parser.add_argument('-e', help='extensões aceitáveis', nargs='+', action='append')
parser.add_argument('-o', help='se passado, adiciona seção de saída do arquivo', action='store_true')
parser.add_argument('-x', help='se passado, executa o arquivo (caso haja suporte) e captura a saída. \
Ao passar tal argumento, o argumento "-o" é ativado por padrão.', action='store_true')
if parser.parse_args().e:
# Filtra as extensões.
extensions = list(map(lambda x: '.' + x if '.' not in x else x, parser.parse_args().e[0]))
# Adiciona o regex aos nomes inclusos para cada extensão
# obtida via parâmetro.
for i in extensions:
including_names.append('.+\\' + i + '$')
execute(parser)
if __name__ == "__main__":
main()