Persano commited on
Commit
ca2b093
·
verified ·
1 Parent(s): 8f7b09a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +186 -194
app.py CHANGED
@@ -1,221 +1,213 @@
1
- from flask import Flask, render_template_string, request, send_file, redirect, url_for
2
- import matplotlib.pyplot as plt
3
- import matplotlib
4
- from matplotlib.ticker import MaxNLocator
5
- import os
6
  from datetime import datetime, timedelta
7
- from fpdf import FPDF
8
-
9
- # Ajustes para Hugging Face (sem permissão de escrita em /.config)
10
- matplotlib.use('Agg')
11
 
12
  app = Flask(__name__)
13
 
14
- # Estimativas de tempo de obra (em dias)
15
- TEMPOS_OBRA = {
16
- "Demolição": 3,
17
- "Hidráulica": 5,
18
- "Elétrica": 5,
19
- "Gesso": 4,
20
- "Pintura": 4,
21
- "Piso": 5,
22
- "Ar Condicionado": 3,
23
- "Nivelamento de Sacada": 2,
24
- "Limpeza Final": 2
25
- }
26
-
27
- @app.route("/", methods=["GET", "POST"])
28
- def index():
29
- if request.method == "POST":
30
- # Dados do cliente e obra
31
- cliente = request.form["cliente"]
32
- endereco = request.form["endereco"]
33
- data_inicio = request.form["data_inicio"]
34
- metragem = float(request.form["metragem"])
35
- quartos = int(request.form["quartos"])
36
- banheiros = int(request.form["banheiros"])
37
- tamanhos_banheiros = [request.form[f"tamanho_banheiro_{i}"] for i in range(banheiros)]
38
- padroes_banheiros = [request.form[f"padrao_banheiro_{i}"] for i in range(banheiros)]
39
- tamanho_cozinha = request.form["tamanho_cozinha"]
40
- padrao_cozinha = request.form["padrao_cozinha"]
41
- padrao_geral = request.form["padrao_geral"]
42
- servicos = request.form.getlist("servicos")
43
-
44
- # Lógica de cálculo
45
- total = metragem * (1000 if padrao_geral == "econômico" else 1500 if padrao_geral == "médio" else 2000)
46
- total += sum(500 if t == "pequeno" else 1000 if t == "médio" else 1500 for t in tamanhos_banheiros)
47
- total += 500 if tamanho_cozinha == "pequeno" else 1000 if tamanho_cozinha == "médio" else 1500
48
-
49
- # Adiciona valor de acordo com padrão de cozinha
50
- total += 1000 if padrao_cozinha == "luxo" else 500 if padrao_cozinha == "médio" else 0
51
-
52
- for s in servicos:
53
- total += 1000 # valor base por serviço
54
-
55
- # Cronograma
56
- cronograma = []
57
- data_atual = datetime.strptime(data_inicio, "%Y-%m-%d")
58
- for etapa in ["Demolição"] + servicos + ["Limpeza Final"]:
59
- duracao = TEMPOS_OBRA.get(etapa, 3)
60
- cronograma.append((etapa, data_atual.strftime("%d/%m"), (data_atual + timedelta(days=duracao)).strftime("%d/%m")))
61
- data_atual += timedelta(days=duracao)
62
-
63
- # Gerar contrato PDF
64
- contrato_path = gerar_contrato(cliente, endereco, data_inicio, total, cronograma)
65
-
66
- # Gerar gráfico
67
- gerar_grafico_gantt(cronograma)
68
-
69
- return render_template_string(SUCESSO_HTML, cliente=cliente, total=total, contrato=contrato_path)
70
-
71
- return render_template_string(FORM_HTML)
72
-
73
- @app.route("/grafico")
74
- def grafico():
75
- return send_file("grafico.png", mimetype="image/png")
76
-
77
- @app.route("/contrato")
78
- def contrato():
79
- return send_file("contrato.pdf", as_attachment=True)
80
-
81
- def gerar_contrato(cliente, endereco, data_inicio, total, cronograma):
82
- pdf = FPDF()
83
- pdf.add_page()
84
- pdf.set_font("Arial", size=12)
85
-
86
- pdf.cell(200, 10, txt="CONTRATO DE PRESTAÇÃO DE SERVIÇOS DE REFORMA", ln=True, align='C')
87
- pdf.ln(10)
88
- pdf.multi_cell(0, 10, f"""
89
- Pelo presente instrumento, de um lado {cliente}, residente no endereço {endereco},
90
- contrata os serviços de reforma, com início previsto em {data_inicio}.
91
-
92
- O valor total do contrato é de R$ {total:,.2f}, dividido em parcelas semanais conforme cronograma abaixo:
93
- """)
94
-
95
- for etapa, inicio, fim in cronograma:
96
- pdf.cell(0, 10, f"{etapa}: {inicio} a {fim}", ln=True)
97
-
98
- pdf.ln(10)
99
- pdf.multi_cell(0, 10, """
100
- Cláusulas adicionais:
101
- 1. A contratada se compromete a seguir o cronograma acordado.
102
- 2. Qualquer atraso ou alteração deverá ser comunicada com 48h de antecedência.
103
- 3. Os materiais estão inclusos no orçamento total (salvo ajuste prévio).
104
- 4. Pagamentos semanais deverão ser realizados na data de início de cada etapa.
105
-
106
- Assinaturas:
107
- ______________________________________
108
- Cliente
109
-
110
- ______________________________________
111
- Prestador de Serviços
112
- """)
113
- pdf.output("contrato.pdf")
114
- return "contrato.pdf"
115
-
116
- def gerar_grafico_gantt(cronograma):
117
- fig, ax = plt.subplots(figsize=(10, 6))
118
- for i, (etapa, inicio, fim) in enumerate(cronograma):
119
- start = datetime.strptime(inicio, "%d/%m")
120
- end = datetime.strptime(fim, "%d/%m")
121
- ax.barh(i, (end - start).days, left=start.toordinal(), height=0.6, align='center')
122
- ax.text(start.toordinal(), i, f"{etapa}", va='center', ha='left', fontsize=10)
123
- ax.set_yticks(range(len(cronograma)))
124
- ax.set_yticklabels([e[0] for e in cronograma])
125
- ax.xaxis.set_major_locator(MaxNLocator(integer=True))
126
- ax.set_title("Cronograma da Reforma (Gantt)")
127
- fig.tight_layout()
128
- plt.savefig("grafico.png")
129
- plt.close()
130
-
131
- FORM_HTML = """
132
  <!DOCTYPE html>
133
- <html>
134
  <head>
 
135
  <title>Simulador de Reforma</title>
136
  <style>
137
- body { font-family: sans-serif; max-width: 800px; margin: auto; padding: 20px; }
138
- input, select { width: 100%; margin-bottom: 10px; padding: 8px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  </style>
140
  </head>
141
  <body>
142
- <h1>Simulador de Reforma</h1>
143
- <form method="post">
144
- <h3>Dados do Cliente</h3>
145
- <input name="cliente" placeholder="Nome completo" required>
146
- <input name="endereco" placeholder="Endereço da obra" required>
147
- <label>Data de Início:</label>
148
- <input type="date" name="data_inicio" required>
149
-
150
- <h3>Informações do Imóvel</h3>
151
- <input type="number" name="metragem" placeholder="Metragem total (m²)" required>
152
- <input type="number" name="quartos" placeholder="Número de quartos" required>
153
- <input type="number" name="banheiros" placeholder="Quantidade de banheiros" min="1" max="5" required>
154
- {% for i in range(5) %}
155
- <div>
156
- <label>Banheiro {{ i+1 }} - Tamanho:</label>
157
- <select name="tamanho_banheiro_{{ i }}">
158
  <option value="pequeno">Pequeno</option>
159
- <option value="médio">Médio</option>
160
  <option value="grande">Grande</option>
161
  </select>
162
- <label>Banheiro {{ i+1 }} - Padrão:</label>
163
- <select name="padrao_banheiro_{{ i }}">
164
- <option value="econômico">Econômico</option>
165
- <option value="médio">Médio</option>
 
 
166
  <option value="luxo">Luxo</option>
167
  </select>
168
- </div>
169
- {% endfor %}
170
- <label>Tamanho da Cozinha:</label>
171
- <select name="tamanho_cozinha">
172
- <option value="pequeno">Pequeno</option>
173
- <option value="médio">Médio</option>
174
- <option value="grande">Grande</option>
175
- </select>
176
- <label>Padrão da Cozinha:</label>
177
- <select name="padrao_cozinha">
178
- <option value="econômico">Econômico</option>
179
- <option value="médio">Médio</option>
180
- <option value="luxo">Luxo</option>
181
- </select>
182
- <label>Padrão Construtivo do Imóvel:</label>
183
- <select name="padrao_geral">
184
- <option value="econômico">Econômico</option>
185
- <option value="médio">Médio</option>
186
- <option value="alto padrão">Alto Padrão</option>
187
- </select>
188
-
189
- <h3>Serviços Desejados:</h3>
190
- <label><input type="checkbox" name="servicos" value="Hidráulica"> Hidráulica</label><br>
191
- <label><input type="checkbox" name="servicos" value="Elétrica"> Elétrica</label><br>
192
- <label><input type="checkbox" name="servicos" value="Gesso"> Gesso</label><br>
193
- <label><input type="checkbox" name="servicos" value="Pintura"> Pintura</label><br>
194
- <label><input type="checkbox" name="servicos" value="Piso"> Piso</label><br>
195
- <label><input type="checkbox" name="servicos" value="Ar Condicionado"> Ar Condicionado</label><br>
196
- <label><input type="checkbox" name="servicos" value="Nivelamento de Sacada"> Nivelamento de Sacada</label><br>
197
-
198
- <button type="submit">Gerar Contrato e Cronograma</button>
199
- </form>
200
  </body>
201
  </html>
202
  """
203
 
204
- SUCESSO_HTML = """
205
  <!DOCTYPE html>
206
- <html>
207
- <head><title>Resultado</title></head>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  <body>
209
- <h1>Simulação Finalizada</h1>
210
- <p>Cliente: {{ cliente }}</p>
211
- <p>Valor total estimado: R$ {{ '{:,.2f}'.format(total).replace(',', 'X').replace('.', ',').replace('X', '.') }}</p>
212
- <a href="/contrato">📄 Baixar Contrato em PDF</a><br><br>
213
- <a href="/grafico">📊 Ver Gráfico do Cronograma</a>
 
 
 
 
 
 
 
 
 
 
214
  </body>
215
  </html>
216
  """
217
 
 
218
  if __name__ == "__main__":
219
- app.run(host="0.0.0.0", port=7860)
220
-
221
-
 
1
+ from flask import Flask, request, render_template_string
 
 
 
 
2
  from datetime import datetime, timedelta
3
+ import os
 
 
 
4
 
5
  app = Flask(__name__)
6
 
7
+ # --- Página Inicial ---
8
+ @app.route("/", methods=["GET"])
9
+ def home():
10
+ return render_template_string(HTML_FORM)
11
+
12
+ # --- Processamento da Obra ---
13
+ @app.route("/salvar_obra", methods=["POST"])
14
+ def salvar_obra():
15
+ dados = request.form
16
+
17
+ cliente = dados["cliente"]
18
+ endereco = dados["endereco"]
19
+ metros = int(dados["metros"])
20
+ banheiros = int(dados["banheiros"])
21
+ tamanho_banheiro = dados["tamanho_banheiro"]
22
+ cozinha_m2 = int(dados["cozinha_m2"])
23
+ acabamento = dados["acabamento"]
24
+ quartos = int(dados["quartos"])
25
+ inicio = datetime.strptime(dados["inicio"], "%Y-%m-%d")
26
+
27
+ fator_acabamento = {"simples": 1.0, "medio": 1.2, "luxo": 1.5}[acabamento]
28
+ fator_banheiro = {"pequeno": 3000, "medio": 4000, "grande": 6000}[tamanho_banheiro]
29
+
30
+ itens = [
31
+ {"nome": "Hidráulica", "dias": 2, "base": 30 * metros},
32
+ {"nome": "Elétrica", "dias": 2, "base": 25 * metros},
33
+ {"nome": "Gesso", "dias": 2, "base": 20 * metros},
34
+ {"nome": "Pintura", "dias": 2, "base": 18 * metros},
35
+ {"nome": "Ar Condicionado", "dias": 1, "base": 3000},
36
+ {"nome": "Nivelamento de Sacada", "dias": 1, "base": 2400},
37
+ {"nome": "Piso de Madeira", "dias": 3, "base": 60 * metros},
38
+ {"nome": "Banheiros", "dias": 3, "base": fator_banheiro * banheiros},
39
+ {"nome": "Cozinha", "dias": 3, "base": 5000 + (cozinha_m2 * 200)},
40
+ ]
41
+
42
+ itens_formatados = []
43
+ data_atual = inicio
44
+ for item in itens:
45
+ custo = round(item["base"] * fator_acabamento)
46
+ fim = data_atual + timedelta(days=item["dias"])
47
+ itens_formatados.append({
48
+ "nome": item["nome"],
49
+ "inicio": data_atual.strftime("%d/%m/%Y"),
50
+ "fim": fim.strftime("%d/%m/%Y"),
51
+ "custo": custo
52
+ })
53
+ data_atual = fim + timedelta(days=1)
54
+
55
+ total = sum(i["custo"] for i in itens_formatados)
56
+ pagamentos = []
57
+ for i in range(4):
58
+ data_pag = datetime.today() + timedelta(days=i * 15)
59
+ pagamentos.append({
60
+ "data": data_pag.strftime("%d/%m/%Y"),
61
+ "valor": round(total / 4, 2)
62
+ })
63
+
64
+ return render_template_string(HTML_RELATORIO,
65
+ cliente=cliente,
66
+ endereco=endereco,
67
+ inicio=inicio.strftime("%d/%m/%Y"),
68
+ itens=itens_formatados,
69
+ total=total,
70
+ pagamentos=pagamentos
71
+ )
72
+
73
+ # --- HTMLs Embutidos ---
74
+
75
+ HTML_FORM = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  <!DOCTYPE html>
77
+ <html lang="pt-br">
78
  <head>
79
+ <meta charset="UTF-8">
80
  <title>Simulador de Reforma</title>
81
  <style>
82
+ body {
83
+ font-family: 'Segoe UI', sans-serif;
84
+ background-color: #f0f4f8;
85
+ color: #333;
86
+ padding: 40px;
87
+ }
88
+ .container {
89
+ max-width: 700px;
90
+ margin: auto;
91
+ background: white;
92
+ padding: 30px;
93
+ border-radius: 15px;
94
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
95
+ }
96
+ h1 {
97
+ color: #004080;
98
+ }
99
+ label {
100
+ display: block;
101
+ margin: 15px 0 5px;
102
+ }
103
+ input, select {
104
+ width: 100%;
105
+ padding: 8px;
106
+ margin-bottom: 10px;
107
+ border-radius: 6px;
108
+ border: 1px solid #ccc;
109
+ }
110
+ button {
111
+ background-color: #0070cc;
112
+ color: white;
113
+ padding: 10px 20px;
114
+ border: none;
115
+ border-radius: 8px;
116
+ font-size: 16px;
117
+ cursor: pointer;
118
+ }
119
+ button:hover {
120
+ background-color: #005bb5;
121
+ }
122
  </style>
123
  </head>
124
  <body>
125
+ <div class="container">
126
+ <h1>Simulador de Reforma</h1>
127
+ <form action="/salvar_obra" method="post">
128
+ <label>Nome do Cliente: <input type="text" name="cliente" required></label>
129
+ <label>Endereço da Obra: <input type="text" name="endereco" required></label>
130
+ <label> da Obra: <input type="number" name="metros" required></label>
131
+ <label>Quantidade de Quartos: <input type="number" name="quartos" required></label>
132
+ <label>Número de Banheiros: <input type="number" name="banheiros" required></label>
133
+ <label>Tamanho dos Banheiros:
134
+ <select name="tamanho_banheiro">
 
 
 
 
 
 
135
  <option value="pequeno">Pequeno</option>
136
+ <option value="medio">Médio</option>
137
  <option value="grande">Grande</option>
138
  </select>
139
+ </label>
140
+ <label>Tamanho da Cozinha (m²): <input type="number" name="cozinha_m2" required></label>
141
+ <label>Padrão de Acabamento:
142
+ <select name="acabamento">
143
+ <option value="simples">Simples</option>
144
+ <option value="medio">Médio</option>
145
  <option value="luxo">Luxo</option>
146
  </select>
147
+ </label>
148
+ <label>Início da Obra: <input type="date" name="inicio" required></label>
149
+ <button type="submit">Gerar Orçamento</button>
150
+ </form>
151
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  </body>
153
  </html>
154
  """
155
 
156
+ HTML_RELATORIO = """
157
  <!DOCTYPE html>
158
+ <html lang="pt-br">
159
+ <head>
160
+ <meta charset="UTF-8">
161
+ <title>Relatório da Obra</title>
162
+ <style>
163
+ body {
164
+ font-family: 'Segoe UI', sans-serif;
165
+ background-color: #f8f9fa;
166
+ padding: 40px;
167
+ }
168
+ .container {
169
+ max-width: 800px;
170
+ margin: auto;
171
+ background: white;
172
+ padding: 30px;
173
+ border-radius: 15px;
174
+ box-shadow: 0 4px 20px rgba(0,0,0,0.1);
175
+ }
176
+ h1, h2 {
177
+ color: #004080;
178
+ }
179
+ .item {
180
+ padding: 6px 0;
181
+ border-bottom: 1px solid #ddd;
182
+ }
183
+ .total {
184
+ font-size: 1.2em;
185
+ color: #006600;
186
+ margin-top: 20px;
187
+ font-weight: bold;
188
+ }
189
+ </style>
190
+ </head>
191
  <body>
192
+ <div class="container">
193
+ <h1>Relatório da Obra</h1>
194
+ <p><strong>Cliente:</strong> {{cliente}}</p>
195
+ <p><strong>Endereço:</strong> {{endereco}}</p>
196
+ <p><strong>Início da Obra:</strong> {{inicio}}</p>
197
+ <h2>Itens e Custos:</h2>
198
+ {% for i in itens %}
199
+ <div class="item">{{i.nome}} ({{i.inicio}} - {{i.fim}}) → R$ {{i.custo}}</div>
200
+ {% endfor %}
201
+ <p class="total">Total: R$ {{total}}</p>
202
+ <h2>Pagamentos:</h2>
203
+ {% for p in pagamentos %}
204
+ <div class="item">{{p.data}} → R$ {{p.valor}}</div>
205
+ {% endfor %}
206
+ </div>
207
  </body>
208
  </html>
209
  """
210
 
211
+ # --- Executa no Hugging Face Spaces ---
212
  if __name__ == "__main__":
213
+ app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))