jcalbornoz commited on
Commit
25cd901
verified
1 Parent(s): cf50f79

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -17
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
  from flask import Flask, request, jsonify, render_template
3
  import PyPDF2
4
  from openai import OpenAI
@@ -8,9 +9,11 @@ import pytesseract
8
  import io
9
 
10
  # 1. Configuraci贸n de Flask
 
11
  app = Flask(__name__, template_folder='.')
12
 
13
  # 2. Configuraci贸n de OpenAI
 
14
  client = OpenAI(
15
  api_key=os.environ.get("OPENAI_API_KEY"),
16
  )
@@ -19,6 +22,7 @@ def ocr_page(img_bytes):
19
  """Realiza OCR en una imagen (byte stream) usando Tesseract."""
20
  try:
21
  image = Image.open(io.BytesIO(img_bytes))
 
22
  text = pytesseract.image_to_string(image, lang='spa')
23
  return text
24
  except Exception as e:
@@ -27,8 +31,8 @@ def ocr_page(img_bytes):
27
 
28
  def extract_text_from_file(file):
29
  """
30
- Extrae texto de todas las p谩ginas de un PDF, con fallback a Tesseract OCR para escaneados.
31
- (La l贸gica de extracci贸n y OCR permanece igual)
32
  """
33
  file_bytes = file.read()
34
  total_text = ""
@@ -40,6 +44,7 @@ def extract_text_from_file(file):
40
  for page in pdf_reader.pages:
41
  total_text += page.extract_text() or ""
42
 
 
43
  if len(total_text.strip()) > 100:
44
  return total_text.strip()
45
 
@@ -55,9 +60,10 @@ def extract_text_from_file(file):
55
  document = fitz.open(stream=file_bytes, filetype="pdf")
56
  ocr_text = ""
57
 
 
58
  for i in range(len(document)):
59
  page = document.load_page(i)
60
- pix = page.get_pixmap(dpi=300)
61
 
62
  img_bytes = pix.tobytes("ppm")
63
 
@@ -67,6 +73,7 @@ def extract_text_from_file(file):
67
  return ocr_text.strip()
68
 
69
  except Exception as e:
 
70
  raise Exception("Fallo la extracci贸n de texto del PDF. Aseg煤rate de que el documento no sea un archivo de imagen corrupto.")
71
 
72
  return ""
@@ -74,10 +81,10 @@ def extract_text_from_file(file):
74
 
75
  def generate_summary_openai(text):
76
  """
77
- Genera un an谩lisis experto y devuelve los datos estructurados en formato JSON.
78
  """
79
  try:
80
- # Definici贸n del esquema JSON para una salida estructurada
81
  json_schema = {
82
  "type": "object",
83
  "properties": {
@@ -95,40 +102,39 @@ def generate_summary_openai(text):
95
 
96
  prompt_text = (
97
  "Eres un **experto legal en derecho inmobiliario colombiano y reglamentos de Juntas de Acci贸n Comunal (JAC)**. "
98
- "Tu tarea es analizar el documento adjunto (que puede ser un Certificado de Tradici贸n y Libertad o un documento de JAC) y extraer la informaci贸n clave para un estudio de t铆tulos o de propiedad comunal. "
99
  "DEBES DEVOLVER TU RESPUESTA EXCLUSIVAMENTE EN FORMATO JSON, siguiendo el esquema proporcionado. No a帽adas texto explicativo fuera del JSON."
100
  f"\n\nTexto del Documento:\n\n{text}"
101
  )
102
 
103
  response = client.chat.completions.create(
104
- model="gpt-4o-mini", # Usamos GPT-4o-mini, que es mejor para tareas de JSON y r谩pido.
 
105
  messages=[
106
  {"role": "system", "content": prompt_text}
107
  ],
108
- # Configuramos la respuesta para que sea JSON con el esquema definido
109
  response_format={"type": "json_object", "schema": json_schema},
110
  temperature=0.3,
111
  )
112
 
113
- # El contenido de la respuesta ser谩 una cadena JSON
114
  json_string = response.choices[0].message.content.strip()
115
 
116
- # Intentamos parsear la cadena JSON a un objeto Python
117
- import json
118
  structured_data = json.loads(json_string)
119
 
120
- # Devolvemos el objeto structured_data
121
  return structured_data
122
 
123
  except Exception as e:
124
  print(f"Error al generar el resumen/JSON con OpenAI: {e}")
125
- # Capturamos errores de la API o de formato JSON inv谩lido
126
  raise Exception("Error al generar el JSON. Verifica que la APIKey o que el documento sea legible y contenga texto relevante.")
127
 
128
  # --- Rutas de Flask ---
129
 
130
  @app.route('/')
131
  def index():
 
132
  return render_template('index.html')
133
 
134
  @app.route('/summarize', methods=['POST'])
@@ -144,18 +150,24 @@ def summarize():
144
  raw_text = extract_text_from_file(file)
145
 
146
  if not raw_text:
147
- return jsonify({'error': 'No se pudo extraer texto. Documento ilegible o sin texto.'}), 400
148
 
149
- # Llamamos a la funci贸n que ahora devuelve un objeto Python (diccionario)
150
  structured_summary = generate_summary_openai(raw_text)
151
 
152
- # Convertimos el diccionario a una lista de cadenas para mostrar en el frontend
 
153
  summary_list = [f"**{k.replace('_', ' ').title()}:** {v}" for k, v in structured_summary.items()]
154
 
155
- return jsonify({'summary': summary_list})
 
 
 
 
156
 
157
  except Exception as e:
158
  print(f"Error general en la ruta /summarize: {e}")
 
159
  return jsonify({'error': f"Error interno del servidor: {str(e)}"}), 500
160
 
161
  if __name__ == '__main__':
 
1
  import os
2
+ import json
3
  from flask import Flask, request, jsonify, render_template
4
  import PyPDF2
5
  from openai import OpenAI
 
9
  import io
10
 
11
  # 1. Configuraci贸n de Flask
12
+ # Usa '.' como carpeta de plantillas si index.html est谩 en la ra铆z.
13
  app = Flask(__name__, template_folder='.')
14
 
15
  # 2. Configuraci贸n de OpenAI
16
+ # Lee la clave de API desde la variable de entorno 'OPENAI_API_KEY'.
17
  client = OpenAI(
18
  api_key=os.environ.get("OPENAI_API_KEY"),
19
  )
 
22
  """Realiza OCR en una imagen (byte stream) usando Tesseract."""
23
  try:
24
  image = Image.open(io.BytesIO(img_bytes))
25
+ # Usa el idioma espa帽ol ('spa').
26
  text = pytesseract.image_to_string(image, lang='spa')
27
  return text
28
  except Exception as e:
 
31
 
32
  def extract_text_from_file(file):
33
  """
34
+ Extrae texto de un archivo PDF, usando PyPDF2 primero y luego Tesseract OCR
35
+ como fallback si el PDF es escaneado. Procesa TODAS las p谩ginas.
36
  """
37
  file_bytes = file.read()
38
  total_text = ""
 
44
  for page in pdf_reader.pages:
45
  total_text += page.extract_text() or ""
46
 
47
+ # Si se extrajo una cantidad significativa de texto, 煤salo.
48
  if len(total_text.strip()) > 100:
49
  return total_text.strip()
50
 
 
60
  document = fitz.open(stream=file_bytes, filetype="pdf")
61
  ocr_text = ""
62
 
63
+ # ITERAR SOBRE TODAS las p谩ginas
64
  for i in range(len(document)):
65
  page = document.load_page(i)
66
+ pix = page.get_pixmap(dpi=300) # 300 DPI para buena precisi贸n
67
 
68
  img_bytes = pix.tobytes("ppm")
69
 
 
73
  return ocr_text.strip()
74
 
75
  except Exception as e:
76
+ print(f"Fallo el proceso OCR con Tesseract: {e}")
77
  raise Exception("Fallo la extracci贸n de texto del PDF. Aseg煤rate de que el documento no sea un archivo de imagen corrupto.")
78
 
79
  return ""
 
81
 
82
  def generate_summary_openai(text):
83
  """
84
+ Genera un an谩lisis experto en formato JSON.
85
  """
86
  try:
87
+ # Define el esquema JSON para asegurar la estructura de la respuesta
88
  json_schema = {
89
  "type": "object",
90
  "properties": {
 
102
 
103
  prompt_text = (
104
  "Eres un **experto legal en derecho inmobiliario colombiano y reglamentos de Juntas de Acci贸n Comunal (JAC)**. "
105
+ "Tu tarea es analizar el documento adjunto y extraer la informaci贸n clave para un estudio de t铆tulos o de propiedad comunal. "
106
  "DEBES DEVOLVER TU RESPUESTA EXCLUSIVAMENTE EN FORMATO JSON, siguiendo el esquema proporcionado. No a帽adas texto explicativo fuera del JSON."
107
  f"\n\nTexto del Documento:\n\n{text}"
108
  )
109
 
110
  response = client.chat.completions.create(
111
+ # Usamos un modelo bueno para JSON
112
+ model="gpt-4o-mini",
113
  messages=[
114
  {"role": "system", "content": prompt_text}
115
  ],
116
+ # Forzamos la respuesta a ser un JSON v谩lido
117
  response_format={"type": "json_object", "schema": json_schema},
118
  temperature=0.3,
119
  )
120
 
 
121
  json_string = response.choices[0].message.content.strip()
122
 
123
+ # Parseamos la cadena JSON a un diccionario de Python
 
124
  structured_data = json.loads(json_string)
125
 
126
+ # Devolvemos el diccionario estructurado
127
  return structured_data
128
 
129
  except Exception as e:
130
  print(f"Error al generar el resumen/JSON con OpenAI: {e}")
 
131
  raise Exception("Error al generar el JSON. Verifica que la APIKey o que el documento sea legible y contenga texto relevante.")
132
 
133
  # --- Rutas de Flask ---
134
 
135
  @app.route('/')
136
  def index():
137
+ # Renderiza index.html desde la ra铆z
138
  return render_template('index.html')
139
 
140
  @app.route('/summarize', methods=['POST'])
 
150
  raw_text = extract_text_from_file(file)
151
 
152
  if not raw_text:
153
+ return jsonify({'error': 'No se pudo extraer texto. Documento ilegible, escaneado de baja calidad o sin texto.'}), 400
154
 
155
+ # structured_summary es ahora un diccionario de Python (JSON)
156
  structured_summary = generate_summary_openai(raw_text)
157
 
158
+ # Convertimos el diccionario a una lista de strings formateados para el frontend
159
+ # Esto es lo que el JS de index.html usar谩 para el panel de Summary
160
  summary_list = [f"**{k.replace('_', ' ').title()}:** {v}" for k, v in structured_summary.items()]
161
 
162
+ # Devolvemos el JSON original (structured_summary) y la lista formateada (summary_list)
163
+ return jsonify({
164
+ 'structured_data': structured_summary, # JSON completo
165
+ 'summary': summary_list # Lista formateada para f谩cil visualizaci贸n
166
+ })
167
 
168
  except Exception as e:
169
  print(f"Error general en la ruta /summarize: {e}")
170
+ # Aseguramos un retorno de error 500 con un mensaje 煤til
171
  return jsonify({'error': f"Error interno del servidor: {str(e)}"}), 500
172
 
173
  if __name__ == '__main__':