├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── Netflix_Content_Analysis.pptx ├── Netflix_Content_Analysis_Themed.pptx ├── README.md ├── auto_pptx ├── Scripts │ ├── Activate.ps1 │ ├── activate │ ├── activate.bat │ ├── deactivate.bat │ ├── f2py.exe │ ├── fonttools.exe │ ├── ipython.exe │ ├── ipython3.exe │ ├── jupyter-kernel.exe │ ├── jupyter-kernelspec.exe │ ├── jupyter-migrate.exe │ ├── jupyter-run.exe │ ├── jupyter-troubleshoot.exe │ ├── jupyter.exe │ ├── numpy-config.exe │ ├── pip.exe │ ├── pip3.9.exe │ ├── pip3.exe │ ├── pyftmerge.exe │ ├── pyftsubset.exe │ ├── pygmentize.exe │ ├── python.exe │ ├── pythonw.exe │ ├── pywin32_postinstall.py │ ├── pywin32_testall.py │ ├── ttx.exe │ └── vba_extract.py ├── pyvenv.cfg └── share │ ├── jupyter │ └── kernels │ │ └── python3 │ │ ├── kernel.json │ │ ├── logo-32x32.png │ │ ├── logo-64x64.png │ │ └── logo-svg.svg │ └── man │ └── man1 │ ├── ipython.1 │ └── ttx.1 ├── automate_presentation.ipynb ├── automate_presentation.py ├── data └── netflix_titles.csv ├── img ├── article │ ├── img_0.png │ ├── img_1.png │ └── img_2.png └── slides │ ├── content_by_year.png │ ├── content_types.png │ ├── duration_vs_year.png │ ├── movie_duration_dist.png │ ├── rating_distribution.png │ ├── top_countries.png │ └── top_genres.png └── requirements.txt /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Format Python code 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: # Permet l'exécution manuelle du workflow 9 | 10 | jobs: 11 | format: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up Python 3.10 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: '3.10' 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install ruff 23 | - name: Format with Ruff 24 | run: | 25 | ruff check --fix . --exclude auto_pptx 26 | - name: Commit changes 27 | run: | 28 | git config --local user.email "action@github.com" 29 | git config --local user.name "GitHub Action" 30 | git add -A 31 | git diff-index --quiet HEAD || git commit -m "Format code with Ruff" -a 32 | - name: Push changes 33 | uses: ad-m/github-push-action@master 34 | with: 35 | github_token: ${{ secrets.GITHUB_TOKEN }} 36 | branch: ${{ github.ref }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 110 | .pdm.toml 111 | .pdm-python 112 | .pdm-build/ 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | #.idea/ 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Gaël Penessot 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Netflix_Content_Analysis.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/Netflix_Content_Analysis.pptx -------------------------------------------------------------------------------- /Netflix_Content_Analysis_Themed.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/Netflix_Content_Analysis_Themed.pptx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automate Presentation Creation with Python 2 | --- 3 | 4 | This repository contains the code and resources used in the article ["Stop Wasting Time, Automate Your Presentation with Python"](https://medium.com/@gael.penessot/stop-wasting-time-automate-your-presentation-with-python-a721fe39c8e5) published on Medium. 5 | 6 | ## What's included: 7 | 8 | * `automate_presentation.py`: The main Python script used to create presentations. 9 | * `automate_presentation.ipynb`: A Jupyter notebook demonstrating the script's usage. 10 | * `data`: A directory containing input data used to create presentation. 11 | * `img`: A directory containing images generated to create presentation. 12 | * `requirements.txt`: A file listing the required Python libraries. 13 | 14 | ## Getting started: 15 | 16 | Clone the repository: 17 | 18 | ```bash 19 | git clone git@github.com:gpenessot/Automate-Presentation-Creation-with-Python.git 20 | ``` 21 | 22 | Install the required libraries: 23 | ```bash 24 | pip install -r requirements.txt 25 | ``` 26 | 27 | Run the script: 28 | 29 | ```bash 30 | python automate_presentation.py 31 | ``` 32 | 33 | This will create a new presentation file in the current directory. 34 | 35 | ## Additional resources: 36 | 37 | * The [article](https://medium.com/@gael.penessot/stop-wasting-time-automate-your-presentation-with-python-a721fe39c8e5) 38 | * The [Jupyter notebook](./automate_presentation.ipynb) 39 | 40 | ## License: 41 | 42 | This repository is licensed under the MIT License. 43 | -------------------------------------------------------------------------------- /auto_pptx/Scripts/Activate.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Activate a Python virtual environment for the current PowerShell session. 4 | 5 | .Description 6 | Pushes the python executable for a virtual environment to the front of the 7 | $Env:PATH environment variable and sets the prompt to signify that you are 8 | in a Python virtual environment. Makes use of the command line switches as 9 | well as the `pyvenv.cfg` file values present in the virtual environment. 10 | 11 | .Parameter VenvDir 12 | Path to the directory that contains the virtual environment to activate. The 13 | default value for this is the parent of the directory that the Activate.ps1 14 | script is located within. 15 | 16 | .Parameter Prompt 17 | The prompt prefix to display when this virtual environment is activated. By 18 | default, this prompt is the name of the virtual environment folder (VenvDir) 19 | surrounded by parentheses and followed by a single space (ie. '(.venv) '). 20 | 21 | .Example 22 | Activate.ps1 23 | Activates the Python virtual environment that contains the Activate.ps1 script. 24 | 25 | .Example 26 | Activate.ps1 -Verbose 27 | Activates the Python virtual environment that contains the Activate.ps1 script, 28 | and shows extra information about the activation as it executes. 29 | 30 | .Example 31 | Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv 32 | Activates the Python virtual environment located in the specified location. 33 | 34 | .Example 35 | Activate.ps1 -Prompt "MyPython" 36 | Activates the Python virtual environment that contains the Activate.ps1 script, 37 | and prefixes the current prompt with the specified string (surrounded in 38 | parentheses) while the virtual environment is active. 39 | 40 | .Notes 41 | On Windows, it may be required to enable this Activate.ps1 script by setting the 42 | execution policy for the user. You can do this by issuing the following PowerShell 43 | command: 44 | 45 | PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser 46 | 47 | For more information on Execution Policies: 48 | https://go.microsoft.com/fwlink/?LinkID=135170 49 | 50 | #> 51 | Param( 52 | [Parameter(Mandatory = $false)] 53 | [String] 54 | $VenvDir, 55 | [Parameter(Mandatory = $false)] 56 | [String] 57 | $Prompt 58 | ) 59 | 60 | <# Function declarations --------------------------------------------------- #> 61 | 62 | <# 63 | .Synopsis 64 | Remove all shell session elements added by the Activate script, including the 65 | addition of the virtual environment's Python executable from the beginning of 66 | the PATH variable. 67 | 68 | .Parameter NonDestructive 69 | If present, do not remove this function from the global namespace for the 70 | session. 71 | 72 | #> 73 | function global:deactivate ([switch]$NonDestructive) { 74 | # Revert to original values 75 | 76 | # The prior prompt: 77 | if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { 78 | Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt 79 | Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT 80 | } 81 | 82 | # The prior PYTHONHOME: 83 | if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { 84 | Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME 85 | Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME 86 | } 87 | 88 | # The prior PATH: 89 | if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { 90 | Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH 91 | Remove-Item -Path Env:_OLD_VIRTUAL_PATH 92 | } 93 | 94 | # Just remove the VIRTUAL_ENV altogether: 95 | if (Test-Path -Path Env:VIRTUAL_ENV) { 96 | Remove-Item -Path env:VIRTUAL_ENV 97 | } 98 | 99 | # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: 100 | if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { 101 | Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force 102 | } 103 | 104 | # Leave deactivate function in the global namespace if requested: 105 | if (-not $NonDestructive) { 106 | Remove-Item -Path function:deactivate 107 | } 108 | } 109 | 110 | <# 111 | .Description 112 | Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the 113 | given folder, and returns them in a map. 114 | 115 | For each line in the pyvenv.cfg file, if that line can be parsed into exactly 116 | two strings separated by `=` (with any amount of whitespace surrounding the =) 117 | then it is considered a `key = value` line. The left hand string is the key, 118 | the right hand is the value. 119 | 120 | If the value starts with a `'` or a `"` then the first and last character is 121 | stripped from the value before being captured. 122 | 123 | .Parameter ConfigDir 124 | Path to the directory that contains the `pyvenv.cfg` file. 125 | #> 126 | function Get-PyVenvConfig( 127 | [String] 128 | $ConfigDir 129 | ) { 130 | Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" 131 | 132 | # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). 133 | $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue 134 | 135 | # An empty map will be returned if no config file is found. 136 | $pyvenvConfig = @{ } 137 | 138 | if ($pyvenvConfigPath) { 139 | 140 | Write-Verbose "File exists, parse `key = value` lines" 141 | $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath 142 | 143 | $pyvenvConfigContent | ForEach-Object { 144 | $keyval = $PSItem -split "\s*=\s*", 2 145 | if ($keyval[0] -and $keyval[1]) { 146 | $val = $keyval[1] 147 | 148 | # Remove extraneous quotations around a string value. 149 | if ("'""".Contains($val.Substring(0, 1))) { 150 | $val = $val.Substring(1, $val.Length - 2) 151 | } 152 | 153 | $pyvenvConfig[$keyval[0]] = $val 154 | Write-Verbose "Adding Key: '$($keyval[0])'='$val'" 155 | } 156 | } 157 | } 158 | return $pyvenvConfig 159 | } 160 | 161 | 162 | <# Begin Activate script --------------------------------------------------- #> 163 | 164 | # Determine the containing directory of this script 165 | $VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition 166 | $VenvExecDir = Get-Item -Path $VenvExecPath 167 | 168 | Write-Verbose "Activation script is located in path: '$VenvExecPath'" 169 | Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" 170 | Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" 171 | 172 | # Set values required in priority: CmdLine, ConfigFile, Default 173 | # First, get the location of the virtual environment, it might not be 174 | # VenvExecDir if specified on the command line. 175 | if ($VenvDir) { 176 | Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" 177 | } 178 | else { 179 | Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." 180 | $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") 181 | Write-Verbose "VenvDir=$VenvDir" 182 | } 183 | 184 | # Next, read the `pyvenv.cfg` file to determine any required value such 185 | # as `prompt`. 186 | $pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir 187 | 188 | # Next, set the prompt from the command line, or the config file, or 189 | # just use the name of the virtual environment folder. 190 | if ($Prompt) { 191 | Write-Verbose "Prompt specified as argument, using '$Prompt'" 192 | } 193 | else { 194 | Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" 195 | if ($pyvenvCfg -and $pyvenvCfg['prompt']) { 196 | Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" 197 | $Prompt = $pyvenvCfg['prompt']; 198 | } 199 | else { 200 | Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)" 201 | Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" 202 | $Prompt = Split-Path -Path $venvDir -Leaf 203 | } 204 | } 205 | 206 | Write-Verbose "Prompt = '$Prompt'" 207 | Write-Verbose "VenvDir='$VenvDir'" 208 | 209 | # Deactivate any currently active virtual environment, but leave the 210 | # deactivate function in place. 211 | deactivate -nondestructive 212 | 213 | # Now set the environment variable VIRTUAL_ENV, used by many tools to determine 214 | # that there is an activated venv. 215 | $env:VIRTUAL_ENV = $VenvDir 216 | 217 | if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { 218 | 219 | Write-Verbose "Setting prompt to '$Prompt'" 220 | 221 | # Set the prompt to include the env name 222 | # Make sure _OLD_VIRTUAL_PROMPT is global 223 | function global:_OLD_VIRTUAL_PROMPT { "" } 224 | Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT 225 | New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt 226 | 227 | function global:prompt { 228 | Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " 229 | _OLD_VIRTUAL_PROMPT 230 | } 231 | } 232 | 233 | # Clear PYTHONHOME 234 | if (Test-Path -Path Env:PYTHONHOME) { 235 | Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME 236 | Remove-Item -Path Env:PYTHONHOME 237 | } 238 | 239 | # Add the venv to the PATH 240 | Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH 241 | $Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" 242 | -------------------------------------------------------------------------------- /auto_pptx/Scripts/activate: -------------------------------------------------------------------------------- 1 | # This file must be used with "source bin/activate" *from bash* 2 | # you cannot run it directly 3 | 4 | deactivate () { 5 | # reset old environment variables 6 | if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then 7 | PATH="${_OLD_VIRTUAL_PATH:-}" 8 | export PATH 9 | unset _OLD_VIRTUAL_PATH 10 | fi 11 | if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then 12 | PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" 13 | export PYTHONHOME 14 | unset _OLD_VIRTUAL_PYTHONHOME 15 | fi 16 | 17 | # This should detect bash and zsh, which have a hash command that must 18 | # be called to get it to forget past commands. Without forgetting 19 | # past commands the $PATH changes we made may not be respected 20 | if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then 21 | hash -r 2> /dev/null 22 | fi 23 | 24 | if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then 25 | PS1="${_OLD_VIRTUAL_PS1:-}" 26 | export PS1 27 | unset _OLD_VIRTUAL_PS1 28 | fi 29 | 30 | unset VIRTUAL_ENV 31 | if [ ! "${1:-}" = "nondestructive" ] ; then 32 | # Self destruct! 33 | unset -f deactivate 34 | fi 35 | } 36 | 37 | # unset irrelevant variables 38 | deactivate nondestructive 39 | 40 | VIRTUAL_ENV="C:\Users\gaelp\Documents\Github\Automate-Presentation-Creation-with-Python\auto_pptx" 41 | export VIRTUAL_ENV 42 | 43 | _OLD_VIRTUAL_PATH="$PATH" 44 | PATH="$VIRTUAL_ENV/Scripts:$PATH" 45 | export PATH 46 | 47 | # unset PYTHONHOME if set 48 | # this will fail if PYTHONHOME is set to the empty string (which is bad anyway) 49 | # could use `if (set -u; : $PYTHONHOME) ;` in bash 50 | if [ -n "${PYTHONHOME:-}" ] ; then 51 | _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" 52 | unset PYTHONHOME 53 | fi 54 | 55 | if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then 56 | _OLD_VIRTUAL_PS1="${PS1:-}" 57 | PS1="(auto_pptx) ${PS1:-}" 58 | export PS1 59 | fi 60 | 61 | # This should detect bash and zsh, which have a hash command that must 62 | # be called to get it to forget past commands. Without forgetting 63 | # past commands the $PATH changes we made may not be respected 64 | if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then 65 | hash -r 2> /dev/null 66 | fi 67 | -------------------------------------------------------------------------------- /auto_pptx/Scripts/activate.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem This file is UTF-8 encoded, so we need to update the current code page while executing it 4 | for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do ( 5 | set _OLD_CODEPAGE=%%a 6 | ) 7 | if defined _OLD_CODEPAGE ( 8 | "%SystemRoot%\System32\chcp.com" 65001 > nul 9 | ) 10 | 11 | set VIRTUAL_ENV=C:\Users\gaelp\Documents\Github\Automate-Presentation-Creation-with-Python\auto_pptx 12 | 13 | if not defined PROMPT set PROMPT=$P$G 14 | 15 | if defined _OLD_VIRTUAL_PROMPT set PROMPT=%_OLD_VIRTUAL_PROMPT% 16 | if defined _OLD_VIRTUAL_PYTHONHOME set PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME% 17 | 18 | set _OLD_VIRTUAL_PROMPT=%PROMPT% 19 | set PROMPT=(auto_pptx) %PROMPT% 20 | 21 | if defined PYTHONHOME set _OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME% 22 | set PYTHONHOME= 23 | 24 | if defined _OLD_VIRTUAL_PATH set PATH=%_OLD_VIRTUAL_PATH% 25 | if not defined _OLD_VIRTUAL_PATH set _OLD_VIRTUAL_PATH=%PATH% 26 | 27 | set PATH=%VIRTUAL_ENV%\Scripts;%PATH% 28 | 29 | :END 30 | if defined _OLD_CODEPAGE ( 31 | "%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul 32 | set _OLD_CODEPAGE= 33 | ) 34 | -------------------------------------------------------------------------------- /auto_pptx/Scripts/deactivate.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | if defined _OLD_VIRTUAL_PROMPT ( 4 | set "PROMPT=%_OLD_VIRTUAL_PROMPT%" 5 | ) 6 | set _OLD_VIRTUAL_PROMPT= 7 | 8 | if defined _OLD_VIRTUAL_PYTHONHOME ( 9 | set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" 10 | set _OLD_VIRTUAL_PYTHONHOME= 11 | ) 12 | 13 | if defined _OLD_VIRTUAL_PATH ( 14 | set "PATH=%_OLD_VIRTUAL_PATH%" 15 | ) 16 | 17 | set _OLD_VIRTUAL_PATH= 18 | 19 | set VIRTUAL_ENV= 20 | 21 | :END 22 | -------------------------------------------------------------------------------- /auto_pptx/Scripts/f2py.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/f2py.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/fonttools.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/fonttools.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/ipython.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/ipython.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/ipython3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/ipython3.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/jupyter-kernel.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/jupyter-kernel.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/jupyter-kernelspec.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/jupyter-kernelspec.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/jupyter-migrate.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/jupyter-migrate.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/jupyter-run.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/jupyter-run.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/jupyter-troubleshoot.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/jupyter-troubleshoot.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/jupyter.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/jupyter.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/numpy-config.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/numpy-config.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/pip.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/pip.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/pip3.9.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/pip3.9.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/pip3.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/pip3.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/pyftmerge.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/pyftmerge.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/pyftsubset.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/pyftsubset.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/pygmentize.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/pygmentize.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/python.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/python.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/pythonw.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/pythonw.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/pywin32_postinstall.py: -------------------------------------------------------------------------------- 1 | # postinstall script for pywin32 2 | # 3 | # copies PyWinTypesxx.dll and PythonCOMxx.dll into the system directory, 4 | # and creates a pth file 5 | import glob 6 | import os 7 | import shutil 8 | import sys 9 | import sysconfig 10 | 11 | try: 12 | import winreg as winreg 13 | except: 14 | import winreg 15 | 16 | # Send output somewhere so it can be found if necessary... 17 | import tempfile 18 | 19 | tee_f = open(os.path.join(tempfile.gettempdir(), "pywin32_postinstall.log"), "w") 20 | 21 | 22 | class Tee: 23 | def __init__(self, file): 24 | self.f = file 25 | 26 | def write(self, what): 27 | if self.f is not None: 28 | try: 29 | self.f.write(what.replace("\n", "\r\n")) 30 | except IOError: 31 | pass 32 | tee_f.write(what) 33 | 34 | def flush(self): 35 | if self.f is not None: 36 | try: 37 | self.f.flush() 38 | except IOError: 39 | pass 40 | tee_f.flush() 41 | 42 | 43 | # For some unknown reason, when running under bdist_wininst we will start up 44 | # with sys.stdout as None but stderr is hooked up. This work-around allows 45 | # bdist_wininst to see the output we write and display it at the end of 46 | # the install. 47 | if sys.stdout is None: 48 | sys.stdout = sys.stderr 49 | 50 | sys.stderr = Tee(sys.stderr) 51 | sys.stdout = Tee(sys.stdout) 52 | 53 | com_modules = [ 54 | # module_name, class_names 55 | ("win32com.servers.interp", "Interpreter"), 56 | ("win32com.servers.dictionary", "DictionaryPolicy"), 57 | ("win32com.axscript.client.pyscript", "PyScript"), 58 | ] 59 | 60 | # Is this a 'silent' install - ie, avoid all dialogs. 61 | # Different than 'verbose' 62 | silent = 0 63 | 64 | # Verbosity of output messages. 65 | verbose = 1 66 | 67 | root_key_name = "Software\\Python\\PythonCore\\" + sys.winver 68 | 69 | try: 70 | # When this script is run from inside the bdist_wininst installer, 71 | # file_created() and directory_created() are additional builtin 72 | # functions which write lines to Python23\pywin32-install.log. This is 73 | # a list of actions for the uninstaller, the format is inspired by what 74 | # the Wise installer also creates. 75 | file_created 76 | is_bdist_wininst = True 77 | except NameError: 78 | is_bdist_wininst = False # we know what it is not - but not what it is :) 79 | 80 | def file_created(file): 81 | pass 82 | 83 | def directory_created(directory): 84 | pass 85 | 86 | def get_root_hkey(): 87 | try: 88 | winreg.OpenKey( 89 | winreg.HKEY_LOCAL_MACHINE, root_key_name, 0, winreg.KEY_CREATE_SUB_KEY 90 | ) 91 | return winreg.HKEY_LOCAL_MACHINE 92 | except OSError: 93 | # Either not exist, or no permissions to create subkey means 94 | # must be HKCU 95 | return winreg.HKEY_CURRENT_USER 96 | 97 | 98 | try: 99 | create_shortcut 100 | except NameError: 101 | # Create a function with the same signature as create_shortcut provided 102 | # by bdist_wininst 103 | def create_shortcut( 104 | path, description, filename, arguments="", workdir="", iconpath="", iconindex=0 105 | ): 106 | import pythoncom 107 | from win32com.shell import shell 108 | 109 | ilink = pythoncom.CoCreateInstance( 110 | shell.CLSID_ShellLink, 111 | None, 112 | pythoncom.CLSCTX_INPROC_SERVER, 113 | shell.IID_IShellLink, 114 | ) 115 | ilink.SetPath(path) 116 | ilink.SetDescription(description) 117 | if arguments: 118 | ilink.SetArguments(arguments) 119 | if workdir: 120 | ilink.SetWorkingDirectory(workdir) 121 | if iconpath or iconindex: 122 | ilink.SetIconLocation(iconpath, iconindex) 123 | # now save it. 124 | ipf = ilink.QueryInterface(pythoncom.IID_IPersistFile) 125 | ipf.Save(filename, 0) 126 | 127 | # Support the same list of "path names" as bdist_wininst. 128 | def get_special_folder_path(path_name): 129 | from win32com.shell import shell, shellcon 130 | 131 | for maybe in """ 132 | CSIDL_COMMON_STARTMENU CSIDL_STARTMENU CSIDL_COMMON_APPDATA 133 | CSIDL_LOCAL_APPDATA CSIDL_APPDATA CSIDL_COMMON_DESKTOPDIRECTORY 134 | CSIDL_DESKTOPDIRECTORY CSIDL_COMMON_STARTUP CSIDL_STARTUP 135 | CSIDL_COMMON_PROGRAMS CSIDL_PROGRAMS CSIDL_PROGRAM_FILES_COMMON 136 | CSIDL_PROGRAM_FILES CSIDL_FONTS""".split(): 137 | if maybe == path_name: 138 | csidl = getattr(shellcon, maybe) 139 | return shell.SHGetSpecialFolderPath(0, csidl, False) 140 | raise ValueError("%s is an unknown path ID" % (path_name,)) 141 | 142 | 143 | def CopyTo(desc, src, dest): 144 | import win32api 145 | import win32con 146 | 147 | while 1: 148 | try: 149 | win32api.CopyFile(src, dest, 0) 150 | return 151 | except win32api.error as details: 152 | if details.winerror == 5: # access denied - user not admin. 153 | raise 154 | if silent: 155 | # Running silent mode - just re-raise the error. 156 | raise 157 | full_desc = ( 158 | "Error %s\n\n" 159 | "If you have any Python applications running, " 160 | "please close them now\nand select 'Retry'\n\n%s" 161 | % (desc, details.strerror) 162 | ) 163 | rc = win32api.MessageBox( 164 | 0, full_desc, "Installation Error", win32con.MB_ABORTRETRYIGNORE 165 | ) 166 | if rc == win32con.IDABORT: 167 | raise 168 | elif rc == win32con.IDIGNORE: 169 | return 170 | # else retry - around we go again. 171 | 172 | 173 | # We need to import win32api to determine the Windows system directory, 174 | # so we can copy our system files there - but importing win32api will 175 | # load the pywintypes.dll already in the system directory preventing us 176 | # from updating them! 177 | # So, we pull the same trick pywintypes.py does, but it loads from 178 | # our pywintypes_system32 directory. 179 | def LoadSystemModule(lib_dir, modname): 180 | # See if this is a debug build. 181 | import importlib.machinery 182 | import importlib.util 183 | 184 | suffix = "_d" if "_d.pyd" in importlib.machinery.EXTENSION_SUFFIXES else "" 185 | filename = "%s%d%d%s.dll" % ( 186 | modname, 187 | sys.version_info[0], 188 | sys.version_info[1], 189 | suffix, 190 | ) 191 | filename = os.path.join(lib_dir, "pywin32_system32", filename) 192 | loader = importlib.machinery.ExtensionFileLoader(modname, filename) 193 | spec = importlib.machinery.ModuleSpec(name=modname, loader=loader, origin=filename) 194 | mod = importlib.util.module_from_spec(spec) 195 | spec.loader.exec_module(mod) 196 | 197 | 198 | def SetPyKeyVal(key_name, value_name, value): 199 | root_hkey = get_root_hkey() 200 | root_key = winreg.OpenKey(root_hkey, root_key_name) 201 | try: 202 | my_key = winreg.CreateKey(root_key, key_name) 203 | try: 204 | winreg.SetValueEx(my_key, value_name, 0, winreg.REG_SZ, value) 205 | if verbose: 206 | print("-> %s\\%s[%s]=%r" % (root_key_name, key_name, value_name, value)) 207 | finally: 208 | my_key.Close() 209 | finally: 210 | root_key.Close() 211 | 212 | 213 | def UnsetPyKeyVal(key_name, value_name, delete_key=False): 214 | root_hkey = get_root_hkey() 215 | root_key = winreg.OpenKey(root_hkey, root_key_name) 216 | try: 217 | my_key = winreg.OpenKey(root_key, key_name, 0, winreg.KEY_SET_VALUE) 218 | try: 219 | winreg.DeleteValue(my_key, value_name) 220 | if verbose: 221 | print("-> DELETE %s\\%s[%s]" % (root_key_name, key_name, value_name)) 222 | finally: 223 | my_key.Close() 224 | if delete_key: 225 | winreg.DeleteKey(root_key, key_name) 226 | if verbose: 227 | print("-> DELETE %s\\%s" % (root_key_name, key_name)) 228 | except OSError as why: 229 | winerror = getattr(why, "winerror", why.errno) 230 | if winerror != 2: # file not found 231 | raise 232 | finally: 233 | root_key.Close() 234 | 235 | 236 | def RegisterCOMObjects(register=True): 237 | import win32com.server.register 238 | 239 | if register: 240 | func = win32com.server.register.RegisterClasses 241 | else: 242 | func = win32com.server.register.UnregisterClasses 243 | flags = {} 244 | if not verbose: 245 | flags["quiet"] = 1 246 | for module, klass_name in com_modules: 247 | __import__(module) 248 | mod = sys.modules[module] 249 | flags["finalize_register"] = getattr(mod, "DllRegisterServer", None) 250 | flags["finalize_unregister"] = getattr(mod, "DllUnregisterServer", None) 251 | klass = getattr(mod, klass_name) 252 | func(klass, **flags) 253 | 254 | 255 | def RegisterHelpFile(register=True, lib_dir=None): 256 | if lib_dir is None: 257 | lib_dir = sysconfig.get_paths()["platlib"] 258 | if register: 259 | # Register the .chm help file. 260 | chm_file = os.path.join(lib_dir, "PyWin32.chm") 261 | if os.path.isfile(chm_file): 262 | # This isn't recursive, so if 'Help' doesn't exist, we croak 263 | SetPyKeyVal("Help", None, None) 264 | SetPyKeyVal("Help\\Pythonwin Reference", None, chm_file) 265 | return chm_file 266 | else: 267 | print("NOTE: PyWin32.chm can not be located, so has not " "been registered") 268 | else: 269 | UnsetPyKeyVal("Help\\Pythonwin Reference", None, delete_key=True) 270 | return None 271 | 272 | 273 | def RegisterPythonwin(register=True, lib_dir=None): 274 | """Add (or remove) Pythonwin to context menu for python scripts. 275 | ??? Should probably also add Edit command for pys files also. 276 | Also need to remove these keys on uninstall, but there's no function 277 | like file_created to add registry entries to uninstall log ??? 278 | """ 279 | import os 280 | 281 | if lib_dir is None: 282 | lib_dir = sysconfig.get_paths()["platlib"] 283 | classes_root = get_root_hkey() 284 | ## Installer executable doesn't seem to pass anything to postinstall script indicating if it's a debug build, 285 | pythonwin_exe = os.path.join(lib_dir, "Pythonwin", "Pythonwin.exe") 286 | pythonwin_edit_command = pythonwin_exe + ' -edit "%1"' 287 | 288 | keys_vals = [ 289 | ( 290 | "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\Pythonwin.exe", 291 | "", 292 | pythonwin_exe, 293 | ), 294 | ( 295 | "Software\\Classes\\Python.File\\shell\\Edit with Pythonwin", 296 | "command", 297 | pythonwin_edit_command, 298 | ), 299 | ( 300 | "Software\\Classes\\Python.NoConFile\\shell\\Edit with Pythonwin", 301 | "command", 302 | pythonwin_edit_command, 303 | ), 304 | ] 305 | 306 | try: 307 | if register: 308 | for key, sub_key, val in keys_vals: 309 | ## Since winreg only uses the character Api functions, this can fail if Python 310 | ## is installed to a path containing non-ascii characters 311 | hkey = winreg.CreateKey(classes_root, key) 312 | if sub_key: 313 | hkey = winreg.CreateKey(hkey, sub_key) 314 | winreg.SetValueEx(hkey, None, 0, winreg.REG_SZ, val) 315 | hkey.Close() 316 | else: 317 | for key, sub_key, val in keys_vals: 318 | try: 319 | if sub_key: 320 | hkey = winreg.OpenKey(classes_root, key) 321 | winreg.DeleteKey(hkey, sub_key) 322 | hkey.Close() 323 | winreg.DeleteKey(classes_root, key) 324 | except OSError as why: 325 | winerror = getattr(why, "winerror", why.errno) 326 | if winerror != 2: # file not found 327 | raise 328 | finally: 329 | # tell windows about the change 330 | from win32com.shell import shell, shellcon 331 | 332 | shell.SHChangeNotify( 333 | shellcon.SHCNE_ASSOCCHANGED, shellcon.SHCNF_IDLIST, None, None 334 | ) 335 | 336 | 337 | def get_shortcuts_folder(): 338 | if get_root_hkey() == winreg.HKEY_LOCAL_MACHINE: 339 | try: 340 | fldr = get_special_folder_path("CSIDL_COMMON_PROGRAMS") 341 | except OSError: 342 | # No CSIDL_COMMON_PROGRAMS on this platform 343 | fldr = get_special_folder_path("CSIDL_PROGRAMS") 344 | else: 345 | # non-admin install - always goes in this user's start menu. 346 | fldr = get_special_folder_path("CSIDL_PROGRAMS") 347 | 348 | try: 349 | install_group = winreg.QueryValue( 350 | get_root_hkey(), root_key_name + "\\InstallPath\\InstallGroup" 351 | ) 352 | except OSError: 353 | vi = sys.version_info 354 | install_group = "Python %d.%d" % (vi[0], vi[1]) 355 | return os.path.join(fldr, install_group) 356 | 357 | 358 | # Get the system directory, which may be the Wow64 directory if we are a 32bit 359 | # python on a 64bit OS. 360 | def get_system_dir(): 361 | import win32api # we assume this exists. 362 | 363 | try: 364 | import pythoncom 365 | import win32process 366 | from win32com.shell import shell, shellcon 367 | 368 | try: 369 | if win32process.IsWow64Process(): 370 | return shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_SYSTEMX86) 371 | return shell.SHGetSpecialFolderPath(0, shellcon.CSIDL_SYSTEM) 372 | except (pythoncom.com_error, win32process.error): 373 | return win32api.GetSystemDirectory() 374 | except ImportError: 375 | return win32api.GetSystemDirectory() 376 | 377 | 378 | def fixup_dbi(): 379 | # We used to have a dbi.pyd with our .pyd files, but now have a .py file. 380 | # If the user didn't uninstall, they will find the .pyd which will cause 381 | # problems - so handle that. 382 | import win32api 383 | import win32con 384 | 385 | pyd_name = os.path.join(os.path.dirname(win32api.__file__), "dbi.pyd") 386 | pyd_d_name = os.path.join(os.path.dirname(win32api.__file__), "dbi_d.pyd") 387 | py_name = os.path.join(os.path.dirname(win32con.__file__), "dbi.py") 388 | for this_pyd in (pyd_name, pyd_d_name): 389 | this_dest = this_pyd + ".old" 390 | if os.path.isfile(this_pyd) and os.path.isfile(py_name): 391 | try: 392 | if os.path.isfile(this_dest): 393 | print( 394 | "Old dbi '%s' already exists - deleting '%s'" 395 | % (this_dest, this_pyd) 396 | ) 397 | os.remove(this_pyd) 398 | else: 399 | os.rename(this_pyd, this_dest) 400 | print("renamed '%s'->'%s.old'" % (this_pyd, this_pyd)) 401 | file_created(this_pyd + ".old") 402 | except os.error as exc: 403 | print("FAILED to rename '%s': %s" % (this_pyd, exc)) 404 | 405 | 406 | def install(lib_dir): 407 | import traceback 408 | 409 | # The .pth file is now installed as a regular file. 410 | # Create the .pth file in the site-packages dir, and use only relative paths 411 | # We used to write a .pth directly to sys.prefix - clobber it. 412 | if os.path.isfile(os.path.join(sys.prefix, "pywin32.pth")): 413 | os.unlink(os.path.join(sys.prefix, "pywin32.pth")) 414 | # The .pth may be new and therefore not loaded in this session. 415 | # Setup the paths just in case. 416 | for name in "win32 win32\\lib Pythonwin".split(): 417 | sys.path.append(os.path.join(lib_dir, name)) 418 | # It is possible people with old versions installed with still have 419 | # pywintypes and pythoncom registered. We no longer need this, and stale 420 | # entries hurt us. 421 | for name in "pythoncom pywintypes".split(): 422 | keyname = "Software\\Python\\PythonCore\\" + sys.winver + "\\Modules\\" + name 423 | for root in winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER: 424 | try: 425 | winreg.DeleteKey(root, keyname + "\\Debug") 426 | except WindowsError: 427 | pass 428 | try: 429 | winreg.DeleteKey(root, keyname) 430 | except WindowsError: 431 | pass 432 | LoadSystemModule(lib_dir, "pywintypes") 433 | LoadSystemModule(lib_dir, "pythoncom") 434 | import win32api 435 | 436 | # and now we can get the system directory: 437 | files = glob.glob(os.path.join(lib_dir, "pywin32_system32\\*.*")) 438 | if not files: 439 | raise RuntimeError("No system files to copy!!") 440 | # Try the system32 directory first - if that fails due to "access denied", 441 | # it implies a non-admin user, and we use sys.prefix 442 | for dest_dir in [get_system_dir(), sys.prefix]: 443 | # and copy some files over there 444 | worked = 0 445 | try: 446 | for fname in files: 447 | base = os.path.basename(fname) 448 | dst = os.path.join(dest_dir, base) 449 | CopyTo("installing %s" % base, fname, dst) 450 | if verbose: 451 | print("Copied %s to %s" % (base, dst)) 452 | # Register the files with the uninstaller 453 | file_created(dst) 454 | worked = 1 455 | # Nuke any other versions that may exist - having 456 | # duplicates causes major headaches. 457 | bad_dest_dirs = [ 458 | os.path.join(sys.prefix, "Library\\bin"), 459 | os.path.join(sys.prefix, "Lib\\site-packages\\win32"), 460 | ] 461 | if dest_dir != sys.prefix: 462 | bad_dest_dirs.append(sys.prefix) 463 | for bad_dest_dir in bad_dest_dirs: 464 | bad_fname = os.path.join(bad_dest_dir, base) 465 | if os.path.exists(bad_fname): 466 | # let exceptions go here - delete must succeed 467 | os.unlink(bad_fname) 468 | if worked: 469 | break 470 | except win32api.error as details: 471 | if details.winerror == 5: 472 | # access denied - user not admin - try sys.prefix dir, 473 | # but first check that a version doesn't already exist 474 | # in that place - otherwise that one will still get used! 475 | if os.path.exists(dst): 476 | msg = ( 477 | "The file '%s' exists, but can not be replaced " 478 | "due to insufficient permissions. You must " 479 | "reinstall this software as an Administrator" % dst 480 | ) 481 | print(msg) 482 | raise RuntimeError(msg) 483 | continue 484 | raise 485 | else: 486 | raise RuntimeError( 487 | "You don't have enough permissions to install the system files" 488 | ) 489 | 490 | # Pythonwin 'compiles' config files - record them for uninstall. 491 | pywin_dir = os.path.join(lib_dir, "Pythonwin", "pywin") 492 | for fname in glob.glob(os.path.join(pywin_dir, "*.cfg")): 493 | file_created(fname[:-1] + "c") # .cfg->.cfc 494 | 495 | # Register our demo COM objects. 496 | try: 497 | try: 498 | RegisterCOMObjects() 499 | except win32api.error as details: 500 | if details.winerror != 5: # ERROR_ACCESS_DENIED 501 | raise 502 | print("You do not have the permissions to install COM objects.") 503 | print("The sample COM objects were not registered.") 504 | except Exception: 505 | print("FAILED to register the Python COM objects") 506 | traceback.print_exc() 507 | 508 | # There may be no main Python key in HKCU if, eg, an admin installed 509 | # python itself. 510 | winreg.CreateKey(get_root_hkey(), root_key_name) 511 | 512 | chm_file = None 513 | try: 514 | chm_file = RegisterHelpFile(True, lib_dir) 515 | except Exception: 516 | print("Failed to register help file") 517 | traceback.print_exc() 518 | else: 519 | if verbose: 520 | print("Registered help file") 521 | 522 | # misc other fixups. 523 | fixup_dbi() 524 | 525 | # Register Pythonwin in context menu 526 | try: 527 | RegisterPythonwin(True, lib_dir) 528 | except Exception: 529 | print("Failed to register pythonwin as editor") 530 | traceback.print_exc() 531 | else: 532 | if verbose: 533 | print("Pythonwin has been registered in context menu") 534 | 535 | # Create the win32com\gen_py directory. 536 | make_dir = os.path.join(lib_dir, "win32com", "gen_py") 537 | if not os.path.isdir(make_dir): 538 | if verbose: 539 | print("Creating directory %s" % (make_dir,)) 540 | directory_created(make_dir) 541 | os.mkdir(make_dir) 542 | 543 | try: 544 | # create shortcuts 545 | # CSIDL_COMMON_PROGRAMS only available works on NT/2000/XP, and 546 | # will fail there if the user has no admin rights. 547 | fldr = get_shortcuts_folder() 548 | # If the group doesn't exist, then we don't make shortcuts - its 549 | # possible that this isn't a "normal" install. 550 | if os.path.isdir(fldr): 551 | dst = os.path.join(fldr, "PythonWin.lnk") 552 | create_shortcut( 553 | os.path.join(lib_dir, "Pythonwin\\Pythonwin.exe"), 554 | "The Pythonwin IDE", 555 | dst, 556 | "", 557 | sys.prefix, 558 | ) 559 | file_created(dst) 560 | if verbose: 561 | print("Shortcut for Pythonwin created") 562 | # And the docs. 563 | if chm_file: 564 | dst = os.path.join(fldr, "Python for Windows Documentation.lnk") 565 | doc = "Documentation for the PyWin32 extensions" 566 | create_shortcut(chm_file, doc, dst) 567 | file_created(dst) 568 | if verbose: 569 | print("Shortcut to documentation created") 570 | else: 571 | if verbose: 572 | print("Can't install shortcuts - %r is not a folder" % (fldr,)) 573 | except Exception as details: 574 | print(details) 575 | 576 | # importing win32com.client ensures the gen_py dir created - not strictly 577 | # necessary to do now, but this makes the installation "complete" 578 | try: 579 | import win32com.client # noqa 580 | except ImportError: 581 | # Don't let this error sound fatal 582 | pass 583 | print("The pywin32 extensions were successfully installed.") 584 | 585 | if is_bdist_wininst: 586 | # Open a web page with info about the .exe installers being deprecated. 587 | import webbrowser 588 | 589 | try: 590 | webbrowser.open("https://mhammond.github.io/pywin32_installers.html") 591 | except webbrowser.Error: 592 | print("Please visit https://mhammond.github.io/pywin32_installers.html") 593 | 594 | 595 | def uninstall(lib_dir): 596 | # First ensure our system modules are loaded from pywin32_system, so 597 | # we can remove the ones we copied... 598 | LoadSystemModule(lib_dir, "pywintypes") 599 | LoadSystemModule(lib_dir, "pythoncom") 600 | 601 | try: 602 | RegisterCOMObjects(False) 603 | except Exception as why: 604 | print("Failed to unregister COM objects: %s" % (why,)) 605 | 606 | try: 607 | RegisterHelpFile(False, lib_dir) 608 | except Exception as why: 609 | print("Failed to unregister help file: %s" % (why,)) 610 | else: 611 | if verbose: 612 | print("Unregistered help file") 613 | 614 | try: 615 | RegisterPythonwin(False, lib_dir) 616 | except Exception as why: 617 | print("Failed to unregister Pythonwin: %s" % (why,)) 618 | else: 619 | if verbose: 620 | print("Unregistered Pythonwin") 621 | 622 | try: 623 | # remove gen_py directory. 624 | gen_dir = os.path.join(lib_dir, "win32com", "gen_py") 625 | if os.path.isdir(gen_dir): 626 | shutil.rmtree(gen_dir) 627 | if verbose: 628 | print("Removed directory %s" % (gen_dir,)) 629 | 630 | # Remove pythonwin compiled "config" files. 631 | pywin_dir = os.path.join(lib_dir, "Pythonwin", "pywin") 632 | for fname in glob.glob(os.path.join(pywin_dir, "*.cfc")): 633 | os.remove(fname) 634 | 635 | # The dbi.pyd.old files we may have created. 636 | try: 637 | os.remove(os.path.join(lib_dir, "win32", "dbi.pyd.old")) 638 | except os.error: 639 | pass 640 | try: 641 | os.remove(os.path.join(lib_dir, "win32", "dbi_d.pyd.old")) 642 | except os.error: 643 | pass 644 | 645 | except Exception as why: 646 | print("Failed to remove misc files: %s" % (why,)) 647 | 648 | try: 649 | fldr = get_shortcuts_folder() 650 | for link in ("PythonWin.lnk", "Python for Windows Documentation.lnk"): 651 | fqlink = os.path.join(fldr, link) 652 | if os.path.isfile(fqlink): 653 | os.remove(fqlink) 654 | if verbose: 655 | print("Removed %s" % (link,)) 656 | except Exception as why: 657 | print("Failed to remove shortcuts: %s" % (why,)) 658 | # Now remove the system32 files. 659 | files = glob.glob(os.path.join(lib_dir, "pywin32_system32\\*.*")) 660 | # Try the system32 directory first - if that fails due to "access denied", 661 | # it implies a non-admin user, and we use sys.prefix 662 | try: 663 | for dest_dir in [get_system_dir(), sys.prefix]: 664 | # and copy some files over there 665 | worked = 0 666 | for fname in files: 667 | base = os.path.basename(fname) 668 | dst = os.path.join(dest_dir, base) 669 | if os.path.isfile(dst): 670 | try: 671 | os.remove(dst) 672 | worked = 1 673 | if verbose: 674 | print("Removed file %s" % (dst)) 675 | except Exception: 676 | print("FAILED to remove %s" % (dst,)) 677 | if worked: 678 | break 679 | except Exception as why: 680 | print("FAILED to remove system files: %s" % (why,)) 681 | 682 | 683 | # NOTE: If this script is run from inside the bdist_wininst created 684 | # binary installer or uninstaller, the command line args are either 685 | # '-install' or '-remove'. 686 | 687 | # Important: From inside the binary installer this script MUST NOT 688 | # call sys.exit() or raise SystemExit, otherwise not only this script 689 | # but also the installer will terminate! (Is there a way to prevent 690 | # this from the bdist_wininst C code?) 691 | 692 | 693 | def verify_destination(location): 694 | if not os.path.isdir(location): 695 | raise argparse.ArgumentTypeError('Path "{}" does not exist!'.format(location)) 696 | return location 697 | 698 | 699 | def main(): 700 | import argparse 701 | 702 | parser = argparse.ArgumentParser( 703 | formatter_class=argparse.RawDescriptionHelpFormatter, 704 | description="""A post-install script for the pywin32 extensions. 705 | 706 | * Typical usage: 707 | 708 | > python pywin32_postinstall.py -install 709 | 710 | If you installed pywin32 via a .exe installer, this should be run 711 | automatically after installation, but if it fails you can run it again. 712 | 713 | If you installed pywin32 via PIP, you almost certainly need to run this to 714 | setup the environment correctly. 715 | 716 | Execute with script with a '-install' parameter, to ensure the environment 717 | is setup correctly. 718 | """, 719 | ) 720 | parser.add_argument( 721 | "-install", 722 | default=False, 723 | action="store_true", 724 | help="Configure the Python environment correctly for pywin32.", 725 | ) 726 | parser.add_argument( 727 | "-remove", 728 | default=False, 729 | action="store_true", 730 | help="Try and remove everything that was installed or copied.", 731 | ) 732 | parser.add_argument( 733 | "-wait", 734 | type=int, 735 | help="Wait for the specified process to terminate before starting.", 736 | ) 737 | parser.add_argument( 738 | "-silent", 739 | default=False, 740 | action="store_true", 741 | help='Don\'t display the "Abort/Retry/Ignore" dialog for files in use.', 742 | ) 743 | parser.add_argument( 744 | "-quiet", 745 | default=False, 746 | action="store_true", 747 | help="Don't display progress messages.", 748 | ) 749 | parser.add_argument( 750 | "-destination", 751 | default=sysconfig.get_paths()["platlib"], 752 | type=verify_destination, 753 | help="Location of the PyWin32 installation", 754 | ) 755 | 756 | args = parser.parse_args() 757 | 758 | if not args.quiet: 759 | print("Parsed arguments are: {}".format(args)) 760 | 761 | if not args.install ^ args.remove: 762 | parser.error("You need to either choose to -install or -remove!") 763 | 764 | if args.wait is not None: 765 | try: 766 | os.waitpid(args.wait, 0) 767 | except os.error: 768 | # child already dead 769 | pass 770 | 771 | silent = args.silent 772 | verbose = not args.quiet 773 | 774 | if args.install: 775 | install(args.destination) 776 | 777 | if args.remove: 778 | if not is_bdist_wininst: 779 | uninstall(args.destination) 780 | 781 | 782 | if __name__ == "__main__": 783 | main() 784 | -------------------------------------------------------------------------------- /auto_pptx/Scripts/pywin32_testall.py: -------------------------------------------------------------------------------- 1 | """A test runner for pywin32""" 2 | import os 3 | import site 4 | import subprocess 5 | import sys 6 | 7 | # locate the dirs based on where this script is - it may be either in the 8 | # source tree, or in an installed Python 'Scripts' tree. 9 | this_dir = os.path.dirname(__file__) 10 | site_packages = [ 11 | site.getusersitepackages(), 12 | ] + site.getsitepackages() 13 | 14 | failures = [] 15 | 16 | 17 | # Run a test using subprocess and wait for the result. 18 | # If we get an returncode != 0, we know that there was an error, but we don't 19 | # abort immediately - we run as many tests as we can. 20 | def run_test(script, cmdline_extras): 21 | dirname, scriptname = os.path.split(script) 22 | # some tests prefer to be run from their directory. 23 | cmd = [sys.executable, "-u", scriptname] + cmdline_extras 24 | print("--- Running '%s' ---" % script) 25 | sys.stdout.flush() 26 | result = subprocess.run(cmd, check=False, cwd=dirname) 27 | print("*** Test script '%s' exited with %s" % (script, result.returncode)) 28 | sys.stdout.flush() 29 | if result.returncode: 30 | failures.append(script) 31 | 32 | 33 | def find_and_run(possible_locations, extras): 34 | for maybe in possible_locations: 35 | if os.path.isfile(maybe): 36 | run_test(maybe, extras) 37 | break 38 | else: 39 | raise RuntimeError( 40 | "Failed to locate a test script in one of %s" % possible_locations 41 | ) 42 | 43 | 44 | def main(): 45 | import argparse 46 | 47 | code_directories = [this_dir] + site_packages 48 | 49 | parser = argparse.ArgumentParser( 50 | description="A script to trigger tests in all subprojects of PyWin32." 51 | ) 52 | parser.add_argument( 53 | "-no-user-interaction", 54 | default=False, 55 | action="store_true", 56 | help="(This is now the default - use `-user-interaction` to include them)", 57 | ) 58 | 59 | parser.add_argument( 60 | "-user-interaction", 61 | action="store_true", 62 | help="Include tests which require user interaction", 63 | ) 64 | 65 | parser.add_argument( 66 | "-skip-adodbapi", 67 | default=False, 68 | action="store_true", 69 | help="Skip the adodbapi tests; useful for CI where there's no provider", 70 | ) 71 | 72 | args, remains = parser.parse_known_args() 73 | 74 | # win32, win32ui / Pythonwin 75 | 76 | extras = [] 77 | if args.user_interaction: 78 | extras += ["-user-interaction"] 79 | extras.extend(remains) 80 | scripts = [ 81 | "win32/test/testall.py", 82 | "Pythonwin/pywin/test/all.py", 83 | ] 84 | for script in scripts: 85 | maybes = [os.path.join(directory, script) for directory in code_directories] 86 | find_and_run(maybes, extras) 87 | 88 | # win32com 89 | maybes = [ 90 | os.path.join(directory, "win32com", "test", "testall.py") 91 | for directory in [ 92 | os.path.join(this_dir, "com"), 93 | ] 94 | + site_packages 95 | ] 96 | extras = remains + ["1"] # only run "level 1" tests in CI 97 | find_and_run(maybes, extras) 98 | 99 | # adodbapi 100 | if not args.skip_adodbapi: 101 | maybes = [ 102 | os.path.join(directory, "adodbapi", "test", "adodbapitest.py") 103 | for directory in code_directories 104 | ] 105 | find_and_run(maybes, remains) 106 | # This script has a hard-coded sql server name in it, (and markh typically 107 | # doesn't have a different server to test on) but there is now supposed to be a server out there on the Internet 108 | # just to run these tests, so try it... 109 | maybes = [ 110 | os.path.join(directory, "adodbapi", "test", "test_adodbapi_dbapi20.py") 111 | for directory in code_directories 112 | ] 113 | find_and_run(maybes, remains) 114 | 115 | if failures: 116 | print("The following scripts failed") 117 | for failure in failures: 118 | print(">", failure) 119 | sys.exit(1) 120 | print("All tests passed \\o/") 121 | 122 | 123 | if __name__ == "__main__": 124 | main() 125 | -------------------------------------------------------------------------------- /auto_pptx/Scripts/ttx.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/Scripts/ttx.exe -------------------------------------------------------------------------------- /auto_pptx/Scripts/vba_extract.py: -------------------------------------------------------------------------------- 1 | #!C:\Users\gaelp\Documents\Github\Automate-Presentation-Creation-with-Python\auto_pptx\Scripts\python.exe 2 | 3 | ############################################################################## 4 | # 5 | # vba_extract - A simple utility to extract a vbaProject.bin binary from an 6 | # Excel 2007+ xlsm file for insertion into an XlsxWriter file. 7 | # 8 | # SPDX-License-Identifier: BSD-2-Clause 9 | # Copyright 2013-2024, John McNamara, jmcnamara@cpan.org 10 | # 11 | import sys 12 | from zipfile import ZipFile 13 | from zipfile import BadZipFile 14 | 15 | 16 | def extract_file(xlsm_zip, filename): 17 | # Extract a single file from an Excel xlsm macro file. 18 | data = xlsm_zip.read("xl/" + filename) 19 | 20 | # Write the data to a local file. 21 | file = open(filename, "wb") 22 | file.write(data) 23 | file.close() 24 | 25 | 26 | # The VBA project file and project signature file we want to extract. 27 | vba_filename = "vbaProject.bin" 28 | vba_signature_filename = "vbaProjectSignature.bin" 29 | 30 | # Get the xlsm file name from the commandline. 31 | if len(sys.argv) > 1: 32 | xlsm_file = sys.argv[1] 33 | else: 34 | print( 35 | "\nUtility to extract a vbaProject.bin binary from an Excel 2007+ " 36 | "xlsm macro file for insertion into an XlsxWriter file.\n" 37 | "If the macros are digitally signed, extracts also a vbaProjectSignature.bin " 38 | "file.\n" 39 | "\n" 40 | "See: https://xlsxwriter.readthedocs.io/working_with_macros.html\n" 41 | "\n" 42 | "Usage: vba_extract file.xlsm\n" 43 | ) 44 | exit() 45 | 46 | try: 47 | # Open the Excel xlsm file as a zip file. 48 | xlsm_zip = ZipFile(xlsm_file, "r") 49 | 50 | # Read the xl/vbaProject.bin file. 51 | extract_file(xlsm_zip, vba_filename) 52 | print("Extracted: %s" % vba_filename) 53 | 54 | if "xl/" + vba_signature_filename in xlsm_zip.namelist(): 55 | extract_file(xlsm_zip, vba_signature_filename) 56 | print("Extracted: %s" % vba_signature_filename) 57 | 58 | 59 | except IOError as e: 60 | print("File error: %s" % str(e)) 61 | exit() 62 | 63 | except KeyError as e: 64 | # Usually when there isn't a xl/vbaProject.bin member in the file. 65 | print("File error: %s" % str(e)) 66 | print("File may not be an Excel xlsm macro file: '%s'" % xlsm_file) 67 | exit() 68 | 69 | except BadZipFile as e: 70 | # Usually if the file is an xls file and not an xlsm file. 71 | print("File error: %s: '%s'" % (str(e), xlsm_file)) 72 | print("File may not be an Excel xlsm macro file.") 73 | exit() 74 | 75 | except Exception as e: 76 | # Catch any other exceptions. 77 | print("File error: %s" % str(e)) 78 | exit() 79 | -------------------------------------------------------------------------------- /auto_pptx/pyvenv.cfg: -------------------------------------------------------------------------------- 1 | home = C:\Users\gaelp\anaconda3\envs\projet 2 | include-system-site-packages = false 3 | version = 3.9.16 4 | -------------------------------------------------------------------------------- /auto_pptx/share/jupyter/kernels/python3/kernel.json: -------------------------------------------------------------------------------- 1 | { 2 | "argv": [ 3 | "python", 4 | "-m", 5 | "ipykernel_launcher", 6 | "-f", 7 | "{connection_file}" 8 | ], 9 | "display_name": "Python 3 (ipykernel)", 10 | "language": "python", 11 | "metadata": { 12 | "debugger": true 13 | } 14 | } -------------------------------------------------------------------------------- /auto_pptx/share/jupyter/kernels/python3/logo-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/share/jupyter/kernels/python3/logo-32x32.png -------------------------------------------------------------------------------- /auto_pptx/share/jupyter/kernels/python3/logo-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/auto_pptx/share/jupyter/kernels/python3/logo-64x64.png -------------------------------------------------------------------------------- /auto_pptx/share/jupyter/kernels/python3/logo-svg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 25 | 26 | 28 | image/svg+xml 29 | 31 | 32 | 33 | 34 | 61 | 63 | 65 | 69 | 73 | 74 | 76 | 80 | 84 | 85 | 87 | 91 | 95 | 96 | 98 | 102 | 106 | 107 | 109 | 113 | 117 | 118 | 120 | 124 | 128 | 129 | 138 | 147 | 157 | 167 | 177 | 187 | 197 | 207 | 218 | 228 | 238 | 249 | 250 | 254 | 258 | 265 | 266 | -------------------------------------------------------------------------------- /auto_pptx/share/man/man1/ipython.1: -------------------------------------------------------------------------------- 1 | .\" Hey, EMACS: -*- nroff -*- 2 | .\" First parameter, NAME, should be all caps 3 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection 4 | .\" other parameters are allowed: see man(7), man(1) 5 | .TH IPYTHON 1 "July 15, 2011" 6 | .\" Please adjust this date whenever revising the manpage. 7 | .\" 8 | .\" Some roff macros, for reference: 9 | .\" .nh disable hyphenation 10 | .\" .hy enable hyphenation 11 | .\" .ad l left justify 12 | .\" .ad b justify to both left and right margins 13 | .\" .nf disable filling 14 | .\" .fi enable filling 15 | .\" .br insert line break 16 | .\" .sp insert n+1 empty lines 17 | .\" for manpage-specific macros, see man(7) and groff_man(7) 18 | .\" .SH section heading 19 | .\" .SS secondary section heading 20 | .\" 21 | .\" 22 | .\" To preview this page as plain text: nroff -man ipython.1 23 | .\" 24 | .SH NAME 25 | ipython \- Tools for Interactive Computing in Python. 26 | .SH SYNOPSIS 27 | .B ipython 28 | .RI [ options ] " files" ... 29 | 30 | .B ipython subcommand 31 | .RI [ options ] ... 32 | 33 | .SH DESCRIPTION 34 | An interactive Python shell with automatic history (input and output), dynamic 35 | object introspection, easier configuration, command completion, access to the 36 | system shell, integration with numerical and scientific computing tools, 37 | web notebook, Qt console, and more. 38 | 39 | For more information on how to use IPython, see 'ipython \-\-help', 40 | or 'ipython \-\-help\-all' for all available command\(hyline options. 41 | 42 | .SH "ENVIRONMENT VARIABLES" 43 | .sp 44 | .PP 45 | \fIIPYTHONDIR\fR 46 | .RS 4 47 | This is the location where IPython stores all its configuration files. The default 48 | is $HOME/.ipython if IPYTHONDIR is not defined. 49 | 50 | You can see the computed value of IPYTHONDIR with `ipython locate`. 51 | 52 | .SH FILES 53 | 54 | IPython uses various configuration files stored in profiles within IPYTHONDIR. 55 | To generate the default configuration files and start configuring IPython, 56 | do 'ipython profile create', and edit '*_config.py' files located in 57 | IPYTHONDIR/profile_default. 58 | 59 | .SH AUTHORS 60 | IPython is written by the IPython Development Team . 61 | -------------------------------------------------------------------------------- /auto_pptx/share/man/man1/ttx.1: -------------------------------------------------------------------------------- 1 | .Dd May 18, 2004 2 | .\" ttx is not specific to any OS, but contrary to what groff_mdoc(7) 3 | .\" seems to imply, entirely omitting the .Os macro causes 'BSD' to 4 | .\" be used, so I give a zero-width space as its argument. 5 | .Os \& 6 | .\" The "FontTools Manual" argument apparently has no effect in 7 | .\" groff 1.18.1. I think it is a bug in the -mdoc groff package. 8 | .Dt TTX 1 "FontTools Manual" 9 | .Sh NAME 10 | .Nm ttx 11 | .Nd tool for manipulating TrueType and OpenType fonts 12 | .Sh SYNOPSIS 13 | .Nm 14 | .Bk 15 | .Op Ar option ... 16 | .Ek 17 | .Bk 18 | .Ar file ... 19 | .Ek 20 | .Sh DESCRIPTION 21 | .Nm 22 | is a tool for manipulating TrueType and OpenType fonts. It can convert 23 | TrueType and OpenType fonts to and from an 24 | .Tn XML Ns -based format called 25 | .Tn TTX . 26 | .Tn TTX 27 | files have a 28 | .Ql .ttx 29 | extension. 30 | .Pp 31 | For each 32 | .Ar file 33 | argument it is given, 34 | .Nm 35 | detects whether it is a 36 | .Ql .ttf , 37 | .Ql .otf 38 | or 39 | .Ql .ttx 40 | file and acts accordingly: if it is a 41 | .Ql .ttf 42 | or 43 | .Ql .otf 44 | file, it generates a 45 | .Ql .ttx 46 | file; if it is a 47 | .Ql .ttx 48 | file, it generates a 49 | .Ql .ttf 50 | or 51 | .Ql .otf 52 | file. 53 | .Pp 54 | By default, every output file is created in the same directory as the 55 | corresponding input file and with the same name except for the 56 | extension, which is substituted appropriately. 57 | .Nm 58 | never overwrites existing files; if necessary, it appends a suffix to 59 | the output file name before the extension, as in 60 | .Pa Arial#1.ttf . 61 | .Ss "General options" 62 | .Bl -tag -width ".Fl t Ar table" 63 | .It Fl h 64 | Display usage information. 65 | .It Fl d Ar dir 66 | Write the output files to directory 67 | .Ar dir 68 | instead of writing every output file to the same directory as the 69 | corresponding input file. 70 | .It Fl o Ar file 71 | Write the output to 72 | .Ar file 73 | instead of writing it to the same directory as the 74 | corresponding input file. 75 | .It Fl v 76 | Be verbose. Write more messages to the standard output describing what 77 | is being done. 78 | .It Fl a 79 | Allow virtual glyphs ID's on compile or decompile. 80 | .El 81 | .Ss "Dump options" 82 | The following options control the process of dumping font files 83 | (TrueType or OpenType) to 84 | .Tn TTX 85 | files. 86 | .Bl -tag -width ".Fl t Ar table" 87 | .It Fl l 88 | List table information. Instead of dumping the font to a 89 | .Tn TTX 90 | file, display minimal information about each table. 91 | .It Fl t Ar table 92 | Dump table 93 | .Ar table . 94 | This option may be given multiple times to dump several tables at 95 | once. When not specified, all tables are dumped. 96 | .It Fl x Ar table 97 | Exclude table 98 | .Ar table 99 | from the list of tables to dump. This option may be given multiple 100 | times to exclude several tables from the dump. The 101 | .Fl t 102 | and 103 | .Fl x 104 | options are mutually exclusive. 105 | .It Fl s 106 | Split tables. Dump each table to a separate 107 | .Tn TTX 108 | file and write (under the name that would have been used for the output 109 | file if the 110 | .Fl s 111 | option had not been given) one small 112 | .Tn TTX 113 | file containing references to the individual table dump files. This 114 | file can be used as input to 115 | .Nm 116 | as long as the referenced files can be found in the same directory. 117 | .It Fl i 118 | .\" XXX: I suppose OpenType programs (exist and) are also affected. 119 | Don't disassemble TrueType instructions. When this option is specified, 120 | all TrueType programs (glyph programs, the font program and the 121 | pre-program) are written to the 122 | .Tn TTX 123 | file as hexadecimal data instead of 124 | assembly. This saves some time and results in smaller 125 | .Tn TTX 126 | files. 127 | .It Fl y Ar n 128 | When decompiling a TrueType Collection (TTC) file, 129 | decompile font number 130 | .Ar n , 131 | starting from 0. 132 | .El 133 | .Ss "Compilation options" 134 | The following options control the process of compiling 135 | .Tn TTX 136 | files into font files (TrueType or OpenType): 137 | .Bl -tag -width ".Fl t Ar table" 138 | .It Fl m Ar fontfile 139 | Merge the input 140 | .Tn TTX 141 | file 142 | .Ar file 143 | with 144 | .Ar fontfile . 145 | No more than one 146 | .Ar file 147 | argument can be specified when this option is used. 148 | .It Fl b 149 | Don't recalculate glyph bounding boxes. Use the values in the 150 | .Tn TTX 151 | file as is. 152 | .El 153 | .Sh "THE TTX FILE FORMAT" 154 | You can find some information about the 155 | .Tn TTX 156 | file format in 157 | .Pa documentation.html . 158 | In particular, you will find in that file the list of tables understood by 159 | .Nm 160 | and the relations between TrueType GlyphIDs and the glyph names used in 161 | .Tn TTX 162 | files. 163 | .Sh EXAMPLES 164 | In the following examples, all files are read from and written to the 165 | current directory. Additionally, the name given for the output file 166 | assumes in every case that it did not exist before 167 | .Nm 168 | was invoked. 169 | .Pp 170 | Dump the TrueType font contained in 171 | .Pa FreeSans.ttf 172 | to 173 | .Pa FreeSans.ttx : 174 | .Pp 175 | .Dl ttx FreeSans.ttf 176 | .Pp 177 | Compile 178 | .Pa MyFont.ttx 179 | into a TrueType or OpenType font file: 180 | .Pp 181 | .Dl ttx MyFont.ttx 182 | .Pp 183 | List the tables in 184 | .Pa FreeSans.ttf 185 | along with some information: 186 | .Pp 187 | .Dl ttx -l FreeSans.ttf 188 | .Pp 189 | Dump the 190 | .Sq cmap 191 | table from 192 | .Pa FreeSans.ttf 193 | to 194 | .Pa FreeSans.ttx : 195 | .Pp 196 | .Dl ttx -t cmap FreeSans.ttf 197 | .Sh NOTES 198 | On MS\-Windows and MacOS, 199 | .Nm 200 | is available as a graphical application to which files can be dropped. 201 | .Sh SEE ALSO 202 | .Pa documentation.html 203 | .Pp 204 | .Xr fontforge 1 , 205 | .Xr ftinfo 1 , 206 | .Xr gfontview 1 , 207 | .Xr xmbdfed 1 , 208 | .Xr Font::TTF 3pm 209 | .Sh AUTHORS 210 | .Nm 211 | was written by 212 | .An -nosplit 213 | .An "Just van Rossum" Aq just@letterror.com . 214 | .Pp 215 | This manual page was written by 216 | .An "Florent Rougon" Aq f.rougon@free.fr 217 | for the Debian GNU/Linux system based on the existing FontTools 218 | documentation. It may be freely used, modified and distributed without 219 | restrictions. 220 | .\" For Emacs: 221 | .\" Local Variables: 222 | .\" fill-column: 72 223 | .\" sentence-end: "[.?!][]\"')}]*\\($\\| $\\| \\| \\)[ \n]*" 224 | .\" sentence-end-double-space: t 225 | .\" End: -------------------------------------------------------------------------------- /automate_presentation.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "text/html": [ 11 | "
\n", 12 | "\n", 25 | "\n", 26 | " \n", 27 | " \n", 28 | " \n", 29 | " \n", 30 | " \n", 31 | " \n", 32 | " \n", 33 | " \n", 34 | " \n", 35 | " \n", 36 | " \n", 37 | " \n", 38 | " \n", 39 | " \n", 40 | " \n", 41 | " \n", 42 | " \n", 43 | " \n", 44 | " \n", 45 | " \n", 46 | " \n", 47 | " \n", 48 | " \n", 49 | " \n", 50 | " \n", 51 | " \n", 52 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | "
show_idtypetitledirectorcastcountrydate_addedrelease_yearratingdurationlisted_indescription
0s1MovieDick Johnson Is DeadKirsten JohnsonNaNUnited StatesSeptember 25, 20212020PG-1390 minDocumentariesAs her father nears the end of his life, filmm...
1s2TV ShowBlood & WaterNaNAma Qamata, Khosi Ngema, Gail Mabalane, Thaban...South AfricaSeptember 24, 20212021TV-MA2 SeasonsInternational TV Shows, TV Dramas, TV MysteriesAfter crossing paths at a party, a Cape Town t...
2s3TV ShowGanglandsJulien LeclercqSami Bouajila, Tracy Gotoas, Samuel Jouy, Nabi...NaNSeptember 24, 20212021TV-MA1 SeasonCrime TV Shows, International TV Shows, TV Act...To protect his family from a powerful drug lor...
3s4TV ShowJailbirds New OrleansNaNNaNNaNSeptember 24, 20212021TV-MA1 SeasonDocuseries, Reality TVFeuds, flirtations and toilet talk go down amo...
4s5TV ShowKota FactoryNaNMayur More, Jitendra Kumar, Ranjan Raj, Alam K...IndiaSeptember 24, 20212021TV-MA2 SeasonsInternational TV Shows, Romantic TV Shows, TV ...In a city of coaching centers known to train I...
\n", 121 | "
" 122 | ], 123 | "text/plain": [ 124 | " show_id type title director \\\n", 125 | "0 s1 Movie Dick Johnson Is Dead Kirsten Johnson \n", 126 | "1 s2 TV Show Blood & Water NaN \n", 127 | "2 s3 TV Show Ganglands Julien Leclercq \n", 128 | "3 s4 TV Show Jailbirds New Orleans NaN \n", 129 | "4 s5 TV Show Kota Factory NaN \n", 130 | "\n", 131 | " cast country \\\n", 132 | "0 NaN United States \n", 133 | "1 Ama Qamata, Khosi Ngema, Gail Mabalane, Thaban... South Africa \n", 134 | "2 Sami Bouajila, Tracy Gotoas, Samuel Jouy, Nabi... NaN \n", 135 | "3 NaN NaN \n", 136 | "4 Mayur More, Jitendra Kumar, Ranjan Raj, Alam K... India \n", 137 | "\n", 138 | " date_added release_year rating duration \\\n", 139 | "0 September 25, 2021 2020 PG-13 90 min \n", 140 | "1 September 24, 2021 2021 TV-MA 2 Seasons \n", 141 | "2 September 24, 2021 2021 TV-MA 1 Season \n", 142 | "3 September 24, 2021 2021 TV-MA 1 Season \n", 143 | "4 September 24, 2021 2021 TV-MA 2 Seasons \n", 144 | "\n", 145 | " listed_in \\\n", 146 | "0 Documentaries \n", 147 | "1 International TV Shows, TV Dramas, TV Mysteries \n", 148 | "2 Crime TV Shows, International TV Shows, TV Act... \n", 149 | "3 Docuseries, Reality TV \n", 150 | "4 International TV Shows, Romantic TV Shows, TV ... \n", 151 | "\n", 152 | " description \n", 153 | "0 As her father nears the end of his life, filmm... \n", 154 | "1 After crossing paths at a party, a Cape Town t... \n", 155 | "2 To protect his family from a powerful drug lor... \n", 156 | "3 Feuds, flirtations and toilet talk go down amo... \n", 157 | "4 In a city of coaching centers known to train I... " 158 | ] 159 | }, 160 | "execution_count": 1, 161 | "metadata": {}, 162 | "output_type": "execute_result" 163 | } 164 | ], 165 | "source": [ 166 | "import pandas as pd\n", 167 | "import matplotlib.pyplot as plt\n", 168 | "import seaborn as sns\n", 169 | "from pptx import Presentation\n", 170 | "from pptx.util import Inches, Pt\n", 171 | "from pptx.dml.color import RGBColor\n", 172 | "\n", 173 | "# Load the dataset\n", 174 | "df = pd.read_csv('./data/netflix_titles.csv')\n", 175 | "df.head()" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 9, 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "# Express but not realistic cleaning\n", 185 | "df = df.dropna()\n", 186 | "\n", 187 | "# Convert duration to minutes for movies\n", 188 | "df['duration_min'] = df[df['type'] == 'Movie']['duration'].str.extract('(\\d+)').astype(float)" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 10, 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [ 197 | "# Count of movies vs. TV shows\n", 198 | "content_types = df['type'].value_counts()\n", 199 | "\n", 200 | "# Top 10 countries producing content\n", 201 | "top_countries = df['country'].value_counts().nlargest(10)\n", 202 | "\n", 203 | "# Content added over the years\n", 204 | "# add arg format='mixed' to correct an error in the dataset\n", 205 | "df['year_added'] = pd.to_datetime(df['date_added'], format='mixed').dt.year\n", 206 | "content_by_year = df['year_added'].value_counts().sort_index()\n", 207 | "\n", 208 | "# Top 10 genres\n", 209 | "df['genre'] = df['listed_in'].str.split(',').str[0]\n", 210 | "top_genres = df['genre'].value_counts().nlargest(10)" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": 11, 216 | "metadata": {}, 217 | "outputs": [], 218 | "source": [ 219 | "# Set the Seaborn style\n", 220 | "sns.set(style=\"whitegrid\")\n", 221 | "\n", 222 | "# Function to create and save a chart\n", 223 | "def create_chart(data, title, filename, kind='bar', figsize=(10, 6)):\n", 224 | " \"\"\"\n", 225 | " Create a chart based on the input data.\n", 226 | "\n", 227 | " Parameters:\n", 228 | " data (list): A list of data points to be plotted on the chart.\n", 229 | "\n", 230 | " Returns:\n", 231 | " None\n", 232 | " \"\"\"\n", 233 | " plt.figure(figsize=figsize)\n", 234 | " if kind == 'bar':\n", 235 | " ax = sns.barplot(x=data.index, y=data.values)\n", 236 | " elif kind == 'line':\n", 237 | " ax = sns.lineplot(x=data.index, y=data.values)\n", 238 | " \n", 239 | " ax.set_title(title, fontsize=16)\n", 240 | " ax.set_xlabel('')\n", 241 | " ax.tick_params(axis='x', rotation=45)\n", 242 | " plt.tight_layout()\n", 243 | " plt.savefig('./img/slides/'+filename)\n", 244 | " plt.close()\n", 245 | "\n", 246 | "# Create charts\n", 247 | "create_chart(content_types, 'Movies vs. TV Shows', 'content_types.png')\n", 248 | "create_chart(top_countries, 'Top 10 Countries Producing Netflix Content', 'top_countries.png')\n", 249 | "create_chart(content_by_year, 'Content Added by Year', 'content_by_year.png', kind='line')\n", 250 | "create_chart(top_genres, 'Top 10 Genres on Netflix', 'top_genres.png')" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": 14, 256 | "metadata": {}, 257 | "outputs": [], 258 | "source": [ 259 | "# Distribution of movie durations\n", 260 | "plt.figure(figsize=(10, 6))\n", 261 | "sns.histplot(df[df['type'] == 'Movie']['duration'].str.extract('(\\d+)').astype(int), kde=True)\n", 262 | "plt.title('Distribution of Movie Durations')\n", 263 | "plt.xlabel('Duration (minutes)')\n", 264 | "plt.savefig('./img/slides/movie_duration_dist.png')\n", 265 | "plt.close()\n", 266 | "\n", 267 | "# Relationship between release year and duration for movies\n", 268 | "plt.figure(figsize=(10, 6))\n", 269 | "sns.scatterplot(data=df[df['type'] == 'Movie'], \n", 270 | " x='release_year', \n", 271 | " y='duration_min')\n", 272 | "plt.title('Movie Duration vs. Release Year')\n", 273 | "plt.xlabel('Release Year')\n", 274 | "plt.ylabel('Duration (minutes)')\n", 275 | "plt.savefig('./img/slides/duration_vs_year.png')\n", 276 | "plt.close()\n", 277 | "\n", 278 | "# Content ratings distribution\n", 279 | "plt.figure(figsize=(10, 6))\n", 280 | "sns.countplot(data=df, y='rating', order=df['rating'].value_counts().index)\n", 281 | "plt.title('Distribution of Content Ratings')\n", 282 | "plt.xlabel('Count')\n", 283 | "plt.ylabel('Rating')\n", 284 | "plt.savefig('./img/slides/rating_distribution.png')\n", 285 | "plt.close()" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": 16, 291 | "metadata": {}, 292 | "outputs": [ 293 | { 294 | "name": "stdout", 295 | "output_type": "stream", 296 | "text": [ 297 | "Presentation saved as Netflix_Content_Analysis.pptx\n" 298 | ] 299 | } 300 | ], 301 | "source": [ 302 | "# Create a new presentation\n", 303 | "prs = Presentation()\n", 304 | "\n", 305 | "# Define custom colors\n", 306 | "DARK_BLUE = RGBColor(0, 32, 96)\n", 307 | "LIGHT_BLUE = RGBColor(197, 217, 241)\n", 308 | "\n", 309 | "# Function to add a title slide\n", 310 | "def add_title_slide(title, subtitle):\n", 311 | " \"\"\"\n", 312 | " Add a title slide to a presentation.\n", 313 | "\n", 314 | " Parameters:\n", 315 | " title (str): The title of the slide.\n", 316 | " slide_content (str): The content of the slide.\n", 317 | "\n", 318 | " Returns:\n", 319 | " None\n", 320 | " \"\"\"\n", 321 | " slide_layout = prs.slide_layouts[0]\n", 322 | " slide = prs.slides.add_slide(slide_layout)\n", 323 | " slide.shapes.title.text = title\n", 324 | " slide.placeholders[1].text = subtitle\n", 325 | " \n", 326 | " # Customize title font\n", 327 | " title_shape = slide.shapes.title\n", 328 | " title_shape.text_frame.paragraphs[0].font.color.rgb = DARK_BLUE\n", 329 | " title_shape.text_frame.paragraphs[0].font.size = Pt(44)\n", 330 | "\n", 331 | "# Function to add a content slide with an image\n", 332 | "def add_content_slide(title, image_path):\n", 333 | " \"\"\"\n", 334 | " Add content to a slide.\n", 335 | "\n", 336 | " Parameters:\n", 337 | " content (str): The content to be added to the slide.\n", 338 | "\n", 339 | " Returns:\n", 340 | " None\n", 341 | " \"\"\"\n", 342 | " slide_layout = prs.slide_layouts[5]\n", 343 | " slide = prs.slides.add_slide(slide_layout)\n", 344 | " slide.shapes.title.text = title\n", 345 | " \n", 346 | " # Add image\n", 347 | " left = Inches(1)\n", 348 | " top = Inches(1.5)\n", 349 | " width = Inches(8)\n", 350 | " height = Inches(5.5)\n", 351 | " slide.shapes.add_picture(image_path, left, top, width, height)\n", 352 | " \n", 353 | " # Customize title font\n", 354 | " title_shape = slide.shapes.title\n", 355 | " title_shape.text_frame.paragraphs[0].font.color.rgb = DARK_BLUE\n", 356 | " title_shape.text_frame.paragraphs[0].font.size = Pt(32)\n", 357 | "\n", 358 | "# Add slides\n", 359 | "add_title_slide(\"Netflix Content Analysis\", \"Insights from the Netflix Movies and TV Shows Dataset\")\n", 360 | "add_content_slide(\"Movies vs. TV Shows\", \"./img/slides/content_types.png\")\n", 361 | "add_content_slide(\"Top 10 Countries Producing Netflix Content\", \"./img/slides/top_countries.png\")\n", 362 | "add_content_slide(\"Content Added by Year\", \"./img/slides/content_by_year.png\")\n", 363 | "add_content_slide(\"Top 10 Genres on Netflix\", \"./img/slides/top_genres.png\")\n", 364 | "add_content_slide(\"Distribution of Movie Durations\", \"./img/slides/movie_duration_dist.png\")\n", 365 | "add_content_slide(\"Movie Duration vs. Release Year\", \"./img/slides/duration_vs_year.png\")\n", 366 | "add_content_slide(\"Distribution of Content Ratings\", \"./img/slides/rating_distribution.png\")\n", 367 | "\n", 368 | "# Save the presentation\n", 369 | "prs.save('Netflix_Content_Analysis.pptx')\n", 370 | "print(\"Presentation saved as Netflix_Content_Analysis.pptx\")" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": 17, 376 | "metadata": {}, 377 | "outputs": [ 378 | { 379 | "name": "stdout", 380 | "output_type": "stream", 381 | "text": [ 382 | "Themed presentation saved as Netflix_Content_Analysis_Themed.pptx\n" 383 | ] 384 | } 385 | ], 386 | "source": [ 387 | "# Customize the slide master\n", 388 | "slide_master = prs.slide_master\n", 389 | "\n", 390 | "# Set background color\n", 391 | "background = slide_master.background\n", 392 | "fill = background.fill\n", 393 | "fill.solid()\n", 394 | "fill.fore_color.rgb = LIGHT_BLUE\n", 395 | "\n", 396 | "# Customize title style\n", 397 | "title_style = slide_master.slide_layouts[0].placeholders[0].text_frame.paragraphs[0].font\n", 398 | "title_style.name = 'Arial'\n", 399 | "title_style.size = Pt(44)\n", 400 | "title_style.color.rgb = DARK_BLUE\n", 401 | "\n", 402 | "# Customize body text style\n", 403 | "body_style = slide_master.slide_layouts[1].placeholders[1].text_frame.paragraphs[0].font\n", 404 | "body_style.name = 'Arial'\n", 405 | "body_style.size = Pt(18)\n", 406 | "body_style.color.rgb = RGBColor(0, 0, 0)\n", 407 | "\n", 408 | "# Save the updated presentation\n", 409 | "prs.save('Netflix_Content_Analysis_Themed.pptx')\n", 410 | "print(\"Themed presentation saved as Netflix_Content_Analysis_Themed.pptx\")" 411 | ] 412 | } 413 | ], 414 | "metadata": { 415 | "kernelspec": { 416 | "display_name": "projet", 417 | "language": "python", 418 | "name": "python3" 419 | }, 420 | "language_info": { 421 | "codemirror_mode": { 422 | "name": "ipython", 423 | "version": 3 424 | }, 425 | "file_extension": ".py", 426 | "mimetype": "text/x-python", 427 | "name": "python", 428 | "nbconvert_exporter": "python", 429 | "pygments_lexer": "ipython3", 430 | "version": "3.9.16" 431 | } 432 | }, 433 | "nbformat": 4, 434 | "nbformat_minor": 2 435 | } 436 | -------------------------------------------------------------------------------- /automate_presentation.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import matplotlib.pyplot as plt 3 | import seaborn as sns 4 | from pptx import Presentation 5 | from pptx.util import Inches, Pt 6 | from pptx.dml.color import RGBColor 7 | from loguru import logger 8 | from pathlib import Path 9 | import sys 10 | 11 | 12 | # Define custom colors for presentation 13 | DARK_BLUE = RGBColor(0, 32, 96) 14 | LIGHT_BLUE = RGBColor(197, 217, 241) 15 | 16 | 17 | def load_and_clean_data(file_path: str) -> pd.DataFrame: 18 | """Loads the Netflix dataset, cleans it and preprocesses it. 19 | 20 | Args: 21 | file_path (str): The path to the CSV file. 22 | 23 | Returns: 24 | pd.DataFrame: The cleaned and processed DataFrame. 25 | """ 26 | try: 27 | df = pd.read_csv(file_path) 28 | # Drop null values 29 | df.dropna(inplace=True) 30 | 31 | # Convert duration to minutes for movies 32 | df['duration_min'] = df[df['type'] == 'Movie']['duration'].str.extract('(\d+)').astype(float) 33 | 34 | # Add 'genre' column 35 | df['genre'] = df['listed_in'].str.split(',').str[0] 36 | 37 | # Convert 'date_added' to datetime 38 | df['year_added'] = pd.to_datetime(df['date_added'], format='mixed').dt.year 39 | 40 | logger.info("Data loaded and cleaned successfully.") 41 | return df 42 | except FileNotFoundError as e: 43 | logger.error(f"Error loading data: {e}") 44 | sys.exit(1) # Exit with error code 45 | 46 | def create_chart(data: pd.Series, title: str, filename: str, kind: str = 'bar', figsize: tuple = (10, 6)) -> None: 47 | """Creates and saves a chart. 48 | 49 | Args: 50 | data (pd.Series): The data to plot. 51 | title (str): The title of the chart. 52 | filename (str): The name of the file to save the chart as. 53 | kind (str, optional): The type of chart ('bar' or 'line'). Defaults to 'bar'. 54 | figsize (tuple, optional): The size of the figure. Defaults to (10, 6). 55 | """ 56 | plt.figure(figsize=figsize) 57 | sns.set_style("whitegrid") 58 | 59 | if kind == 'bar': 60 | sns.barplot(x=data.index, y=data.values) 61 | elif kind == 'line': 62 | sns.lineplot(x=data.index, y=data.values) 63 | elif kind == 'hist': 64 | sns.histplot(data, kde=True) 65 | elif kind == 'scatter': 66 | sns.scatterplot(data=data, x='release_year', y='duration_min') 67 | elif kind == 'count': 68 | sns.countplot(data=data, y='rating', order=data['rating'].value_counts().index) 69 | 70 | plt.title(title, fontsize=16) 71 | plt.xlabel('') # Remove default x-axis label 72 | plt.xticks(rotation=45) 73 | plt.tight_layout() 74 | 75 | img_dir = Path("img/slides") 76 | img_dir.mkdir(parents=True, exist_ok=True) 77 | 78 | plt.savefig(img_dir / filename) 79 | logger.info(f"Saved chart: {filename}") 80 | plt.close() 81 | 82 | 83 | def add_title_slide(prs: Presentation, title: str, subtitle: str) -> None: 84 | """Adds a title slide to the presentation. 85 | 86 | Args: 87 | prs (Presentation): The presentation object. 88 | title (str): The title of the slide. 89 | subtitle (str): The subtitle of the slide. 90 | """ 91 | slide = prs.slides.add_slide(prs.slide_layouts[0]) 92 | slide.shapes.title.text = title 93 | slide.placeholders[1].text = subtitle 94 | title_shape = slide.shapes.title 95 | title_shape.text_frame.paragraphs[0].font.color.rgb = DARK_BLUE 96 | title_shape.text_frame.paragraphs[0].font.size = Pt(44) 97 | 98 | 99 | def add_content_slide(prs: Presentation, title: str, image_path: str) -> None: 100 | """Adds a content slide with an image to the presentation. 101 | 102 | Args: 103 | prs (Presentation): The presentation object. 104 | title (str): The title of the slide. 105 | image_path (str): The path to the image file. 106 | """ 107 | 108 | slide = prs.slides.add_slide(prs.slide_layouts[5]) 109 | slide.shapes.title.text = title 110 | 111 | left = Inches(1) 112 | top = Inches(1.5) 113 | width = Inches(8) 114 | height = Inches(5.5) 115 | slide.shapes.add_picture(image_path, left, top, width, height) 116 | 117 | # Customize title font 118 | title_shape = slide.shapes.title 119 | title_shape.text_frame.paragraphs[0].font.color.rgb = DARK_BLUE 120 | title_shape.text_frame.paragraphs[0].font.size = Pt(32) 121 | 122 | def customize_slide_master(prs: Presentation) -> None: 123 | """Customizes the slide master of the presentation. 124 | 125 | Args: 126 | prs (Presentation): The presentation object. 127 | """ 128 | slide_master = prs.slide_master 129 | background = slide_master.background 130 | fill = background.fill 131 | fill.solid() 132 | fill.fore_color.rgb = LIGHT_BLUE 133 | 134 | title_style = slide_master.slide_layouts[0].placeholders[0].text_frame.paragraphs[0].font 135 | title_style.name = 'Arial' 136 | title_style.size = Pt(44) 137 | title_style.color.rgb = DARK_BLUE 138 | 139 | body_style = slide_master.slide_layouts[1].placeholders[1].text_frame.paragraphs[0].font 140 | body_style.name = 'Arial' 141 | body_style.size = Pt(18) 142 | body_style.color.rgb = RGBColor(0, 0, 0) 143 | 144 | 145 | 146 | def main(): 147 | logger.info("Starting Netflix content analysis...") 148 | 149 | # Load and clean data 150 | df = load_and_clean_data('./data/netflix_titles.csv') 151 | 152 | # Generate Charts 153 | create_chart(df['type'].value_counts(), 'Movies vs. TV Shows', 'content_types.png') 154 | create_chart(df['country'].value_counts().nlargest(10), 'Top 10 Countries Producing Netflix Content', 'top_countries.png') 155 | create_chart(df['year_added'].value_counts().sort_index(), 'Content Added by Year', 'content_by_year.png', kind='line') 156 | create_chart(df['genre'].value_counts().nlargest(10), 'Top 10 Genres on Netflix', 'top_genres.png') 157 | 158 | # Movie-specific charts 159 | create_chart(df[df['type'] == 'Movie']['duration_min'], 'Distribution of Movie Durations', 'movie_duration_dist.png', kind='hist') 160 | create_chart(df[df['type'] == 'Movie'], 'Movie Duration vs. Release Year', 'duration_vs_year.png', kind='scatter') 161 | create_chart(df, 'Distribution of Content Ratings', 'rating_distribution.png', kind='count') 162 | 163 | 164 | # Create Presentation 165 | prs = Presentation() 166 | add_title_slide(prs, "Netflix Content Analysis", "Insights from the Netflix Movies and TV Shows Dataset") 167 | 168 | # Add slides with images 169 | img_dir = Path("img/slides") 170 | for img_path in img_dir.iterdir(): 171 | add_content_slide(prs, img_path.stem.replace('_', ' ').title(), str(img_path)) 172 | 173 | # Save the presentation 174 | prs.save('Netflix_Content_Analysis.pptx') 175 | logger.info("Presentation saved as Netflix_Content_Analysis.pptx") 176 | 177 | # Customize and Save Themed Presentation 178 | customize_slide_master(prs) 179 | prs.save('Netflix_Content_Analysis_Themed.pptx') 180 | logger.info("Themed presentation saved as Netflix_Content_Analysis_Themed.pptx") 181 | 182 | 183 | if __name__ == "__main__": 184 | main() 185 | 186 | -------------------------------------------------------------------------------- /img/article/img_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/img/article/img_0.png -------------------------------------------------------------------------------- /img/article/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/img/article/img_1.png -------------------------------------------------------------------------------- /img/article/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/img/article/img_2.png -------------------------------------------------------------------------------- /img/slides/content_by_year.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/img/slides/content_by_year.png -------------------------------------------------------------------------------- /img/slides/content_types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/img/slides/content_types.png -------------------------------------------------------------------------------- /img/slides/duration_vs_year.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/img/slides/duration_vs_year.png -------------------------------------------------------------------------------- /img/slides/movie_duration_dist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/img/slides/movie_duration_dist.png -------------------------------------------------------------------------------- /img/slides/rating_distribution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/img/slides/rating_distribution.png -------------------------------------------------------------------------------- /img/slides/top_countries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/img/slides/top_countries.png -------------------------------------------------------------------------------- /img/slides/top_genres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gpenessot/Automate-Presentation-Creation-with-Python/99c23826593fa792d6343e668b966dc11862f2c5/img/slides/top_genres.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | seaborn 3 | matplotlib 4 | python-pptx 5 | loguru --------------------------------------------------------------------------------