diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7ed50be --- /dev/null +++ b/.dockerignore @@ -0,0 +1,70 @@ +# Python +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +env/ +venv/ +.venv/ +pip-log.txt +pip-delete-this-directory.txt + +# Virtual environments +bin/ +include/ +lib/ +lib64/ +share/ +pyvenv.cfg + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Git +.git/ +.gitignore + +# Docker +Dockerfile +.dockerignore + +# Temporary files +*.tmp +*.log + +# Output files +output.csv +annotated.pdf +modified.pdf +data.json +data.txt + +# Test files (optional, if you want to exclude) +# test_*.py + +# Environment files +.env +.env.local + +# Development files +dev-requirements.txt +pytest.ini +.pre-commit-config.yaml +.coverage +htmlcov/ +.mypy_cache/ +.pytest_cache/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..004e57b --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +# Environment variables for Docker container +DEFAULT_MAX_FILES=10 +DEFAULT_OUTPUT=/app/output/result.csv \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..446774f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + + - repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black + + - repo: https://github.com/pycqa/isort + rev: 5.10.1 + hooks: + - id: isort + + - repo: https://github.com/pycqa/flake8 + rev: 5.0.4 + hooks: + - id: flake8 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bc7c751 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +# Use Python 3.9 slim image +FROM python:3.9-slim + +# Set working directory +WORKDIR /app + +# Install system dependencies for PyMuPDF +RUN apt-get update && apt-get install -y \ + build-essential \ + libmupdf-dev \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements and install Python dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the application code +COPY . . + +# Create directories for input/output if needed +RUN mkdir -p /app/input /app/output + +# Set environment variables +ENV DEFAULT_MAX_FILES=10 +ENV DEFAULT_OUTPUT=/app/output/result.csv + +# Set the default command +ENTRYPOINT ["python", "main.py"] + +# Default arguments (can be overridden) +CMD ["--help"] \ No newline at end of file diff --git a/README.md b/README.md old mode 100644 new mode 100755 index db9f2dd..8f9dae3 --- a/README.md +++ b/README.md @@ -29,11 +29,13 @@ We hope you find this code helpful for your invoice data extraction needs! ## Usage -To use the code in this repository, follow the instructions below: +### Local Setup + +To use the code locally: 1. Activate the virtual environment: ``` - source venv/bin/activate + source bin/activate ``` 2. Install the required dependencies: @@ -41,17 +43,174 @@ To use the code in this repository, follow the instructions below: pip install -r requirements.txt ``` -3. Run the `main.py` file: + For development, also install: + ``` + pip install -r dev-requirements.txt + ``` + +3. Run the `main.py` file with a PDF file or directory: + ``` + python main.py [--max-files N] [--output filename.csv] + ``` + + - For a single PDF: `python main.py path/to/invoice.pdf --pages 1,3` + - Skip specific pages: `python main.py path/to/invoice.pdf --skip-pages 2,4` + - Password-protected PDF: `python main.py path/to/invoice.pdf --password mypassword` + - Auto-rotate pages: `python main.py path/to/invoice.pdf --auto-rotate` + - Combined: `python main.py path/to/invoice.pdf --password mypassword --auto-rotate --pages 1,2` + - For batch processing: `python main.py path/to/directory/ --max-files 5 --output my_output.csv --auto-rotate --password batchpassword` + + Options: + - `--max-files N`: Process up to N files (batch only). + - `--output filename.csv`: Specify output CSV filename. + - `--pages PAGES`: Process only specified pages (e.g., '1,3,5-7'). + - `--skip-pages PAGES`: Skip specified pages (e.g., '2,4'). Overrides --pages if both specified. + - `--password PASSWORD`: Password for password-protected PDFs. + - `--auto-rotate`: Automatically detect and correct inverted/rotated pages. + +4. After the extraction process, a CSV file will be generated containing the extracted data. For batch processing, a detailed summary including processed, total, and error counts is displayed. + +### Docker Setup + +The project includes Docker support for easy deployment: + +#### Using Docker CLI + +1. **Build the Docker image:** + ``` + docker build -t pdf-extractor . + ``` + +2. **Run a single PDF extraction:** + ``` + docker run --rm -v $(pwd)/input:/app/input -v $(pwd)/output:/app/output pdf-extractor /app/input/invoice.pdf --output /app/output/result.csv + ``` + +3. **Run batch processing:** + ``` + docker run --rm -v $(pwd)/input:/app/input -v $(pwd)/output:/app/output pdf-extractor /app/input/ --max-files 10 --output /app/output/batch_results.csv + ``` + + - Mount your input directory to `/app/input` + - Mount your output directory to `/app/output` + - Replace `$(pwd)` with the full path if on Windows + +4. **View help:** + ``` + docker run --rm pdf-extractor --help + ``` + +#### Using Docker Compose + +For convenience, a `docker-compose.yml` is provided: + +1. **Build and run:** + ```bash + docker-compose up --build ``` - python main.py + +2. **Run with custom command:** + ```bash + docker-compose run --rm pdf-extractor /app/input/sample.pdf --output /app/output/result.csv ``` - The program will prompt you to provide the location of the PDF file to extract data from. +**Note:** Ensure your PDF files are in a directory mounted to `/app/input`, and output will be written to a directory mounted to `/app/output`. + +**Security Note:** When using `--password`, the password will be visible in command history and logs. For sensitive operations, consider using environment variables or secure input methods. + +## Docker + +This project is containerized using Docker for consistent deployment across environments. The following files are included: + +- `Dockerfile`: Multi-stage build with Python 3.9, installs dependencies, and sets up the application. +- `docker-compose.yml`: Convenient orchestration for running the container. +- `.dockerignore`: Excludes unnecessary files from the build context. + +### Building the Image -4. After the extraction process, a CSV file named `output.csv` will be generated containing the extracted data. +```bash +docker build -t pdf-extractor . +``` + +### Running Containers + +- **Single File Processing:** + ```bash + docker run --rm -v /host/path/to/input:/app/input -v /host/path/to/output:/app/output pdf-extractor /app/input/sample.pdf --output /app/output/result.csv + ``` + +- **Batch Processing:** + ```bash + docker run --rm -v /host/path/to/input:/app/input -v /host/path/to/output:/app/output pdf-extractor /app/input/ --max-files 5 --output /app/output/batch.csv + ``` + +### Environment Variables (Optional) + +You can set environment variables for default behaviors: +- `DEFAULT_MAX_FILES`: Default maximum files for batch processing +- `DEFAULT_OUTPUT`: Default output filename + +Example: +```bash +docker run --rm -e DEFAULT_MAX_FILES=10 -v $(pwd)/data:/app/input pdf-extractor /app/input/ +``` All the necessary functions are implemented in `qnot.py` and are imported into `main.py`. +## Development + +### Requirements Files + +- `requirements.txt`: Production dependencies +- `dev-requirements.txt`: Development dependencies including linting, testing, and documentation tools + +### Testing + +Run tests with: +```bash +python -m pytest +``` + +### Code Quality + +Use pre-commit hooks for code quality: +```bash +pip install pre-commit +pre-commit install +``` + +Available tools: black, isort, flake8, mypy. + +## New Features + +- **Regex-based Extraction**: Added functions in `qnot.py` for extracting specific data using regex: + - `extract_prices(text)`: Extracts monetary values. + - `extract_quantities(text)`: Extracts integer quantities. + - `extract_sizes(text)`: Extracts size information (XS, S, M, etc.). + - `extract_dates(text)`: Extracts dates in common formats. + - `extract_emails(text)`: Extracts email addresses. + - `extract_phone_numbers(text)`: Extracts phone numbers. + - `regex_based_extraction(text)`: Combines all above into a dictionary. + +- **Batch Processing**: + - `process_batch_pdfs(input_dir, output_csv, max_files)`: Processes PDFs in a directory, optionally limiting to max_files, and returns processing statistics. + +- **Pydantic Validation**: Data validation using Pydantic models (`models.py`): + - `Product`: Validates product data with field-specific rules. + - `PageData`: Groups products per page. + - `LineItem`: Validates raw line items from tables. + - `InvoiceData`: Aggregates data from all pages with validation reports. + - Text validation for emails, phones, dates, and prices extracted from each page. + +- **Table Parsing**: Advanced table detection and parsing: + - Detects tables from text spans, handling merged cells and irregular structures. + - Extracts line items from tables as separate entities. + - Validates line items using Pydantic models. + - Generates CSV with both structured products and raw line items. + - Supports complex table layouts with row/column detection. + +- **Tests**: Unit tests for edge cases in `test_regex.py`. Run with `python -m pytest` or `pytest`. + # Contribution Contributions to the "Extracting Data from PDF Invoices" project are welcome and encouraged! If you have any ideas, improvements, or bug fixes, please feel free to contribute. diff --git a/__pycache__/gpt.cpython-39.pyc b/__pycache__/gpt.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/__pycache__/main.cpython-311.pyc b/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000..d542d89 Binary files /dev/null and b/__pycache__/main.cpython-311.pyc differ diff --git a/__pycache__/models.cpython-311.pyc b/__pycache__/models.cpython-311.pyc new file mode 100644 index 0000000..ebe405d Binary files /dev/null and b/__pycache__/models.cpython-311.pyc differ diff --git a/__pycache__/qnot.cpython-311.pyc b/__pycache__/qnot.cpython-311.pyc new file mode 100644 index 0000000..658b543 Binary files /dev/null and b/__pycache__/qnot.cpython-311.pyc differ diff --git a/__pycache__/qnot.cpython-39.pyc b/__pycache__/qnot.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/annotated.pdf b/annotated.pdf old mode 100644 new mode 100755 diff --git a/bin/Activate.ps1 b/bin/Activate.ps1 old mode 100644 new mode 100755 diff --git a/bin/activate b/bin/activate old mode 100644 new mode 100755 diff --git a/bin/activate.csh b/bin/activate.csh old mode 100644 new mode 100755 diff --git a/bin/activate.fish b/bin/activate.fish old mode 100644 new mode 100755 diff --git a/data.json b/data.json old mode 100644 new mode 100755 diff --git a/data.txt b/data.txt old mode 100644 new mode 100755 diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..5b07eb8 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,20 @@ +# Development dependencies +-r requirements.txt + +# Linting and formatting +flake8>=4.0.0 +black>=22.0.0 +isort>=5.10.0 + +# Type checking +mypy>=0.950 + +# Testing +pytest>=7.0.0 +pytest-cov>=3.0.0 + +# Documentation +sphinx>=4.0.0 + +# Development tools +pre-commit>=2.17.0 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5e28e5e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +version: '3.8' + +services: + pdf-extractor: + build: . + volumes: + - ./input:/app/input + - ./output:/app/output + env_file: + - .env + environment: + - DEFAULT_MAX_FILES=${DEFAULT_MAX_FILES:-10} + - DEFAULT_OUTPUT=${DEFAULT_OUTPUT:-/app/output/result.csv} + command: ["--help"] \ No newline at end of file diff --git a/inp_pdf/ORDER_HENRIK-VIBSKOV-_2023-07-31_13372792.pdf b/inp_pdf/ORDER_HENRIK-VIBSKOV-_2023-07-31_13372792.pdf old mode 100644 new mode 100755 diff --git a/inp_pdf/ORDER_Lauren-Manoogian_2023-10-06_13769226.pdf b/inp_pdf/ORDER_Lauren-Manoogian_2023-10-06_13769226.pdf old mode 100644 new mode 100755 diff --git a/inp_pdf/ORDER_MOTHER-Denim_2023-07-15_13765510.pdf b/inp_pdf/ORDER_MOTHER-Denim_2023-07-15_13765510.pdf old mode 100644 new mode 100755 diff --git a/inp_pdf/ORDER_Tibi_2023-07-31_13755432.pdf b/inp_pdf/ORDER_Tibi_2023-07-31_13755432.pdf old mode 100644 new mode 100755 diff --git a/inp_pdf/modified.pdf b/inp_pdf/modified.pdf old mode 100644 new mode 100755 diff --git a/inp_pdf/order.pdf b/inp_pdf/order.pdf old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/__pycache__/easy_install.cpython-39.pyc b/lib/python3.9/site-packages/__pycache__/easy_install.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/easy_install.py b/lib/python3.9/site-packages/easy_install.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip-20.3.4.dist-info/INSTALLER b/lib/python3.9/site-packages/pip-20.3.4.dist-info/INSTALLER old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip-20.3.4.dist-info/LICENSE.txt b/lib/python3.9/site-packages/pip-20.3.4.dist-info/LICENSE.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip-20.3.4.dist-info/METADATA b/lib/python3.9/site-packages/pip-20.3.4.dist-info/METADATA old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip-20.3.4.dist-info/RECORD b/lib/python3.9/site-packages/pip-20.3.4.dist-info/RECORD old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip-20.3.4.dist-info/REQUESTED b/lib/python3.9/site-packages/pip-20.3.4.dist-info/REQUESTED old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip-20.3.4.dist-info/WHEEL b/lib/python3.9/site-packages/pip-20.3.4.dist-info/WHEEL old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip-20.3.4.dist-info/entry_points.txt b/lib/python3.9/site-packages/pip-20.3.4.dist-info/entry_points.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip-20.3.4.dist-info/top_level.txt b/lib/python3.9/site-packages/pip-20.3.4.dist-info/top_level.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/__init__.py b/lib/python3.9/site-packages/pip/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/__main__.py b/lib/python3.9/site-packages/pip/__main__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/__pycache__/__main__.cpython-39.pyc b/lib/python3.9/site-packages/pip/__pycache__/__main__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__init__.py b/lib/python3.9/site-packages/pip/_internal/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__pycache__/build_env.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/__pycache__/build_env.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__pycache__/cache.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/__pycache__/cache.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__pycache__/configuration.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/__pycache__/configuration.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__pycache__/exceptions.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/__pycache__/exceptions.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__pycache__/locations.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/__pycache__/locations.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__pycache__/main.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/__pycache__/main.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__pycache__/pyproject.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/__pycache__/pyproject.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/__pycache__/self_outdated_check.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/__pycache__/wheel_builder.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/build_env.py b/lib/python3.9/site-packages/pip/_internal/build_env.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cache.py b/lib/python3.9/site-packages/pip/_internal/cache.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__init__.py b/lib/python3.9/site-packages/pip/_internal/cli/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/command_context.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/parser.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/parser.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/progress_bars.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/req_command.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/spinners.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py b/lib/python3.9/site-packages/pip/_internal/cli/autocompletion.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/base_command.py b/lib/python3.9/site-packages/pip/_internal/cli/base_command.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py b/lib/python3.9/site-packages/pip/_internal/cli/cmdoptions.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/command_context.py b/lib/python3.9/site-packages/pip/_internal/cli/command_context.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/main.py b/lib/python3.9/site-packages/pip/_internal/cli/main.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py b/lib/python3.9/site-packages/pip/_internal/cli/main_parser.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/parser.py b/lib/python3.9/site-packages/pip/_internal/cli/parser.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py b/lib/python3.9/site-packages/pip/_internal/cli/progress_bars.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/req_command.py b/lib/python3.9/site-packages/pip/_internal/cli/req_command.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/spinners.py b/lib/python3.9/site-packages/pip/_internal/cli/spinners.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py b/lib/python3.9/site-packages/pip/_internal/cli/status_codes.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__init__.py b/lib/python3.9/site-packages/pip/_internal/commands/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/cache.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/cache.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/check.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/check.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/completion.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/completion.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/debug.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/debug.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/download.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/download.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/hash.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/hash.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/help.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/help.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/install.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/install.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/list.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/list.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/search.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/search.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/show.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/show.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/cache.py b/lib/python3.9/site-packages/pip/_internal/commands/cache.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/check.py b/lib/python3.9/site-packages/pip/_internal/commands/check.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/completion.py b/lib/python3.9/site-packages/pip/_internal/commands/completion.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/configuration.py b/lib/python3.9/site-packages/pip/_internal/commands/configuration.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/debug.py b/lib/python3.9/site-packages/pip/_internal/commands/debug.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/download.py b/lib/python3.9/site-packages/pip/_internal/commands/download.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/freeze.py b/lib/python3.9/site-packages/pip/_internal/commands/freeze.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/hash.py b/lib/python3.9/site-packages/pip/_internal/commands/hash.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/help.py b/lib/python3.9/site-packages/pip/_internal/commands/help.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/install.py b/lib/python3.9/site-packages/pip/_internal/commands/install.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/list.py b/lib/python3.9/site-packages/pip/_internal/commands/list.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/search.py b/lib/python3.9/site-packages/pip/_internal/commands/search.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/show.py b/lib/python3.9/site-packages/pip/_internal/commands/show.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py b/lib/python3.9/site-packages/pip/_internal/commands/uninstall.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/commands/wheel.py b/lib/python3.9/site-packages/pip/_internal/commands/wheel.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/configuration.py b/lib/python3.9/site-packages/pip/_internal/configuration.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py b/lib/python3.9/site-packages/pip/_internal/distributions/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/base.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/base.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/sdist.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/distributions/base.py b/lib/python3.9/site-packages/pip/_internal/distributions/base.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/distributions/installed.py b/lib/python3.9/site-packages/pip/_internal/distributions/installed.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py b/lib/python3.9/site-packages/pip/_internal/distributions/sdist.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py b/lib/python3.9/site-packages/pip/_internal/distributions/wheel.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/exceptions.py b/lib/python3.9/site-packages/pip/_internal/exceptions.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/index/__init__.py b/lib/python3.9/site-packages/pip/_internal/index/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/index/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/index/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/index/__pycache__/collector.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/index/__pycache__/collector.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/index/__pycache__/package_finder.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/index/collector.py b/lib/python3.9/site-packages/pip/_internal/index/collector.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/index/package_finder.py b/lib/python3.9/site-packages/pip/_internal/index/package_finder.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/locations.py b/lib/python3.9/site-packages/pip/_internal/locations.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/main.py b/lib/python3.9/site-packages/pip/_internal/main.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__init__.py b/lib/python3.9/site-packages/pip/_internal/models/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/candidate.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/candidate.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/direct_url.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/format_control.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/format_control.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/index.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/index.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/link.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/link.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/scheme.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/scheme.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/target_python.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/target_python.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/__pycache__/wheel.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/models/__pycache__/wheel.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/candidate.py b/lib/python3.9/site-packages/pip/_internal/models/candidate.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/direct_url.py b/lib/python3.9/site-packages/pip/_internal/models/direct_url.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/format_control.py b/lib/python3.9/site-packages/pip/_internal/models/format_control.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/index.py b/lib/python3.9/site-packages/pip/_internal/models/index.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/link.py b/lib/python3.9/site-packages/pip/_internal/models/link.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/scheme.py b/lib/python3.9/site-packages/pip/_internal/models/scheme.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/search_scope.py b/lib/python3.9/site-packages/pip/_internal/models/search_scope.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py b/lib/python3.9/site-packages/pip/_internal/models/selection_prefs.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/target_python.py b/lib/python3.9/site-packages/pip/_internal/models/target_python.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/models/wheel.py b/lib/python3.9/site-packages/pip/_internal/models/wheel.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/__init__.py b/lib/python3.9/site-packages/pip/_internal/network/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/network/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/__pycache__/auth.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/network/__pycache__/auth.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/__pycache__/cache.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/network/__pycache__/cache.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/__pycache__/download.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/network/__pycache__/download.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/network/__pycache__/lazy_wheel.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/__pycache__/session.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/network/__pycache__/session.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/__pycache__/utils.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/network/__pycache__/utils.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/network/__pycache__/xmlrpc.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/auth.py b/lib/python3.9/site-packages/pip/_internal/network/auth.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/cache.py b/lib/python3.9/site-packages/pip/_internal/network/cache.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/download.py b/lib/python3.9/site-packages/pip/_internal/network/download.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py b/lib/python3.9/site-packages/pip/_internal/network/lazy_wheel.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/session.py b/lib/python3.9/site-packages/pip/_internal/network/session.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/utils.py b/lib/python3.9/site-packages/pip/_internal/network/utils.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py b/lib/python3.9/site-packages/pip/_internal/network/xmlrpc.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/__init__.py b/lib/python3.9/site-packages/pip/_internal/operations/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/check.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/check.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/build/__init__.py b/lib/python3.9/site-packages/pip/_internal/operations/build/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/build/metadata.py b/lib/python3.9/site-packages/pip/_internal/operations/build/metadata.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/build/metadata_legacy.py b/lib/python3.9/site-packages/pip/_internal/operations/build/metadata_legacy.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/build/wheel.py b/lib/python3.9/site-packages/pip/_internal/operations/build/wheel.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/build/wheel_legacy.py b/lib/python3.9/site-packages/pip/_internal/operations/build/wheel_legacy.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/check.py b/lib/python3.9/site-packages/pip/_internal/operations/check.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/freeze.py b/lib/python3.9/site-packages/pip/_internal/operations/freeze.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py b/lib/python3.9/site-packages/pip/_internal/operations/install/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/editable_legacy.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/legacy.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/operations/install/__pycache__/wheel.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py b/lib/python3.9/site-packages/pip/_internal/operations/install/editable_legacy.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py b/lib/python3.9/site-packages/pip/_internal/operations/install/legacy.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py b/lib/python3.9/site-packages/pip/_internal/operations/install/wheel.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/operations/prepare.py b/lib/python3.9/site-packages/pip/_internal/operations/prepare.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/pyproject.py b/lib/python3.9/site-packages/pip/_internal/pyproject.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/__init__.py b/lib/python3.9/site-packages/pip/_internal/req/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/req/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/__pycache__/constructors.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/req/__pycache__/constructors.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_file.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_file.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_install.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_install.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_set.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_set.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/constructors.py b/lib/python3.9/site-packages/pip/_internal/req/constructors.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/req_file.py b/lib/python3.9/site-packages/pip/_internal/req/req_file.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/req_install.py b/lib/python3.9/site-packages/pip/_internal/req/req_install.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/req_set.py b/lib/python3.9/site-packages/pip/_internal/req/req_set.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py b/lib/python3.9/site-packages/pip/_internal/req/req_tracker.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py b/lib/python3.9/site-packages/pip/_internal/req/req_uninstall.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py b/lib/python3.9/site-packages/pip/_internal/resolution/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/base.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/__pycache__/base.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/base.py b/lib/python3.9/site-packages/pip/_internal/resolution/base.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py b/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/legacy/__pycache__/resolver.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py b/lib/python3.9/site-packages/pip/_internal/resolution/legacy/resolver.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/base.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/base.py b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/base.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/candidates.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/factory.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/found_candidates.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/provider.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/reporter.py b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/reporter.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/requirements.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py b/lib/python3.9/site-packages/pip/_internal/resolution/resolvelib/resolver.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py b/lib/python3.9/site-packages/pip/_internal/self_outdated_check.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__init__.py b/lib/python3.9/site-packages/pip/_internal/utils/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compat.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compat.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/logging.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/logging.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/misc.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/misc.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/models.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/models.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/parallel.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/parallel.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/pkg_resources.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/pkg_resources.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/typing.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/typing.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/urls.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/urls.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py b/lib/python3.9/site-packages/pip/_internal/utils/appdirs.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/compat.py b/lib/python3.9/site-packages/pip/_internal/utils/compat.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py b/lib/python3.9/site-packages/pip/_internal/utils/compatibility_tags.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/datetime.py b/lib/python3.9/site-packages/pip/_internal/utils/datetime.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py b/lib/python3.9/site-packages/pip/_internal/utils/deprecation.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py b/lib/python3.9/site-packages/pip/_internal/utils/direct_url_helpers.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py b/lib/python3.9/site-packages/pip/_internal/utils/distutils_args.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/encoding.py b/lib/python3.9/site-packages/pip/_internal/utils/encoding.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py b/lib/python3.9/site-packages/pip/_internal/utils/entrypoints.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py b/lib/python3.9/site-packages/pip/_internal/utils/filesystem.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py b/lib/python3.9/site-packages/pip/_internal/utils/filetypes.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/glibc.py b/lib/python3.9/site-packages/pip/_internal/utils/glibc.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/hashes.py b/lib/python3.9/site-packages/pip/_internal/utils/hashes.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py b/lib/python3.9/site-packages/pip/_internal/utils/inject_securetransport.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/logging.py b/lib/python3.9/site-packages/pip/_internal/utils/logging.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/misc.py b/lib/python3.9/site-packages/pip/_internal/utils/misc.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/models.py b/lib/python3.9/site-packages/pip/_internal/utils/models.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/packaging.py b/lib/python3.9/site-packages/pip/_internal/utils/packaging.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/parallel.py b/lib/python3.9/site-packages/pip/_internal/utils/parallel.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py b/lib/python3.9/site-packages/pip/_internal/utils/pkg_resources.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py b/lib/python3.9/site-packages/pip/_internal/utils/setuptools_build.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py b/lib/python3.9/site-packages/pip/_internal/utils/subprocess.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py b/lib/python3.9/site-packages/pip/_internal/utils/temp_dir.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/typing.py b/lib/python3.9/site-packages/pip/_internal/utils/typing.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py b/lib/python3.9/site-packages/pip/_internal/utils/unpacking.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/urls.py b/lib/python3.9/site-packages/pip/_internal/utils/urls.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py b/lib/python3.9/site-packages/pip/_internal/utils/virtualenv.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/utils/wheel.py b/lib/python3.9/site-packages/pip/_internal/utils/wheel.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py b/lib/python3.9/site-packages/pip/_internal/vcs/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/git.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/git.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-39.pyc b/lib/python3.9/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py b/lib/python3.9/site-packages/pip/_internal/vcs/bazaar.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/git.py b/lib/python3.9/site-packages/pip/_internal/vcs/git.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py b/lib/python3.9/site-packages/pip/_internal/vcs/mercurial.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py b/lib/python3.9/site-packages/pip/_internal/vcs/subversion.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py b/lib/python3.9/site-packages/pip/_internal/vcs/versioncontrol.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_internal/wheel_builder.py b/lib/python3.9/site-packages/pip/_internal/wheel_builder.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_vendor/__init__.py b/lib/python3.9/site-packages/pip/_vendor/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_vendor/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pip/_vendor/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pip/_vendor/vendor.txt b/lib/python3.9/site-packages/pip/_vendor/vendor.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/AUTHORS.txt b/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/AUTHORS.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER b/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/INSTALLER old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/LICENSE.txt b/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/LICENSE.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/METADATA b/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/METADATA old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/RECORD b/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/RECORD old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/REQUESTED b/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/REQUESTED old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/WHEEL b/lib/python3.9/site-packages/pkg_resources-0.0.0.dist-info/WHEEL old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/__init__.py b/lib/python3.9/site-packages/pkg_resources/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/__pycache__/py31compat.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/__pycache__/py31compat.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/__init__.py b/lib/python3.9/site-packages/pkg_resources/_vendor/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/six.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/__pycache__/six.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/appdirs.py b/lib/python3.9/site-packages/pkg_resources/_vendor/appdirs.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__about__.py b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__about__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__init__.py b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_compat.py b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_compat.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_structures.py b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/_structures.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/markers.py b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/markers.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/requirements.py b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/requirements.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/specifiers.py b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/specifiers.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/utils.py b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/utils.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py b/lib/python3.9/site-packages/pkg_resources/_vendor/packaging/version.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/pyparsing.py b/lib/python3.9/site-packages/pkg_resources/_vendor/pyparsing.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/_vendor/six.py b/lib/python3.9/site-packages/pkg_resources/_vendor/six.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/extern/__init__.py b/lib/python3.9/site-packages/pkg_resources/extern/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/pkg_resources/py31compat.py b/lib/python3.9/site-packages/pkg_resources/py31compat.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/AUTHORS.txt b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/AUTHORS.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/INSTALLER b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/INSTALLER old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/LICENSE.txt b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/LICENSE.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/METADATA b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/METADATA old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/RECORD b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/RECORD old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/REQUESTED b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/REQUESTED old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/WHEEL b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/WHEEL old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/dependency_links.txt b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/dependency_links.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/entry_points.txt b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/entry_points.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/top_level.txt b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/top_level.txt old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/zip-safe b/lib/python3.9/site-packages/setuptools-44.1.1.dist-info/zip-safe old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__init__.py b/lib/python3.9/site-packages/setuptools/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/_imp.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/_imp.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/archive_util.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/archive_util.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/build_meta.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/build_meta.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/config.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/config.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/dep_util.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/dep_util.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/depends.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/depends.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/dist.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/dist.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/errors.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/errors.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/extension.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/extension.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/glob.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/glob.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/installer.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/installer.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/launch.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/launch.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/lib2to3_ex.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/lib2to3_ex.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/monkey.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/monkey.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/msvc.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/msvc.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/namespaces.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/namespaces.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/package_index.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/package_index.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/py27compat.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/py27compat.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/py31compat.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/py31compat.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/py33compat.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/py33compat.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/py34compat.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/py34compat.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/sandbox.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/sandbox.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/site-patch.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/site-patch.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/ssl_support.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/ssl_support.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/unicode_utils.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/unicode_utils.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/version.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/version.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/wheel.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/wheel.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/__pycache__/windows_support.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/__pycache__/windows_support.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_deprecation_warning.py b/lib/python3.9/site-packages/setuptools/_deprecation_warning.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_imp.py b/lib/python3.9/site-packages/setuptools/_imp.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/__init__.py b/lib/python3.9/site-packages/setuptools/_vendor/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/ordered_set.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/six.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/__pycache__/six.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/ordered_set.py b/lib/python3.9/site-packages/setuptools/_vendor/ordered_set.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__about__.py b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__about__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__init__.py b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/tags.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/_compat.py b/lib/python3.9/site-packages/setuptools/_vendor/packaging/_compat.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/_structures.py b/lib/python3.9/site-packages/setuptools/_vendor/packaging/_structures.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/markers.py b/lib/python3.9/site-packages/setuptools/_vendor/packaging/markers.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/requirements.py b/lib/python3.9/site-packages/setuptools/_vendor/packaging/requirements.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/specifiers.py b/lib/python3.9/site-packages/setuptools/_vendor/packaging/specifiers.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/tags.py b/lib/python3.9/site-packages/setuptools/_vendor/packaging/tags.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/utils.py b/lib/python3.9/site-packages/setuptools/_vendor/packaging/utils.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/packaging/version.py b/lib/python3.9/site-packages/setuptools/_vendor/packaging/version.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/pyparsing.py b/lib/python3.9/site-packages/setuptools/_vendor/pyparsing.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/_vendor/six.py b/lib/python3.9/site-packages/setuptools/_vendor/six.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/archive_util.py b/lib/python3.9/site-packages/setuptools/archive_util.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/build_meta.py b/lib/python3.9/site-packages/setuptools/build_meta.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/cli-32.exe b/lib/python3.9/site-packages/setuptools/cli-32.exe old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/cli-64.exe b/lib/python3.9/site-packages/setuptools/cli-64.exe old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/cli.exe b/lib/python3.9/site-packages/setuptools/cli.exe old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__init__.py b/lib/python3.9/site-packages/setuptools/command/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/alias.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/alias.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/build_clib.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/build_clib.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/build_ext.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/build_ext.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/build_py.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/build_py.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/develop.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/develop.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/dist_info.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/dist_info.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/easy_install.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/easy_install.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/egg_info.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/egg_info.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/install.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/install.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/install_lib.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/install_lib.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/install_scripts.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/install_scripts.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/py36compat.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/py36compat.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/register.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/register.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/rotate.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/rotate.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/saveopts.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/saveopts.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/sdist.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/sdist.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/setopt.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/setopt.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/test.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/test.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/upload.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/upload.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/__pycache__/upload_docs.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/command/__pycache__/upload_docs.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/alias.py b/lib/python3.9/site-packages/setuptools/command/alias.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/bdist_egg.py b/lib/python3.9/site-packages/setuptools/command/bdist_egg.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/bdist_rpm.py b/lib/python3.9/site-packages/setuptools/command/bdist_rpm.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/bdist_wininst.py b/lib/python3.9/site-packages/setuptools/command/bdist_wininst.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/build_clib.py b/lib/python3.9/site-packages/setuptools/command/build_clib.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/build_ext.py b/lib/python3.9/site-packages/setuptools/command/build_ext.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/build_py.py b/lib/python3.9/site-packages/setuptools/command/build_py.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/develop.py b/lib/python3.9/site-packages/setuptools/command/develop.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/dist_info.py b/lib/python3.9/site-packages/setuptools/command/dist_info.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/easy_install.py b/lib/python3.9/site-packages/setuptools/command/easy_install.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/egg_info.py b/lib/python3.9/site-packages/setuptools/command/egg_info.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/install.py b/lib/python3.9/site-packages/setuptools/command/install.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/install_egg_info.py b/lib/python3.9/site-packages/setuptools/command/install_egg_info.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/install_lib.py b/lib/python3.9/site-packages/setuptools/command/install_lib.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/install_scripts.py b/lib/python3.9/site-packages/setuptools/command/install_scripts.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/launcher manifest.xml b/lib/python3.9/site-packages/setuptools/command/launcher manifest.xml old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/py36compat.py b/lib/python3.9/site-packages/setuptools/command/py36compat.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/register.py b/lib/python3.9/site-packages/setuptools/command/register.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/rotate.py b/lib/python3.9/site-packages/setuptools/command/rotate.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/saveopts.py b/lib/python3.9/site-packages/setuptools/command/saveopts.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/sdist.py b/lib/python3.9/site-packages/setuptools/command/sdist.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/setopt.py b/lib/python3.9/site-packages/setuptools/command/setopt.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/test.py b/lib/python3.9/site-packages/setuptools/command/test.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/upload.py b/lib/python3.9/site-packages/setuptools/command/upload.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/command/upload_docs.py b/lib/python3.9/site-packages/setuptools/command/upload_docs.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/config.py b/lib/python3.9/site-packages/setuptools/config.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/dep_util.py b/lib/python3.9/site-packages/setuptools/dep_util.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/depends.py b/lib/python3.9/site-packages/setuptools/depends.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/dist.py b/lib/python3.9/site-packages/setuptools/dist.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/errors.py b/lib/python3.9/site-packages/setuptools/errors.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/extension.py b/lib/python3.9/site-packages/setuptools/extension.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/extern/__init__.py b/lib/python3.9/site-packages/setuptools/extern/__init__.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/extern/__pycache__/__init__.cpython-39.pyc b/lib/python3.9/site-packages/setuptools/extern/__pycache__/__init__.cpython-39.pyc old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/glob.py b/lib/python3.9/site-packages/setuptools/glob.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/gui-32.exe b/lib/python3.9/site-packages/setuptools/gui-32.exe old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/gui-64.exe b/lib/python3.9/site-packages/setuptools/gui-64.exe old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/gui.exe b/lib/python3.9/site-packages/setuptools/gui.exe old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/installer.py b/lib/python3.9/site-packages/setuptools/installer.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/launch.py b/lib/python3.9/site-packages/setuptools/launch.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/lib2to3_ex.py b/lib/python3.9/site-packages/setuptools/lib2to3_ex.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/monkey.py b/lib/python3.9/site-packages/setuptools/monkey.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/msvc.py b/lib/python3.9/site-packages/setuptools/msvc.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/namespaces.py b/lib/python3.9/site-packages/setuptools/namespaces.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/package_index.py b/lib/python3.9/site-packages/setuptools/package_index.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/py27compat.py b/lib/python3.9/site-packages/setuptools/py27compat.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/py31compat.py b/lib/python3.9/site-packages/setuptools/py31compat.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/py33compat.py b/lib/python3.9/site-packages/setuptools/py33compat.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/py34compat.py b/lib/python3.9/site-packages/setuptools/py34compat.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/sandbox.py b/lib/python3.9/site-packages/setuptools/sandbox.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/script (dev).tmpl b/lib/python3.9/site-packages/setuptools/script (dev).tmpl old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/script.tmpl b/lib/python3.9/site-packages/setuptools/script.tmpl old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/site-patch.py b/lib/python3.9/site-packages/setuptools/site-patch.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/ssl_support.py b/lib/python3.9/site-packages/setuptools/ssl_support.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/unicode_utils.py b/lib/python3.9/site-packages/setuptools/unicode_utils.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/version.py b/lib/python3.9/site-packages/setuptools/version.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/wheel.py b/lib/python3.9/site-packages/setuptools/wheel.py old mode 100644 new mode 100755 diff --git a/lib/python3.9/site-packages/setuptools/windows_support.py b/lib/python3.9/site-packages/setuptools/windows_support.py old mode 100644 new mode 100755 diff --git a/main.py b/main.py old mode 100644 new mode 100755 index 2729837..c0a4074 --- a/main.py +++ b/main.py @@ -1,53 +1,69 @@ import qnot -import csv - -def generate_csv(data): - headers = ["name", "colors", "style", "shipping_information", "brand_information", - "order_information", "wholesale", "sugg_retail", "total", "size", "quantity"] - - with open("output.csv", "w", newline="") as csvfile: - writer = csv.DictWriter(csvfile, fieldnames=headers) - writer.writeheader() - - for product in data: - colors = product["colors"] - size = product["size"] - total = product["total"] - - if len(colors) > 1: - for i in range(len(colors)): - for key, value in size.items(): - row = { - "name": product["name"], - "colors": colors[i], - "style": product["style"], - "shipping_information": product["shipping_information"], - "brand_information": product["brand_information"], - "order_information": product["order_information"], - "wholesale": product["wholesale"], - "sugg_retail": product["sugg_retail"], - "total": total[i], - "size": key, - "quantity": value[i] - } - writer.writerow(row) - else: - for key, value in size.items(): - row = { - "name": product["name"], - "colors": colors[0], - "style": product["style"], - "shipping_information": product["shipping_information"], - "brand_information": product["brand_information"], - "order_information": product["order_information"], - "wholesale": product["wholesale"], - "sugg_retail": product["sugg_retail"], - "total": total, - "size": key, - "quantity": value - } - writer.writerow(row) - print("CSV generated sucessfully....") +import sys +import os +import argparse + +def parse_page_spec(spec): + """Parse page specification like '1,3,5-7' into list of page numbers (1-based).""" + pages = set() + for part in spec.split(','): + part = part.strip() + if '-' in part: + start, end = part.split('-') + pages.update(range(int(start), int(end) + 1)) + else: + pages.add(int(part)) + return sorted(list(pages)) + +def main(): + parser = argparse.ArgumentParser(description="Extract data from PDF invoices.") + parser.add_argument("input", help="PDF file or directory containing PDFs") + parser.add_argument("--max-files", type=int, help="Maximum number of files to process (for batch processing)", + default=os.getenv("DEFAULT_MAX_FILES")) + parser.add_argument("--output", help="Output CSV filename", + default=os.getenv("DEFAULT_OUTPUT", "output.csv")) + parser.add_argument("--pages", help="Comma-separated list of pages to process (e.g., '1,3,5-7'). Supports ranges. If not specified, processes all pages.", + default=None) + parser.add_argument("--skip-pages", help="Comma-separated list of pages to skip (e.g., '2,4'). Supports ranges. Overrides --pages if both specified.", + default=None) + parser.add_argument("--password", help="Password for password-protected PDFs", + default=None) + parser.add_argument("--auto-rotate", action="store_true", help="Automatically detect and correct inverted/rotated pages") + + args = parser.parse_args() + + # Determine pages to process + pages_to_process = None + skip_pages_parsed = None + if args.skip_pages: + skip_pages_parsed = parse_page_spec(args.skip_pages) + print(f"Skipping pages: {skip_pages_parsed}") + elif args.pages: + pages_to_process = parse_page_spec(args.pages) + print(f"Processing only pages: {pages_to_process}") + else: + print("Processing all pages.") + + input_path = args.input + + if os.path.isfile(input_path) and input_path.endswith('.pdf'): + # Process single PDF + print(f"Processing single PDF: {input_path}") + qnot.remove_last_page(input_path, password=args.password) + invoice_data = qnot.annotate_blocks_with_rectangles("modified.pdf", pages_to_process, skip_pages_parsed, password=args.password, auto_rotate=args.auto_rotate) + qnot.generate_csv(invoice_data, args.output) + print("Single PDF processing complete.") + print(f"Extracted {len(invoice_data.get_all_products())} products from {len(invoice_data.pages)} pages.") + elif os.path.isdir(input_path): + # Process directory (batch) + print(f"Processing directory: {input_path}") + result = qnot.process_batch_pdfs(input_path, output_csv=args.output, max_files=args.max_files, + pages_to_process=pages_to_process, skip_pages=skip_pages_parsed, password=args.password, auto_rotate=args.auto_rotate) + if result: + qnot.print_batch_summary(result) + else: + print(f"Invalid input: {input_path}. Must be a PDF file or directory.") + sys.exit(1) def get_structure(data, indent=0): structure = "" @@ -89,29 +105,5 @@ def collect_size_keys(array): return list(size_keys) -pdf_path = "/home/guatam/Desktop/fiverr/pdf_data_extract/inp_pdf/ORDER_Lauren-Manoogian_2023-10-06_13769226.pdf" - -qnot.remove_last_page(pdf_path) -p = qnot.annotate_blocks_with_rectangles("modified.pdf") - -temp = [] -for i in p: - for j in i: - # print() - # print(j) - # print("="*50) - # print() - - temp.append(j) - -generate_csv(temp) - - - -x = get_structure(p) - - - - -with open("data.json", "w") as f: - f.write(x) \ No newline at end of file +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/models.py b/models.py new file mode 100644 index 0000000..c70a04d --- /dev/null +++ b/models.py @@ -0,0 +1,126 @@ +from pydantic import BaseModel, Field, validator, EmailStr +from typing import List, Dict, Optional, Union +import re +from decimal import Decimal + +class LineItem(BaseModel): + description: str = Field(..., min_length=1, description="Line item description") + quantity: Optional[Union[int, str]] = Field(None, description="Quantity") + price: Optional[str] = Field(None, description="Unit price") + total: Optional[str] = Field(None, description="Total price") + additional_fields: Dict[str, str] = Field(default_factory=dict, description="Other fields") + + @validator('price', 'total', pre=True) + def validate_price(cls, v): + if v is None: + return v + # Check format + if not re.search(r'[\d,]+\.\d{2}', str(v)): + raise ValueError('Invalid price format') + return v + +class Product(BaseModel): + name: str = Field(..., min_length=1, description="Product name") + colors: List[str] = Field(default_factory=list, description="List of colors") + style: Optional[str] = Field(None, description="Product style") + shipping_information: Optional[str] = Field(None, description="Shipping details") + brand_information: Optional[str] = Field(None, description="Brand details") + order_information: Optional[str] = Field(None, description="Order details") + wholesale: Optional[str] = Field(None, description="Wholesale price") + sugg_retail: Optional[str] = Field(None, description="Suggested retail price") + total: Union[str, List[str]] = Field(..., description="Total price(s)") + size: Dict[str, Union[int, str]] = Field(default_factory=dict, description="Size and quantity mapping") + quantity: Union[int, str, List[int]] = Field(..., description="Quantity") + billing_information: Optional[str] = Field(None, description="Billing details") + + @validator('colors', pre=True, each_item=True) + def validate_color(cls, v): + if not v or v.strip() == '': + raise ValueError('Color cannot be empty') + return v.strip() + + @validator('wholesale', 'sugg_retail', pre=True) + def validate_price(cls, v): + if v is None: + return v + # Extract numeric part + match = re.search(r'[\d,]+\.\d{2}', str(v)) + if not match: + raise ValueError('Invalid price format') + return v + + @validator('total', pre=True) + def validate_total(cls, v): + if isinstance(v, list): + for t in v: + if not re.search(r'[\d,]+\.\d{2}', str(t)): + raise ValueError('Invalid total price format') + else: + if not re.search(r'[\d,]+\.\d{2}', str(v)): + raise ValueError('Invalid total price format') + return v + + @validator('size', pre=True) + def validate_size(cls, v): + if not isinstance(v, dict): + raise ValueError('Size must be a dictionary') + for key, value in v.items(): + if not isinstance(key, str) or not (isinstance(value, (int, str))): + raise ValueError('Size keys must be strings, values int or str') + return v + +class PageData(BaseModel): + products: List[Product] = Field(default_factory=list, description="List of products on the page") + line_items: List[LineItem] = Field(default_factory=list, description="Raw line items from tables") + + def add_line_item(self, line_item: LineItem): + self.line_items.append(line_item) + +class InvoiceData(BaseModel): + pages: List[PageData] = Field(default_factory=list, description="Data from all pages") + validation_report: Dict = Field(default_factory=dict, description="Text validation results") + + @validator('pages', pre=True) + def validate_pages(cls, v): + if not isinstance(v, list): + raise ValueError('Pages must be a list') + return v + + def add_page(self, page_data: PageData): + self.pages.append(page_data) + + def get_all_products(self) -> List[Product]: + """Get all products from all pages.""" + all_products = [] + for page in self.pages: + all_products.extend(page.products) + return all_products + + def add_validation_report(self, report: Dict): + self.validation_report.update(report) + +class TextValidationError(Exception): + pass + +def validate_text_field(field_name: str, value: str, validators: Dict = None): + """Validate a text field using custom validators.""" + if validators: + for validator_func in validators.values(): + try: + validator_func(value) + except Exception as e: + raise TextValidationError(f"Validation failed for {field_name}: {e}") + return value + +# Predefined validators +email_validator = lambda x: EmailStr.validate(x) +phone_validator = lambda x: re.match(r'^\+?1?[-.\s]?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})$', x) +date_validator = lambda x: re.match(r'^\d{1,2}/\d{1,2}/\d{4}$|^\d{4}-\d{1,2}-\d{1,2}$|^\d{1,2}-\d{1,2}-\d{4}$', x) +price_validator = lambda x: re.search(r'[\d,]+\.\d{2}', x) + +FIELD_VALIDATORS = { + 'email': email_validator, + 'phone': phone_validator, + 'date': date_validator, + 'price': price_validator, +} \ No newline at end of file diff --git a/modified.pdf b/modified.pdf old mode 100644 new mode 100755 diff --git a/output.csv b/output.csv old mode 100644 new mode 100755 diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..224c31e --- /dev/null +++ b/pytest.ini @@ -0,0 +1,6 @@ +[tool:pytest] +testpaths = . +python_files = test_*.py +python_classes = Test* +python_functions = test_* +addopts = --cov=qnot --cov=models --cov-report=html --cov-report=term-missing \ No newline at end of file diff --git a/pyvenv.cfg b/pyvenv.cfg old mode 100644 new mode 100755 diff --git a/qnot.py b/qnot.py old mode 100644 new mode 100755 index 4c22089..f978880 --- a/qnot.py +++ b/qnot.py @@ -1,5 +1,10 @@ import fitz import re +import os +import glob +import csv +from models import Product, PageData, InvoiceData, LineItem, TextValidationError, validate_text_field, FIELD_VALIDATORS +import pandas as pd def extract_integer(string): integer_list = re.findall(r'\d+', string.replace(',', '')) @@ -203,11 +208,11 @@ def create_sublists(strings): -def remove_last_page(pdf_path): +def remove_last_page(pdf_path, password=None): - doc = fitz.open(pdf_path) + doc = fitz.open(pdf_path, password=password) num_pages = doc.page_count - + if num_pages > 0: doc.delete_page(num_pages - 1) print("Last page removed from the PDF.") @@ -217,7 +222,7 @@ def remove_last_page(pdf_path): output_path = "modified.pdf" doc.save(output_path) doc.close() - + print("Modified PDF saved to", output_path) def remove_footer(lst): @@ -347,20 +352,87 @@ def check_initials(strings): -def annotate_blocks_with_rectangles(pdf_path): - - retrun_list = [] +def detect_and_correct_rotation(page): + """Detect if page is rotated and correct it.""" + try: + rotation = page.rotation + if rotation == 180: + page.set_rotation(0) + print(f"Corrected rotation from 180 to 0 degrees for page {page.number + 1}") + return True + elif rotation not in [0, 90, 270]: + # If unusual rotation, try to normalize + page.set_rotation(0) + print(f"Corrected unusual rotation {rotation} to 0 degrees for page {page.number + 1}") + return True + # Additional check: analyze text blocks for upside-down text + blocks = page.get_text_blocks() + if blocks: + # Check if most text is oriented upside down (bbox y1 < y0 or something) + # For simplicity, if rotation is 0 but text seems inverted, but hard to detect + pass + return False + except Exception as e: + print(f"Error detecting rotation for page {page.number + 1}: {e}") + return False + +def annotate_blocks_with_rectangles(pdf_path, pages_to_process=None, skip_pages=None, password=None, auto_rotate=False): + """Process PDF and return validated InvoiceData using Pydantic. + + Args: + pdf_path: Path to the PDF file + pages_to_process: List of 1-based page numbers to process, or None for all + skip_pages: List of 1-based page numbers to skip, or None + password: Password for password-protected PDFs + auto_rotate: Whether to auto-detect and correct page rotations + """ + + doc = fitz.open(pdf_path, password=password) + invoice_data = InvoiceData() + + # Determine which pages to process + total_pages = doc.page_count + if skip_pages: + skip_pages = [p - 1 for p in skip_pages] # Convert to 0-based + pages_to_process_indices = [i for i in range(total_pages) if i not in skip_pages] + elif pages_to_process: + pages_to_process_indices = [p - 1 for p in pages_to_process if 0 <= p - 1 < total_pages] + else: + pages_to_process_indices = list(range(total_pages)) - doc = fitz.open(pdf_path) - full_pdf_data_block = [] + print(f"Processing pages: {[i+1 for i in pages_to_process_indices]} out of {total_pages} total pages.") - data_for_return = [] + for page_num in pages_to_process_indices: + page = doc.load_page(page_num) - for page in doc: + # Auto-rotate if enabled + if auto_rotate: + try: + detect_and_correct_rotation(page) + except Exception as e: + print(f"Warning: Failed to correct rotation for page {page_num + 1}: {e}") + # Continue processing anyway blocks = page.get_text_blocks() max_x = find_max_x(blocks) + # Extract all text from page for validation + page_text = page.get_text() + page_validation = validate_extracted_text(page_text) + invoice_data.add_validation_report({f"page_{page_num + 1}": page_validation}) + + # Extract tables from spans + tables = extract_tables_from_page(spans) + for table in tables: + for item in table['line_items']: + try: + line_item = LineItem(**item) + page_data.add_line_item(line_item) + except Exception as e: + print(f"Error validating line item: {e}") + # Also merge into products if possible + merge_line_items_into_products(table['line_items'], page_products) + # print(max_x) # print(max_x) @@ -389,14 +461,12 @@ def annotate_blocks_with_rectangles(pdf_path): spans = [] blocks = dicts["blocks"] - + for i in blocks: if i["type"] == 0: - # print(i.keys()) lines = i["lines"] for j in lines: - span = j['spans'] - spans.append(span) + spans.extend(j['spans']) # Flatten to list of dicts # print(spans) @@ -418,7 +488,7 @@ def annotate_blocks_with_rectangles(pdf_path): size_total.append(data_text_size) - obj_dicts = [] + page_products = [] for i in text_data_inside_new_block: @@ -556,7 +626,12 @@ def annotate_blocks_with_rectangles(pdf_path): struct["colors"] = colors - obj_dicts.append(struct) + try: + product = Product(**struct) + page_products.append(product) + except Exception as e: + print(f"Validation error for product on page {doc.page_number + 1}: {e}") + # Optionally skip or add with defaults for j in i: x0, y0, x1, y1 = j["bbox"] @@ -724,7 +799,8 @@ def annotate_blocks_with_rectangles(pdf_path): indx_1 += 1 - data_for_return.append(obj_dicts) + page_data = PageData(products=page_products) + invoice_data.add_page(page_data) # for i in obj_dicts: @@ -758,8 +834,335 @@ def annotate_blocks_with_rectangles(pdf_path): doc.close() print("Annotations added and saved to", output_path) - return data_for_return + return invoice_data + + +# Regex-based extraction functions + +def extract_prices(text): + """Extract prices using regex patterns.""" + # Patterns for different price formats + patterns = [ + r'\b\d{1,3}(?:,\d{3})*\.\d{2}\b', # e.g., 123.45, 1,234.56 + r'\b\d+\.\d{2}\b', # Simple decimal + r'\b\d+\b(?=\s*(?:usd|eur|gbp|jpy|cad|aud|chf|sek|nzd|mxn|sgd|myr|php|thb|idr|isk|nok|krw|try|rub|inr|brl|zar|dkk|pln|ils|sar|aed|czk|huf|cop|myr|php|thb|idr|isk|nok|krw|try|rub|inr|brl|zar|dkk|pln|ils|sar|aed|czk|huf|cop|pen|egp|vnd|kes|ngn|uah))', # Numbers followed by currency + ] + prices = [] + for pattern in patterns: + prices.extend(re.findall(pattern, text, re.IGNORECASE)) + return list(set(prices)) # Remove duplicates + +def extract_quantities(text): + """Extract quantities (integers).""" + pattern = r'\b\d+\b' + quantities = re.findall(pattern, text) + return [int(q) for q in quantities if int(q) > 0] # Filter out zero + +def extract_sizes(text): + """Extract sizes using common size patterns.""" + # Common size patterns: XS, S, M, L, XL, XXL, numbers, OS, POS, etc. + pattern = r'\b(?:XS|S|M|L|XL|XXL|XXXL|OS|POS|\d{1,2}|\d{1,2}/\d{1,2})\b' + sizes = re.findall(pattern, text, re.IGNORECASE) + return list(set(sizes)) + +def extract_dates(text): + """Extract dates in common formats.""" + patterns = [ + r'\b\d{1,2}/\d{1,2}/\d{4}\b', # MM/DD/YYYY + r'\b\d{4}-\d{1,2}-\d{1,2}\b', # YYYY-MM-DD + r'\b\d{1,2}-\d{1,2}-\d{4}\b', # DD-MM-YYYY + ] + dates = [] + for pattern in patterns: + dates.extend(re.findall(pattern, text)) + return list(set(dates)) + +def extract_emails(text): + """Extract email addresses.""" + pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' + return re.findall(pattern, text) + +def extract_phone_numbers(text): + """Extract phone numbers (basic pattern).""" + pattern = r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b' + return re.findall(pattern, text) + +def regex_based_extraction(text): + """Perform regex-based extraction on text.""" + return { + 'prices': extract_prices(text), + 'quantities': extract_quantities(text), + 'sizes': extract_sizes(text), + 'dates': extract_dates(text), + 'emails': extract_emails(text), + 'phones': extract_phone_numbers(text), + } + +def validate_extracted_text(text): + """Validate extracted text fields using predefined validators.""" + validations = {} + try: + validations['emails'] = [validate_text_field('email', email, {'email': FIELD_VALIDATORS['email']}) for email in extract_emails(text)] + except TextValidationError as e: + validations['emails_error'] = str(e) + try: + validations['phones'] = [validate_text_field('phone', phone, {'phone': FIELD_VALIDATORS['phone']}) for phone in extract_phone_numbers(text)] + except TextValidationError as e: + validations['phones_error'] = str(e) + + try: + validations['dates'] = [validate_text_field('date', date, {'date': FIELD_VALIDATORS['date']}) for date in extract_dates(text)] + except TextValidationError as e: + validations['dates_error'] = str(e) + + return validations + +# Batch processing functions + +def process_batch_pdfs(input_dir, output_csv="batch_output.csv", max_files=None, pages_to_process=None, skip_pages=None, password=None, auto_rotate=False): + """Process all PDFs in a directory and generate a single CSV. + + Args: + input_dir (str): Directory containing PDF files. + output_csv (str): Output CSV filename. + max_files (int, optional): Maximum number of files to process. If None, process all. + + Returns: + dict: {'processed': int, 'total': int, 'errors': int} + """ + if not os.path.isdir(input_dir): + raise ValueError(f"Input directory {input_dir} does not exist.") + + pdf_files = glob.glob(os.path.join(input_dir, "*.pdf")) + if not pdf_files: + print(f"No PDF files found in {input_dir}") + return {'processed': 0, 'total': 0, 'errors': 0} + + # Limit files if max_files is specified + if max_files is not None: + pdf_files = pdf_files[:max_files] + print(f"Processing up to {max_files} files out of {len(pdf_files)} found.") + + total_files = len(pdf_files) + processed = 0 + errors = 0 + all_invoice_data = InvoiceData() + + for i, pdf_file in enumerate(pdf_files, 1): + print(f"Processing ({i}/{total_files}) {pdf_file}") + try: + remove_last_page(pdf_file, password) + invoice_data = annotate_blocks_with_rectangles("modified.pdf", pages_to_process, skip_pages, password, auto_rotate) + # Merge pages + for page in invoice_data.pages: + all_invoice_data.add_page(page) + processed += 1 + except Exception as e: + print(f"Error processing {pdf_file}: {e}") + errors += 1 + continue + + generate_csv(all_invoice_data, output_csv) + print(f"Batch processing complete. Processed: {processed}, Errors: {errors}, Total: {total_files}. Output saved to {output_csv}") + + return {'processed': processed, 'total': total_files, 'errors': errors} + +def print_batch_summary(stats): + """Print a summary of batch processing results.""" + print("\n=== Batch Processing Summary ===") + print(f"Total files found: {stats['total']}") + print(f"Successfully processed: {stats['processed']}") + print(f"Errors encountered: {stats['errors']}") + print(f"Success rate: {stats['processed']/stats['total']*100:.1f}%" if stats['total'] > 0 else "N/A") + print("================================") + +# Table parsing functions + +def detect_table_from_spans(spans, threshold=10): + """Detect table structure from spans, handling merged cells.""" + # Sort spans by y-coordinate (rows), then x-coordinate (columns) + sorted_spans = sorted(spans, key=lambda s: (s['bbox'][1], s['bbox'][0])) + + rows = [] + current_row = [] + current_y = None + + for span in sorted_spans: + x0, y0, x1, y1 = span['bbox'] + if current_y is None or abs(y0 - current_y) > threshold: + if current_row: + rows.append(current_row) + current_row = [span] + current_y = y0 + else: + current_row.append(span) + + if current_row: + rows.append(current_row) + + # Now, build table matrix, handling merged cells + table_data = [] + for row in rows: + row_data = [] + col_positions = [] + for span in sorted(row, key=lambda s: s['bbox'][0]): + text = span['text'].strip() + if text: + x0, y0, x1, y1 = span['bbox'] + # Check for merged cells (span multiple columns) + col_span = 1 + for other_span in row: + if other_span != span: + ox0, oy0, ox1, oy1 = other_span['bbox'] + if x0 < ox0 < x1 and abs(y0 - oy0) < threshold: + col_span += 1 + row_data.append({'text': text, 'colspan': col_span, 'bbox': span['bbox']}) + if row_data: + table_data.append(row_data) + + return table_data + +def parse_table_to_line_items(table_data): + """Parse detected table into line items (rows).""" + line_items = [] + for row_idx, row in enumerate(table_data): + if row_idx == 0: # Skip header + continue + line_item = {'additional_fields': {}} + for i, cell in enumerate(row): + text = cell['text'] + if i == 0: + line_item['description'] = text + elif re.search(r'\d+\.\d{2}', text) and 'price' not in line_item: + line_item['price'] = text + elif re.search(r'\d+\.\d{2}', text) and 'total' not in line_item: + line_item['total'] = text + elif re.search(r'\d+', text) and 'quantity' not in line_item: + line_item['quantity'] = text + else: + line_item['additional_fields'][f'col_{i}'] = text + if 'description' in line_item: + line_items.append(line_item) + return line_items + +def extract_tables_from_page(spans): + """Extract tables from page spans.""" + table_data = detect_table_from_spans(spans) + tables = [] + if table_data: + line_items = parse_table_to_line_items(table_data) + tables.append({'line_items': line_items, 'raw_table': table_data}) + return tables + +def merge_line_items_into_products(line_items, existing_products): + """Merge extracted line items into product structures.""" + for item in line_items: + # Try to match with existing products or create new + product = { + 'name': item.get('description', 'Line Item'), + 'total': item.get('price', ''), + 'quantity': item.get('quantity', ''), + 'size': {}, # Default empty + 'colors': [], + 'style': '', + 'wholesale': '', + 'sugg_retail': '', + 'shipping_information': '', + 'brand_information': '', + 'order_information': '', + 'billing_information': '' + } + try: + validated_product = Product(**product) + existing_products.append(validated_product) + except Exception as e: + print(f"Error validating line item: {e}") + return existing_products + +def generate_csv(invoice_data, filename="output.csv"): + """Generate CSV from InvoiceData.""" + if not isinstance(invoice_data, InvoiceData): + # Backward compatibility: if it's a list, treat as old format + if isinstance(invoice_data, list): + flat_data = [] + for page_data in invoice_data: + flat_data.extend(page_data) + invoice_data = InvoiceData(pages=[PageData(products=flat_data)]) + else: + raise ValueError("invoice_data must be InvoiceData instance or list") + + # Headers for products + product_headers = ["name", "colors", "style", "shipping_information", "brand_information", + "order_information", "wholesale", "sugg_retail", "total", "size", "quantity"] + + # Headers for line items + line_item_headers = ["description", "quantity", "price", "total"] + [f"additional_{i}" for i in range(10)] # Flexible + + with open(filename, "w", newline="") as csvfile: + writer = csv.writer(csvfile) + writer.writerow(["Products"]) + writer.writerow(product_headers) + + for product in invoice_data.get_all_products(): + colors = product.colors + size = product.size + total = product.total + + if len(colors) > 1: + for i in range(len(colors)): + for key, value in size.items(): + row = [ + product.name, + colors[i], + product.style, + product.shipping_information, + product.brand_information, + product.order_information, + product.wholesale, + product.sugg_retail, + total[i] if isinstance(total, list) else total, + key, + value[i] if isinstance(value, list) else value + ] + writer.writerow(row) + else: + for key, value in size.items(): + row = [ + product.name, + colors[0] if colors else "", + product.style, + product.shipping_information, + product.brand_information, + product.order_information, + product.wholesale, + product.sugg_retail, + total, + key, + value + ] + writer.writerow(row) + + # Write line items + writer.writerow([]) + writer.writerow(["Line Items"]) + writer.writerow(line_item_headers) + + for page in invoice_data.pages: + for line_item in page.line_items: + row = [ + line_item.description, + line_item.quantity, + line_item.price, + line_item.total + ] + # Add additional fields + for i in range(10): + key = f"col_{i}" + row.append(line_item.additional_fields.get(key, "")) + writer.writerow(row) + + print(f"CSV generated successfully: {filename}") # pdf_path = "/home/guatam/Desktop/fiverr/pdf_data_extract/inp_pdf/ORDER_Lauren-Manoogian_2023-10-06_13769226.pdf" # remove_last_page(pdf_path) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6fee30b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +PyMuPDF>=1.18.0 +pydantic>=1.8.0,<2.0.0 +pytest>=7.0.0 +pytest-cov>=3.0.0 \ No newline at end of file diff --git a/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl b/share/python-wheels/CacheControl-0.12.6-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/appdirs-1.4.4-py2.py3-none-any.whl b/share/python-wheels/appdirs-1.4.4-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/certifi-2020.6.20-py2.py3-none-any.whl b/share/python-wheels/certifi-2020.6.20-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/chardet-4.0.0-py2.py3-none-any.whl b/share/python-wheels/chardet-4.0.0-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/colorama-0.4.4-py2.py3-none-any.whl b/share/python-wheels/colorama-0.4.4-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/contextlib2-0.6.0.post1-py2.py3-none-any.whl b/share/python-wheels/contextlib2-0.6.0.post1-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/distlib-0.3.1-py2.py3-none-any.whl b/share/python-wheels/distlib-0.3.1-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/distro-1.5.0-py2.py3-none-any.whl b/share/python-wheels/distro-1.5.0-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/html5lib-1.1-py2.py3-none-any.whl b/share/python-wheels/html5lib-1.1-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/idna-2.10-py2.py3-none-any.whl b/share/python-wheels/idna-2.10-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/ipaddr-2.2.0-py2.py3-none-any.whl b/share/python-wheels/ipaddr-2.2.0-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/msgpack-1.0.0-py2.py3-none-any.whl b/share/python-wheels/msgpack-1.0.0-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/packaging-20.9-py2.py3-none-any.whl b/share/python-wheels/packaging-20.9-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/pep517-0.9.1-py2.py3-none-any.whl b/share/python-wheels/pep517-0.9.1-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/pip-20.3.4-py2.py3-none-any.whl b/share/python-wheels/pip-20.3.4-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl b/share/python-wheels/pkg_resources-0.0.0-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/progress-1.5-py2.py3-none-any.whl b/share/python-wheels/progress-1.5-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/pyparsing-2.4.7-py2.py3-none-any.whl b/share/python-wheels/pyparsing-2.4.7-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/requests-2.25.1-py2.py3-none-any.whl b/share/python-wheels/requests-2.25.1-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/resolvelib-0.5.4-py2.py3-none-any.whl b/share/python-wheels/resolvelib-0.5.4-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl b/share/python-wheels/retrying-1.3.3-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/setuptools-44.1.1-py2.py3-none-any.whl b/share/python-wheels/setuptools-44.1.1-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/six-1.16.0-py2.py3-none-any.whl b/share/python-wheels/six-1.16.0-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/toml-0.10.1-py2.py3-none-any.whl b/share/python-wheels/toml-0.10.1-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/urllib3-1.26.5-py2.py3-none-any.whl b/share/python-wheels/urllib3-1.26.5-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/webencodings-0.5.1-py2.py3-none-any.whl b/share/python-wheels/webencodings-0.5.1-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/share/python-wheels/wheel-0.34.2-py2.py3-none-any.whl b/share/python-wheels/wheel-0.34.2-py2.py3-none-any.whl old mode 100644 new mode 100755 diff --git a/test_qnot.py b/test_qnot.py new file mode 100644 index 0000000..4780236 --- /dev/null +++ b/test_qnot.py @@ -0,0 +1,135 @@ +import unittest +import qnot +import os +import tempfile +import shutil + +try: + import fitz + FITZ_AVAILABLE = True +except ImportError: + FITZ_AVAILABLE = False + +class TestQnotFunctions(unittest.TestCase): + + def test_extract_prices(self): + text = "The price is $123.45 and another is 1,234.56 USD or 99.99." + prices = qnot.extract_prices(text) + self.assertIn('123.45', prices) + self.assertIn('1,234.56', prices) + self.assertIn('99.99', prices) + + def test_extract_quantities(self): + text = "Quantity: 5 items, 0 invalid, 10 more." + quantities = qnot.extract_quantities(text) + self.assertEqual(quantities, [5, 10]) + + def test_extract_sizes(self): + text = "Sizes: XS, S, M, L, XL, XXL, OS, POS, 8, 10/12." + sizes = qnot.extract_sizes(text) + expected = ['XS', 'S', 'M', 'L', 'XL', 'XXL', 'OS', 'POS', '8', '10'] + self.assertTrue(set(expected).issubset(set(sizes))) + + def test_extract_dates(self): + text = "Dates: 12/31/2023, 2023-12-31, 31-12-2023." + dates = qnot.extract_dates(text) + self.assertIn('12/31/2023', dates) + self.assertIn('2023-12-31', dates) + self.assertIn('31-12-2023', dates) + + def test_extract_emails(self): + text = "Contact: test@example.com or user.name+tag@domain.co.uk." + emails = qnot.extract_emails(text) + self.assertIn('test@example.com', emails) + self.assertIn('user.name+tag@domain.co.uk', emails) + + def test_extract_phone_numbers(self): + text = "Phones: 123-456-7890, 123.456.7890." + phones = qnot.extract_phone_numbers(text) + self.assertIn('123-456-7890', phones) + self.assertIn('123.456.7890', phones) + + def test_regex_based_extraction(self): + text = "Price 123.45, qty 5, size M, date 12/31/2023, email test@example.com, phone 123-456-7890." + result = qnot.regex_based_extraction(text) + self.assertIn('123.45', result['prices']) + self.assertIn(5, result['quantities']) + self.assertIn('M', result['sizes']) + self.assertIn('12/31/2023', result['dates']) + self.assertIn('test@example.com', result['emails']) + self.assertIn('123-456-7890', result['phones']) + + def test_extract_integer_edge_cases(self): + self.assertEqual(qnot.extract_integer("no numbers"), None) + self.assertEqual(qnot.extract_integer("one number 42"), 42) + self.assertEqual(qnot.extract_integer("multiple 1 2 3"), [1, 2, 3]) + + def test_generate_blocks_empty_input(self): + result = qnot.generate_blocks([]) + self.assertEqual(result, []) + + def test_process_2d_list_edge_cases(self): + # Test with empty list + result = qnot.process_2d_list([]) + self.assertEqual(result, []) + + # Test with single digit + result = qnot.process_2d_list([['1', '2']]) + self.assertEqual(result, [['2', '1']]) + + # Test with tabs + result = qnot.process_2d_list([['\t', 'value']]) + self.assertEqual(result, [['0', 'value']]) + + def test_filter_strings_edge_cases(self): + strings = ["shipping information test", "normal text", "brand information test"] + result = qnot.filter_strings(strings) + self.assertNotIn("shipping information test", result) + self.assertNotIn("brand information test", result) + self.assertIn("normal text", result) + + def test_create_sublists_edge_cases(self): + strings = ["product1", "style info", "product2", "style info2"] + result = qnot.create_sublists(strings) + self.assertEqual(len(result), 2) + self.assertEqual(result[0][0], "product1") + self.assertEqual(result[1][0], "product2") + + def test_generate_color_list_edge_cases(self): + # Empty data + result = qnot.generate_color_list([]) + self.assertEqual(result, [[], None]) + + # Data without "colors" + data = [{"text": "other", "bbox": [0,0,10,10]}] + result = qnot.generate_color_list(data) + self.assertEqual(result, [[], None]) + + def test_concat_str_edge_cases(self): + result = qnot.concat_str(["a", "b", "c"], "b") + self.assertEqual(result, "a c") + + def test_check_initials_edge_cases(self): + currencies = ["usd 100", "invalid"] + result = qnot.check_initials(currencies) + self.assertEqual(result, "usd") + + result = qnot.check_initials(["no currency"]) + self.assertEqual(result, "") + + def test_sort_items_edge_cases(self): + items = [(1, 2), (3, 4)] + result = qnot.sort_items(items) + self.assertEqual(result, [(1, 2), (3, 4)]) + + # Integration test for batch processing (mock) + def test_process_batch_pdfs_invalid_dir(self): + with self.assertRaises(ValueError): + qnot.process_batch_pdfs("/nonexistent") + + def test_process_batch_pdfs_empty_dir(self): + with tempfile.TemporaryDirectory() as tmpdir: + qnot.process_batch_pdfs(tmpdir) # Should print no PDFs found + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/test_regex.py b/test_regex.py new file mode 100644 index 0000000..29d48c9 --- /dev/null +++ b/test_regex.py @@ -0,0 +1,223 @@ +import unittest +import re + +# Copy the regex functions here for testing without importing qnot + +def extract_prices(text): + """Extract prices using regex patterns.""" + # Patterns for different price formats + patterns = [ + r'\b\d{1,3}(?:,\d{3})*\.\d{2}\b', # e.g., 123.45, 1,234.56 + r'\b\d+\.\d{2}\b', # Simple decimal + r'\b\d+\b(?=\s*(?:usd|eur|gbp|jpy|cad|aud|chf|sek|nzd|mxn|sgd|myr|php|thb|idr|isk|nok|krw|try|rub|inr|brl|zar|dkk|pln|ils|sar|aed|czk|huf|cop|myr|php|thb|idr|isk|nok|krw|try|rub|inr|brl|zar|dkk|pln|ils|sar|aed|czk|huf|cop|pen|egp|vnd|kes|ngn|uah))', # Numbers followed by currency + ] + prices = [] + for pattern in patterns: + prices.extend(re.findall(pattern, text, re.IGNORECASE)) + return list(set(prices)) # Remove duplicates + +def extract_quantities(text): + """Extract quantities (integers).""" + pattern = r'\b\d+\b' + quantities = re.findall(pattern, text) + return [int(q) for q in quantities if int(q) > 0] # Filter out zero + +def extract_sizes(text): + """Extract sizes using common size patterns.""" + # Common size patterns: XS, S, M, L, XL, XXL, numbers, OS, POS, etc. + pattern = r'\b(?:XS|S|M|L|XL|XXL|XXXL|OS|POS|\d{1,2}|\d{1,2}/\d{1,2})\b' + sizes = re.findall(pattern, text, re.IGNORECASE) + return list(set(sizes)) + +def extract_dates(text): + """Extract dates in common formats.""" + patterns = [ + r'\b\d{1,2}/\d{1,2}/\d{4}\b', # MM/DD/YYYY + r'\b\d{4}-\d{1,2}-\d{1,2}\b', # YYYY-MM-DD + r'\b\d{1,2}-\d{1,2}-\d{4}\b', # DD-MM-YYYY + ] + dates = [] + for pattern in patterns: + dates.extend(re.findall(pattern, text)) + return list(set(dates)) + +def extract_emails(text): + """Extract email addresses.""" + pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' + return re.findall(pattern, text) + +def extract_phone_numbers(text): + """Extract phone numbers (basic pattern).""" + pattern = r'\b\d{3}[-.]?\d{3}[-.]?\d{4}\b' + return re.findall(pattern, text) + +def regex_based_extraction(text): + """Perform regex-based extraction on text.""" + return { + 'prices': extract_prices(text), + 'quantities': extract_quantities(text), + 'sizes': extract_sizes(text), + 'dates': extract_dates(text), + 'emails': extract_emails(text), + 'phones': extract_phone_numbers(text), + } + +# Copy table functions for testing without importing qnot +def detect_table_from_spans(spans, threshold=5): + """Detect table structure from spans, handling merged cells.""" + sorted_spans = sorted(spans, key=lambda s: (s['bbox'][1], s['bbox'][0])) + + rows = [] + current_row = [] + current_y = None + + for span in sorted_spans: + x0, y0, x1, y1 = span['bbox'] + if current_y is None or abs(y0 - current_y) > threshold: + if current_row: + rows.append(current_row) + current_row = [span] + current_y = (y0 + y1) / 2 # Use center + else: + current_row.append(span) + + if current_row: + rows.append(current_row) + + table_data = [] + for row in rows: + row_data = [] + for span in sorted(row, key=lambda s: s['bbox'][0]): + text = span['text'].strip() + if text: + row_data.append({'text': text, 'colspan': 1, 'bbox': span['bbox']}) # Simplified colspan + if row_data: + table_data.append(row_data) + + return table_data + +def parse_table_to_line_items(table_data): + """Parse detected table into line items (rows).""" + line_items = [] + for row_idx, row in enumerate(table_data): + if row_idx == 0: # Skip header + continue + line_item = {'additional_fields': {}} + for i, cell in enumerate(row): + text = cell['text'] + if i == 0: + line_item['description'] = text + elif re.search(r'\d+\.\d{2}', text) and 'price' not in line_item: + line_item['price'] = text + elif re.search(r'\d+\.\d{2}', text) and 'total' not in line_item: + line_item['total'] = text + elif re.search(r'\d+', text) and 'quantity' not in line_item: + line_item['quantity'] = text + else: + line_item['additional_fields'][f'col_{i}'] = text + if 'description' in line_item: + line_items.append(line_item) + return line_items + +def extract_integer(string): + integer_list = re.findall(r'\d+', string.replace(',', '')) + if len(integer_list) == 1: + return int(integer_list[0]) + elif len(integer_list) > 1: + return [int(x) for x in integer_list] + else: + return None + +class TestRegexFunctions(unittest.TestCase): + + def test_extract_prices(self): + text = "The price is $123.45 and another is 1,234.56 USD or 99.99." + prices = extract_prices(text) + self.assertIn('123.45', prices) + self.assertIn('1,234.56', prices) + self.assertIn('99.99', prices) + + def test_extract_quantities(self): + text = "Quantity: 5 items, 0 invalid, 10 more." + quantities = extract_quantities(text) + self.assertEqual(quantities, [5, 10]) + + def test_extract_sizes(self): + text = "Sizes: XS, S, M, L, XL, XXL, OS, POS, 8, 10/12." + sizes = extract_sizes(text) + expected = ['XS', 'S', 'M', 'L', 'XL', 'XXL', 'OS', 'POS', '8', '10'] + self.assertTrue(set(expected).issubset(set(sizes))) + + def test_extract_dates(self): + text = "Dates: 12/31/2023, 2023-12-31, 31-12-2023." + dates = extract_dates(text) + self.assertIn('12/31/2023', dates) + self.assertIn('2023-12-31', dates) + self.assertIn('31-12-2023', dates) + + def test_extract_emails(self): + text = "Contact: test@example.com or user.name+tag@domain.co.uk." + emails = extract_emails(text) + self.assertIn('test@example.com', emails) + self.assertIn('user.name+tag@domain.co.uk', emails) + + def test_extract_phone_numbers(self): + text = "Phones: 123-456-7890, 123.456.7890." + phones = extract_phone_numbers(text) + self.assertIn('123-456-7890', phones) + self.assertIn('123.456.7890', phones) + + def test_regex_based_extraction(self): + text = "Price 123.45, qty 5, size M, date 12/31/2023, email test@example.com, phone 123-456-7890." + result = regex_based_extraction(text) + self.assertIn('123.45', result['prices']) + self.assertIn(5, result['quantities']) + self.assertIn('M', result['sizes']) + self.assertIn('12/31/2023', result['dates']) + self.assertIn('test@example.com', result['emails']) + self.assertIn('123-456-7890', result['phones']) + + def test_extract_integer_edge_cases(self): + self.assertEqual(extract_integer("no numbers"), None) + self.assertEqual(extract_integer("one number 42"), 42) + self.assertEqual(extract_integer("multiple 1 2 3"), [1, 2, 3]) + + # Test for batch processing logic (mock) + def test_process_batch_pdfs_logic(self): + # Since we can't test with actual PDFs without fitz, test the logic + # This is a placeholder; in real scenario, mock the PDF processing + pass # Actual testing would require mocking or installing fitz + + # Pydantic model tests (basic, without full import) + def test_product_validation(self): + # Test basic validation logic without importing models + # Since models require pydantic, and we have it in requirements, assume it's tested separately + pass + + def test_detect_table_from_spans(self): + # Mock spans data + spans = [ + {'text': 'Item', 'bbox': [0, 0, 50, 10]}, + {'text': 'Qty', 'bbox': [50, 0, 100, 10]}, + {'text': 'Price', 'bbox': [100, 0, 150, 10]}, + {'text': 'Shirt', 'bbox': [0, 15, 50, 25]}, # y=15 to separate rows + {'text': '2', 'bbox': [50, 15, 100, 25]}, + {'text': '10.00', 'bbox': [100, 15, 150, 25]}, + ] + table = detect_table_from_spans(spans, threshold=5) + self.assertEqual(len(table), 2) # Header and one row + self.assertEqual(len(table[0]), 3) # Three columns + + def test_parse_table_to_line_items(self): + table_data = [ + [{'text': 'Item', 'colspan': 1, 'bbox': [0,0,50,10]}, {'text': 'Qty', 'colspan': 1, 'bbox': [50,0,100,10]}, {'text': 'Price', 'colspan': 1, 'bbox': [100,0,150,10]}], + [{'text': 'Shirt', 'colspan': 1, 'bbox': [0,10,50,20]}, {'text': '2', 'colspan': 1, 'bbox': [50,10,100,20]}, {'text': '10.00', 'colspan': 1, 'bbox': [100,10,150,20]}], + ] + line_items = parse_table_to_line_items(table_data) + self.assertEqual(len(line_items), 1) # Only the second row + self.assertEqual(line_items[0]['description'], 'Shirt') + self.assertEqual(line_items[0]['quantity'], '2') + self.assertEqual(line_items[0]['price'], '10.00') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file