Spaces:
Sleeping
Sleeping
Completa as equações de custo com legendas compactas
Browse filesEstende à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 +25 -10
- document/evolutivo.py +2 -4
- document/footnotes.py +7 -0
- tests/test_equations.py +19 -2
- tests/test_evolutivo_documento.py +1 -1
document/equations.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
"""Editable Word equations with compact legends for
|
| 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
|
| 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 |
-
|
| 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
|
| 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)
|