Remove TxtToPDF function
This commit is contained in:
parent
f067f527c6
commit
1eb255b879
6 changed files with 12 additions and 178 deletions
|
|
@ -47,8 +47,7 @@ RUN pip install --no-cache-dir ocrmypdf==14.4.0 pdf2image==1.16.3
|
||||||
RUN pip install --no-cache-dir \
|
RUN pip install --no-cache-dir \
|
||||||
google-generativeai==0.3.1 \
|
google-generativeai==0.3.1 \
|
||||||
openai==1.3.0 \
|
openai==1.3.0 \
|
||||||
mistralai==0.0.7 \
|
mistralai==0.0.7
|
||||||
beautifulsoup4==4.12.2
|
|
||||||
|
|
||||||
# Copy backend code
|
# Copy backend code
|
||||||
COPY backend/ /app/backend/
|
COPY backend/ /app/backend/
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from flask_login import LoginManager, login_user, logout_user, login_required, c
|
||||||
from auth import User, verify_user, init_default_users
|
from auth import User, verify_user, init_default_users
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from models import get_models, add_model, get_languages, update_prompt, get_prompt, add_language, delete_prompt, custom_prompts, default_prompts
|
from models import get_models, add_model, get_languages, update_prompt, get_prompt, add_language, delete_prompt, custom_prompts, default_prompts
|
||||||
from utils import process_file, translate_file_by_pages, convert_txt_to_pdf
|
from utils import process_file, translate_file_by_pages
|
||||||
import time
|
import time
|
||||||
|
|
||||||
app = Flask(__name__, static_folder="static", static_url_path="")
|
app = Flask(__name__, static_folder="static", static_url_path="")
|
||||||
|
|
@ -386,22 +386,6 @@ def upload_config():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"error": str(e)}), 400
|
return jsonify({"error": str(e)}), 400
|
||||||
|
|
||||||
@app.route('/api/txttopdf', methods=['POST'])
|
|
||||||
@login_required
|
|
||||||
def txt_to_pdf_endpoint():
|
|
||||||
data = request.get_json()
|
|
||||||
filename = data.get("filename")
|
|
||||||
if not filename:
|
|
||||||
return jsonify({"error": "Missing filename parameter"}), 400
|
|
||||||
txt_path = os.path.join(OUTPUT_FOLDER, filename)
|
|
||||||
if not os.path.exists(txt_path):
|
|
||||||
return jsonify({"error": "File not found"}), 404
|
|
||||||
try:
|
|
||||||
pdf_path = convert_txt_to_pdf(txt_path)
|
|
||||||
return jsonify({"message": "TXT to PDF conversion completed", "pdf_file": os.path.basename(pdf_path)})
|
|
||||||
except Exception as e:
|
|
||||||
return jsonify({"error": str(e)}), 500
|
|
||||||
|
|
||||||
@app.route('/', defaults={'path': ''})
|
@app.route('/', defaults={'path': ''})
|
||||||
@app.route('/<path:path>')
|
@app.route('/<path:path>')
|
||||||
def serve(path):
|
def serve(path):
|
||||||
|
|
|
||||||
152
backend/utils.py
152
backend/utils.py
|
|
@ -12,13 +12,7 @@ import base64
|
||||||
import re
|
import re
|
||||||
from models import get_prompt
|
from models import get_prompt
|
||||||
|
|
||||||
from reportlab.lib.pagesizes import A4
|
# These imports were only used by TxtToPdf functionality and have been removed
|
||||||
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
|
|
||||||
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
|
||||||
from reportlab.lib.enums import TA_JUSTIFY, TA_CENTER
|
|
||||||
|
|
||||||
# Se agrega BeautifulSoup para procesar HTML
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
|
|
||||||
UPLOAD_FOLDER = "uploads"
|
UPLOAD_FOLDER = "uploads"
|
||||||
OUTPUT_FOLDER = "outputs"
|
OUTPUT_FOLDER = "outputs"
|
||||||
|
|
@ -607,147 +601,3 @@ def process_file(file_path, api, model, mode, prompt_key, update_progress, is_ca
|
||||||
|
|
||||||
update_progress(100, "🎉 Process completed")
|
update_progress(100, "🎉 Process completed")
|
||||||
return processed_text
|
return processed_text
|
||||||
|
|
||||||
def organize_paragraphs(text):
|
|
||||||
"""
|
|
||||||
Organiza el texto plano en párrafos de forma más flexible.
|
|
||||||
Si se detecta doble salto de línea se usa como separador;
|
|
||||||
si no, se procesa línea a línea para unirlas en párrafos, creando uno nuevo cuando:
|
|
||||||
- Se encuentra una línea vacía, o
|
|
||||||
- La línea actual termina en punto.
|
|
||||||
Dentro de cada párrafo se unen las líneas; se inserta un <br/> si la línea termina en punto.
|
|
||||||
"""
|
|
||||||
text = text.strip()
|
|
||||||
paragraphs = []
|
|
||||||
if "\n\n" in text:
|
|
||||||
blocks = re.split(r'\n\s*\n', text)
|
|
||||||
else:
|
|
||||||
lines = text.splitlines()
|
|
||||||
blocks = []
|
|
||||||
buffer = ""
|
|
||||||
for line in lines:
|
|
||||||
stripped = line.strip()
|
|
||||||
if not stripped:
|
|
||||||
if buffer:
|
|
||||||
blocks.append(buffer)
|
|
||||||
buffer = ""
|
|
||||||
else:
|
|
||||||
if buffer and buffer.endswith("."):
|
|
||||||
blocks.append(buffer)
|
|
||||||
buffer = stripped
|
|
||||||
else:
|
|
||||||
if buffer:
|
|
||||||
buffer += " " + stripped
|
|
||||||
else:
|
|
||||||
buffer = stripped
|
|
||||||
if buffer:
|
|
||||||
blocks.append(buffer)
|
|
||||||
for block in blocks:
|
|
||||||
line_list = block.splitlines()
|
|
||||||
if len(line_list) == 1:
|
|
||||||
paragraphs.append(line_list[0].strip())
|
|
||||||
else:
|
|
||||||
new_block = ""
|
|
||||||
for i, line in enumerate(line_list):
|
|
||||||
line = line.strip()
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
if i < len(line_list) - 1:
|
|
||||||
if line.endswith("."):
|
|
||||||
new_block += line + "<br/>"
|
|
||||||
else:
|
|
||||||
new_block += line + " "
|
|
||||||
else:
|
|
||||||
new_block += line
|
|
||||||
paragraphs.append(new_block.strip())
|
|
||||||
return paragraphs
|
|
||||||
|
|
||||||
def convert_txt_to_pdf(txt_file_path):
|
|
||||||
"""
|
|
||||||
Convierte un archivo TXT a PDF siguiendo estas reglas:
|
|
||||||
- Se detecta el patrón [Page XXXX] para separar páginas (solo al inicio).
|
|
||||||
- Este marcador se convierte en un encabezado (h1).
|
|
||||||
- Si el contenido proviene de un bloque markdown con ```html se elimina ese marcador y
|
|
||||||
se parsea con BeautifulSoup para generar párrafos independientes.
|
|
||||||
- Para contenido en texto plano se organiza en párrafos con organize_paragraphs.
|
|
||||||
- Se inserta un PageBreak después de cada bloque de página.
|
|
||||||
"""
|
|
||||||
with open(txt_file_path, "r", encoding="utf-8") as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
base_name = os.path.splitext(os.path.basename(txt_file_path))[0]
|
|
||||||
output_pdf = os.path.join(OUTPUT_FOLDER, base_name + "_txt.pdf")
|
|
||||||
doc = SimpleDocTemplate(
|
|
||||||
output_pdf,
|
|
||||||
pagesize=A4,
|
|
||||||
rightMargin=40, leftMargin=40,
|
|
||||||
topMargin=40, bottomMargin=40
|
|
||||||
)
|
|
||||||
styles = getSampleStyleSheet()
|
|
||||||
header_styles = {
|
|
||||||
"h1": ParagraphStyle('Heading1', parent=styles['Heading1'], alignment=TA_CENTER),
|
|
||||||
"h2": ParagraphStyle('Heading2', parent=styles['Heading2'], alignment=TA_CENTER),
|
|
||||||
"h3": ParagraphStyle('Heading3', parent=styles['Heading3'], alignment=TA_CENTER),
|
|
||||||
"h4": ParagraphStyle('Heading4', parent=styles['Heading4'], alignment=TA_CENTER),
|
|
||||||
"h5": ParagraphStyle('Heading5', parent=styles['Heading5'], alignment=TA_CENTER),
|
|
||||||
"h6": ParagraphStyle('Heading6', parent=styles['Heading6'], alignment=TA_CENTER),
|
|
||||||
}
|
|
||||||
normal_style = ParagraphStyle(
|
|
||||||
'Normal',
|
|
||||||
parent=styles['Normal'],
|
|
||||||
alignment=TA_JUSTIFY,
|
|
||||||
leading=15,
|
|
||||||
leftIndent=20
|
|
||||||
)
|
|
||||||
|
|
||||||
flowables = []
|
|
||||||
# Patrón para detectar el marcador [Page XXXX]
|
|
||||||
page_pattern = re.compile(r'\[Page\s+\d{4}\]')
|
|
||||||
parts = re.split(r'(\[Page\s+\d{4}\])', content)
|
|
||||||
first_page_encountered = False
|
|
||||||
|
|
||||||
for part in parts:
|
|
||||||
part = part.strip()
|
|
||||||
if not part:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if page_pattern.fullmatch(part):
|
|
||||||
if first_page_encountered:
|
|
||||||
flowables.append(PageBreak())
|
|
||||||
else:
|
|
||||||
first_page_encountered = True
|
|
||||||
header_para = Paragraph(part, styles['Heading1'])
|
|
||||||
flowables.append(header_para)
|
|
||||||
flowables.append(Spacer(1, 12))
|
|
||||||
else:
|
|
||||||
if part.startswith("```"):
|
|
||||||
lines = part.splitlines()
|
|
||||||
if lines and lines[0].startswith("```"):
|
|
||||||
lines = lines[1:]
|
|
||||||
if lines and lines[-1].strip() == "```":
|
|
||||||
lines = lines[:-1]
|
|
||||||
part = "\n".join(lines)
|
|
||||||
# Si parece HTML, se procesa para separar cada etiqueta de interés
|
|
||||||
if re.search(r'<\s*html', part, re.IGNORECASE) or re.search(r'<\s*(p|h[1-6])', part, re.IGNORECASE):
|
|
||||||
if not part.lower().startswith("<html"):
|
|
||||||
part = "<html>" + part + "</html>"
|
|
||||||
soup = BeautifulSoup(part, "html.parser")
|
|
||||||
for element in soup.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p']):
|
|
||||||
if element.name.lower() in ["h1", "h2", "h3", "h4", "h5", "h6"]:
|
|
||||||
style = header_styles.get(element.name.lower(), styles['Heading1'])
|
|
||||||
else:
|
|
||||||
style = normal_style
|
|
||||||
text_content = element.get_text().strip()
|
|
||||||
if text_content:
|
|
||||||
flowables.append(Paragraph(text_content, style))
|
|
||||||
flowables.append(Spacer(1, 12))
|
|
||||||
else:
|
|
||||||
# Texto plano -> organizar en párrafos
|
|
||||||
paragraphs = organize_paragraphs(part)
|
|
||||||
for para_text in paragraphs:
|
|
||||||
if para_text:
|
|
||||||
flowables.append(Paragraph(para_text, normal_style))
|
|
||||||
flowables.append(Spacer(1, 12))
|
|
||||||
|
|
||||||
doc.build(flowables)
|
|
||||||
return output_pdf
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,12 @@
|
||||||
- Finalizing image description functionality
|
- Finalizing image description functionality
|
||||||
|
|
||||||
## Recent Changes
|
## Recent Changes
|
||||||
|
- Removed TxtToPdf functionality:
|
||||||
|
- Removed TxtToPdf component from frontend
|
||||||
|
- Removed TxtToPdf route from App.js navigation
|
||||||
|
- Removed txttopdf endpoint from backend API
|
||||||
|
- Removed convert_txt_to_pdf and organize_paragraphs functions from utils.py
|
||||||
|
- Removed beautifulsoup4 dependency from Dockerfile
|
||||||
- Added Flask-Login for backend authentication
|
- Added Flask-Login for backend authentication
|
||||||
- Created auth.py for user management
|
- Created auth.py for user management
|
||||||
- Added Login component to frontend
|
- Added Login component to frontend
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,9 @@
|
||||||
- Testing: 40% complete
|
- Testing: 40% complete
|
||||||
- Overall Progress: ~85% complete
|
- Overall Progress: ~85% complete
|
||||||
|
|
||||||
|
## Recent Changes
|
||||||
|
- ✅ Removed: TxtToPdf functionality has been completely removed from the application as it's no longer needed
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
- ✅ Fixed: 'Files' object has no attribute 'upload_blob' error in image description functionality
|
- ✅ Fixed: 'Files' object has no attribute 'upload_blob' error in image description functionality
|
||||||
- ✅ Fixed: '504 Deadline Exceeded' error in Gemini API calls with large images
|
- ✅ Fixed: '504 Deadline Exceeded' error in Gemini API calls with large images
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import FileUpload from './components/FileUpload';
|
||||||
import Login from './components/Login';
|
import Login from './components/Login';
|
||||||
import FileList from './components/FileList';
|
import FileList from './components/FileList';
|
||||||
import Configurations from './components/Configurations';
|
import Configurations from './components/Configurations';
|
||||||
import TxtToPdf from './components/TxtToPdf';
|
|
||||||
import Notifications from './components/Notifications';
|
import Notifications from './components/Notifications';
|
||||||
import ChangePassword from './components/ChangePassword';
|
import ChangePassword from './components/ChangePassword';
|
||||||
import ImageDescription from './components/ImageDescription';
|
import ImageDescription from './components/ImageDescription';
|
||||||
|
|
@ -108,19 +107,12 @@ function App() {
|
||||||
>
|
>
|
||||||
⚙️ Configurations
|
⚙️ Configurations
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
onClick={() => setActiveTab('txttopdf')}
|
|
||||||
className={activeTab === 'txttopdf' ? 'active tab-txttopdf' : 'tab-txttopdf'}
|
|
||||||
>
|
|
||||||
📝 TXT to PDF
|
|
||||||
</button>
|
|
||||||
</nav>
|
</nav>
|
||||||
<main>
|
<main>
|
||||||
{activeTab === 'DocuLens' && <FileUpload onJobCompleted={handleJobCompleted} />}
|
{activeTab === 'DocuLens' && <FileUpload onJobCompleted={handleJobCompleted} />}
|
||||||
{activeTab === 'image-description' && <ImageDescription onJobCompleted={handleJobCompleted} />}
|
{activeTab === 'image-description' && <ImageDescription onJobCompleted={handleJobCompleted} />}
|
||||||
{activeTab === 'files' && <FileList />}
|
{activeTab === 'files' && <FileList />}
|
||||||
{activeTab === 'configurations' && <Configurations />}
|
{activeTab === 'configurations' && <Configurations />}
|
||||||
{activeTab === 'txttopdf' && <TxtToPdf />}
|
|
||||||
</main>
|
</main>
|
||||||
<Notifications notifications={notifications} onClear={clearNotifications} />
|
<Notifications notifications={notifications} onClear={clearNotifications} />
|
||||||
{showChangePassword && (
|
{showChangePassword && (
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue