Repository navigation
Expand file tree
/
Copy pathapp.py
More file actions
185 lines (151 loc) · 7.04 KB
/
Copy pathapp.py
File metadata and controls
185 lines (151 loc) · 7.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import os
import json
import streamlit as st
import google.generativeai as genai
from dotenv import load_dotenv
# Importação dos módulos utilitários locais
from utils.text_extractor import extract_text_from_file
from utils.pdf_generator import generate_pdf_from_text
from utils.url_extractor import extract_text_from_url
# ==========================================
# 1. CONFIGURAÇÃO INICIAL E AMBIENTE
# ==========================================
# Carrega variáveis de ambiente salvas no ficheiro .env
load_dotenv()
# Obtém a chave da API do Gemini armazenada
api_key = os.getenv("GEMINI_API_KEY")
# Configura a biblioteca da Google com a chave obtida
if api_key:
genai.configure(api_key=api_key)
# Configuração do título e layout da página no navegador
st.set_page_config(page_title="CV Matcher & Optimizer", page_icon="📄", layout="wide")
st.title("📄 CV Matcher & Optimizer")
st.write("Analise a compatibilidade do seu currículo com uma vaga e gere uma versão otimizada com IA.")
# Validação: interrompe a execução se a chave não for encontrada
if not api_key:
st.error("Chave da API do Gemini não encontrada. Adicione GEMINI_API_KEY no seu ficheiro .env.")
st.stop()
# ==========================================
# 2. ENTRADA DE DADOS (INTERFACE)
# ==========================================
# Criação de duas colunas na tela para organizar as entradas de dados
col_cv, col_job = st.columns(2)
cv_text = ""
job_text = ""
# Coluna 1: Entrada do Currículo
with col_cv:
st.subheader("1. Seu Currículo")
cv_input_type = st.radio("Formato de entrada (CV):", ["Texto Direto", "Upload de Ficheiro"], key="cv_type")
if cv_input_type == "Texto Direto":
cv_text = st.text_area("Cole o texto do seu currículo aqui:", height=250)
else:
uploaded_cv = st.file_uploader("Carregue o ficheiro do CV (PDF ou DOCX)", type=["pdf", "docx"], key="cv_file")
if uploaded_cv:
cv_text = extract_text_from_file(uploaded_cv)
st.success("Currículo carregado com sucesso!")
# Coluna 2: Entrada da Descrição da Vaga (Agora com opção de URL)
with col_job:
st.subheader("2. Descrição da Vaga")
job_input_type = st.radio(
"Formato de entrada (Vaga):",
["Texto Direto", "Upload de Ficheiro", "URL da Vaga"],
key="job_type"
)
if job_input_type == "Texto Direto":
job_text = st.text_area("Cole a descrição da vaga aqui:", height=250)
elif job_input_type == "Upload de Ficheiro":
uploaded_job = st.file_uploader("Carregue o ficheiro da vaga (PDF ou DOCX)", type=["pdf", "docx"], key="job_file")
if uploaded_job:
job_text = extract_text_from_file(uploaded_job)
st.success("Descrição da vaga carregada com sucesso!")
else:
job_url = st.text_input("Cole a URL da vaga aqui (ex: https://site.com/vaga):")
if job_url:
with st.spinner("A ler o conteúdo da URL..."):
try:
job_text = extract_text_from_url(job_url)
st.success("Conteúdo da URL extraído com sucesso!")
# Exibe uma prévia expansível do texto lido da página
with st.expander("Ver texto extraído do link"):
st.write(job_text[:1000] + ("..." if len(job_text) > 1000 else ""))
except Exception as e:
st.error(f"Erro ao extrair texto do link: {e}")
# ==========================================
# 3. PROCESSAMENTO COM A API DO GEMINI
# ==========================================
if st.button("🚀 Analisar e Otimizar Currículo", type="primary", use_container_width=True):
if not cv_text.strip() or not job_text.strip():
st.warning("Por favor, forneça tanto o currículo quanto a descrição da vaga antes de continuar.")
else:
with st.spinner("Analisando e otimizando o currículo..."):
try:
# Inicialização do modelo Gemini 3.5 Flash
model = genai.GenerativeModel("gemini-3.5-flash")
# Instruções extremamente detalhadas para garantir o JSON estritamente válido
prompt = f"""
Você é um especialista em recrutamento e otimização de currículos (ATS). Sua tarefa é analisar o currículo fornecido em relação à descrição da vaga.
CURRÍCULO:
{cv_text}
DESCRIÇÃO DA VAGA:
{job_text}
ESTRUTURA JSON OBRIGATÓRIA:
{{
"score": 85,
"pontos_fortes": ["ponto 1", "ponto 2"],
"lacunas": ["lacuna 1", "lacuna 2"],
"melhorias_aplicadas": ["melhoria 1", "melhoria 2"],
"curriculo_otimizado": "Texto completo do currículo otimizado aqui"
}}
"""
response = model.generate_content(
prompt,
generation_config={"response_mime_type": "application/json"}
)
# Salva o resultado no estado da sessão do Streamlit
st.session_state["analysis_result"] = json.loads(response.text)
except Exception as e:
st.error(f"Erro ao processar a requisição: {e}")
st.stop()
# ==========================================
# 4. EXIBIÇÃO DE RESULTADOS E EXPORTAÇÃO
# ==========================================
if "analysis_result" in st.session_state:
result = st.session_state["analysis_result"]
st.divider()
st.subheader("📊 Resultados da Análise")
# Exibe a pontuação de compatibilidade
score = result.get("score", 0)
st.metric(label="Nível de Compatibilidade", value=f"{score}%")
st.progress(score / 100)
# Organiza os detalhes em 3 colunas
col_a, col_b, col_c = st.columns(3)
with col_a:
st.subheader("✅ Pontos Fortes")
for item in result.get("pontos_fortes", []):
st.write(f"- {item}")
with col_b:
st.subheader("⚠️ Lacunas Identificadas")
for item in result.get("lacunas", []):
st.write(f"- {item}")
with col_c:
st.subheader("🛠️ Melhorias Aplicadas")
for item in result.get("melhorias_aplicadas", []):
st.write(f"- {item}")
st.divider()
st.header("✏️ Prévia e Edição do Currículo Otimizado")
# Área de texto editável para o currículo otimizado
optimized_cv_edited = st.text_area(
"Você pode fazer ajustes manuais no texto abaixo antes de gerar o PDF:",
value=result.get("curriculo_otimizado", ""),
height=400
)
# Gera o buffer do PDF a partir do texto editado
pdf_buffer = generate_pdf_from_text(optimized_cv_edited)
# Botão para descarregar o PDF final
st.download_button(
label="📥 Baixar Currículo Otimizado em PDF",
data=pdf_buffer,
file_name="curriculo_otimizado.pdf",
mime="application/pdf",
type="primary",
)