Hace unas semanas, mi supervisor me propuso el desafío de ver si podía idear un flujo de trabajo para un problema particular que teníamos. Queríamos recibir cartas Pre/ACT en nuestro SMS (Sistema de gestión de estudiantes), que en nuestro caso era Skyward. El problema con el que nos topamos es que las cartas Pre/ACT están en un PDF masivo o en un PDF individual, y para ingresar a Skyward necesitaríamos tener un PDF para el nombre de cada estudiante como su número de identificación. Para lograr esto, decidí escribir un programa en Python, usando Streamlit para la interfaz de usuario.
Veamos los problemas que debemos abordar, comenzando con el PDF. Tenía más sentido simplemente tomar la exportación masiva de las cartas en PDF, esto significaba que necesitábamos dividir la exportación masiva en archivos PDF individuales. Si bien cada carta suele tener 2 páginas, ese no siempre es el caso, por lo que un simple salto entre páginas probablemente sea propenso a errores.
El segundo problema fue leer el PDF de cada estudiante y cambiarle el nombre al número de identificación correspondiente. Esto dependía principalmente de un patrón Regex que obtenía lo que necesitaba.
Dado que esto también era un desafío de tiempo, trabajé con IA para ayudar a generar el código. NOTA: Esto no reemplaza el conocimiento de la lógica y el lenguaje que está utilizando. Al escribir esto con AI/LLM utilicé el enfoque de cadena de pensamiento, dando fragmentos pequeños de lo que quería y luego depurando y probando cada fragmento antes de agregar más. El siguiente código es el código final que se utilizó; desglosaré cada sección por sección. Si está buscando implementar esto como una solución en su distrito, consulte los TLDR al final de esta publicación.
Esta parte es bastante sencilla y es la base sobre la que se ejecuta el programa.
Contenido de requisitos.txt
streamlit pypdf2 fitz pymupdf
La aplicación.py importa
import PyPDF2 import fitz # PyMuPDF import re from pathlib import Path import concurrent.futures import streamlit as st import shutil import zipfile import os
El siguiente fragmento trata de encontrar los ID en el PDF masivo y crear una lista de páginas que se usarán para dividirlos; esta es la parte que depende de la expresión regular y es posible que deba cambiarse según su situación.
def find_id_pages(input_pdf): doc = fitz.open(input_pdf) id_pages = [] id_pattern = re.compile(r'\(ID#:\s*(\d )\)') for i, page in enumerate(doc): text = page.get_text() if id_pattern.search(text): id_pages.append(i) return id_pages
Como dice el título, esto se utiliza para dividir los archivos PDF. Esto utilizará una función para extraer los nombres de cada PDF individual. También notarás que esto los divide en paralelo, hasta 10 a la vez, para mejorar el rendimiento.
def split_pdf(input_pdf, output_folder, progress_callback): input_path = Path(input_pdf) output_folder = Path(output_folder) output_folder.mkdir(parents=True, exist_ok=True) # Find pages with IDs id_pages = find_id_pages(input_pdf) if not id_pages: st.error("No ID pages found in the PDF.") return pdf_reader = PyPDF2.PdfReader(str(input_path)) total_pages = len(pdf_reader.pages) temp_pdfs = [] for i in range(len(id_pages)): start_page = id_pages[i] end_page = id_pages[i 1] if i 1def extract_and_rename_pdf(pdf_path, output_folder): doc = fitz.open(pdf_path) text_first_page = doc[0].get_text() # Extract ID using a regex pattern for the format (ID#: 01234) match_first_page = re.search(r'\(ID#:\s*(\d )\)', text_first_page) if match_first_page: id_value = match_first_page.group(1) new_pdf_path = output_folder / f'{id_value}.pdf' pdf_path.rename(new_pdf_path) else: new_pdf_path = output_folder / f'unknown_{pdf_path.stem}.pdf' pdf_path.rename(new_pdf_path)Casi llegamos
A continuación hay un par de funciones breves, una para comprimir todos los archivos PDF divididos (en caso de que desee ejecutar esto en un servidor interno) y otra para limpiar los archivos temporales para que no quede información PII de los estudiantes por ahí. no necesita vivir.
def zip_output_folder(output_folder, zip_name): shutil.make_archive(zip_name, 'zip', output_folder)def clean_up(output_folder, zip_name): shutil.rmtree(output_folder) os.remove(f"{zip_name}.zip")Construyendo la interfaz de usuario
El último fragmento de código es para la interfaz de usuario. Streamlit es una interfaz de usuario web para mayor versatilidad (sí, puedes ejecutarla solo). Después de algunos intentos y considerando la usabilidad. Manteniéndolo simple, lo resumí en un botón de carga, un botón de acción (es decir, dividir) y un botón de descarga para obtener los archivos PDF comprimidos.
# Streamlit App Portion st.title("PDF Splitter and Renamer") uploaded_file = st.file_uploader("Choose a PDF file", type="pdf") output_folder = "output_folder" if st.button("Split and Rename PDF"): if uploaded_file and output_folder: try: # Save uploaded file temporarily with open("temp_input.pdf", "wb") as f: f.write(uploaded_file.getbuffer()) progress_bar = st.progress(0) def update_progress(progress): progress_bar.progress(progress) split_pdf("temp_input.pdf", output_folder, update_progress) zip_name = "output_pdfs" zip_output_folder(output_folder, zip_name) st.success("PDF split and renamed successfully!") with open(f"{zip_name}.zip", "rb") as f: st.download_button( label="Download ZIP", data=f, file_name=f"{zip_name}.zip", mime="application/zip" ) # Remove temporary file Path("temp_input.pdf").unlink() clean_up(output_folder, zip_name) except Exception as e: st.error(f"An error occurred: {e}") else: st.error("Please upload a PDF file and specify an output folder.")TLDR para comenzar a funcionar
Para que todo esté en funcionamiento, simplemente use los siguientes comandos (esto supone Linux, WSL y MacOS). y podrás acceder a la aplicación yendo a http://localhost:8501.
git clone https://github.com/Blacknight318/act-to-sms.git cd act-to-sms python3 -m venv venv source venv/bin/activate pip install -r requirements.txt streamlit run app.pyPara concluir
Si estás en una escuela K12, espero que esto te resulte útil. Si es así, aplaude o considera invitarme a un café. Hasta la próxima, vientos favorables y mares favorables.
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3