Persano commited on
Commit
b8958aa
·
verified ·
1 Parent(s): e6f6e51

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +262 -129
app.py CHANGED
@@ -1,157 +1,290 @@
1
  import os
2
  os.environ['FONTCONFIG_PATH'] = '/tmp/fontconfig'
3
  os.makedirs('/tmp/fontconfig', exist_ok=True)
 
4
  from flask import Flask, render_template, request, send_file
5
  import matplotlib.pyplot as plt
6
  import pandas as pd
7
  import io
8
  import base64
 
 
9
  from datetime import datetime
10
 
11
  app = Flask(__name__)
12
 
13
- # Função para formatar em reais (R$)
14
  def formatar_brl(valor):
 
15
  return f"R$ {valor:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
16
 
17
- # Função principal para simular os investimentos
18
- def simular_investimentos(capital, studio_ret, valorizacao, franquia_ret, acoes_ret, renda_fixa, inflacao, anos=10):
19
- resultados = {
20
- 'Studio': [capital],
21
- 'Imóvel': [capital],
22
- 'Franquia': [capital],
23
- 'Ações': [capital],
24
- 'Renda Fixa': [capital]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
26
 
27
- for ano in range(1, anos + 1):
28
- resultados['Studio'].append(resultados['Studio'][-1] * (1 + (studio_ret * 12 / 100)))
29
- resultados['Imóvel'].append(resultados['Imóvel'][-1] * (1 + valorizacao / 100))
30
- resultados['Franquia'].append(resultados['Franquia'][-1] + franquia_ret)
31
- resultados['Ações'].append(resultados['Ações'][-1] * (1 + acoes_ret / 100))
32
- resultados['Renda Fixa'].append(resultados['Renda Fixa'][-1] * (1 + renda_fixa / 100))
33
-
34
- anos_lista = list(range(0, anos + 1))
35
- df = pd.DataFrame(resultados, index=anos_lista)
36
- df.index.name = 'Ano'
37
-
38
- return df
39
-
40
- # Função para gerar gráfico em base64
41
- def gerar_grafico(df):
42
- plt.figure(figsize=(10, 5))
43
- for coluna in df.columns:
44
- plt.plot(df.index, df[coluna], label=coluna)
45
- plt.title('Projeção de Investimentos em 10 Anos')
46
- plt.xlabel('Ano')
47
- plt.ylabel('Valor (R$)')
48
  plt.legend()
49
  plt.grid(True)
50
-
51
- buffer = io.BytesIO()
52
  plt.tight_layout()
53
- plt.savefig(buffer, format='png')
 
 
 
 
54
  plt.close()
55
- buffer.seek(0)
56
- return base64.b64encode(buffer.getvalue()).decode('utf-8')
57
 
58
- # Rota principal
59
- @app.route("/", methods=["GET", "POST"])
60
- def index():
61
- if request.method == "POST":
62
- try:
63
- # Captura os dados do formulário
64
- capital = float(request.form["capital"])
65
- studio_ret = float(request.form["studio_ret"])
66
- valorizacao = float(request.form["valorizacao"])
67
- franquia_ret = float(request.form["franquia_ret"])
68
- acoes_ret = float(request.form["acoes_ret"])
69
- renda_fixa = float(request.form["renda_fixa"])
70
- inflacao = float(request.form["inflacao"])
71
-
72
- # Simula os investimentos
73
- df = simular_investimentos(capital, studio_ret, valorizacao, franquia_ret, acoes_ret, renda_fixa, inflacao)
74
- grafico = gerar_grafico(df)
75
- valor_final = df.iloc[-1]
76
- investimento_mais_valorizado = valor_final.idxmax()
77
- valor_mais_alto = formatar_brl(valor_final.max())
78
-
79
- # Converte o dataframe para HTML formatado
80
- df_formatado = df.applymap(formatar_brl)
81
- tabela_html = df_formatado.to_html(classes="table table-striped", border=0)
82
-
83
- # Renderiza o template com os resultados
84
- return render_template(
85
- "index.html",
86
- tabela=tabela_html,
87
- grafico=grafico,
88
- investimento_mais_valorizado=investimento_mais_valorizado,
89
- valor_mais_alto=valor_mais_alto,
90
- capital=capital,
91
- studio_ret=studio_ret,
92
- valorizacao=valorizacao,
93
- franquia_ret=franquia_ret,
94
- acoes_ret=acoes_ret,
95
- renda_fixa=renda_fixa,
96
- inflacao=inflacao
97
- )
98
- except Exception as e:
99
- return f"Erro: {e}"
100
- else:
101
- return render_template("index.html")
102
-
103
- # Rota para download do PDF
104
- @app.route("/download_pdf", methods=["POST"])
105
- def download_pdf():
106
- capital = float(request.form["capital"])
107
- studio_ret = float(request.form["studio_ret"])
108
- valorizacao = float(request.form["valorizacao"])
109
- franquia_ret = float(request.form["franquia_ret"])
110
- acoes_ret = float(request.form["acoes_ret"])
111
- renda_fixa = float(request.form["renda_fixa"])
112
- inflacao = float(request.form["inflacao"])
113
-
114
- df = simular_investimentos(capital, studio_ret, valorizacao, franquia_ret, acoes_ret, renda_fixa, inflacao)
115
- valor_final = df.iloc[-1]
116
- investimento_mais_valorizado = valor_final.idxmax()
117
- valor_mais_alto = formatar_brl(valor_final.max())
118
-
119
- # Gera gráfico
120
- grafico = gerar_grafico(df)
121
- img_data = base64.b64decode(grafico)
122
-
123
- # Gera tabela em HTML
124
- df_formatado = df.applymap(formatar_brl)
125
- tabela_html = df_formatado.to_html(classes="table table-striped", border=0)
126
-
127
- from xhtml2pdf import pisa
128
- from flask import make_response
129
- from jinja2 import Template
130
-
131
- html = f"""
132
- <html>
133
- <body>
134
- <h1>Relatório de Investimentos</h1>
135
- <p><strong>Investimento com maior retorno:</strong> {investimento_mais_valorizado}</p>
136
- <p><strong>Valor final estimado:</strong> {valor_mais_alto}</p>
137
- <img src="data:image/png;base64,{grafico}" style="width: 100%; max-height: 400px;" />
138
- {tabela_html}
139
- </body>
140
- </html>
141
- """
142
 
143
- # Converte HTML para PDF
144
- pdf_buffer = io.BytesIO()
145
- pisa.CreatePDF(io.StringIO(html), dest=pdf_buffer)
146
- pdf_buffer.seek(0)
147
 
148
- return send_file(
149
- pdf_buffer,
150
- as_attachment=True,
151
- download_name=f"relatorio_{datetime.now().strftime('%Y-%m-%d')}.pdf",
152
- mimetype='application/pdf'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  )
154
 
 
 
 
 
 
 
 
155
 
156
  if __name__ == '__main__':
157
- app.run(host='0.0.0.0', port=7860, debug=True)
 
1
  import os
2
  os.environ['FONTCONFIG_PATH'] = '/tmp/fontconfig'
3
  os.makedirs('/tmp/fontconfig', exist_ok=True)
4
+
5
  from flask import Flask, render_template, request, send_file
6
  import matplotlib.pyplot as plt
7
  import pandas as pd
8
  import io
9
  import base64
10
+ from matplotlib.ticker import FuncFormatter
11
+ from xhtml2pdf import pisa
12
  from datetime import datetime
13
 
14
  app = Flask(__name__)
15
 
 
16
  def formatar_brl(valor):
17
+ """Formata número para moeda brasileira: R$ 1.234,56"""
18
  return f"R$ {valor:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
19
 
20
+ def gerar_analise(investimentos_finais, capital):
21
+ melhor = max(investimentos_finais, key=investimentos_finais.get)
22
+ valor_melhor = investimentos_finais[melhor]
23
+ retorno_pct = ((valor_melhor - capital) / capital) * 100
24
+ analise = f"""
25
+ Após análise dos cenários projetados para 5 anos, o investimento <strong>{melhor}</strong> apresenta o melhor desempenho,
26
+ com um valor final estimado de <strong>{formatar_brl(valor_melhor)}</strong>, equivalente a um retorno de <strong>{retorno_pct:.1f}%</strong>.
27
+ """
28
+ return analise
29
+
30
+ @app.route("/", methods=["GET", "POST"])
31
+ def index():
32
+ if request.method == "POST":
33
+ capital = float(request.form["capital"])
34
+ studio_ret = float(request.form["studio_ret"])
35
+ valorizacao = float(request.form["valorizacao"])
36
+ franquia_ret = float(request.form["franquia_ret"])
37
+ acoes_ret = float(request.form["acoes_ret"])
38
+ renda_fixa = float(request.form["renda_fixa"])
39
+ inflacao = float(request.form["inflacao"])
40
+
41
+ anos = list(range(1, 6))
42
+
43
+ # Valorização patrimonial do studio (capital cresce com valorização anual)
44
+ patrimonio_studio = [capital * ((1 + valorizacao / 100) ** ano) for ano in anos]
45
+
46
+ # Renda acumulada do studio (retorno mensal composto)
47
+ renda_acumulada_studio = [capital * (((1 + studio_ret / 100) ** (12 * ano)) - 1) for ano in anos]
48
+
49
+ # Total studio = patrimônio + renda acumulada
50
+ studio_total = [p + r for p, r in zip(patrimonio_studio, renda_acumulada_studio)]
51
+
52
+ # Outros investimentos
53
+ franquia = [capital + (franquia_ret * ano) for ano in anos]
54
+ acoes = [capital * ((1 + acoes_ret / 100) ** ano) for ano in anos]
55
+ renda_fixa_valores = [capital * ((1 + renda_fixa / 100) ** ano) for ano in anos]
56
+
57
+ # Studio ajustado pela inflação (considerando só patrimônio)
58
+ studio_ajustado_inflacao = [p / ((1 + inflacao / 100) ** ano) for p, ano in zip(patrimonio_studio, anos)]
59
+
60
+ dados = {
61
+ "Ano": anos,
62
+ "Studio (Patrimônio + Renda)": studio_total,
63
+ "Franquia": franquia,
64
+ "Ações": acoes,
65
+ "Renda Fixa": renda_fixa_valores,
66
+ "Studio Patrimônio c/ Inflação": studio_ajustado_inflacao,
67
+ }
68
+ df = pd.DataFrame(dados)
69
+
70
+ investimentos_finais = {
71
+ "Studio": studio_total[-1],
72
+ "Franquia": franquia[-1],
73
+ "Ações": acoes[-1],
74
+ "Renda Fixa": renda_fixa_valores[-1],
75
+ }
76
+
77
+ investimento_mais_valorizado = max(investimentos_finais, key=investimentos_finais.get)
78
+ valor_mais_alto = investimentos_finais[investimento_mais_valorizado]
79
+
80
+ plt.figure(figsize=(8, 5))
81
+ plt.plot(anos, studio_total, label="Studio (Patrimônio + Renda)", marker="o")
82
+ plt.plot(anos, franquia, label="Franquia", marker="o")
83
+ plt.plot(anos, acoes, label="Ações", marker="o")
84
+ plt.plot(anos, renda_fixa_valores, label="Renda Fixa", marker="o")
85
+ plt.plot(anos, studio_ajustado_inflacao, label="Studio Patrimônio c/ Inflação", linestyle="--", marker="o")
86
+ plt.title("Projeção de Investimentos")
87
+ plt.xlabel("Ano")
88
+ plt.ylabel("Valor (R$)")
89
+ plt.legend()
90
+ plt.grid(True)
91
+
92
+ def formatar_moeda(x, _):
93
+ return f"R${x:,.0f}".replace(",", ".")
94
+
95
+ plt.gca().yaxis.set_major_formatter(FuncFormatter(formatar_moeda))
96
+ plt.tight_layout()
97
+
98
+ buf = io.BytesIO()
99
+ plt.savefig(buf, format="png")
100
+ buf.seek(0)
101
+ grafico_base64 = base64.b64encode(buf.getvalue()).decode("utf-8")
102
+ buf.close()
103
+ plt.close()
104
+
105
+ # Formatação da tabela com moeda brasileira
106
+ df_formatado = df.copy()
107
+ for col in df.columns:
108
+ if col != "Ano":
109
+ df_formatado[col] = df_formatado[col].apply(formatar_brl)
110
+
111
+ tabela = df_formatado.to_html(index=False, classes="table table-striped table-sm", border=0)
112
+
113
+ analise_final = gerar_analise(investimentos_finais, capital)
114
+
115
+ resumo = []
116
+ for nome, valor_final in investimentos_finais.items():
117
+ retorno_abs = valor_final - capital
118
+ retorno_pct = (retorno_abs / capital) * 100
119
+ resumo.append({
120
+ "Investimento": nome,
121
+ "Valor Final": formatar_brl(valor_final),
122
+ "Retorno Absoluto": formatar_brl(retorno_abs),
123
+ "Retorno (%)": f"{retorno_pct:.1f}%"
124
+ })
125
+ resumo = sorted(resumo, key=lambda x: float(x["Retorno (%)"].replace("%", "").replace(",", ".")), reverse=True)
126
+
127
+ diferenca_pct = float(resumo[0]["Retorno (%)"].replace("%", "").replace(",", ".")) - float(resumo[1]["Retorno (%)"].replace("%", "").replace(",", "."))
128
+ comentario_extra = ""
129
+ if diferenca_pct < 5:
130
+ comentario_extra = f"""
131
+ Apesar do <strong>{resumo[0]['Investimento']}</strong> ter se destacado, a diferença em relação ao segundo colocado
132
+ (<strong>{resumo[1]['Investimento']}</strong>) foi de apenas {diferenca_pct:.1f} pontos percentuais.
133
+ Isso indica que ambas as estratégias podem ser consideradas, dependendo do perfil de risco e objetivos do investidor.
134
+ """
135
+
136
+ return render_template(
137
+ "index.html",
138
+ capital=capital,
139
+ studio_ret=studio_ret,
140
+ valorizacao=valorizacao,
141
+ franquia_ret=franquia_ret,
142
+ acoes_ret=acoes_ret,
143
+ renda_fixa=renda_fixa,
144
+ inflacao=inflacao,
145
+ anos=anos,
146
+ tabela=tabela,
147
+ grafico=grafico_base64,
148
+ investimento_mais_valorizado=investimento_mais_valorizado,
149
+ valor_mais_alto=valor_mais_alto,
150
+ analise_final=analise_final,
151
+ comentario_extra=comentario_extra,
152
+ resumo=resumo
153
+ )
154
+
155
+ return render_template("index.html")
156
+
157
+
158
+ @app.route("/download_pdf", methods=["POST"])
159
+ def download_pdf():
160
+ capital_inicial = float(request.form["capital"])
161
+ retorno_mensal_studio = float(request.form["studio_ret"])
162
+ valorizacao_anual = float(request.form["valorizacao"])
163
+ lucro_anual_franquia = float(request.form["franquia_ret"])
164
+ retorno_anual_acoes = float(request.form["acoes_ret"])
165
+ retorno_anual_renda_fixa = float(request.form["renda_fixa"])
166
+ inflacao_anual = float(request.form["inflacao"])
167
+
168
+ anos = list(range(1, 6))
169
+
170
+ patrimonio_studio = [capital_inicial * ((1 + valorizacao_anual / 100) ** ano) for ano in anos]
171
+ renda_acumulada_studio = [capital_inicial * (((1 + retorno_mensal_studio / 100) ** (12 * ano)) - 1) for ano in anos]
172
+ studio_total = [p + r for p, r in zip(patrimonio_studio, renda_acumulada_studio)]
173
+
174
+ franquia = [capital_inicial + (lucro_anual_franquia * ano) for ano in anos]
175
+ acoes = [capital_inicial * ((1 + retorno_anual_acoes / 100) ** ano) for ano in anos]
176
+ renda_fixa = [capital_inicial * ((1 + retorno_anual_renda_fixa / 100) ** ano) for ano in anos]
177
+ studio_ajustado_inflacao = [p / ((1 + inflacao_anual / 100) ** ano) for p, ano in zip(patrimonio_studio, anos)]
178
+
179
+ df = pd.DataFrame({
180
+ "Ano": anos,
181
+ "Studio (Patrimônio + Renda)": studio_total,
182
+ "Franquia": franquia,
183
+ "Ações": acoes,
184
+ "Renda Fixa": renda_fixa,
185
+ "Studio Patrimônio c/ Inflação": studio_ajustado_inflacao,
186
+ })
187
+
188
+ investimentos_finais = {
189
+ "Studio": studio_total[-1],
190
+ "Franquia": franquia[-1],
191
+ "Ações": acoes[-1],
192
+ "Renda Fixa": renda_fixa[-1],
193
  }
194
 
195
+ investimento_mais_valorizado = max(investimentos_finais, key=investimentos_finais.get)
196
+ valor_mais_alto = investimentos_finais[investimento_mais_valorizado]
197
+
198
+ plt.figure(figsize=(8, 5))
199
+ plt.plot(anos, studio_total, label="Studio (Patrimônio + Renda)", marker="o")
200
+ plt.plot(anos, franquia, label="Franquia", marker="o")
201
+ plt.plot(anos, acoes, label="Ações", marker="o")
202
+ plt.plot(anos, renda_fixa, label="Renda Fixa", marker="o")
203
+ plt.plot(anos, studio_ajustado_inflacao, label="Studio Patrimônio c/ Inflação", linestyle="--", marker="o")
204
+ plt.title("Comparação dos Investimentos")
205
+ plt.xlabel("Ano")
206
+ plt.ylabel("Valor (R$)")
 
 
 
 
 
 
 
 
 
207
  plt.legend()
208
  plt.grid(True)
 
 
209
  plt.tight_layout()
210
+
211
+ img_buffer = io.BytesIO()
212
+ plt.savefig(img_buffer, format='png')
213
+ img_buffer.seek(0)
214
+ grafico_base64 = base64.b64encode(img_buffer.getvalue()).decode('utf-8')
215
  plt.close()
 
 
216
 
217
+ df_formatado = df.copy()
218
+ for col in df.columns:
219
+ if col != "Ano":
220
+ df_formatado[col] = df_formatado[col].apply(formatar_brl)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
+ tabela = df_formatado.to_html(index=False, classes="table table-sm table-bordered", border=1)
 
 
 
223
 
224
+ analise_final = gerar_analise(investimentos_finais, capital_inicial)
225
+
226
+ resumo = []
227
+ for nome, valor_final in investimentos_finais.items():
228
+ retorno_abs = valor_final - capital_inicial
229
+ retorno_pct = (retorno_abs / capital_inicial) * 100
230
+ resumo.append({
231
+ "Investimento": nome,
232
+ "Valor Final": formatar_brl(valor_final),
233
+ "Retorno Absoluto": formatar_brl(retorno_abs),
234
+ "Retorno (%)": f"{retorno_pct:.1f}%"
235
+ })
236
+ resumo = sorted(resumo, key=lambda x: float(x["Retorno (%)"].replace("%", "").replace(",", ".")), reverse=True)
237
+
238
+ diferenca_pct = float(resumo[0]["Retorno (%)"].replace("%", "").replace(",", ".")) - float(resumo[1]["Retorno (%)"].replace("%", "").replace(",", "."))
239
+ comentario_extra = ""
240
+ if diferenca_pct < 5:
241
+ comentario_extra = f"""
242
+ Apesar do <strong>{resumo[0]['Investimento']}</strong> ter se destacado, a diferença em relação ao segundo colocado
243
+ (<strong>{resumo[1]['Investimento']}</strong>) foi de apenas {diferenca_pct:.1f} pontos percentuais.
244
+ Isso indica que ambas as estratégias podem ser consideradas, dependendo do perfil de risco e objetivos do investidor.
245
+ """
246
+
247
+ # Explicação detalhada para o PDF
248
+ explicacao_detalhada = f"""
249
+ <h3>Explicações e Detalhes</h3>
250
+ <p>O investimento <strong>Studio</strong> considera dois componentes importantes: a valorização patrimonial do imóvel e a renda mensal obtida com o aluguel ou uso.</p>
251
+ <ul>
252
+ <li><strong>Valorização Patrimonial:</strong> o valor do imóvel cresce à taxa anual de {valorizacao_anual:.2f}% ao ano.</li>
253
+ <li><strong>Renda Mensal:</strong> o retorno mensal de {retorno_mensal_studio:.2f}% é composto ao longo dos meses, acumulando no total projetado.</li>
254
+ <li>Esses dois efeitos juntos proporcionam o valor total do Studio ao longo dos anos.</li>
255
+ </ul>
256
+ <p>Os demais investimentos são projetados de acordo com seus respectivos retornos e aportes.</p>
257
+ <p>A inflação anual considerada é de {inflacao_anual:.2f}%, usada para ajustar o valor patrimonial do Studio e mostrar a valorização real.</p>
258
+ """
259
+
260
+ html = render_template(
261
+ "relatorio_pdf.html",
262
+ capital=capital_inicial,
263
+ studio_ret=retorno_mensal_studio,
264
+ valorizacao=valorizacao_anual,
265
+ franquia_ret=lucro_anual_franquia,
266
+ acoes_ret=retorno_anual_acoes,
267
+ renda_fixa=retorno_anual_renda_fixa,
268
+ inflacao=inflacao_anual,
269
+ anos=anos,
270
+ tabela=tabela,
271
+ grafico=grafico_base64,
272
+ investimento_mais_valorizado=investimento_mais_valorizado,
273
+ valor_mais_alto=valor_mais_alto,
274
+ analise_final=analise_final,
275
+ comentario_extra=comentario_extra,
276
+ resumo=resumo,
277
+ explicacao_detalhada=explicacao_detalhada,
278
+ now=datetime.now()
279
  )
280
 
281
+ pdf = io.BytesIO()
282
+ pisa_status = pisa.CreatePDF(html, dest=pdf)
283
+ if pisa_status.err:
284
+ return "Erro ao gerar PDF", 500
285
+
286
+ pdf.seek(0)
287
+ return send_file(pdf, mimetype="application/pdf", as_attachment=True, download_name="relatorio_simulacao.pdf")
288
 
289
  if __name__ == '__main__':
290
+ app.run(host='0.0.0.0', port=7860, debug=True)