From c44e2cf49b0cceb4d14b72eb780d5f09c642820a Mon Sep 17 00:00:00 2001 From: jlonge4 <91354480+jlonge4@users.noreply.github.com> Date: Thu, 23 Nov 2023 10:46:41 -0500 Subject: [PATCH] feat: add microsoft pptx file converter (#6399) * Create pptx.py * feat: pptx converter import __init__.py * feat: add pptx import __init__.py * feat: add python-pptx dependency * feat: add sample pptx for testing * feat: add pptx file-converter test * feat: release note pptx-file-converter-3e494d2747637eb2.yaml * feat: Update releasenotes/notes/pptx-file-converter-3e494d2747637eb2.yaml Co-authored-by: Stefano Fiorucci <44616784+anakin87@users.noreply.github.com> * feat: refactor haystack/nodes/file_converter/pptx.py Co-authored-by: Stefano Fiorucci <44616784+anakin87@users.noreply.github.com> * fix imports --------- Co-authored-by: Stefano Fiorucci <44616784+anakin87@users.noreply.github.com> --- haystack/nodes/__init__.py | 1 + haystack/nodes/file_converter/__init__.py | 1 + haystack/nodes/file_converter/pptx.py | 85 ++++++++++++++++++ pyproject.toml | 1 + .../pptx-file-converter-3e494d2747637eb2.yaml | 4 + test/nodes/test_file_converter.py | 8 ++ test/samples/pptx/sample_pptx.pptx | Bin 0 -> 33983 bytes 7 files changed, 100 insertions(+) create mode 100644 haystack/nodes/file_converter/pptx.py create mode 100644 releasenotes/notes/pptx-file-converter-3e494d2747637eb2.yaml create mode 100644 test/samples/pptx/sample_pptx.pptx diff --git a/haystack/nodes/__init__.py b/haystack/nodes/__init__.py index a969e245be..9ac1f3268b 100644 --- a/haystack/nodes/__init__.py +++ b/haystack/nodes/__init__.py @@ -7,6 +7,7 @@ from haystack.nodes.file_converter import ( BaseConverter, DocxToTextConverter, + PptxConverter, ImageToTextConverter, MarkdownConverter, PDFToTextConverter, diff --git a/haystack/nodes/file_converter/__init__.py b/haystack/nodes/file_converter/__init__.py index 0c24dbcdfb..76a5dd1aa3 100644 --- a/haystack/nodes/file_converter/__init__.py +++ b/haystack/nodes/file_converter/__init__.py @@ -3,6 +3,7 @@ from haystack.nodes.file_converter.csv import CsvTextConverter from haystack.nodes.file_converter.docx import DocxToTextConverter +from haystack.nodes.file_converter.pptx import PptxConverter from haystack.nodes.file_converter.json import JsonConverter from haystack.nodes.file_converter.tika import TikaConverter, TikaXHTMLParser from haystack.nodes.file_converter.txt import TextConverter diff --git a/haystack/nodes/file_converter/pptx.py b/haystack/nodes/file_converter/pptx.py new file mode 100644 index 0000000000..84fe46b1d2 --- /dev/null +++ b/haystack/nodes/file_converter/pptx.py @@ -0,0 +1,85 @@ +from typing import List, Optional, Dict +from pathlib import Path +import logging + +from haystack.schema import Document +from haystack.lazy_imports import LazyImport +from haystack.nodes.file_converter.base import BaseConverter + + +logger = logging.getLogger(__name__) + + +with LazyImport("Run 'pip install python-pptx'") as pptx_import: + from pptx import Presentation + + +class PptxConverter(BaseConverter): + def __init__( + self, + remove_numeric_tables: bool = False, + valid_languages: Optional[List[str]] = None, + id_hash_keys: Optional[List[str]] = None, + progress_bar: bool = True, + ): + pptx_import.check() + super().__init__( + remove_numeric_tables=remove_numeric_tables, + valid_languages=valid_languages, + id_hash_keys=id_hash_keys, + progress_bar=progress_bar, + ) + + def convert( + self, + file_path: Path, + meta: Optional[Dict[str, str]] = None, + remove_numeric_tables: Optional[bool] = None, + valid_languages: Optional[List[str]] = None, + encoding: Optional[str] = None, + id_hash_keys: Optional[List[str]] = None, + ) -> List[Document]: + """ + Extract text from a .pptx file. + Note: As pptx doesn't contain "page" information, we actually extract and return a list of texts from each slide here. + For compliance with other converters we nevertheless opted for keeping the methods name. + + :param file_path: Path to the .pptx file you want to convert + :param meta: dictionary of meta data key-value pairs to append in the returned document. + :param remove_numeric_tables: This option uses heuristics to remove numeric rows from the tables. + The tabular structures in documents might be noise for the reader model if it + does not have table parsing capability for finding answers. However, tables + may also have long strings that could possible candidate for searching answers. + The rows containing strings are thus retained in this option. + :param valid_languages: validate languages from a list of languages specified in the ISO 639-1 + (https://en.wikipedia.org/wiki/ISO_639-1) format. + This option can be used to add test for encoding errors. If the extracted text is + not one of the valid languages, then it might likely be encoding error resulting + in garbled text. + :param encoding: Not applicable + :param id_hash_keys: Generate the document id from a custom list of strings that refer to the document's + attributes. If you want to ensure you don't have duplicate documents in your DocumentStore but texts are + not unique, you can modify the metadata and pass e.g. `"meta"` to this field (e.g. [`"content"`, `"meta"`]). + In this case the id will be generated by using the content and the defined metadata. + """ + if remove_numeric_tables is None: + remove_numeric_tables = self.remove_numeric_tables + if valid_languages is None: + valid_languages = self.valid_languages + if remove_numeric_tables is True: + raise Exception("'remove_numeric_tables' is not supported by PptxToTextConverter.") + if valid_languages is True: + raise Exception("Language validation using 'valid_languages' is not supported by PptxToTextConverter.") + if id_hash_keys is None: + id_hash_keys = self.id_hash_keys + + pres = Presentation(file_path) + text_parts = [] + for slide in pres.slides: + for shape in slide.shapes: + if hasattr(shape, "text"): + text_parts.append(shape.text) + text = "\n".join(text_parts) + + document = Document(content=text, meta=meta, id_hash_keys=id_hash_keys) + return [document] diff --git a/pyproject.toml b/pyproject.toml index 06982853b0..8def0a45c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -165,6 +165,7 @@ preprocessing = [ file-conversion = [ "azure-ai-formrecognizer>=3.2.0b2", # Microsoft Azure's Form Recognizer service (text and table exctrator) "python-docx", + "python-pptx", "tika", # Apache Tika (text & metadata extractor) "beautifulsoup4", "markdown", diff --git a/releasenotes/notes/pptx-file-converter-3e494d2747637eb2.yaml b/releasenotes/notes/pptx-file-converter-3e494d2747637eb2.yaml new file mode 100644 index 0000000000..6090da6d44 --- /dev/null +++ b/releasenotes/notes/pptx-file-converter-3e494d2747637eb2.yaml @@ -0,0 +1,4 @@ +--- + features: + - | + Add PptxConverter: a node to convert pptx files to Haystack Documents. diff --git a/test/nodes/test_file_converter.py b/test/nodes/test_file_converter.py index c61ed90c46..9daee4b587 100644 --- a/test/nodes/test_file_converter.py +++ b/test/nodes/test_file_converter.py @@ -16,6 +16,7 @@ AzureConverter, CsvTextConverter, DocxToTextConverter, + PptxConverter, JsonConverter, MarkdownConverter, ParsrConverter, @@ -207,6 +208,13 @@ def test_docx_converter(samples_path): assert document.content.startswith("Sample Docx File") +@pytest.mark.unit +def test_pptx_converter(samples_path): + converter = PptxConverter() + document = converter.convert(file_path=samples_path / "pptx" / "sample_pptx.pptx")[0] + assert document.content.startswith("Sample Pptx File") + + @pytest.mark.unit def test_markdown_converter(samples_path): converter = MarkdownConverter() diff --git a/test/samples/pptx/sample_pptx.pptx b/test/samples/pptx/sample_pptx.pptx new file mode 100644 index 0000000000000000000000000000000000000000..f34bde75e19d0d6585fafcdeafb4a50707927b21 GIT binary patch literal 33983 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