gui-sparim commited on
Commit
6bc379e
·
verified ·
1 Parent(s): 9c306e9

Completa as equações de custo com legendas compactas

Browse files

Estende às equações de CUNIT e VC a apresentação editável com frações, subscrito e legendas de 8 pt. Preserva as notas do CUB na primeira ocorrência, inclusive junto a equações. Validado com 114 testes e renderização do corpo completo com cinco exercícios, três construções e nove tabelas comparativas.

document/equations.py CHANGED
@@ -1,4 +1,4 @@
1
- """Editable Word equations with compact legends for depreciation factors."""
2
  from docx.oxml import OxmlElement
3
  from docx.oxml.ns import qn
4
  from docx.enum.text import WD_ALIGN_PARAGRAPH
@@ -39,15 +39,7 @@ def _script(kind, base, script):
39
  return node
40
 
41
 
42
- def depreciation_equations(doc):
43
- formulas = [
44
- ([_run('Ross',True), _run(' = '), _fraction([_run('q',True), _run(' + '), _script('sSup',_run('q',True),_run('2'))],[_run('2')])],
45
- 'Ross: parcela de depreciação associada à idade; q: fração de vida útil consumida, obtida dividindo o percentual de vida útil consumida por 100.'),
46
- ([_run('K',True),_run(' = 1 − (1 − '),_run('Ross',True),_run(') × (1 − '),_run('H',True),_run(')')],
47
- 'K: coeficiente de depreciação de Ross-Heidecke; H: coeficiente correspondente ao estado de conservação adotado.'),
48
- ([_script('sSub',_run('K',True),_run('d',True)),_run(' = '),_run('R',True),_run(' + (1 − '),_run('K',True),_run(') × (1 − '),_run('R',True),_run(')')],
49
- 'Kd: fator remanescente aplicado ao custo de reedição, com consideração do valor residual; R: valor residual em forma decimal (percentual ÷ 100); K: coeficiente de depreciação.'),
50
- ]
51
  for index,(elements, legend) in enumerate(formulas):
52
  paragraph = doc.add_paragraph()
53
  paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
@@ -62,3 +54,26 @@ def depreciation_equations(doc):
62
  alinhamento=WD_ALIGN_PARAGRAPH.LEFT,espaco_depois=8)
63
  note.paragraph_format.keep_together = True
64
  note.paragraph_format.keep_with_next = index < len(formulas)-1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Editable Word equations with compact legends for cost and depreciation."""
2
  from docx.oxml import OxmlElement
3
  from docx.oxml.ns import qn
4
  from docx.enum.text import WD_ALIGN_PARAGRAPH
 
39
  return node
40
 
41
 
42
+ def _formula_blocks(doc, formulas):
 
 
 
 
 
 
 
 
43
  for index,(elements, legend) in enumerate(formulas):
44
  paragraph = doc.add_paragraph()
45
  paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
 
54
  alinhamento=WD_ALIGN_PARAGRAPH.LEFT,espaco_depois=8)
55
  note.paragraph_format.keep_together = True
56
  note.paragraph_format.keep_with_next = index < len(formulas)-1
57
+
58
+
59
+ def cost_equations(doc):
60
+ _formula_blocks(doc,[
61
+ ([_run('CUNIT'),_run(' = '),_run('CUB'),_run(' × '),
62
+ _fraction([_run('% CUB')],[_run('100')])],
63
+ 'CUNIT: custo unitário após a adequação (R$/m²); CUB: custo unitário básico histórico (R$/m²); % CUB: percentual de adequação aplicado ao CUB.'),
64
+ ([_run('VC'),_run(' = '),_run('A',True),_run(' × '),_run('CUNIT'),_run(' × (1 + '),
65
+ _fraction([_run('BDI')],[_run('100')]),_run(') × '),_script('sSub',_run('K',True),_run('d',True))],
66
+ 'VC: valor da construção após a depreciação (R$); A: área de cálculo (m²); CUNIT: custo unitário após a adequação (R$/m²); BDI: benefícios e despesas indiretas, em percentual; Kd: fator remanescente com consideração do valor residual.'),
67
+ ])
68
+
69
+
70
+ def depreciation_equations(doc):
71
+ formulas = [
72
+ ([_run('Ross',True), _run(' = '), _fraction([_run('q',True), _run(' + '), _script('sSup',_run('q',True),_run('2'))],[_run('2')])],
73
+ 'Ross: parcela de depreciação associada à idade; q: fração de vida útil consumida, obtida dividindo o percentual de vida útil consumida por 100.'),
74
+ ([_run('K',True),_run(' = 1 − (1 − '),_run('Ross',True),_run(') × (1 − '),_run('H',True),_run(')')],
75
+ 'K: coeficiente de depreciação de Ross-Heidecke; H: coeficiente correspondente ao estado de conservação adotado.'),
76
+ ([_script('sSub',_run('K',True),_run('d',True)),_run(' = '),_run('R',True),_run(' + (1 − '),_run('K',True),_run(') × (1 − '),_run('R',True),_run(')')],
77
+ 'Kd: fator remanescente aplicado ao custo de reedição, com consideração do valor residual; R: valor residual em forma decimal (percentual ÷ 100); K: coeficiente de depreciação.'),
78
+ ]
79
+ _formula_blocks(doc,formulas)
document/evolutivo.py CHANGED
@@ -15,7 +15,7 @@ from .mesa_content import content_for
15
  from .evo_adjustments import REDUCERS, applied_reducers
16
  from .fundamentacao import number, global_foundation
17
  from .footnotes import add_footnote, add_first_footnote, order_footnotes
18
- from .equations import depreciation_equations
19
  from .formatters.table import set_table_font_size
20
  from .formatters.table_fit import fit_table_to_page
21
  from .anexos.metodologia import (_gerar_variaveis, _adicionar_pesquisa,
@@ -235,10 +235,8 @@ def gerar_corpo_evolutivo(doc, imported, num, caracteristicas_refs=None):
235
  refs['construcoes'] = heading(doc,num,'AVALIAÇÃO DAS CONSTRUÇÕES',2)
236
  heading(doc,num,'Aplicação do método da quantificação de custo',3)
237
  add_body_text(doc,'O custo de reedição é documentado a partir da área de cálculo, do custo unitário básico (CUB) histórico, das adequações adotadas, do BDI e da depreciação física com valor residual. Os valores e coeficientes são os registrados para cada sequência construtiva.')
238
- equation(doc,'CUNIT = CUB × (% CUB / 100)')
239
- equation(doc,'VC = área de cálculo × CUNIT × (1 + BDI / 100) × Kd')
240
  content = content_for(source(rows[0]))
241
- definitions_for(doc,rows,['constructionValue','unitCost'])
242
  heading(doc,num,'Características das construções e áreas',4)
243
  for note in content.get('notas_area',[]): add_body_text(doc,note)
244
  groups = construction_groups(rows)
 
15
  from .evo_adjustments import REDUCERS, applied_reducers
16
  from .fundamentacao import number, global_foundation
17
  from .footnotes import add_footnote, add_first_footnote, order_footnotes
18
+ from .equations import cost_equations, depreciation_equations
19
  from .formatters.table import set_table_font_size
20
  from .formatters.table_fit import fit_table_to_page
21
  from .anexos.metodologia import (_gerar_variaveis, _adicionar_pesquisa,
 
235
  refs['construcoes'] = heading(doc,num,'AVALIAÇÃO DAS CONSTRUÇÕES',2)
236
  heading(doc,num,'Aplicação do método da quantificação de custo',3)
237
  add_body_text(doc,'O custo de reedição é documentado a partir da área de cálculo, do custo unitário básico (CUB) histórico, das adequações adotadas, do BDI e da depreciação física com valor residual. Os valores e coeficientes são os registrados para cada sequência construtiva.')
238
+ cost_equations(doc)
 
239
  content = content_for(source(rows[0]))
 
240
  heading(doc,num,'Características das construções e áreas',4)
241
  for note in content.get('notas_area',[]): add_body_text(doc,note)
242
  groups = construction_groups(rows)
document/footnotes.py CHANGED
@@ -16,6 +16,13 @@ def add_first_footnote(doc, pattern, text):
16
  """Attach a note immediately after the first body/table occurrence of a term."""
17
  for element in doc.element.body.iter(qn('w:p')):
18
  paragraph = Paragraph(element, doc._body)
 
 
 
 
 
 
 
19
  match = re.search(pattern, paragraph.text)
20
  if not match:
21
  continue
 
16
  """Attach a note immediately after the first body/table occurrence of a term."""
17
  for element in doc.element.body.iter(qn('w:p')):
18
  paragraph = Paragraph(element, doc._body)
19
+ if element.find('.//'+qn('m:oMath')) is not None:
20
+ display_text = ''.join(node.text or '' for node in element.iter()
21
+ if node.tag in (qn('w:t'),qn('m:t')))
22
+ if re.search(pattern,display_text):
23
+ # Word footnote references belong outside the math object.
24
+ # Keep the expression editable and place its marker after it.
25
+ return add_footnote(doc,paragraph,text)
26
  match = re.search(pattern, paragraph.text)
27
  if not match:
28
  continue
tests/test_equations.py CHANGED
@@ -2,6 +2,7 @@ import copy
2
  import io
3
  import unittest
4
  import zipfile
 
5
 
6
  from docx import Document
7
  from docx.oxml import parse_xml
@@ -9,15 +10,31 @@ from docx.oxml.ns import qn
9
  from docx.enum.text import WD_ALIGN_PARAGRAPH
10
  from unittest.mock import patch
11
 
12
- from document.equations import depreciation_equations
13
  from document.generator import LaudoGenerator
14
  from document.public_text import public_text
15
- from document.footnotes import add_footnote
16
  from document.formatters import add_body_text
17
  from test_evolutivo_documento import fixture,resolved
18
 
19
 
20
  class DepreciationEquationTests(unittest.TestCase):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  def test_editable_equations_keep_fraction_power_subscript_and_small_legends(self):
22
  doc=Document();depreciation_equations(doc)
23
  stream=io.BytesIO();doc.save(stream);stream.seek(0)
 
2
  import io
3
  import unittest
4
  import zipfile
5
+ from lxml import etree
6
 
7
  from docx import Document
8
  from docx.oxml import parse_xml
 
10
  from docx.enum.text import WD_ALIGN_PARAGRAPH
11
  from unittest.mock import patch
12
 
13
+ from document.equations import cost_equations, depreciation_equations
14
  from document.generator import LaudoGenerator
15
  from document.public_text import public_text
16
+ from document.footnotes import add_footnote, add_first_footnote
17
  from document.formatters import add_body_text
18
  from test_evolutivo_documento import fixture,resolved
19
 
20
 
21
  class DepreciationEquationTests(unittest.TestCase):
22
+ def test_cost_formulas_preserve_percentage_conversion_and_first_occurrence_note(self):
23
+ doc=Document();cost_equations(doc)
24
+ formulas=list(doc.element.body.iter(qn('m:oMath')))
25
+ self.assertEqual(len(formulas),2)
26
+ self.assertEqual([''.join(t.text or '' for t in m.iter(qn('m:t'))) for m in formulas],
27
+ ['CUNIT = CUB × % CUB100','VC = A × CUNIT × (1 + BDI100) × Kd'])
28
+ self.assertEqual(len(list(formulas[0].iter(qn('m:f')))),1)
29
+ self.assertEqual(len(list(formulas[1].iter(qn('m:f')))),1)
30
+ expression_xml=etree.tostring(formulas[0])
31
+ add_first_footnote(doc,r'%\s+CUB','Percentual de adequação do CUB.')
32
+ self.assertEqual(etree.tostring(formulas[0]),expression_xml)
33
+ self.assertEqual(len(list(doc.paragraphs[0]._p.iter(qn('w:footnoteReference')))),1)
34
+ self.assertEqual(len(list(doc.paragraphs[1]._p.iter(qn('w:footnoteReference')))),0)
35
+ for index in (1,3):
36
+ self.assertEqual({r.font.size.pt for r in doc.paragraphs[index].runs},{8})
37
+
38
  def test_editable_equations_keep_fraction_power_subscript_and_small_legends(self):
39
  doc=Document();depreciation_equations(doc)
40
  stream=io.BytesIO();doc.save(stream);stream.seek(0)
tests/test_evolutivo_documento.py CHANGED
@@ -242,7 +242,7 @@ class EvolutivoDocumentTests(unittest.TestCase):
242
  conclusion_table(doc,imported)
243
  text = all_text(doc)
244
  for part in ['3.3 AVALIAÇÃO DO TERRENO','3.3.1.1 Variáveis','3.4 AVALIAÇÃO DAS CONSTRUÇÕES','3.4.1.3 BDI','3.5 AVALIAÇÃO DO IMÓVEL OBJETO',
245
- 'Ross-Heidecke','CUNIT = CUB','Kd: fator remanescente','Unidade autônoma da gleba','Composição justificada do caso','50.000,00','4.126,73','10/2021','R$ 427.500,00','R$ 420.000,00','quatrocentos e vinte mil reais','Critério da adoção','CUB composto','Caracterização salva']:
246
  self.assertIn(part,text)
247
  self.assertNotIn('SEGREDO INTERNO',text)
248
  self.assertNotIn('11/2021',text)
 
242
  conclusion_table(doc,imported)
243
  text = all_text(doc)
244
  for part in ['3.3 AVALIAÇÃO DO TERRENO','3.3.1.1 Variáveis','3.4 AVALIAÇÃO DAS CONSTRUÇÕES','3.4.1.3 BDI','3.5 AVALIAÇÃO DO IMÓVEL OBJETO',
245
+ 'Ross-Heidecke','CUNIT: custo unitário','Kd: fator remanescente','Unidade autônoma da gleba','Composição justificada do caso','50.000,00','4.126,73','10/2021','R$ 427.500,00','R$ 420.000,00','quatrocentos e vinte mil reais','Critério da adoção','CUB composto','Caracterização salva']:
246
  self.assertIn(part,text)
247
  self.assertNotIn('SEGREDO INTERNO',text)
248
  self.assertNotIn('11/2021',text)