From 65d800685ab1e74c2c3ade92990410357b0ee409 Mon Sep 17 00:00:00 2001 From: GeorgBrantegger Date: Mon, 16 Mar 2026 12:07:57 +0100 Subject: [PATCH] initial script for importing trainer contacts --- .gitignore | 177 +++++++++++ Nextcloud Contact Infos/convert_trainer.ipynb | 220 ++++++++++++++ package_loader.py | 286 ++++++++++++++++++ 3 files changed, 683 insertions(+) create mode 100644 .gitignore create mode 100644 Nextcloud Contact Infos/convert_trainer.ipynb create mode 100644 package_loader.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e66547a --- /dev/null +++ b/.gitignore @@ -0,0 +1,177 @@ +############################################ +# project specific gitignore entries +.venv +*.txt +*temp/ +######################################################################################## + # project specific gitignore entries + + ############################################ + ############################################ + # standard gitignore entries + ############################################ + # Byte-compiled / optimized / DLL files + __pycache__/ + *.py[codz] + *$py.class + + # C extensions + *.so + + # Distribution / packaging + .Python + build/ + develop-eggs/ + dist/ + downloads/ + eggs/ + .eggs/ + lib/ + lib64/ + parts/ + sdist/ + var/ + wheels/ + share/python-wheels/ + *.egg-info/ + .installed.cfg + *.egg + MANIFEST + + # PyInstaller + *.manifest + *.spec + + # Installer logs + pip-log.txt + pip-delete-this-directory.txt + + # Unit test / coverage reports + htmlcov/ + .tox/ + .nox/ + .coverage + .coverage.* + .cache + nosetests.xml + coverage.xml + *.cover + *.py.cover + .hypothesis/ + .pytest_cache/ + cover/ + + # Translations + *.mo + *.pot + + # Django stuff: + *.log + local_settings.py + db.sqlite3 + db.sqlite3-journal + + # Flask stuff: + instance/ + .webassets-cache + + # Scrapy stuff: + .scrapy + + # Sphinx documentation + docs/_build/ + + # PyBuilder + .pybuilder/ + target/ + + # Jupyter Notebook + .ipynb_checkpoints + + # IPython + profile_default/ + ipython_config.py + + # pyenv + # .python-version + + # pipenv + #Pipfile.lock + + # UV + #uv.lock + + # poetry + #poetry.lock + #poetry.toml + + # pdm + #pdm.lock + #pdm.toml + .pdm-python + .pdm-build/ + + # pixi + .pixi + + # PEP 582 + __pypackages__/ + + # Celery stuff + celerybeat-schedule + celerybeat.pid + + # SageMath parsed files + *.sage.py + + # Environments + .env + .envrc + .venv + env/ + venv/ + ENV/ + env.bak/ + venv.bak/ + + # Spyder project settings + .spyderproject + .spyproject + + # Rope project settings + .ropeproject + + # mkdocs documentation + /site + + # mypy + .mypy_cache/ + .dmypy.json + dmypy.json + + # Pyre type checker + .pyre/ + + # pytype static type analyzer + .pytype/ + + # Cython debug symbols + cython_debug/ + + # Abstra + .abstra/ + + # Ruff + .ruff_cache/ + + # PyPI config + .pypirc + + # Cursor + .cursorignore + .cursorindexingignore + + # Marimo + marimo/_static/ + marimo/_lsp/ + __marimo__/ \ No newline at end of file diff --git a/Nextcloud Contact Infos/convert_trainer.ipynb b/Nextcloud Contact Infos/convert_trainer.ipynb new file mode 100644 index 0000000..3d6ca52 --- /dev/null +++ b/Nextcloud Contact Infos/convert_trainer.ipynb @@ -0,0 +1,220 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "1293f184", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import os\n", + "import requests\n", + "import urllib\n", + "import io\n", + "import base64\n", + "\n", + "from matplotlib import pyplot as plt\n", + "import matplotlib.image as mpimg" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2946897b", + "metadata": {}, + "outputs": [], + "source": [ + "for file in os.listdir('temp'):\n", + " os.remove(os.path.join('temp',file))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb57d754", + "metadata": {}, + "outputs": [], + "source": [ + "user = \"Georg Brantegger\"\n", + "with open('app_pw.txt','r') as f:\n", + " app_pw = f.readline()\n", + "\n", + "\n", + "base_url_csv = \"https://nextcloud.karnelegger.eu/remote.php/webdav\"\n", + "folders = [\"Forms\",\"4 - Kontaktdatenerhebung Trainerteam\",\"Ergebnisse CSV\"]\n", + "folder_string = '/'.join([urllib.parse.quote(folder) for folder in folders])\n", + "filename_csv = \"Kontaktdatenerhebung Trainerteam (Antworten).csv\"\n", + "\n", + "url_csv = '/'.join([base_url_csv,folder_string,urllib.parse.quote(filename_csv)])\n", + "\n", + "\n", + "r = requests.get(url_csv, auth=(user, app_pw))\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a74d9129", + "metadata": {}, + "outputs": [], + "source": [ + "df = pd.read_csv(io.StringIO(r.text))\n", + "\n", + "df[['Nachname','Vorname']] = df.apply(lambda row: row['Nachname Vorname'].strip().split(' ',maxsplit=1),axis=1,result_type='expand')\n", + "df = df.copy()\n", + "df['Nachname'] = df['Nachname'].replace({'Anton': 'Pfurtscheller','David': 'Kobau'})\n", + "df['Vorname'] = df['Vorname'].replace({'Franz Pfurtscheller': 'Anton Franz', 'Kobau': 'David'})\n", + "df['Telefonnummer'] = df['Telefonnummer'].str.replace(\"'\",'').str.replace('06','+436',n=1)\n", + "df['Foto'] = df['Foto'].str.replace(' ','-')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc8568d6", + "metadata": {}, + "outputs": [], + "source": [ + "df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "df2c876c", + "metadata": {}, + "outputs": [], + "source": [ + "def get_photo_for_row(row):\n", + "\n", + " filename = row['Foto']\n", + "\n", + " found_photo = False\n", + " i = 0\n", + " while found_photo is False:\n", + " print(f\"trying folder {i}\")\n", + " base_url_photo = \"https://nextcloud.karnelegger.eu/remote.php/webdav\"\n", + " folders = [\"Forms\",\"4 - Kontaktdatenerhebung Trainerteam\",f\"{i}\",\"19 - Foto\"]\n", + " folder_string = '/'.join([urllib.parse.quote(folder) for folder in folders])\n", + " filename_photo = urllib.parse.quote(filename)\n", + "\n", + " url_photo = '/'.join([base_url_photo,folder_string,urllib.parse.quote(filename_photo)])\n", + "\n", + " r = requests.get(url_photo, auth=(user, app_pw))\n", + " if r.status_code != 200:\n", + " i+=1\n", + " if i > 100:\n", + " break\n", + " continue\n", + " else:\n", + " photo_bytes = r.content\n", + " found_photo = True\n", + " photo_b64 = base64.b64encode(photo_bytes).decode(\"utf-8\")\n", + " \n", + " i = base64.b64decode(photo_b64)\n", + " i = io.BytesIO(i)\n", + " i = mpimg.imread(i, format='JPG')\n", + "\n", + " plt.imshow(i, interpolation='nearest')\n", + " plt.show()\n", + "\n", + " return photo_b64\n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c9db8d6", + "metadata": {}, + "outputs": [], + "source": [ + "def convert_rows_to_vcf(row):\n", + " # Map CSV columns to vCard fields\n", + " first_name = row['Vorname']\n", + " last_name = row['Nachname']\n", + " phone = row['Telefonnummer']\n", + " org = 'Ruderverein Villach von 1881'\n", + " photo_b64 = get_photo_for_row(row)\n", + " \n", + "\n", + " lines = [\n", + " \"BEGIN:VCARD\",\n", + " \"VERSION:3.0\",\n", + " f\"FN:{first_name} {last_name}\",\n", + " f\"N:{last_name};{first_name};;;\",\n", + " f\"ORG:{org}\",\n", + " f\"TEL;TYPE=CELL:{phone}\"]\n", + "\n", + " if photo_b64:\n", + " lines.append(f\"PHOTO;ENCODING=b;TYPE=JPEG:{photo_b64}\")\n", + "\n", + " lines.append(\"END:VCARD\")\n", + "\n", + " vcard_content = \"\\n\".join(lines)\n", + "\n", + " # Save to file\n", + " filename = f\"{last_name}_{first_name}.vcf\".replace(\" \", \"_\")\n", + " with open(os.path.join('temp', filename), 'w') as f:\n", + " f.write(vcard_content)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61659f5b", + "metadata": {}, + "outputs": [], + "source": [ + "df.apply(convert_rows_to_vcf,axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37b2a51b", + "metadata": {}, + "outputs": [], + "source": [ + "base_url = \"https://nextcloud.karnelegger.eu/remote.php/dav/addressbooks/users/Georg%20Brantegger/rv-villach-trainer/\"\n", + "\n", + "for vcf_file in os.listdir('temp'):\n", + "\n", + " url = base_url + vcf_file\n", + "\n", + " with open(f'temp/{vcf_file}', \"rb\") as f:\n", + " r = requests.put(\n", + " url,\n", + " data=f,\n", + " auth=(user, app_pw),\n", + " headers={\"Content-Type\": \"text/vcard\"}\n", + " )\n", + "\n", + " print(vcf_file, r.status_code)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/package_loader.py b/package_loader.py new file mode 100644 index 0000000..c0869ad --- /dev/null +++ b/package_loader.py @@ -0,0 +1,286 @@ +import os +import sys +import subprocess + +# set the name of the virtual environment +# if you want to find the venv from another folder or subfolder: + # go to Settings "ctrl + ," and set "Python: Venv Path (not synched)" to the path where the venv lives +venv_name = ".venv" + +def get_list_of_packages_to_load(): + # define the packages that should be installed + packages = [] + packages.append('ipykernel') + packages.append('ipympl') + packages.append('numpy') + packages.append('pandas') + packages.append('pyarrow') + packages.append('fsspec') + # packages.append('openpyxl') + # packages.append('matplotlib') + # packages.append('pyqt5') + # packages.append('tirex-ts') + # packages.append('torch') + # packages.append('nbconvert') + # packages.append('pyinstaller') + # packages.append('oracledb') + # packages.append('sqlalchemy') + packages.append('requests') + # packages.append('requests_negotiate_sspi ') + # packages.append('ldap3') + # packages.append('mplcursors') + # packages.append('scipy') + # packages.append('geopandas') + # packages.append('geovoronoi') + # packages.append('ConfigParser') + + # packages.append('tensorflow') # tensorflow for machine learing + + print(packages) + return packages + +# region functions + +def create_venv(venv_name): + # Check if the venv already exists and if not create it + if os.path.isdir(venv_name): + print(f'Venv "{venv_name}" found') + else: + subprocess.check_call([sys.executable, '-m', 'venv', venv_name]) + print(f'Venv "{venv_name}" created') + +def get_path_of_venv_executable(venv_name): + # Get the path to the virtual environment + venv_path = os.path.join(os.getcwd(), venv_name) + + # Determine the correct pip executable path based on the OS + if sys.platform == "win32": + pip_executable = os.path.join(venv_path, 'Scripts', 'pip.exe') + else: + pip_executable = os.path.join(venv_path, 'bin', 'pip') + + return pip_executable + +def install_packages(pip_executable, packages_to_load): + # Function that installs all packages whose names are listed in the packages list + list_of_failed_packages = [] + + for package in packages_to_load: + try: + subprocess.check_call([pip_executable, 'install', package]) + except subprocess.CalledProcessError: + list_of_failed_packages.append(package) + + # Some prints for logging + print("###############################") + print("The following packages failed to install:") + for failed_package in list_of_failed_packages: + print(f"\t\t{failed_package}") + +def initialize_gitignore(venv_name=".venv"): + + STANDARD_GITIGNORE = r""" + ############################################ + # project specific gitignore entries + + ############################################ + ############################################ + # standard gitignore entries + ############################################ + # Byte-compiled / optimized / DLL files + __pycache__/ + *.py[codz] + *$py.class + + # C extensions + *.so + + # Distribution / packaging + .Python + build/ + develop-eggs/ + dist/ + downloads/ + eggs/ + .eggs/ + lib/ + lib64/ + parts/ + sdist/ + var/ + wheels/ + share/python-wheels/ + *.egg-info/ + .installed.cfg + *.egg + MANIFEST + + # PyInstaller + *.manifest + *.spec + + # Installer logs + pip-log.txt + pip-delete-this-directory.txt + + # Unit test / coverage reports + htmlcov/ + .tox/ + .nox/ + .coverage + .coverage.* + .cache + nosetests.xml + coverage.xml + *.cover + *.py.cover + .hypothesis/ + .pytest_cache/ + cover/ + + # Translations + *.mo + *.pot + + # Django stuff: + *.log + local_settings.py + db.sqlite3 + db.sqlite3-journal + + # Flask stuff: + instance/ + .webassets-cache + + # Scrapy stuff: + .scrapy + + # Sphinx documentation + docs/_build/ + + # PyBuilder + .pybuilder/ + target/ + + # Jupyter Notebook + .ipynb_checkpoints + + # IPython + profile_default/ + ipython_config.py + + # pyenv + # .python-version + + # pipenv + #Pipfile.lock + + # UV + #uv.lock + + # poetry + #poetry.lock + #poetry.toml + + # pdm + #pdm.lock + #pdm.toml + .pdm-python + .pdm-build/ + + # pixi + .pixi + + # PEP 582 + __pypackages__/ + + # Celery stuff + celerybeat-schedule + celerybeat.pid + + # SageMath parsed files + *.sage.py + + # Environments + .env + .envrc + .venv + env/ + venv/ + ENV/ + env.bak/ + venv.bak/ + + # Spyder project settings + .spyderproject + .spyproject + + # Rope project settings + .ropeproject + + # mkdocs documentation + /site + + # mypy + .mypy_cache/ + .dmypy.json + dmypy.json + + # Pyre type checker + .pyre/ + + # pytype static type analyzer + .pytype/ + + # Cython debug symbols + cython_debug/ + + # Abstra + .abstra/ + + # Ruff + .ruff_cache/ + + # PyPI config + .pypirc + + # Cursor + .cursorignore + .cursorindexingignore + + # Marimo + marimo/_static/ + marimo/_lsp/ + __marimo__/ + """.strip() + + project_section_start = ( + "############################################\n" + "# project specific gitignore entries\n" + f"{venv_name}\n\n" + "############################################" + ) + + # If .gitignore does not exist create full template + if not os.path.exists(".gitignore"): + with open(".gitignore", "w") as f: + f.write( + project_section_start + + STANDARD_GITIGNORE.split("############################################\n############################################")[0] + ) + return + +# Create the virtual environment with the given name (default = .venv) +create_venv(venv_name) + +# Get the path to the pip executable of the venv +pip_executable = get_path_of_venv_executable(venv_name) + +# Load the list of packages that should be loaded into the venv +packages = get_list_of_packages_to_load() + +# Install the packages +install_packages(pip_executable, packages_to_load=packages) + +# Make sure none of the contents of the venv folder are tracked in git +initialize_gitignore() \ No newline at end of file