teste_docs / document /fundamentacao.py
gui-sparim's picture
Publica gerador evolutivo e carrega modelos do repositório MESA
55d9355 verified
Raw History Blame Contribute Delete
8.72 kB
"""Documentary framing equivalent to MESA modelFoundation/globalFoundation.
No valuation is recalculated. Missing evidence remains pending. Equivalence is
checked against MESA's JavaScript, including the per-item minima.
"""
from decimal import Decimal, InvalidOperation
from .mesa_content import content_for
POINTS = {'Grau III': 3, 'Grau II': 2, 'Grau I': 1, 'Sem enquadramento': 0}
def number(value):
try:
if value is None or value == '' or isinstance(value, bool):
return None
text = str(value)
if ',' in text:
text = text.replace('.', '').replace(',', '.')
result = Decimal(text)
return result if result.is_finite() else None
except (InvalidOperation, ValueError):
return None
def grade(points):
return {3: 'Grau III', 2: 'Grau II', 1: 'Grau I', 0: 'Sem enquadramento'}.get(points, 'Pendente')
def threshold(value, limits):
value = number(value)
if value is None:
return None
return next((3-i for i, limit in enumerate(limits) if value <= Decimal(str(limit))), 0)
def model_foundation(source):
terrain = source.get('terreno') or {}
state = terrain.get('fundamentacaoModelo') or {}
context = terrain.get('contextoModelo') or {}
evidence = context.get('normativeEvidence') or {}
evaluation = terrain.get('avaliacao') or {}
content = content_for(source)
items = []
def add(key, label, points, detail=''):
items.append({'key': key, 'label': label, 'points': points, 'grade': grade(points), 'detail': detail})
for key, label in [('caracterizacao', 'Caracterização do imóvel avaliando'), ('identificacaoDados', 'Identificação dos dados de mercado')]:
saved = state.get(key) or {}
selected = saved.get('grau')
desc = content.get('descricoes_modelo', {}).get(key, {}).get(selected, '')
detail = ' '.join(filter(None, [desc, saved.get('justificativa')]))
add(key, label, POINTS.get(selected), detail or 'Enquadramento não informado pelo avaliador.')
n, k = number(evidence.get('n')), number(evidence.get('k'))
qty = next((points for mult, points in [(6, 3), (4, 2), (3, 1)] if n >= mult*(k+1)), 0) if n is not None and k is not None and k >= 0 else None
add('quantidadeDados', 'Quantidade de dados de mercado', qty, f'n = {n}; k = {k}.' if qty is not None else 'n e k indisponíveis no contexto salvo.')
extrap = POINTS.get(evaluation.get('fundamentacao'))
if extrap is None and 'qtd_extrapolacoes' in evaluation:
count = max(Decimal(0),number(evaluation['qtd_extrapolacoes']) or Decimal(0))
impact = number(evaluation.get('perc_ext')) or Decimal(0)
severe = any(isinstance(item, dict) and item.get('status') == 'grave' for item in (evaluation.get('extrapolacoes') or {}).values())
extrap = 0 if severe or (impact is not None and impact > 20) else 3 if count == 0 else 2 if count == 1 and impact is not None and impact <= 15 else 1
count_text = evaluation.get('qtd_extrapolacoes')
impact_text = evaluation.get('perc_ext')
detail = 'Nenhuma variável extrapolada.' if count_text == 0 else f"Variáveis extrapoladas: {count_text if count_text is not None else 'não informado'}." + (f' Impacto: {impact_text}%.' if impact_text is not None else '')
add('extrapolacao', 'Extrapolação', extrap, detail)
pvalues = [(item.get('name', ''), number(item.get('pValue'))) for item in evidence.get('regressorPValues', []) if isinstance(item, dict)]
worst = max(((name, p) for name, p in pvalues if p is not None), key=lambda item: item[1], default=('', None))
add('significanciaRegressores', 'Significância dos regressores', threshold(worst[1], [.1,.2,.3]), f'Maior p-valor: {worst[1]} ({worst[0]}).' if worst[1] is not None else 'p-valores indisponíveis.')
add('significanciaModelo', 'Teste F', threshold(evidence.get('fPValue'), [.01,.02,.05]), f"p-valor: {evidence.get('fPValue', 'não informado')}.")
by_key = {item['key']: item['points'] for item in items}
p = [by_key[key] for key in ['caracterizacao','quantidadeDados','identificacaoDados','extrapolacao','significanciaRegressores','significanciaModelo']]
total = sum(value for value in p if value is not None)
result = 'Pendente'
requirements = state.get('requisitosGrauIII') or {}
req_keys = [item['key'] for item in content.get('requisitos_modelo', [])]
candidate = all(value is not None for value in p) and total >= 16 and all(value >= minimum for value, minimum in zip(p,[2,3,2,3,3,3]))
missing_req = candidate and any(requirements.get(key) not in ('sim','nao',True,False) for key in req_keys)
if all(value is not None for value in p) and not missing_req:
if candidate and all(requirements.get(key) in ('sim',True) for key in req_keys): result = 'Grau III'
elif total >= 10 and all(value >= minimum for value, minimum in zip(p,[1,2,1,2,2,2])): result = 'Grau II'
elif all(value >= 1 for value in p): result = 'Grau I'
else: result = 'Sem enquadramento'
return {'items': items, 'totalPoints': total, 'grade': result, 'requirements': requirements, 'missingRequirements': bool(missing_req)}
def construction_foundation(source):
construction = source.get('construcao') or {}
saved = construction.get('fundamentacaoMqc') or (construction.get('avaliacao') or {}).get('fundamentacaoMqc') or {}
defs = saved.get('definitions') or {}
# Labels are extracted from the very function that assigns the MESA scores.
import re
rule = content_for(source)['funcoes_mqc']['calcularEnquadramento']
options = re.findall(r"definitions\.(\w+) === '([^']+)' \? (\d)", rule)
items = []
for key, label in [('formaCub','Custo direto'),('tipoBdi','BDI'),('depreciacao','Depreciação física')]:
value = defs.get(key)
points = next((int(score) for field, text, score in options if field == key and value == text), 1 if value else None)
items.append({'label':label,'points':points,'grade':grade(points),'detail':value or 'Critério não informado.'})
p = [item['points'] for item in items]
total = sum(value for value in p if value is not None)
result = 'Pendente'
if all(value is not None for value in p):
if total >= 7 and p[0] == 3 and min(p[1:]) >= 2: result = 'Grau III'
elif total >= 5 and p[0] >= 2 and p[1] >= 2: result = 'Grau II'
elif total >= 3: result = 'Grau I'
else: result = 'Fora dos critérios'
return {'items':items,'totalPoints':total,'grade':result}
def global_foundation(source):
terrain = model_foundation(source)
construction = construction_foundation(source)
result = source.get('resultadoComposicao') or {}
vt = number((source.get('terreno') or {}).get('valorTotal'))
vc = number((source.get('construcao') or {}).get('valorTotal'))
# calculate(year).beforeFc in MESA is the sum of these two saved components.
total_value = vt+vc if vt is not None and vc is not None else None
items = []
for label, value, foundation in [('Terreno',vt,terrain),('Construções',vc,construction)]:
share = value/total_value if value is not None and total_value is not None and total_value > 0 else None
reduced = value is not None and value > 0 and share is not None and share < Decimal('.15')
points = 2 if reduced else POINTS.get(foundation['grade'])
# Global MESA only recognises the three positive grades.
if not reduced and points == 0: points = None
items.append({'label':label,'grade':grade(points),'points':points,'detail':'Participação inferior a 15%; atribuídos dois pontos ao item.' if reduced else f"Fundamentação do componente: {foundation['grade']}.", 'share':share})
fc = str((source.get('fundamentacaoGlobal') or {}).get('fatorComercializacao') or '')
option = next((item for item in content_for(source)['criterios_fc'] if item['value'] == fc), {})
items.append({'label':'Fator de comercialização','grade':option.get('grade','Pendente'),'points':option.get('points'),'detail':option.get('label','Critério do Fc não informado.')})
p = [item['points'] for item in items]
total = sum(value for value in p if value is not None)
result_grade = 'Pendente'
if all(value is not None for value in p):
if total >= 8 and p[0] == p[1] == 3 and p[2] >= 2: result_grade = 'Grau III'
elif total >= 5 and p[0] >= 2 and p[1] >= 2: result_grade = 'Grau II'
elif total >= 3 and min(p) >= 1: result_grade = 'Grau I'
else: result_grade = 'Sem enquadramento'
return {'items':items,'totalPoints':total,'grade':result_grade,'terrain':terrain,'construction':construction}