Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
"""
|
| 2 |
-
🪶 Council Matters Classifier – PT
|
| 3 |
"""
|
| 4 |
|
|
|
|
| 5 |
import gradio as gr
|
| 6 |
import numpy as np
|
| 7 |
import joblib
|
|
@@ -9,14 +10,14 @@ import re
|
|
| 9 |
from pathlib import Path
|
| 10 |
from scipy.sparse import hstack, csr_matrix
|
| 11 |
|
|
|
|
| 12 |
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
except ImportError:
|
| 18 |
-
TORCH_AVAILABLE = False
|
| 19 |
|
|
|
|
| 20 |
|
| 21 |
# ---------------- Classifier ----------------
|
| 22 |
class PortugueseClassifier:
|
|
@@ -30,11 +31,6 @@ class PortugueseClassifier:
|
|
| 30 |
self.optimal_thresholds = None
|
| 31 |
self.trained_base_models = None
|
| 32 |
|
| 33 |
-
if TORCH_AVAILABLE:
|
| 34 |
-
self.bert_tokenizer = None
|
| 35 |
-
self.bert_model = None
|
| 36 |
-
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 37 |
-
|
| 38 |
self.load_models()
|
| 39 |
|
| 40 |
def load_models(self):
|
|
@@ -52,28 +48,24 @@ class PortugueseClassifier:
|
|
| 52 |
self.optimal_thresholds = np.load(thresh_path)
|
| 53 |
self.trained_base_models = joblib.load(base_path)
|
| 54 |
|
| 55 |
-
if TORCH_AVAILABLE:
|
| 56 |
-
self.bert_tokenizer = AutoTokenizer.from_pretrained('neuralmind/bert-base-portuguese-cased')
|
| 57 |
-
self.bert_model = AutoModel.from_pretrained('neuralmind/bert-base-portuguese-cased')
|
| 58 |
-
self.bert_model.eval()
|
| 59 |
-
self.bert_model = self.bert_model.to(self.device)
|
| 60 |
-
|
| 61 |
self.models_loaded = True
|
| 62 |
except Exception as e:
|
| 63 |
print(f"❌ Error loading models: {str(e)}")
|
| 64 |
|
|
|
|
| 65 |
def extract_bert_features(self, text):
|
| 66 |
-
if not TORCH_AVAILABLE or not self.bert_model:
|
| 67 |
-
return np.zeros((1, 768))
|
| 68 |
try:
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
|
|
|
|
|
|
| 75 |
return np.zeros((1, 768))
|
| 76 |
|
|
|
|
| 77 |
def predict(self, text):
|
| 78 |
if not self.models_loaded:
|
| 79 |
return [{"label": "Error", "probability": 0.0, "confidence": "low"}]
|
|
@@ -129,20 +121,10 @@ classifier = PortugueseClassifier()
|
|
| 129 |
|
| 130 |
# ---------------- Suggestions ----------------
|
| 131 |
suggestions = [
|
| 132 |
-
|
| 133 |
-
"Pelo Senhor Presidente foram presentes a esta reunião os documentos relativos à assunção de compromissos plurianuais relativos à Loja do Cidadão, em Alandroal que se anexam à presente ata. \nPonderado e analisado o assunto o Executivo Municipal deliberou por maioria, com os votos a favor dos eleitos pelo PS e a abstenção da eleita pelo Nós, Cidadãos, aprovar a assunção de compromissos plurianuais. \nReferiu o Sr. Presidente que o FAM enviou então o parecer necessário para aprovação destes dois pontos e que refere que \"atendendo ao exposto propõe que a Direção Executiva do FAM emita parecer prévio positivo à proposta de modificação ao orçamento municipal de 2024 apresentado pela Câmara Municipal\". ",
|
| 134 |
-
"-BALANCETE:\n-Operações orçamentas respeitante ao dia vinte e seis de março do corrente ano, é de um milhão oitocentos e onze mil quinhentos e quinze euros e noventa cêntimos.",
|
| 135 |
-
"-APOIO SOLICITADO CEAN-CENTRO EDUCATIVO ALICE NABEIRO-VIAGEM DE FINALISTAS-ANO 2024:\n-Apreciação da informação (registo 5911) dos serviços financeiros, referente ao assunto em epígrafe, que a seguir se transcreve:-“ Na sequência da documentação apresentada pelo CEAN – Centro Educativo Alice Nabeiro no âmbito da realização de uma Viagem de Finalistas a Paris, e após análise dos referidos documentos da mencionada entidade e visto a atividade preponderante ser de índole educativo e social, o valor que sugiro quantifica-se em 500,00 (quinhentos euros) o qual considero razoável para o tipo de atividade referida. O valor sugerido tem cobertura orçamental através da Grande Opção do Plano, 2 232 2011/5044 – Transferências Correntes para Instituições Culturais, cuja dotação global prevista nos Documentos Previsionais para o Exercício 2023 é de 150.000,00 €. Pelo exposto e para efeitos de apreciação por parte de V. Exas., é tudo o que me cabe informar, acerca do assunto mencionado em epígrafe”.\n-A CÂMARA DELIBEROU, POR UNANIMIDADE, ATRIBUIR UM APOIO NO VALOR DE 500,00€ (QUINHENTOS EUROS) AO CEAN-CENTRO EDUCATIVO ALICE NABEIRO, TENDO EM VISTA AJUDAR A CUSTEAR A VIAGEM DE FINALISTA 2024."
|
| 136 |
-
"-APOIO SOLICITADO PELA ASSOCIAÇÃO CULTURAL AXPRESS-ARTE - FESTIVAL INTERNACIONAL DE DANÇA:\n-Apreciação da informação (registo 5835) dos serviços financeiros, referente ao assunto em epígrafe, que a seguir se transcreve:-“ Na sequência da documentação apresentada pela Axpress-Arte – Associação Cultural para o exercício 2024, e em face da documentação apresentada pela referida instituição no âmbito da candidatura referida em epigrafe, e após análise dos referidos documentos da mencionada entidade e visto a atividade preponderante ser de índole cultural, o valor que sugiro quantifica-se em 3.000,00 (três mil euros) anuais o qual considero razoável para este tipo de atividade. O valor sugerido tem cobertura orçamental através da Grande Opção do Plano, 2 251 2011/5075 – Transferências Correntes para Instituições Culturais, cuja dotação global prevista nos Documentos Previsionais para o Exercício 2024 é de 90.000,00 €. Sendo o Histórico dos últimos anos de atribuição de apoios para a referida instituição o seguinte:\n\n————————————Quadro de Apoios 2019 – 2023————————————\nAno,Valor em Euros\n2019,0,00\n2020,0,00\n2021,0,00\n2022,3.000,00\n2023,3.000,00\n————————————————————————————————————————————————————— \n-Pelo exposto e para efeitos de apreciação por parte de V. Exas., é tudo o que me cabe informar, acerca do assunto mencionado em epígrafe”.\n-A CÂMARA DELIBEROU, POR UNANIMIDADE, ATRIBUIR UM APOIO NO VALOR DE 3.000,00€ (TRÊS MIL EUROS) À ASSOCIAÇÃO CULTURAL AXPRESS-ARTE, TENDO EM VISTA A REALIZAÇÃO DO FESTIVAL INTERNACIONAL DE DANÇA.",
|
| 137 |
-
"Presente declaração, emitida pelo Senhor Presidente da Câmara em 07.junho.2024, para ratificação, constante da plataforma de gestão documental SigmaDoc Web/NIPG: 19890/24_Pendente: 100707.\n\nDocumentos que se dão como inteiramente reproduzidos na presente ata e ficam, para todos os efeitos legais, arquivados em pasta própria existente para o efeito.\n\nA Câmara deliberou, ao abrigo da n.º 3, do artigo 35.º do RJAL, aprovado pela Lei n.º 75/2013, de 12 de setembro, na sua redação atual, ratificar a Declaração emitida pelo Senhor Presidente da Camara em 07.junho.2024, em que o Município da Covilhã pretende ceder apoio logístico, institucional e/ou monetário para o evento \"18.ª Santa Bebiana\", atividade esta levada a cabo pela Casa do Povo do Paul, no âmbito da candidatura ao “Cultura ao Centro 2024, da CCDRC”.",
|
| 138 |
-
"3.2. – ICOVI, Infraestruturas e Concessões da Covilhã, EM\n\nFoi presente à Câmara informação constante da plataforma de gestão documental SigmaDoc Web/NIPG: 15508/24_Pendente: 101576, relativa ao ofício da ICOVI – Infraestruturas e Concessões da Covilhã, EM, com a referência 11/23, datado de 02.abril.2024, no qual informa o Resultado Antes de Impostos de 2023 negativo no valor de - 176.266,35€ (cento e setenta e seis mil, duzentos e sessenta e seis euros e trinta e cinco cêntimos) e solicita a cobertura desse resultado.\n\nDocumento que se dá como inteiramente reproduzido na presente ata e fica, para todos os efeitos legais, arquivado em pasta própria existente para o efeito.\n\nO Senhor Vereador Ricardo Miguel Correia Leitão Ferreira da Silva criticou o facto de a empresa somente enviar à Câmara um pedido do valor sem pelo menos enviar o relatório e contas.\n\nNão participou na discussão e votação do presente assunto o Senhor Vereador José Miguel Ribeiro Oliveira, nos termos previstos no n.º 6 do artigo 55.º do Anexo I da Lei nº. 75/2013, de 12 de setembro que aprova o RJAL, conjugado com o artigo 69.º do CPA – Código do Procedimento Administrativo, aprovado pelo Decreto-lei n.º 4/2015, de 7 de janeiro, na nova redação, por exercer as funções de Presidente do Conselho da Administração.\n\nA Câmara deliberou, com o voto contra dos Senhores Vereadores Ricardo Miguel Correia Leitão Ferreira da Silva e Jorge Humberto Martins Simões, tendo em conta que a Empresa Municipal ICOVI – Infraestruturas e Concessões da Covilhã, EM, apresenta um Resultado Antes de Impostos de 2023 negativo no valor de - 176.266,35€ (cento e setenta e seis mil, duzentos e sessenta e seis euros e trinta e cinco cêntimos), nos termos da legislação aplicável, aprovar e efetuar uma transferência financeira no valor de 176.266,35€ (cento e setenta e seis mil, duzentos e sessenta e seis euros e trinta e cinco cêntimos)",
|
| 139 |
-
"Presente à Câmara informação, constante da distribuição no sistema informático de gestão documental com a referência EDOC/2022/27808, propondo a ratificação do Aditamento ao Contrato de Comparticipação entre o Instituto da Habitação e da Reabilitação Urbana, I.P. e o Município da Covilhã, destinado à Construção de edifício multifamiliar para 12 Alojamentos de Emergência (BNAUT).\n\nDocumentos que se dão como inteiramente reproduzidos na presente ata e ficam, para todos os efeitos legais, arquivados em pasta própria existente para o efeito.\n\nA Câmara deliberou ratificar o Aditamento ao Contrato de Comparticipação entre o Instituto da Habitação e da Reabilitação Urbana, I.P. e o Município da Covilhã – BNAUT, em que as Partes acordam em proceder à alteração do n.º 1 da Cláusula Terceira, do n.º 1 da Cláusula Quarta e da alínea a) do nº 1 da Cláusula Sexta do Contrato, e que tem por objeto a concessão de um apoio financeiro não reembolsável destinado a financiar a realização do projeto designado por “Construção de edifício multifamiliar para alojamento de emergência na Rua Comendador Gomes Correia n.º 39 a 65”, enquadrado no Aviso n.º 02/CO2-i02/2021, em que a Entidade Beneficiária é a Beneficiária Final, entidade globalmente responsável pela execução do Projeto de investimento ora contratualizado.",
|
| 140 |
-
"O Presidente da Câmara apresentou declaração de inexistência de conflitos de interesse relativamente aos procedimentos da ordem do dia da presente reunião, constantes dos pontos 1 a 7 e 9 a 18, que se dá aqui por reproduzida e fica arquivada em pasta anexa ao livro de atas.",
|
| 141 |
-
"7. TRÂNSITO – FREGUESIA DE PRAZINS SANTO TIRSO E CORVITE – ALTERAÇÃO DE TRÂNSITO NA TRAVESSA DO CAMPO NOVO - Presente a seguinte proposta: “Por forma a incrementar as condições de segurança e acessibilidade local dos moradores, a Junta de Freguesia submeteu proposta de alteração de trânsito na Travessa do Campo Novo, Freguesia de Prazins Santo Tirso e Corvite, no tramo compreendido entre a Travessa Nova do Campo Novo e a Rua 24 de Junho, aprovada pela Assembleia de Freguesia. Considerando os constrangimentos associados ao reduzido perfil transversal da Travessa do Campo Novo, a alteração potenciará o incremento da segurança rodoviária local, bem como a mitigação da prática de estacionamento em contravenção, submete-se à apreciação Camarária conforme postura anexa.” A postura e as atas dos órgãos executivo e deliberativo da Freguesia dão-se aqui por reproduzidos e ficam arquivados em pasta anexa ao livro de atas. DELIBERADO, POR UNANIMIDADE, SUBMETER À APROVAÇÃO DA ASSEMBLEIA MUNICIPAL.",
|
| 142 |
-
"1. Ata da reunião pública do Executivo Municipal de 11 de novembro de 2024.\nAprovada, por unanimidade, pelos presentes na reunião pública do Executivo Municipal de 11 de novembro de 2024.",
|
| 143 |
]
|
| 144 |
-
example_idx = 0
|
| 145 |
|
|
|
|
| 146 |
def next_example():
|
| 147 |
global example_idx
|
| 148 |
example_idx = (example_idx + 1) % len(suggestions)
|
|
@@ -169,8 +151,7 @@ def classify_display(text):
|
|
| 169 |
chips += f"<span class='output-chip' style='border-color:{color}80;color:{color}'>{label} ({prob:.0%})</span>"
|
| 170 |
return f"<div style='display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:10px'>{chips}</div>"
|
| 171 |
|
| 172 |
-
# ---------------- CSS
|
| 173 |
-
|
| 174 |
custom_css = """
|
| 175 |
body { background-color: #0c0c0c; color: #f1f1f1; font-family: 'Inter', sans-serif; }
|
| 176 |
.gradio-container { background-color: #0c0c0c; color: #f1f1f1; }
|
|
@@ -179,8 +160,8 @@ textarea { background-color: #181818 !important; color: #fff !important; border-
|
|
| 179 |
button { background-color: #007aff !important; color: white !important; font-weight: 600 !important; border-radius: 8px !important; border: none !important; }
|
| 180 |
button:hover { background-color: #00aaff !important; }
|
| 181 |
.output-chip { background-color: #1a1a1a; padding: 5px 12px; border-radius: 8px; font-weight: 500; border: 1px solid #007aff33; }
|
| 182 |
-
.suggestion-box { background-color: #112f50; border-radius: 10px; border: 1px solid #1f3c5a; padding: 10px; display: flex; align-items: center; justify-content: center; color: #eee; margin-top: 25px; position: relative; overflow: scroll; }
|
| 183 |
-
|
| 184 |
.arrow-btn:hover { color: #ffffff; transform: scale(1.3); }
|
| 185 |
.use-btn { background-color:#66b3ff !important; color:#000 !important; font-weight:600 !important; border-radius:6px !important; padding:3px 8px !important; margin-left:5px;}
|
| 186 |
.use-btn:hover { background-color:#99ccff !important; }
|
|
@@ -189,14 +170,12 @@ button:hover { background-color: #00aaff !important; }
|
|
| 189 |
.suggestion-box .suggestion-text { width: 100%; text-align: center; border:none; background:none; color:#eee; font-weight:500; padding-top:8px; overflow-y: scroll;}
|
| 190 |
"""
|
| 191 |
|
| 192 |
-
|
| 193 |
# ---------------- Gradio UI ----------------
|
| 194 |
with gr.Blocks(css=custom_css, theme="gradio/soft") as demo:
|
| 195 |
gr.Markdown("## 🏛️ **Council Matters Classifier – PT**")
|
| 196 |
gr.Markdown("### Insert your text (in portuguese):")
|
| 197 |
|
| 198 |
input_text = gr.Textbox(label="", placeholder="Write your text (in portuguese)...", lines=6)
|
| 199 |
-
|
| 200 |
classify_btn = gr.Button("Classify")
|
| 201 |
output = gr.HTML()
|
| 202 |
classify_btn.click(fn=classify_display, inputs=input_text, outputs=output)
|
|
|
|
| 1 |
"""
|
| 2 |
+
🪶 Council Matters Classifier – PT (HF API BERT)
|
| 3 |
"""
|
| 4 |
|
| 5 |
+
import os
|
| 6 |
import gradio as gr
|
| 7 |
import numpy as np
|
| 8 |
import joblib
|
|
|
|
| 10 |
from pathlib import Path
|
| 11 |
from scipy.sparse import hstack, csr_matrix
|
| 12 |
|
| 13 |
+
from huggingface_hub import InferenceClient
|
| 14 |
|
| 15 |
+
# ---------------- HF API Client ----------------
|
| 16 |
+
HF_TOKEN = os.environ.get("HF_TOKEN")
|
| 17 |
+
if not HF_TOKEN:
|
| 18 |
+
raise ValueError("Set the environment variable HF_TOKEN with your Hugging Face token.")
|
|
|
|
|
|
|
| 19 |
|
| 20 |
+
hf_client = InferenceClient(api_key=HF_TOKEN)
|
| 21 |
|
| 22 |
# ---------------- Classifier ----------------
|
| 23 |
class PortugueseClassifier:
|
|
|
|
| 31 |
self.optimal_thresholds = None
|
| 32 |
self.trained_base_models = None
|
| 33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
self.load_models()
|
| 35 |
|
| 36 |
def load_models(self):
|
|
|
|
| 48 |
self.optimal_thresholds = np.load(thresh_path)
|
| 49 |
self.trained_base_models = joblib.load(base_path)
|
| 50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
self.models_loaded = True
|
| 52 |
except Exception as e:
|
| 53 |
print(f"❌ Error loading models: {str(e)}")
|
| 54 |
|
| 55 |
+
# ---------------- HF API BERT ----------------
|
| 56 |
def extract_bert_features(self, text):
|
|
|
|
|
|
|
| 57 |
try:
|
| 58 |
+
result = hf_client.feature_extraction(
|
| 59 |
+
text,
|
| 60 |
+
model="neuralmind/bert-base-portuguese-cased"
|
| 61 |
+
)
|
| 62 |
+
embedding = np.array(result).mean(axis=1) # mean pooling over tokens
|
| 63 |
+
return embedding
|
| 64 |
+
except Exception as e:
|
| 65 |
+
print(f"⚠️ HF BERT extraction failed: {e}")
|
| 66 |
return np.zeros((1, 768))
|
| 67 |
|
| 68 |
+
# ---------------- Prediction ----------------
|
| 69 |
def predict(self, text):
|
| 70 |
if not self.models_loaded:
|
| 71 |
return [{"label": "Error", "probability": 0.0, "confidence": "low"}]
|
|
|
|
| 121 |
|
| 122 |
# ---------------- Suggestions ----------------
|
| 123 |
suggestions = [
|
| 124 |
+
# [Coloca aqui as mesmas sugestões que tinhas antes]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
]
|
|
|
|
| 126 |
|
| 127 |
+
example_idx = 0
|
| 128 |
def next_example():
|
| 129 |
global example_idx
|
| 130 |
example_idx = (example_idx + 1) % len(suggestions)
|
|
|
|
| 151 |
chips += f"<span class='output-chip' style='border-color:{color}80;color:{color}'>{label} ({prob:.0%})</span>"
|
| 152 |
return f"<div style='display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-top:10px'>{chips}</div>"
|
| 153 |
|
| 154 |
+
# ---------------- CSS ----------------
|
|
|
|
| 155 |
custom_css = """
|
| 156 |
body { background-color: #0c0c0c; color: #f1f1f1; font-family: 'Inter', sans-serif; }
|
| 157 |
.gradio-container { background-color: #0c0c0c; color: #f1f1f1; }
|
|
|
|
| 160 |
button { background-color: #007aff !important; color: white !important; font-weight: 600 !important; border-radius: 8px !important; border: none !important; }
|
| 161 |
button:hover { background-color: #00aaff !important; }
|
| 162 |
.output-chip { background-color: #1a1a1a; padding: 5px 12px; border-radius: 8px; font-weight: 500; border: 1px solid #007aff33; }
|
| 163 |
+
.suggestion-box { background-color: #112f50; border-radius: 10px; border: 1px solid #1f3c5a; padding: 10px; display: flex; align-items: center; justify-content: center; color: #eee; margin-top: 25px; position: relative; overflow: scroll; }
|
| 164 |
+
.arrow-btn { width: 25px; height: 25px; font-size: 12px; padding: 0; background: none; border: none; color: #e0f0ff; cursor: pointer; font-weight: bold; }
|
| 165 |
.arrow-btn:hover { color: #ffffff; transform: scale(1.3); }
|
| 166 |
.use-btn { background-color:#66b3ff !important; color:#000 !important; font-weight:600 !important; border-radius:6px !important; padding:3px 8px !important; margin-left:5px;}
|
| 167 |
.use-btn:hover { background-color:#99ccff !important; }
|
|
|
|
| 170 |
.suggestion-box .suggestion-text { width: 100%; text-align: center; border:none; background:none; color:#eee; font-weight:500; padding-top:8px; overflow-y: scroll;}
|
| 171 |
"""
|
| 172 |
|
|
|
|
| 173 |
# ---------------- Gradio UI ----------------
|
| 174 |
with gr.Blocks(css=custom_css, theme="gradio/soft") as demo:
|
| 175 |
gr.Markdown("## 🏛️ **Council Matters Classifier – PT**")
|
| 176 |
gr.Markdown("### Insert your text (in portuguese):")
|
| 177 |
|
| 178 |
input_text = gr.Textbox(label="", placeholder="Write your text (in portuguese)...", lines=6)
|
|
|
|
| 179 |
classify_btn = gr.Button("Classify")
|
| 180 |
output = gr.HTML()
|
| 181 |
classify_btn.click(fn=classify_display, inputs=input_text, outputs=output)
|