Maison  >  Article  >  développement back-end  >  Divisez et renommez facilement des PDF pour Skyward

Divisez et renommez facilement des PDF pour Skyward

王林
王林original
2024-07-30 01:14:33771parcourir

Easily Split and Rename PDFs for Skyward

Pourquoi le construire et à quoi ça sert

Il y a quelques semaines, mon superviseur m'a lancé un défi pour voir si je pouvais proposer un flux de travail pour un problème particulier que nous rencontrions. Nous voulions intégrer les lettres Pre/ACT dans notre SMS (Student Management System), qui dans notre cas était Skyward. Le problème que nous avons rencontré est que les lettres Pre/ACT sont soit dans un PDF groupé, soit par PDF individuel, et pour accéder à Skyward, nous aurions besoin d'un PDF pour le nom de chaque élève comme numéro d'identification. Pour ce faire, j'ai décidé d'écrire un programme en Python, en utilisant Streamlit pour l'interface utilisateur.

Regardons les problèmes que nous devons résoudre, en commençant par le PDF. Il était plus logique de simplement récupérer l'exportation PDF unique en masse des lettres, cela signifiait que nous devions diviser l'exportation groupée en PDF individuels. Bien que chaque lettre fasse généralement 2 pages, ce n'est pas toujours le cas, donc une simple coupure d'une page sur deux est susceptible d'être sujette aux erreurs.

Le deuxième problème consistait à lire le PDF de chaque élève et à le renommer avec le numéro d'identification correspondant. Cela reposait principalement sur un modèle Regex qui tirait ce dont j'avais besoin.

Comme c'était aussi un défi de temps, j'ai travaillé avec l'IA pour aider à générer le code. REMARQUE : Cela ne remplace pas la connaissance de la logique et du langage que vous utilisez. Lors de l'écriture de ceci avec AI/LLM, j'ai utilisé l'approche de la chaîne de pensée, en donnant de petits morceaux de ce que je voulais, puis en déboguant et en testant chaque morceau avant d'en ajouter d'autres. Le code ci-dessous est le code final qui a été utilisé, je vais décomposer chaque section par section. Si vous cherchez à mettre en œuvre cela comme solution dans votre district, consultez les TLDR à la fin de cet article.

Exigences et importations

Cette partie est assez simple et constitue la base sur laquelle le programme fonctionne.

  • Streamlit pour notre interface utilisateur
  • pypdf2, pymupdf et fitz pour la manipulation de PDF

Contenu du fichier exigences.txt

streamlit
pypdf2
fitz
pymupdf

Les importations app.py

import PyPDF2
import fitz  # PyMuPDF
import re
from pathlib import Path
import concurrent.futures
import streamlit as st
import shutil
import zipfile
import os

Trouver des identifiants

L'extrait suivant concerne la recherche des identifiants dans le PDF en masse et la création d'une liste de pages à utiliser pour les diviser. C'est la partie qui dépend de l'expression régulière et qui devra peut-être être modifiée en fonction de votre situation.

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

Diviser les PDF

Comme le titre l'indique, ceci est utilisé pour diviser les PDF. Cela utilisera une fonction pour extraire les noms de chaque PDF individuel. Vous remarquerez également que cela les divise en parallèle, jusqu'à 10 à la fois, pour améliorer les performances.

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 + 1 < len(id_pages) else total_pages

 pdf_writer = PyPDF2.PdfWriter()
        for j in range(start_page, end_page):
 pdf_writer.add_page(pdf_reader.pages[j])

 temp_pdf_path = output_folder / f'temp_{i}.pdf'
        with open(temp_pdf_path, 'wb') as output_pdf:
 pdf_writer.write(output_pdf)

 temp_pdfs.append(temp_pdf_path)
 progress_callback((i + 1) / len(id_pages))  # Update progress bar

    # Process renaming in parallel
    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
 executor.map(lambda pdf_path: extract_and_rename_pdf(pdf_path, output_folder), temp_pdfs)
def 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)

Presque là

Ensuite, quelques fonctions courtes, une pour compresser tous les PDF fractionnés (au cas où vous souhaiteriez l'exécuter sur un serveur interne), et une pour nettoyer tous les fichiers temporaires afin qu'il n'y ait aucune information PII sur les étudiants qui traîne là où il n'a pas besoin de vivre.

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")

Construire l'interface utilisateur

Le dernier morceau de code est destiné à l'interface utilisateur. Streamlit est une WebUI pour la polyvalence (oui, vous pouvez l'exécuter en solo). Après quelques tentatives et compte tenu de la convivialité. Pour faire simple, je l'ai résumé en un bouton de téléchargement, un bouton d'action (c'est-à-dire diviser) et un bouton de téléchargement pour obtenir les PDF compressés.

# 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 pour être opérationnel

Pour que les choses soient opérationnelles, utilisez simplement les commandes suivantes (cela suppose Linux, WSL et MacOS). et vous pourrez accéder à l'application en allant sur 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.py

En conclusion

Si vous êtes dans une école K12, j'espère que cela vous sera utile. Si c'est le cas, applaudissez ou envisagez de m'offrir un café. A la prochaine fois, bon vent et mer favorable.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn