diff --git a/.gitignore b/.gitignore index 6702cd2..ffb21e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,12 @@ -# Created by .ignore support plugin (hsz.mobi) # Project specific files config.ini invoices/ *.pdf *.xml +*.csv -### Python template -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - +# Python +__pycache__ # Distribution / packaging .Python env/ @@ -36,7 +29,6 @@ var/ # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest -*.spec # Installer logs pip-log.txt @@ -53,48 +45,19 @@ coverage.xml *,cover .hypothesis/ -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - # Flask stuff: instance/ .webassets-cache -# Scrapy stuff: -.scrapy # Sphinx documentation docs/_build/ -# PyBuilder -target/ - -# IPython Notebook -.ipynb_checkpoints # pyenv .python-version -# celery beat schedule file -celerybeat-schedule -# dotenv -.env - -# virtualenv -venv/ -ENV/ - -# Spyder project settings -.spyderproject - -# Rope project settings -.ropeproject ### VirtualEnv template # Virtualenv # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ @@ -107,51 +70,4 @@ ENV/ [Ss]cripts pyvenv.cfg .venv -pip-selfcheck.json -### JetBrains template -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff: -.idea/workspace.xml -.idea/tasks.xml -.idea/dictionaries -.idea/vcs.xml -.idea/jsLibraryMappings.xml - -# Sensitive or high-churn files: -.idea/dataSources.ids -.idea/dataSources.xml -.idea/dataSources.local.xml -.idea/sqlDataSources.xml -.idea/dynamic.xml -.idea/uiDesigner.xml - -# Gradle: -.idea/gradle.xml -.idea/libraries - -# Mongo Explorer plugin: -.idea/mongoSettings.xml - -.idea/ - -## File-based project format: -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties \ No newline at end of file +.idea/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4653a07 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "pynami"] + path = pynami + url = https://github.com/sscholz93/pynami.git diff --git a/accounting.py b/accounting.py old mode 100644 new mode 100755 index b0303ff..4aa8041 --- a/accounting.py +++ b/accounting.py @@ -61,7 +61,7 @@ def process(self): # e.g. Removes first half year entries if second half year shall be booked dpsg_members = self.download_invoices() nof_dpsg_members = dpsg_members.get_nof_unique_members() - + booking_value_dpsg = dpsg_members.get_value_booked_by_dpsg() print('Herunterladen aller aktiven Mitglieder aus der Nami...') result = self._nami.get_active_members() result_schnupper = self._nami.get_schnupper_members() @@ -243,14 +243,15 @@ def process(self): print("") tools.print_info('Verarbeitete Mandate: ' + str(overall)) tools.print_info('Benutzte Mandate: ' + str(used) + '/' + str(overall)) - tools.print_info('Gesamtsumme: ' + str(booking_value) + ' EUR') + tools.print_info(f'Buchungssumme DPSG: {booking_value_dpsg: .2f} €') + tools.print_info(f'Buchungssumme Stamm: {booking_value: .2f} €') print("") if not_used == 0: tools.print_info('Alle SEPA-Mandate wurden erfolgreich verwendet.') else: tools.print_error( 'Nicht alle SEPA-Mandate wurden verwendet. Nochmal die Mandate überprüfen und ggf. bereinigen.') - tools.print_info('-------------------------------------------------------------------------------') + tools.print_info('------------------------------------------------------------------------') print("") self.print_member_entry_this_year_as_schnupper(list_of_members_active_schnupper) @@ -269,7 +270,7 @@ def print_missing_mandate_members(self, members): for m in members: combinedName = m.vorname + ' ' + m.nachname tools.print_error('Mitgliedsnummer: ' + str(m.mitgliedsNummer) + ' Name: ' + combinedName) - tools.print_error('-----------------------------------------------------------------------------') + tools.print_error('------------------------------------------------------------------------') if len(members) != 0: tools.print_error('Bitte die fehlenden Mandate in VR Networld für die Mitglieder anlegen.') print("") @@ -279,19 +280,19 @@ def print_not_used_mandate(self, mandate: VRMandat): for m in mandate: combinedName = m.vorname + ' ' + m.nachname tools.print_error('Mandatsreferenz: ' + m.mandatsreferenz + ' Name: ' + combinedName) - tools.print_error('-------------------------------------------------------------------------------') + tools.print_error('------------------------------------------------------------------------') if len(mandate) != 0: tools.print_error('Bitte nochmal die DPSG Nami und VR-Networld überprüfen und die nicht verwendenten Mandate entfernen.') print("") def print_member_entry_this_year_as_schnupper(self, members): - tools.print_info('-------------- Aktive Schnuppermitglieder Jahr --------------') + tools.print_info('-------------------- Aktive Schnuppermitglieder Jahr -------------------') for m in members: combinedName = m.vorname + ' ' + m.nachname tools.print_info('Initiales Eintrittsdatum: ' + datetime.datetime.strftime(m.eintrittsdatum, self._config.get_datetime_format()) + ' Abrechenbares Eintrittsdatum: ' + datetime.datetime.strftime(m.correct_eintrittsdatum, self._config.get_datetime_format()) + ' Mitglied ' + combinedName) - tools.print_info('-------------------------------------------------------------') + tools.print_info('------------------------------------------------------------------------') print("") def print_member_entry_second_half(self, members): @@ -301,7 +302,7 @@ def print_member_entry_second_half(self, members): tools.print_info('Initiales Eintrittsdatum: ' + datetime.datetime.strftime(m.eintrittsdatum, self._config.get_datetime_format()) + ' Abrechenbares Eintrittsdatum: ' + datetime.datetime.strftime(m.correct_eintrittsdatum, self._config.get_datetime_format()) + ' Mitglied ' + combinedName) - tools.print_info('-----------------------------------------------------------------------------------------') + tools.print_info('------------------------------------------------------------------------') print("") @@ -312,7 +313,7 @@ def print_member_entry_new_year(self, members): tools.print_info('Initiales Eintrittsdatum: ' + datetime.datetime.strftime(m.eintrittsdatum, self._config.get_datetime_format()) + ' Abrechenbares Eintrittsdatum: ' + datetime.datetime.strftime(m.correct_eintrittsdatum, self._config.get_datetime_format()) + ' Mitglied ' + combinedName) - tools.print_info('------------------------------------------------------------------------------') + tools.print_info('------------------------------------------------------------------------') print("") def print_member_booked_by_dpsg_but_not_here(self, members): @@ -322,7 +323,7 @@ def print_member_booked_by_dpsg_but_not_here(self, members): tools.print_info('Rechnungsdatum: ' + datetime.datetime.strftime(m.datumVon, self._config.get_datetime_format()) + ' - ' + datetime.datetime.strftime(m.datumBis, self._config.get_datetime_format()) + ' Mitglied ' + combinedName) - tools.print_info('------------------------------------------------------------------------------------') + tools.print_info('------------------------------------------------------------------------') print("") def print_member_not_booked_by_dpsg(self, members): @@ -332,7 +333,7 @@ def print_member_not_booked_by_dpsg(self, members): tools.print_info('Initiales Eintrittsdatum: ' + datetime.datetime.strftime(m.eintrittsdatum, self._config.get_datetime_format()) + ' Abrechenbares Eintrittsdatum: ' + datetime.datetime.strftime(m.correct_eintrittsdatum, self._config.get_datetime_format()) + ' Mitglied ' + combinedName) - tools.print_info('------------------------------------------------------------------------------------') + tools.print_info('------------------------------------------------------------------------') print("") def download_invoices(self) -> PdfMemberList: diff --git a/build_executable_mac.sh b/build_executable_mac.sh old mode 100644 new mode 100755 index b7c3504..559cab8 --- a/build_executable_mac.sh +++ b/build_executable_mac.sh @@ -1 +1,5 @@ -pyinstaller --clean --onefile --windowed --name "Nami Beitragsrechner" --osx-bundle-identifier "com.nami-beitragsrechner" --icon="img/favicon.ico" --collect-data sv_ttk --collect-data pycountry --copy-metadata schwifty --hidden-import babel.numbers --add-data="venv/lib/python3.10/site-packages/schwifty/bank_registry/:bank_registry" --add-data="venv/lib/python3.10/site-packages/schwifty/iban_registry/:iban_registry" --add-data="img/dpsg_logo.png:img" --add-data="img/favicon.ico:img" main.py \ No newline at end of file + +bash init_venv.sh +source .venv/bin/activate +pyinstaller main.spec +deactivate \ No newline at end of file diff --git a/init_venv.sh b/init_venv.sh new file mode 100644 index 0000000..a6f7ada --- /dev/null +++ b/init_venv.sh @@ -0,0 +1,11 @@ + +echo "Creating venv" +python3 -m venv .venv +source .venv/bin/activate +pip install --upgrade pip +echo "Installing python packages from requirements.txt" +pip install -r requirements.txt +deactivate + +echo "Successfully created venv for nami-beitragsrechner" + diff --git a/main.spec b/main.spec new file mode 100755 index 0000000..33000ad --- /dev/null +++ b/main.spec @@ -0,0 +1,65 @@ +# -*- mode: python ; coding: utf-8 -*- +from PyInstaller.utils.hooks import collect_data_files +from PyInstaller.utils.hooks import copy_metadata +import platform + +python_version = platform.python_version() +last_dot = python_version.rfind('.') +python_version = python_version[0:last_dot] +datas = [('img/dpsg_logo.png', 'img'), ('img/favicon.ico', 'img')] +datas.append((f'.venv/lib/python{python_version}/site-packages/schwifty/bank_registry/', 'bank_registry')) +datas.append((f'.venv/lib/python{python_version}/site-packages/schwifty/iban_registry/', 'iban_registry')) +datas += collect_data_files('sv_ttk') +datas += collect_data_files('pycountry') +datas += collect_data_files('schwifty') +datas += copy_metadata('schwifty') + + +block_cipher = None + + +a = Analysis( + ['main.py'], + pathex=[], + binaries=[], + datas=datas, + hiddenimports=['babel.numbers'], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='Nami Beitragsrechner', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=['img/favicon.ico'], +) +app = BUNDLE( + exe, + name='Nami Beitragsrechner.app', + icon='img/favicon.ico', + bundle_identifier='com.nami-beitragsrechner', +) diff --git a/pdf_converter.py b/pdf_converter.py old mode 100644 new mode 100755 index 89c652b..5b67cab --- a/pdf_converter.py +++ b/pdf_converter.py @@ -45,6 +45,13 @@ def get_nof_unique_members(self): return len(unqiue_members) + def get_value_booked_by_dpsg(self) -> float: + overall_value = 0 + for m in self: + overall_value += m.beitrag + + return overall_value + class PdfConverter: @@ -59,15 +66,20 @@ def convert(pdfpath) -> list: for p in range(1, len(pdf.pages)): page = pdf.pages[p] lines = list(filter(None, page.extract_text(layout=True).splitlines())) - # Search for header line - i = 0 - while 'Mitgliedsnr. Name' not in lines[i]: - i = i+1 - lines = lines[i+1:-1] + # Strip leading and trailing spaces + lines = list(map(str.strip, lines)) + # Remove empty lines + lines = list(filter(None, lines)) + # Remove all lines which are not a member line + lines = [x for x in lines if 'Einzelnachweise:' not in x] + lines = [x for x in lines if 'Mitgliedsnr. Name' not in x] + lines = [x for x in lines if 'Rechnungsnr.:' not in x] overall_lines.extend(lines) for line in overall_lines: line = line.strip() + if line == '': + continue # Extract mglNo pos = line.find(' ') mglNo = int(line[:pos]) diff --git a/pynami b/pynami new file mode 160000 index 0000000..2c71168 --- /dev/null +++ b/pynami @@ -0,0 +1 @@ +Subproject commit 2c7116837fe3ee1eeb8c11f839dba764069cb08d diff --git a/requirements.txt b/requirements.txt old mode 100644 new mode 100755 index 926b660..b0896f1 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,6 @@ future Pygments docutils cffi -PySide6 Pillow pip toml @@ -18,5 +17,6 @@ Wand chardet pycparser setuptools -pynami -sepaxml \ No newline at end of file +./pynami +sepaxml +pyinstaller \ No newline at end of file