├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── publish_docs.yml ├── .gitignore ├── LICENSE ├── README.md ├── development ├── README.md ├── notebooks │ ├── 00_goofing_around │ │ ├── test1.py │ │ ├── test2.py │ │ └── test3.py │ ├── 01_perplexity │ │ ├── perplexity_as_metric.ipynb │ │ ├── text1.txt │ │ └── text2.txt │ ├── 02_dehyphen │ │ └── Untitled.ipynb │ ├── 03_transformers_exp │ │ ├── Pipfile │ │ ├── Pipfile.lock │ │ ├── Untitled-Copy1.ipynb │ │ ├── Untitled.ipynb │ │ ├── text1.txt │ │ └── text2.txt │ ├── 04_ulmfit_exp │ │ ├── Pipfile │ │ ├── Pipfile.lock │ │ └── Untitled.ipynb │ ├── 05_new_tries │ │ ├── Untitled-Copy1.ipynb │ │ ├── Untitled1.ipynb │ │ ├── clean.md │ │ ├── clean10.md │ │ ├── clean2.md │ │ ├── clean3.md │ │ ├── clean4.md │ │ ├── clean5.md │ │ ├── clean6.md │ │ ├── clean7.md │ │ ├── clean71.md │ │ ├── clean8.md │ │ ├── clean9.md │ │ ├── geo.ipynb │ │ └── textsim.ipynb │ ├── 06_next_tries │ │ ├── Untitled-Copy1.ipynb │ │ ├── Untitled-Copy2.ipynb │ │ └── Untitled.ipynb │ ├── 07_pd3f_api │ │ └── Untitled.ipynb │ └── 08_debugging │ │ ├── fix_none_bug.ipynb │ │ ├── idx_page_bug-Copy1.ipynb │ │ └── idx_page_bug.ipynb └── notes │ ├── 01_notes.md │ ├── 02_notes.md │ ├── 03_notes.md │ ├── 04_data.md │ ├── 05_notes.md │ └── 06_blogpost.md ├── docker-compose.yml ├── pd3f ├── __init__.py ├── dehyphen_wrapper.py ├── doc_info.py ├── doc_output.py ├── export.py ├── geometry.py ├── parsr_wrapper.py ├── pd3fConfig.json ├── string_utils.py └── utils.py ├── poetry.lock ├── pyproject.toml ├── scripts └── remote_parsr.sh └── tests ├── __init__.py ├── test_data ├── 00001 │ ├── 00001_112018_FU_Berlin_Richtlinie_2017_1371.pdf │ └── 00001_112018_FU_Berlin_Richtlinie_2017_1371_fast_experimental.txt ├── 00004 │ ├── 00004_09212018_bstbk_Unwandlungsgesetz.pdf │ └── 00004_09212018_bstbk_Unwandlungsgesetz_fast_experimental.txt └── 00020 │ ├── 00020_08112014_Stellungnahme_RAK_Koeln_RefE_Bekaempfung_Korruption.pdf │ └── 00020_08112014_Stellungnahme_RAK_Koeln_RefE_Bekaempfung_Korruption_fast_experimental.txt ├── test_geometry.py ├── test_pdfs.py └── test_utils.py /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | end_of_line = lf 11 | charset = utf-8 12 | 13 | [*.py] 14 | max_line_length = 79 15 | 16 | [*.md] 17 | insert_final_newline = false -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pdf filter=lfs diff=lfs merge=lfs -text 2 | *.txt filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.github/workflows/publish_docs.yml: -------------------------------------------------------------------------------- 1 | name: publish docs 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - "*" 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-python@v2 15 | - uses: Gr1N/setup-poetry@v4 16 | 17 | - name: build docs 18 | run: poetry install && poetry run pdoc3 --html --output-dir docs pd3f 19 | 20 | - name: publish docs 21 | uses: peaceiris/actions-gh-pages@v3 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | publish_dir: ./docs/pd3f 25 | -------------------------------------------------------------------------------- /.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 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 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 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | 132 | data 133 | .vscode/ 134 | out* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `pd3f-core` [![PyPI](https://img.shields.io/pypi/v/pd3f.svg)](https://pypi.org/project/pd3f/) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pd3f.svg)](https://pypi.org/project/pd3f/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/pd3f)](https://pypistats.org/packages/pd3f) 2 | 3 | _Experimental, use with care._ 4 | 5 | `pd3f-core` is Python package to **reconstruct** the original **continuous text** from **PDFs** with language models. 6 | `pd3f-core` assumes your PDF is either text-based or already OCRd. 7 | `pd3f-core` is at the heart of [pd3f](https://github.com/pd3f/pd3f): a full Docker-based text extraction pipeline (including OCR). 8 | 9 | `pd3f-core` first uses [Parsr](https://github.com/axa-group/Parsr) to chunk PDFs into lines and paragraphs. 10 | Then, it uses the Python package [dehyphen](https://github.com/jfilter/dehyphen) to reconstruct the paragraphs in the most probable way. 11 | The probability is derived by calculating the [perplexity](https://en.wikipedia.org/wiki/Perplexity) with [Flair](https://github.com/flairNLP/flair)'s character-based [language models](https://machinelearningmastery.com/statistical-language-modeling-and-neural-language-models/). 12 | Unnecessary hyphens are removed, space or new lines are kept or dropt depending on the surround words. 13 | 14 | It's mainly developed for German but should work with other languages as well. 15 | The project is still in an early stage. 16 | Expect rough edges and rapid changes. 17 | 18 | ## Documentation 19 | 20 | API Documentation of pd3f-core: 21 | 22 | Documentation of pd3f (the ): 23 | 24 | ## Features 25 | 26 | ### Dehyphenation of Lines 27 | 28 | Check if two lines can be joined by removing hyphens ('-'). 29 | 30 | ### Reasonable Joining of Lines 31 | 32 | Decide between adding a simple space (' ') or a new line ('\n') when joining lines. 33 | 34 | ### Reverse Page Break (Experimental) 35 | 36 | Check if the last paragraph of a page und the first paragraph of the following page can be joined. 37 | 38 | ### Footnote to Endnotes (Experimental) 39 | 40 | In order to join paragraphs (and reverse page breaks), detect footnotes and turn them into endnotes. 41 | For now, the footnotes are pulled to the end of a file. 42 | 43 | ### Deduplication of Pager Header / Footer (Experimental) 44 | 45 | If the header or the footer are the same for all pages, only display them once. 46 | Headers are pulled to the start of the document and footer to the end. 47 | Some heuristic based on the similarity of footers are used. (Jaccard distance for text, and compare overlapping shapes) 48 | 49 | 50 | 52 | 53 | ## Installation 54 | 55 | ```bash 56 | pip install pd3f 57 | ``` 58 | 59 | or 60 | 61 | ```bash 62 | poetry add pd3f 63 | ``` 64 | 65 | ## Usage 66 | 67 | Start a local Parsr instance: 68 | 69 | ```bash 70 | docker-compose up 71 | ``` 72 | 73 | (You may also use tunnel a remote Parsr instance ([script](./scripts/locale_parsr.sh)) or choose a remote address.) 74 | 75 | ```python 76 | from pd3f import extract 77 | 78 | text, tables = extract(file_path, tables=False, experimental=False, force_gpu=False, lang="multi", fast=False, parsr_location="localhost:3001") 79 | ``` 80 | 81 | Explanations of the paramaters in the docs: https://pd3f.github.io/pd3f-core/export.html#pd3f.export.extract 82 | 83 | ### GPU Support (CUDA) 84 | 85 | Using CUDA speeds up the evaluation with Flair. 86 | But you need an (expensive) GPU. 87 | You need to set up your GPU with CUDA. 88 | [Here a guide for Ubuntu 18.04](https://towardsdatascience.com/deep-learning-gpu-installation-on-ubuntu-18-4-9b12230a1d31) 89 | 90 | 1. install [conda (via miniconda)](https://docs.conda.io/en/latest/miniconda.html) and [poetry](https://python-poetry.org/docs/) 91 | 2. create a new conda enviroment & activate it 92 | 3. Install [PyTorch](https://pytorch.org/) with CUDA: `conda install pytorch torchvision cudatoolkit=10.2 -c pytorch` (example) 93 | 4. Install `pd3f-core` with poetry: `poetry add pd3f` 94 | 95 | Poetry realizes that it is run within a conda virtual env so it doesn't create a new one. 96 | Since setting up CUDA is hard, install it with the most easy way (with conda). 97 | 98 | ## Background 99 | 100 | ### Parsr Config 101 | 102 | At the heart of `pd3f-core` is the JSON output of Parsr. 103 | Some comments on how and why certain things were chosen. 104 | [Parsr's documentation about the different modules](https://github.com/axa-group/Parsr/tree/master/server/src/processing) 105 | 106 | Parsr has several module to classify paragraphs into certain types. 107 | They offer a list detections as well as an heading detection. 108 | In my experience, the accuracy is too low for both, so we don't use it right now. 109 | This also means all the extracted (output) text is flat (no headings, different formattings etc.). 110 | 111 | We enable Drawing + Image Detection because we may need to understand what paragraph is following which other one. 112 | This may be helpful when to decide whether to join paragraphs. 113 | But it's dropped when activating the `fast` setting. 114 | 115 | In the JSON output is a field `pageNumber`. 116 | This comes from the page detection module. 117 | So `pageNumber` is derived from header / footer of each page. 118 | So it may be different from the index in the page array. 119 | Don't relay on `pageNumber` in the JSON output. 120 | 121 | `words-to-line-new` has be used like this. 122 | There is no error but the accuracy decreases if it used otherwise. 123 | 124 | ```json 125 | "words-to-line-new", 126 | [ 127 | "reading-order-detection", 128 | ``` 129 | 130 | Don't do OCR with Parsr because the results are worse than OCRmyPDF (because the latter uses image preprocessing). 131 | 132 | ## Future Work / TODO 133 | 134 | - make reverse page break work without requiring the experimental features 135 | 136 | ## Developement 137 | 138 | Install and use [poetry](https://python-poetry.org/). 139 | 140 | ## License 141 | 142 | Affero General Public License 3.0 143 | -------------------------------------------------------------------------------- /development/README.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | In `notebooks` are the notebooks that were / are used to develop this package. 4 | Sometimes it just to experiment with an idea. 5 | There are mostly poorly documented. 6 | 7 | In `notes` are some random collection of notes / thoughts. -------------------------------------------------------------------------------- /development/notebooks/00_goofing_around/test1.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import ddd 4 | 5 | for p in Path("../../data/bmjv/").glob("*.pdf"): 6 | if p.name.startswith("00003"): 7 | print(p.name) 8 | # ddd.run_parsr( 9 | # str(p), 10 | # out_dir="out/", 11 | # cleaner_config=[["reading-order-detection", {"minVerticalGapWidth": 20}]], 12 | # ) 13 | 14 | e = ddd.Export(f"out/{p.stem}/data.json") 15 | e.save_markdown(f"out/{p.stem}/clean.md") 16 | 17 | 18 | # run.do_parsr( 19 | # "../data/bmjv/00001_112018_FU_Berlin_Richtlinie_2017_1371.pdf", 20 | # out_dir="out2", 21 | # cleaner_config=[["reading-order-detection", {"minVerticalGapWidth": 20}]], 22 | # ) 23 | 24 | 25 | # run.do_parsr( 26 | # "../data/bmjv/01384_011020_Stellungnahme_DRB_RefE__Belaempfung-Rechtsextremismus-Hasskriminalitaet.pdf", 27 | # out_dir="out1", 28 | 29 | # cleaner_config=[["reading-order-detection", {"minVerticalGapWidth": 10}]], 30 | # ) 31 | 32 | 33 | # output.save( 34 | # "out1/01384_011020_Stellungnahme_DRB_RefE__Belaempfung-Rechtsextremismus-Hasskriminalitaet/data.json", 35 | # "d.txt", 36 | # ) 37 | -------------------------------------------------------------------------------- /development/notebooks/00_goofing_around/test2.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import ddd 4 | 5 | for p in Path("../data/bmjv/").glob("*.pdf"): 6 | if p.name.startswith("00002"): 7 | # print(p.name) 8 | # ddd.do_parsr( 9 | # str(p), 10 | # out_dir="out/", 11 | # cleaner_config=[["reading-order-detection", {"minVerticalGapWidth": 20}]], 12 | # ) 13 | 14 | e = ddd.Export(f"out/{p.stem}/data.json") 15 | e.save_markdown(f"out/{p.stem}/clean.md") 16 | 17 | 18 | # run.do_parsr( 19 | # "../data/bmjv/00001_112018_FU_Berlin_Richtlinie_2017_1371.pdf", 20 | # out_dir="out2", 21 | # cleaner_config=[["reading-order-detection", {"minVerticalGapWidth": 20}]], 22 | # ) 23 | 24 | 25 | # run.do_parsr( 26 | # "../data/bmjv/01384_011020_Stellungnahme_DRB_RefE__Belaempfung-Rechtsextremismus-Hasskriminalitaet.pdf", 27 | # out_dir="out1", 28 | # cleaner_config=[["reading-order-detection", {"minVerticalGapWidth": 10}]], 29 | # ) 30 | 31 | 32 | # output.save( 33 | # "out1/01384_011020_Stellungnahme_DRB_RefE__Belaempfung-Rechtsextremismus-Hasskriminalitaet/data.json", 34 | # "d.txt", 35 | # ) 36 | -------------------------------------------------------------------------------- /development/notebooks/00_goofing_around/test3.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import ddd 4 | 5 | for p in Path("../data/bmjv/").glob("*.pdf"): 6 | if p.name.startswith("00003"): 7 | print(p.name) 8 | ddd.do_parsr( 9 | str(p), 10 | out_dir="out/", 11 | cleaner_config=[["reading-order-detection", {"minVerticalGapWidth": 20}]], 12 | ) 13 | 14 | e = ddd.Export(f"out/{p.stem}/data.json") 15 | e.save_markdown(f"out/{p.stem}/clean.md") 16 | 17 | 18 | # run.do_parsr( 19 | # "../data/bmjv/00001_112018_FU_Berlin_Richtlinie_2017_1371.pdf", 20 | # out_dir="out2", 21 | # cleaner_config=[["reading-order-detection", {"minVerticalGapWidth": 20}]], 22 | # ) 23 | 24 | 25 | # run.do_parsr( 26 | # "../data/bmjv/01384_011020_Stellungnahme_DRB_RefE__Belaempfung-Rechtsextremismus-Hasskriminalitaet.pdf", 27 | # out_dir="out1", 28 | # cleaner_config=[["reading-order-detection", {"minVerticalGapWidth": 10}]], 29 | # ) 30 | 31 | 32 | # output.save( 33 | # "out1/01384_011020_Stellungnahme_DRB_RefE__Belaempfung-Rechtsextremismus-Hasskriminalitaet/data.json", 34 | # "d.txt", 35 | # ) 36 | -------------------------------------------------------------------------------- /development/notebooks/01_perplexity/text1.txt: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8655a6346c74866e9b7233aa40cd86d4754747a1d9c8074b5c22f7b7e497898f 3 | size 41597 4 | -------------------------------------------------------------------------------- /development/notebooks/01_perplexity/text2.txt: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9638e97887c31f199a3df24daa468f50143648ac8acfd460133055f152462c9e 3 | size 41601 4 | -------------------------------------------------------------------------------- /development/notebooks/03_transformers_exp/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | transformers = "*" 10 | jupyterlab = "*" 11 | torch = "*" 12 | cython = "*" 13 | 14 | [requires] 15 | python_version = "3.8" 16 | -------------------------------------------------------------------------------- /development/notebooks/03_transformers_exp/Untitled-Copy1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import math\n", 10 | "from pathlib import Path" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 2, 16 | "metadata": {}, 17 | "outputs": [ 18 | { 19 | "data": { 20 | "application/vnd.jupyter.widget-view+json": { 21 | "model_id": "dd9a12c3b8b74c8fb20b386196b9471a", 22 | "version_major": 2, 23 | "version_minor": 0 24 | }, 25 | "text/plain": [ 26 | "HBox(children=(FloatProgress(value=0.0, description='Downloading', max=239836.0, style=ProgressStyle(descripti…" 27 | ] 28 | }, 29 | "metadata": {}, 30 | "output_type": "display_data" 31 | }, 32 | { 33 | "name": "stdout", 34 | "output_type": "stream", 35 | "text": [ 36 | "\n" 37 | ] 38 | }, 39 | { 40 | "data": { 41 | "application/vnd.jupyter.widget-view+json": { 42 | "model_id": "8c61516f200d46288bed8f96a3b230ba", 43 | "version_major": 2, 44 | "version_minor": 0 45 | }, 46 | "text/plain": [ 47 | "HBox(children=(FloatProgress(value=0.0, description='Downloading', max=464.0, style=ProgressStyle(description_…" 48 | ] 49 | }, 50 | "metadata": {}, 51 | "output_type": "display_data" 52 | }, 53 | { 54 | "name": "stdout", 55 | "output_type": "stream", 56 | "text": [ 57 | "\n" 58 | ] 59 | }, 60 | { 61 | "data": { 62 | "application/vnd.jupyter.widget-view+json": { 63 | "model_id": "fb401e77b9064935ab62bb8f7622c283", 64 | "version_major": 2, 65 | "version_minor": 0 66 | }, 67 | "text/plain": [ 68 | "HBox(children=(FloatProgress(value=0.0, description='Downloading', max=269752043.0, style=ProgressStyle(descri…" 69 | ] 70 | }, 71 | "metadata": {}, 72 | "output_type": "display_data" 73 | }, 74 | { 75 | "name": "stdout", 76 | "output_type": "stream", 77 | "text": [ 78 | "\n" 79 | ] 80 | } 81 | ], 82 | "source": [ 83 | "from transformers import DistilBertTokenizer, DistilBertForMaskedLM\n", 84 | "import torch\n", 85 | "\n", 86 | "tokenizer = DistilBertTokenizer.from_pretrained('distilbert-base-german-cased')\n", 87 | "model = DistilBertForMaskedLM.from_pretrained('distilbert-base-german-cased')" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 5, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "def get_score(sentence):\n", 97 | " print(sentence)\n", 98 | " input_ids = torch.tensor(tokenizer.encode(sentence, add_special_tokens=True)).unsqueeze(0) # Batch size 1\n", 99 | " outputs = model(input_ids, masked_lm_labels=input_ids)\n", 100 | " loss, prediction_scores = outputs[:2]\n", 101 | " print(loss)\n", 102 | " return float(loss)" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 6, 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "t1 = Path('text1.txt').read_text()" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 7, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "t2 = Path('text2.txt').read_text()" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 10, 126 | "metadata": {}, 127 | "outputs": [ 128 | { 129 | "name": "stderr", 130 | "output_type": "stream", 131 | "text": [ 132 | "Since the GPL-licensed package `unidecode` is not installed, using Python's `unicodedata` package which yields worse results.\n" 133 | ] 134 | } 135 | ], 136 | "source": [ 137 | "import cleantext" 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "execution_count": 11, 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "def do_stuff(text):\n", 147 | " text = cleantext.clean(text, lower=False, lang='de')\n", 148 | " res = [get_score(t) for t in text.split('\\n')]\n", 149 | " return res" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 12, 155 | "metadata": {}, 156 | "outputs": [], 157 | "source": [ 158 | "t2=\"Mit freundlichen kollegialen Grüßen Vorstand der Rechtsanwaltskammer Köln\"" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 15, 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "t2=\"\"\"Mit freundlichen kollegialen Grüßen\n", 168 | "Vorstand der Rechtsanwaltskammer Köln\"\"\"" 169 | ] 170 | }, 171 | { 172 | "cell_type": "code", 173 | "execution_count": 13, 174 | "metadata": {}, 175 | "outputs": [ 176 | { 177 | "name": "stdout", 178 | "output_type": "stream", 179 | "text": [ 180 | "Mit freundlichen kollegialen Grüßen Vorstand der Rechtsanwaltskammer Köln\n", 181 | "tensor(1.5195, grad_fn=)\n" 182 | ] 183 | } 184 | ], 185 | "source": [ 186 | "r2 = do_stuff(t2)" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": 18, 192 | "metadata": {}, 193 | "outputs": [ 194 | { 195 | "name": "stdout", 196 | "output_type": "stream", 197 | "text": [ 198 | "Mit freundlichen kollegialen Grüßen\n", 199 | "tensor(2.7068, grad_fn=)\n", 200 | "Vorstand der Rechtsanwaltskammer Köln\n", 201 | "tensor(3.8547, grad_fn=)\n" 202 | ] 203 | } 204 | ], 205 | "source": [ 206 | "r2 = do_stuff(t2)" 207 | ] 208 | }, 209 | { 210 | "cell_type": "code", 211 | "execution_count": 19, 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "t3=\"Köln, 11.08.2014 065-14brak01 L/B\"\n", 216 | "t31=\"\"\"Köln, 11.08.2014\n", 217 | "065-14brak01 L/B\"\"\"" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 20, 223 | "metadata": {}, 224 | "outputs": [ 225 | { 226 | "name": "stdout", 227 | "output_type": "stream", 228 | "text": [ 229 | "Köln, 11.08.2014 065-14brak01 L/B\n", 230 | "tensor(1.5355, grad_fn=)\n" 231 | ] 232 | }, 233 | { 234 | "data": { 235 | "text/plain": [ 236 | "[1.5354911088943481]" 237 | ] 238 | }, 239 | "execution_count": 20, 240 | "metadata": {}, 241 | "output_type": "execute_result" 242 | } 243 | ], 244 | "source": [ 245 | "do_stuff(t3)" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": 21, 251 | "metadata": {}, 252 | "outputs": [ 253 | { 254 | "name": "stdout", 255 | "output_type": "stream", 256 | "text": [ 257 | "Köln, 11.08.2014\n", 258 | "tensor(2.9874, grad_fn=)\n", 259 | "065-14brak01 L/B\n", 260 | "tensor(2.8120, grad_fn=)\n" 261 | ] 262 | }, 263 | { 264 | "data": { 265 | "text/plain": [ 266 | "[2.98736310005188, 2.8119800090789795]" 267 | ] 268 | }, 269 | "execution_count": 21, 270 | "metadata": {}, 271 | "output_type": "execute_result" 272 | } 273 | ], 274 | "source": [ 275 | "do_stuff(t31)" 276 | ] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "execution_count": null, 281 | "metadata": {}, 282 | "outputs": [], 283 | "source": [] 284 | } 285 | ], 286 | "metadata": { 287 | "kernelspec": { 288 | "display_name": "Python 3", 289 | "language": "python", 290 | "name": "python3" 291 | }, 292 | "language_info": { 293 | "codemirror_mode": { 294 | "name": "ipython", 295 | "version": 3 296 | }, 297 | "file_extension": ".py", 298 | "mimetype": "text/x-python", 299 | "name": "python", 300 | "nbconvert_exporter": "python", 301 | "pygments_lexer": "ipython3", 302 | "version": "3.8.4" 303 | } 304 | }, 305 | "nbformat": 4, 306 | "nbformat_minor": 4 307 | } 308 | -------------------------------------------------------------------------------- /development/notebooks/03_transformers_exp/text1.txt: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8655a6346c74866e9b7233aa40cd86d4754747a1d9c8074b5c22f7b7e497898f 3 | size 41597 4 | -------------------------------------------------------------------------------- /development/notebooks/03_transformers_exp/text2.txt: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9638e97887c31f199a3df24daa468f50143648ac8acfd460133055f152462c9e 3 | size 41601 4 | -------------------------------------------------------------------------------- /development/notebooks/04_ulmfit_exp/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | torch = "*" 10 | fastai = "==1.*" 11 | clean-text = "*" 12 | jupyterlab = "*" 13 | 14 | [requires] 15 | python_version = "3.7" 16 | -------------------------------------------------------------------------------- /development/notebooks/04_ulmfit_exp/Untitled.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "Collecting bpemb\n", 13 | " Downloading bpemb-0.3.0-py3-none-any.whl (19 kB)\n", 14 | "Requirement already satisfied: numpy in /Users/user/.local/share/virtualenvs/ddd-ulmfit-KWd1cNl2/lib/python3.7/site-packages (from bpemb) (1.19.0)\n", 15 | "Collecting sentencepiece\n", 16 | " Downloading sentencepiece-0.1.91-cp37-cp37m-macosx_10_6_x86_64.whl (1.1 MB)\n", 17 | "\u001b[K |████████████████████████████████| 1.1 MB 454 kB/s eta 0:00:01\n", 18 | "\u001b[?25hCollecting gensim\n", 19 | " Downloading gensim-3.8.3-cp37-cp37m-macosx_10_9_x86_64.whl (24.2 MB)\n", 20 | "\u001b[K |████████████████████████████████| 24.2 MB 3.5 MB/s eta 0:00:01\n", 21 | "\u001b[?25hRequirement already satisfied: tqdm in /Users/user/.local/share/virtualenvs/ddd-ulmfit-KWd1cNl2/lib/python3.7/site-packages (from bpemb) (4.46.1)\n", 22 | "Requirement already satisfied: requests in /Users/user/.local/share/virtualenvs/ddd-ulmfit-KWd1cNl2/lib/python3.7/site-packages (from bpemb) (2.24.0)\n", 23 | "Requirement already satisfied: scipy>=0.18.1 in /Users/user/.local/share/virtualenvs/ddd-ulmfit-KWd1cNl2/lib/python3.7/site-packages (from gensim->bpemb) (1.5.0)\n", 24 | "Collecting smart-open>=1.8.1\n", 25 | " Downloading smart_open-2.0.0.tar.gz (103 kB)\n", 26 | "\u001b[K |████████████████████████████████| 103 kB 199 kB/s eta 0:00:01\n", 27 | "\u001b[?25hRequirement already satisfied: six>=1.5.0 in /Users/user/.local/share/virtualenvs/ddd-ulmfit-KWd1cNl2/lib/python3.7/site-packages (from gensim->bpemb) (1.15.0)\n", 28 | "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /Users/user/.local/share/virtualenvs/ddd-ulmfit-KWd1cNl2/lib/python3.7/site-packages (from requests->bpemb) (1.25.9)\n", 29 | "Requirement already satisfied: idna<3,>=2.5 in /Users/user/.local/share/virtualenvs/ddd-ulmfit-KWd1cNl2/lib/python3.7/site-packages (from requests->bpemb) (2.9)\n", 30 | "Requirement already satisfied: certifi>=2017.4.17 in /Users/user/.local/share/virtualenvs/ddd-ulmfit-KWd1cNl2/lib/python3.7/site-packages (from requests->bpemb) (2020.6.20)\n", 31 | "Requirement already satisfied: chardet<4,>=3.0.2 in /Users/user/.local/share/virtualenvs/ddd-ulmfit-KWd1cNl2/lib/python3.7/site-packages (from requests->bpemb) (3.0.4)\n", 32 | "Collecting boto\n", 33 | " Downloading boto-2.49.0-py2.py3-none-any.whl (1.4 MB)\n", 34 | "\u001b[K |████████████████████████████████| 1.4 MB 500 kB/s eta 0:00:01\n", 35 | "\u001b[?25hCollecting boto3\n", 36 | " Downloading boto3-1.14.9-py2.py3-none-any.whl (128 kB)\n", 37 | "\u001b[K |████████████████████████████████| 128 kB 3.2 MB/s eta 0:00:01\n", 38 | "\u001b[?25hCollecting s3transfer<0.4.0,>=0.3.0\n", 39 | " Downloading s3transfer-0.3.3-py2.py3-none-any.whl (69 kB)\n", 40 | "\u001b[K |████████████████████████████████| 69 kB 2.5 MB/s eta 0:00:01\n", 41 | "\u001b[?25hCollecting botocore<1.18.0,>=1.17.9\n", 42 | " Downloading botocore-1.17.9-py2.py3-none-any.whl (6.3 MB)\n", 43 | "\u001b[K |████████████████████████████████| 6.3 MB 2.1 MB/s eta 0:00:01\n", 44 | "\u001b[?25hCollecting jmespath<1.0.0,>=0.7.1\n", 45 | " Downloading jmespath-0.10.0-py2.py3-none-any.whl (24 kB)\n", 46 | "Requirement already satisfied: python-dateutil<3.0.0,>=2.1 in /Users/user/.local/share/virtualenvs/ddd-ulmfit-KWd1cNl2/lib/python3.7/site-packages (from botocore<1.18.0,>=1.17.9->boto3->smart-open>=1.8.1->gensim->bpemb) (2.8.1)\n", 47 | "Collecting docutils<0.16,>=0.10\n", 48 | " Downloading docutils-0.15.2-py3-none-any.whl (547 kB)\n", 49 | "\u001b[K |████████████████████████████████| 547 kB 526 kB/s eta 0:00:01\n", 50 | "\u001b[?25hBuilding wheels for collected packages: smart-open\n", 51 | " Building wheel for smart-open (setup.py) ... \u001b[?25ldone\n", 52 | "\u001b[?25h Created wheel for smart-open: filename=smart_open-2.0.0-py3-none-any.whl size=101341 sha256=8ffbb1f490890b0dcc4556f01f4e6b7b08c80681dae7cf62229ecf24671768ba\n", 53 | " Stored in directory: /Users/user/Library/Caches/pip/wheels/bb/1c/9c/412ec03f6d5ac7d41f4b965bde3fc0d1bd201da5ba3e2636de\n", 54 | "Successfully built smart-open\n", 55 | "Installing collected packages: sentencepiece, boto, docutils, jmespath, botocore, s3transfer, boto3, smart-open, gensim, bpemb\n", 56 | "Successfully installed boto-2.49.0 boto3-1.14.9 botocore-1.17.9 bpemb-0.3.0 docutils-0.15.2 gensim-3.8.3 jmespath-0.10.0 s3transfer-0.3.3 sentencepiece-0.1.91 smart-open-2.0.0\n" 57 | ] 58 | } 59 | ], 60 | "source": [ 61 | "! pip install bpemb" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": 3, 67 | "metadata": {}, 68 | "outputs": [ 69 | { 70 | "name": "stderr", 71 | "output_type": "stream", 72 | "text": [ 73 | "Since the GPL-licensed package `unidecode` is not installed, using Python's `unicodedata` package which yields worse results.\n" 74 | ] 75 | }, 76 | { 77 | "name": "stdout", 78 | "output_type": "stream", 79 | "text": [ 80 | "downloading https://nlp.h-its.org/bpemb/de/de.wiki.bpe.vs25000.model\n" 81 | ] 82 | }, 83 | { 84 | "name": "stderr", 85 | "output_type": "stream", 86 | "text": [ 87 | "100%|██████████| 664539/664539 [00:01<00:00, 643434.38B/s]\n" 88 | ] 89 | }, 90 | { 91 | "name": "stdout", 92 | "output_type": "stream", 93 | "text": [ 94 | "downloading https://nlp.h-its.org/bpemb/de/de.wiki.bpe.vs25000.d300.w2v.bin.tar.gz\n" 95 | ] 96 | }, 97 | { 98 | "name": "stderr", 99 | "output_type": "stream", 100 | "text": [ 101 | "100%|██████████| 28012481/28012481 [00:09<00:00, 2873465.23B/s]\n" 102 | ] 103 | } 104 | ], 105 | "source": [ 106 | "from bpemb import BPEmb\n", 107 | "from cleantext import clean\n", 108 | "from fastai.text import *\n", 109 | "\n", 110 | "# this will download the required model for sub-word tokenization\n", 111 | "bpemb_de = BPEmb(lang=\"de\", vs=25000, dim=300)\n", 112 | "\n", 113 | "# contruct the vocabulary\n", 114 | "itos = dict(enumerate(bpemb_de.words + ['xxpad']))\n", 115 | "voc = Vocab(itos)" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 12, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "data_lm = TextLMDataBunch.from_ids('.', vocab=voc, train_ids=[[1]], valid_ids=[[1]])" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 14, 130 | "metadata": {}, 131 | "outputs": [ 132 | { 133 | "data": { 134 | "text/plain": [ 135 | "LanguageLearner(data=TextLMDataBunch;\n", 136 | "\n", 137 | "Train: LabelList (1 items)\n", 138 | "x: LMTextList\n", 139 | "\n", 140 | "y: LMLabelList\n", 141 | "\n", 142 | "Path: .;\n", 143 | "\n", 144 | "Valid: LabelList (1 items)\n", 145 | "x: LMTextList\n", 146 | "\n", 147 | "y: LMLabelList\n", 148 | "\n", 149 | "Path: .;\n", 150 | "\n", 151 | "Test: None, model=SequentialRNN(\n", 152 | " (0): AWD_LSTM(\n", 153 | " (encoder): Embedding(25001, 400, padding_idx=1)\n", 154 | " (encoder_dp): EmbeddingDropout(\n", 155 | " (emb): Embedding(25001, 400, padding_idx=1)\n", 156 | " )\n", 157 | " (rnns): ModuleList(\n", 158 | " (0): WeightDropout(\n", 159 | " (module): LSTM(400, 1150, batch_first=True)\n", 160 | " )\n", 161 | " (1): WeightDropout(\n", 162 | " (module): LSTM(1150, 1150, batch_first=True)\n", 163 | " )\n", 164 | " (2): WeightDropout(\n", 165 | " (module): LSTM(1150, 400, batch_first=True)\n", 166 | " )\n", 167 | " )\n", 168 | " (input_dp): RNNDropout()\n", 169 | " (hidden_dps): ModuleList(\n", 170 | " (0): RNNDropout()\n", 171 | " (1): RNNDropout()\n", 172 | " (2): RNNDropout()\n", 173 | " )\n", 174 | " )\n", 175 | " (1): LinearDecoder(\n", 176 | " (decoder): Linear(in_features=400, out_features=25001, bias=True)\n", 177 | " (output_dp): RNNDropout()\n", 178 | " )\n", 179 | "), opt_func=functools.partial(, betas=(0.9, 0.99)), loss_func=FlattenedLoss of CrossEntropyLoss(), metrics=[], true_wd=True, bn_wd=True, wd=0.01, train_bn=True, path=PosixPath('.'), model_dir='models', callback_fns=[functools.partial(, add_time=True, silent=False)], callbacks=[RNNTrainer\n", 180 | "learn: ...\n", 181 | "alpha: 2.0\n", 182 | "beta: 1.0], layer_groups=[Sequential(\n", 183 | " (0): WeightDropout(\n", 184 | " (module): LSTM(400, 1150, batch_first=True)\n", 185 | " )\n", 186 | " (1): RNNDropout()\n", 187 | "), Sequential(\n", 188 | " (0): WeightDropout(\n", 189 | " (module): LSTM(1150, 1150, batch_first=True)\n", 190 | " )\n", 191 | " (1): RNNDropout()\n", 192 | "), Sequential(\n", 193 | " (0): WeightDropout(\n", 194 | " (module): LSTM(1150, 400, batch_first=True)\n", 195 | " )\n", 196 | " (1): RNNDropout()\n", 197 | "), Sequential(\n", 198 | " (0): Embedding(25001, 400, padding_idx=1)\n", 199 | " (1): EmbeddingDropout(\n", 200 | " (emb): Embedding(25001, 400, padding_idx=1)\n", 201 | " )\n", 202 | " (2): LinearDecoder(\n", 203 | " (decoder): Linear(in_features=400, out_features=25001, bias=True)\n", 204 | " (output_dp): RNNDropout()\n", 205 | " )\n", 206 | ")], add_time=True, silent=False)" 207 | ] 208 | }, 209 | "execution_count": 14, 210 | "metadata": {}, 211 | "output_type": "execute_result" 212 | } 213 | ], 214 | "source": [ 215 | "config = awd_lstm_lm_config.copy()\n", 216 | "config['n_hid'] = 1150\n", 217 | "\n", 218 | "learn_lm = language_model_learner(data_lm, AWD_LSTM, drop_mult=0.5, pretrained=False, config=config)\n", 219 | "learn_lm.load('ulmfit_for_german_jfilter')" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 17, 225 | "metadata": {}, 226 | "outputs": [ 227 | { 228 | "data": { 229 | "text/plain": [ 230 | "SequentialRNN(\n", 231 | " (0): AWD_LSTM(\n", 232 | " (encoder): Embedding(25001, 400, padding_idx=1)\n", 233 | " (encoder_dp): EmbeddingDropout(\n", 234 | " (emb): Embedding(25001, 400, padding_idx=1)\n", 235 | " )\n", 236 | " (rnns): ModuleList(\n", 237 | " (0): WeightDropout(\n", 238 | " (module): LSTM(400, 1150, batch_first=True)\n", 239 | " )\n", 240 | " (1): WeightDropout(\n", 241 | " (module): LSTM(1150, 1150, batch_first=True)\n", 242 | " )\n", 243 | " (2): WeightDropout(\n", 244 | " (module): LSTM(1150, 400, batch_first=True)\n", 245 | " )\n", 246 | " )\n", 247 | " (input_dp): RNNDropout()\n", 248 | " (hidden_dps): ModuleList(\n", 249 | " (0): RNNDropout()\n", 250 | " (1): RNNDropout()\n", 251 | " (2): RNNDropout()\n", 252 | " )\n", 253 | " )\n", 254 | " (1): LinearDecoder(\n", 255 | " (decoder): Linear(in_features=400, out_features=25001, bias=True)\n", 256 | " (output_dp): RNNDropout()\n", 257 | " )\n", 258 | ")" 259 | ] 260 | }, 261 | "execution_count": 17, 262 | "metadata": {}, 263 | "output_type": "execute_result" 264 | } 265 | ], 266 | "source": [ 267 | "learn_lm.model" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 20, 273 | "metadata": {}, 274 | "outputs": [ 275 | { 276 | "data": { 277 | "text/plain": [ 278 | "'berlin ist eine gr m'" 279 | ] 280 | }, 281 | "execution_count": 20, 282 | "metadata": {}, 283 | "output_type": "execute_result" 284 | } 285 | ], 286 | "source": [ 287 | "learn_lm.predict('berlin ist eine gr')" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": null, 293 | "metadata": {}, 294 | "outputs": [], 295 | "source": [ 296 | "learn_lm = language_model_learner(data_lm)\n", 297 | "learn_lm.load('path/to/model/ulmfit_for_german_jfilter.pth')" 298 | ] 299 | } 300 | ], 301 | "metadata": { 302 | "kernelspec": { 303 | "display_name": "Python 3", 304 | "language": "python", 305 | "name": "python3" 306 | }, 307 | "language_info": { 308 | "codemirror_mode": { 309 | "name": "ipython", 310 | "version": 3 311 | }, 312 | "file_extension": ".py", 313 | "mimetype": "text/x-python", 314 | "name": "python", 315 | "nbconvert_exporter": "python", 316 | "pygments_lexer": "ipython3", 317 | "version": "3.7.3" 318 | } 319 | }, 320 | "nbformat": 4, 321 | "nbformat_minor": 4 322 | } 323 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/Untitled1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from dehyphen import *" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 3, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "some_german_text = \"\"\"K I StGB §§ 331 ff.\n", 19 | "Entwurf eines Gesetzes zur Bekampfung der Korruption - Referentenentwurf des BMJV\n", 20 | "\"\"\"\n", 21 | "\n", 22 | "special_format = text_to_format(some_german_text)\n", 23 | "fixed = dehyphen(special_format, lang=\"de\") # you may pass a `lang` argument" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 4, 29 | "metadata": {}, 30 | "outputs": [ 31 | { 32 | "data": { 33 | "text/plain": [ 34 | "[[['K', 'I', 'StGB', '§§', '331', 'ff. '],\n", 35 | " ['Entwurf',\n", 36 | " 'eines',\n", 37 | " 'Gesetzes',\n", 38 | " 'zur',\n", 39 | " 'Bekampfung',\n", 40 | " 'der',\n", 41 | " 'Korruption',\n", 42 | " '-',\n", 43 | " 'Referentenentwurf',\n", 44 | " 'des',\n", 45 | " 'BMJV']]]" 46 | ] 47 | }, 48 | "execution_count": 4, 49 | "metadata": {}, 50 | "output_type": "execute_result" 51 | } 52 | ], 53 | "source": [ 54 | "fixed" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [] 63 | } 64 | ], 65 | "metadata": { 66 | "kernelspec": { 67 | "display_name": "Python 3", 68 | "language": "python", 69 | "name": "python3" 70 | }, 71 | "language_info": { 72 | "codemirror_mode": { 73 | "name": "ipython", 74 | "version": 3 75 | }, 76 | "file_extension": ".py", 77 | "mimetype": "text/x-python", 78 | "name": "python", 79 | "nbconvert_exporter": "python", 80 | "pygments_lexer": "ipython3", 81 | "version": "3.8.4" 82 | } 83 | }, 84 | "nbformat": 4, 85 | "nbformat_minor": 4 86 | } 87 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean.md: -------------------------------------------------------------------------------- 1 | Bundesrechtsanwaltskammer Littenstr. 9 2 | 3 | 10179 Berlin 4 | 5 | Köln, 11.08.2014 065-14brak01 L/B 6 | 7 | BRAK-Nr. 255/2014 8 | K I StGB § 299 9 | K I StGB §§ 331 ff. 10 | Entwurf eines Gesetzes zur Bekampfung der Korruption 11 | 12 | Sehr geehrte Damen und Herren Kolleginnen und Kollegen, sehr geehrter Herr Kollege Johnigk, 13 | 14 | Bezug nehmend auf Ihr Schreiben vom 18.06.2014 möchte der Vorstand der Rechtsanwaltskammer Köln zum Referentenentwurf des BMJV folgendes anmerken: 15 | 16 | 1; Der Referentenentwurf steht vor der Schwierigkeit, Vorgaben aus internationalen Abkommen umsetzen zu müssen, die selbst fragwürdig sind. Dies betrifft insbesondere die Tatbestandsausweitung der Norm des 8 299 StGB auf die Pflichtverletzung gegenüber dem Geschäftsherrn. Inwieweit es für eine international oder auch global geführte Sanktionierung von Korruption erforderlich sein soll, in § 299 StGB zusätzlich den Geschäftsherrn zu schützen, ist nicht ersichtlich. Eine Berührung internationaler Interessen bei fehlender Umsetzung des Geschäftsherrnmodells liegt fern, weil diese Interessen gerade durch das Wettbewerbsmodell geschützt werden. Die Tatbestandsausweitung hat mithin lediglich instrumentellen Charakter, dürfte aber noch im Rahmen des gesetzgeberischen Ermessensspielraums liegen, so dass verfassungsrechtliche Bedenken nicht erhoben werden können. Der Fehler für die unnötige Tatbestandsausweitung liegt mithin nicht im Referentenentwurf, sondern in der Vorarbeit zum Strafrechtsübereinkommen über Korruption des Europarats begründet. 17 | 18 | Die Aufnahme der Bestechung im geschäftlichen Verkehr in dem Vortatenkatalog des 8 261 StGB mag inzwischen zwingend sein (II, 19 | 20 | Wir verweisen auf die Stellungnahme der BRAK zum Referentenentwurf eines Gesetzes zur Verbesserung der Bekämpfung der Geldwäsche und Steuerhinterziehung (BRAK-Stellungnahme-Nr. 34/2010). Dort ist zu Recht ausgeführt, die Gesetzgebungsgeschichte der Norm des 8 261 StGB lehre, dass bei deren Einführung Gesichtspunkte der Normenbestimmtheit und des Übermaßverbots gerade für die Aufstellung des Vortatenkatalogs eine entscheidende Rolle gespielt haben. Mit der seinerzeit vorgeschlagenen Ausweitung werde die enumerative Konstruktion des Katalogs praktisch aufgegeben (Blatt 4 der Stellungnahme). Dies gilt insbesondere unter dem Gesichtspunkt, dass sich nach empirischen Erkenntnissen zur Wirksamkeit des der Geldwäsche zugrunde liegenden Konzepts es sich in der Praxis als weitgehend wirkungslos erwiesen hat. Die hieraus folgende, sich auf den symbolischen Charakter beschränkende Aufwertung der „Vortat“ des 8 299 StGB führt zu dem Dilemma, dass es dem Rechtsstaat fremd und für ihn nicht begründbar ist, aus symbolischen Gründen strafen zu dürfen. 21 | 22 | Mithin führt die Aufnahme des § 299 StGB in den Vortatenkatalog - auch unter der Einschrankung auf besondere Tatbegehungsweisen - zu Bedenken gegenüber dem Bestimmtheitsgrundsatz und dem Rechtsstaatsprinzip. Die Aufnahme der Norm in den Vortatenkatalog ist deshalb - auch gegen die „zwingenden“ Vorgaben in Art. 2 Abs. 3 des EU-Rahmenbeschlusses - wegen Verfassungswidrigkeit zu unterlassen. 23 | 24 | Mit freundlichen kollegialen Grüßen Vorstand der Rechtsanwaltskammer Köln 25 | 26 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean10.md: -------------------------------------------------------------------------------- 1 | Genossenschafts 2 | BERLIN | BRANDENBURG | BREMEN | HAMBURG | HESSEN | MECKLENBURG-VORPOMMERN | . 3 | Verband 4 4 | NIEDERSACHSEN | RHEINLAND-PFALZ | SAARLAND | SACHSEN | SACHSEN-ANHALT | SCHLESWIG-HOLSTEIN | THURINGEN 5 | 6 | Verwaltungssitz Neu-Isenburg 7 | Wilhelm-Haas-Platz 8 | 63263 Neu-Isenburg 9 | Genossenschaftsverband e.V. - Postfach 15 53 - 63235 Neu-Isenburg 10 | www.genossenschaftsverband.de 11 | 12 | An den Bundesminister der Justiz und 13 | 14 | Vorstandsstab 15 | für Verbraucherschutz Barilffierhaus 16 | Herrn Heiko Maas 17 | Telefon 069 6978-3811 18 | 11015 Berlin 19 | Telefax 069 6978-3427 20 | daniel.illerhaus 21 | @genossenschaftsverband.de 22 | 23 | 23. März 2015 24 | KJU 25 | 26 | Stellungnahme zum Gesetzentwurf Korruptionsbekämpfung 27 | im Gesundheitswesen TEN 28 | Die Genossenschaften m 29 | 30 | Sehr geehrter Herr Bundesminister, 31 | 32 | mit dem aktuellen Gesetzentwurf zur Bekämpfung der Korruption im Gesundheitswesen verfolgt die Bundesregierung ein Anliegen, welches der Genossenschaftsverband vorbehaltlos unterstützt. Gleichzeitig herrscht aufgrund unklarer Gesetzesformulierungen bei vielen unserer Mitglieder große Verunsicherung darüber, ob diese weiterhin aktiv ihrem Geschäftsmodell nachgehen können. Als Verband haben wir die Sorge, dass die bestehende und erprobte Praxis in Genossenschaften unbeabsichtigt und zu Unrecht unter den neuen Geltungsbereich des Strafgesetzbuches fallen könnte. 33 | 34 | Der Gesetzgeber fordert einerseits mehr Wettbewerb und mehr Handlungsfreiheit für die Akteure — andererseits würde der vorliegende Entwurf die wirtschaftliche Aktivität von Genossenschaften stark einschränken. Neben den direkten Auswirkungen auf die betroffenen Mitglieder wäre zudem der Genossenschaftsgedanke im Gesundheitswesen nicht mehr am Leben zu erhalten. 35 | 36 | Dem Genossenschaftsverband gehören rund 90 Gesundheitsgenossenschaften in 13 Bundesländern an. Hierzu zählen Ärzte- und Apothekergenossenschaften sowie weitere Heilberufe, die sich wie alle Genossenschaften, die Förderung ihrer Mitglieder durch gemeinschaftlichen Geschäftsbetrieb zum Ziel gesetzt haben. Damit Genossenschaften gleichberechtigt neben anderen Unternehmensformen ihren Platz im Gesundheitswesen behalten können, bitten wir Sie während des Gesetzgebungsprozesses zur Korruptionsbekämpfung im Gesundheitswesen folgende Punkte zu berücksichtigen: 37 | 38 | = Anerkennung des genossenschaftlichen Geschäftsmodells im Gesundheitswesen 39 | Getreu der genossenschaftlichen Überzeugung, was einer alleine nicht schafft, schaffen viele, organisieren Gesundheitsgenossenschaften, wie in Handel und Handwerk seit über 150 Jahren bereits üblich, einen gemeinsamen Einkauf, fördern ihre Mitglieder durch Weiterbildung und die Unterhaltung gemeinsam genutzter Räumlichkeiten und medizinischer Geräte. Als Einkaufsgemeinschaft können die Gesundheitsgenossenschaften Rahmenvereinbarungen mit Unternehmen abschließen, die den Mitgliedern zu Sonderkonditionen Produkte und Dienstleistungen anbieten. Da- 40 | 41 | zu zählen beispielsweise Versicherungen, Praxisbedarf, Fahrzeugbeschaffung oder Kommunikationsdienste. So leisten die Genossenschaften einen Beitrag zum wirtschaftlichen Erfolg ihrer Mitglieder. Monetäre Leistungen der Gesundheitsgenossenschaften an ihre Mitglieder erfolgen explizit nicht. Die wirtschaftlichen Vorteile, die den Mitgliedern einer Genossenschaft entstehen, gehen ausdrücklich nicht zu Lasten der Allgemeinheit. Ganz im Gegenteil ist es so, dass die Entlastungen die den Mitgliedern entstehen, dem Staat und letztlich den Patienten zu Gute kommen. Vergleichbares gilt für Apothekengenossenschaften. Auch hier besteht die klare Zielsetzung, durch gemeinschaftliches Handeln Vorteile für das einzelne Mitglied und auch die Genossenschaft an sich zu erzielen. Auch hier stehen gemeinsamer Einkauf, gemeinsames Marketing, gemeinsame Verhandlungen mit Lieferanten oder Kostenträgern im Fokus, mit dem Ziel, den Einzelnen durch die Genossenschaft zu stärken. 42 | 43 | Die Vorteile einer Genossenschaft können natürliche Personen, die als Vertragsarzt, Psychotherapeut oder Apotheker zugelassen und niedergelassen sind nutzen. Dies gilt auch für die als Arzt oder Psychotherapeut in einem Medizinischen Versorgungszentrum, bei einem zugelassenen und niedergelassenen Arzt oder Psychotherapeuten oder einer zugelassenen Leistungserbringergemeinschaft angestellt sind. Sie müssen hierfür in der Regel keine zusätzlichen monatlichen Aufwendungen in Kauf nehmen. Bei Eintritt ist einmalig ein Genossenschaftsanteil zu bezahlen, der den Mitgliedern bei Austritt aus der Genossenschaft erstattet wird. 44 | Wesensmerkmal und Nutzen von Genossenschaften im Gesundheitswesen sind nicht rein wirtschaftlicher Natur. Wie in allen Genossenschaften spielt auch im Gesundheitswesen der Dialog eine wichtige Rolle. Durch ihre regionale Aufstellung bieten Gesundheitsgenossenschaften insbesondere im ländlichen Raum eine wichtige fachliche Austauschplattform und stellen die medizinische Versorgung für Patienten beispielsweise in Form von Behandlungspfaden sicher. 45 | In vielen Bereichen wie z.B. Landwirtschaft, Handwerk, Handel oder Wohnen ist die genossenschaftliche Tradition ein von Politik und Gesellschaft geschätztes und erwünschtes Element. Im Gesundheitswesen schafft dies vielfältige positive Effekte, weshalb es nicht im Sinne des Gesetzgebers sein kann, Genossenschaften den Zugang zu diesem Feld zu erschweren. 46 | 47 | " Rechtssicherheit schaffen - klare Definitionen und Formulierungen 48 | 49 | 1) "in unlauterer Weise" 50 | Es ist notwendig, das Adjektiv "unlauter" mit Bezug auf das Gesundheitswesen zu präzisieren und gegen das Adjektiv "lauter" genau abzugrenzen. Ansonsten werden die niedergelassenen Ärzte, Apotheker oder Angehörige weiterer Heilberufe aus Gründen der Vorsicht jede Art von Kooperation im Gesundheitswesen in Zukunft vermeiden und sich aus bestehenden zurückziehen. Dies kann nicht gewollt sein, da der Gesetzgeber mit dem Versorgungsstärkungsgesetz genau das Gegenteil, nämlich Kooperationen fördern möchte. Wünschenswert sind deshalb möglichst umfangreiche Auflistungen von Beispielen mit lauteren und unlauteren Handlungen durch niedergelassene Ärzte, Apotheker oder Angehörige weiterer Heilberufe. ° 51 | 52 | 2) "in sonstiger Weise seine Berufsausübungspflichten verletze" 53 | Die Ausführungen unter Punkt 1 gelten in analoger Weise für die Formulierung "in sonstiger Weise seine Berufsausübungspflichten verletze". Zusätzlich ist zu fragen, wer die Verletzung der ärztlichen Berufsausübungspflichten beurteilt. Den Wirtschaftsstrafkammern selbst fehlt hierzu die fachliche Kompetenz. Insofern muss sichergestellt werden, dass in die Beurteilung die Ärztekammern, medizinischen Fachgesellschaften und ärztlichen sowie therapeutischen Berufsverbände einbezogen werden. 54 | 55 | 3) "gewerbsmäßig oder als Mitglied einer Bande" 56 | Hier muss dringend klargestellt werden, ob und in wie weit die Mitglieder, Vorstandsmitglieder, Aufsichtsratsmitglieder und Geschäftsführer einer Gesundheitsgenossenschaft allein aufgrund ihrer Mitgliedschaft in bzw. Tätigkeit für eine Ärztegenossenschaft gewerbsmäßig oder als Mitglied einer Bande handeln (können). 57 | 58 | 4) Rabatte, Skonti, Boni 59 | Wie stellt sich die Situation dar, wenn Lieferanten Rabatte, Skonti und/oder Boni gewähren 60 | - bei Praxisbedarf, der in die Produktkategorien des 8 299a fällt; 61 | - bei Produkten, die in die Produktkategorien des $ 299a fallen (z.B. Katheter, Zangen, Sonden, Stents, Herzschrittmachern etc.) und Bestandteil von pauschal vergüteten Leistungen, z.B. im Rahmen von integrierten Versorgungsverträgen, sind? 62 | 63 | Wir möchten Sie bitten, die Position der Genossenschaften im Gesetzgebungsprozess zu unterstützen. Gerne stehen wir für vertiefende Informationen zu Ärztegenossenschaften, aber auch zu den weiteren 2300 Genossenschaften in unserem Verbandsgebiet, zur Verfügung. 64 | 65 | Mit freundlichen Grüßen 66 | 67 | Genossenschaftsverband e.V. 68 | a 69 | 70 | Rene Rothe i.V. Daniel ibe aus Verbandsdirektor Stv. Leiter Vorstandsstab 71 | 72 | u 73 | KYA gol (6) 94) 74 | 75 | Genossenschaftsverband e.V. Sitz Frankfurt am Main, Vereinsregister-Nummer 14109 76 | Vorstand: WP/StB Dipl.-Kfm. Michael Bockelmann (Verbandspräsident und Vorstandsvorsitzender), Dipl.-Ök. Klaus Bellmann, WPIStB Dipl.-Betriebsw. (FH) Horst Kessel, WP Dipl.-Ök. Rene Rothe, WP/StB Dipl.-Betriebsw. (FH) Edgar Schneider 77 | Vorsitzender des Verbandsrates: Michael Siegers, Stellvertreter: Claus-Rüdiger Bauer 78 | USt-IdNr.: DE 116688346 Bankverbindungen: DZ BANK AG Frankfurt, IBAN DE89 5006 0000 0000 0127 00, BIC GENODE55XXX DZ BANK AG Hannover, IBAN DE62 2606 0000 0000 0404 41, BIC GENODEFF250 - Mitglied der DIERVIFRS ‚Advisory Group 79 | 80 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean2.md: -------------------------------------------------------------------------------- 1 | Bundesrechtsanwaltskammer Littenstr. 9 2 | 3 | 10179 Berlin 4 | 5 | Köln, 11.08.2014 065-14brak01 L/B 6 | 7 | BRAK-Nr. 255/2014 8 | K I StGB § 299 9 | K I StGB §§ 331 ff. 10 | Entwurf eines Gesetzes zur Bekampfung der Korruption 11 | 12 | Sehr geehrte Damen und Herren Kolleginnen und Kollegen, sehr geehrter Herr Kollege Johnigk, 13 | 14 | Bezug nehmend auf Ihr Schreiben vom 18.06.2014 möchte der Vorstand der Rechtsanwaltskammer Köln zum Referentenentwurf des BMJV folgendes anmerken: 15 | 16 | 1; Der Referentenentwurf steht vor der Schwierigkeit, Vorgaben aus internationalen Abkommen umsetzen zu müssen, die selbst fragwürdig sind. Dies betrifft insbesondere die Tatbestandsausweitung der Norm des 8 299 StGB auf die Pflichtverletzung gegenüber dem Geschäftsherrn. Inwieweit es für eine international oder auch global geführte Sanktionierung von Korruption erforderlich sein soll, in § 299 StGB zusätzlich den Geschäftsherrn zu schützen, ist nicht ersichtlich. Eine Berührung internationaler Interessen bei fehlender Umsetzung des Geschäftsherrnmodells liegt fern, weil diese Interessen gerade durch das Wettbewerbsmodell geschützt werden. Die Tatbestandsausweitung hat mithin lediglich instrumentellen Charakter, dürfte aber noch im Rahmen des gesetzgeberischen Ermessensspielraums liegen, so dass verfassungsrechtliche Bedenken nicht erhoben werden können. Der Fehler für die unnötige Tatbestandsausweitung liegt mithin nicht im Referentenentwurf, sondern in der Vorarbeit zum Strafrechtsübereinkommen über Korruption des Europarats begründet. 17 | 18 | Die Aufnahme der Bestechung im geschäftlichen Verkehr in dem Vortatenkatalog des 8 261 StGB mag inzwischen zwingend sein (II, 19 | 20 | Wir verweisen auf die Stellungnahme der BRAK zum Referentenentwurf eines Gesetzes zur Verbesserung der Bekämpfung der Geldwäsche und Steuerhinterziehung (BRAK-Stellungnahme-Nr. 34/2010). Dort ist zu Recht ausgeführt, die Gesetzgebungsgeschichte der Norm des 8 261 StGB lehre, dass bei deren Einführung Gesichtspunkte der Normenbestimmtheit und des Übermaßverbots gerade für die Aufstellung des Vortatenkatalogs eine entscheidende Rolle gespielt haben. Mit der seinerzeit vorgeschlagenen Ausweitung werde die enumerative Konstruktion des Katalogs praktisch aufgegeben (Blatt 4 der Stellungnahme). Dies gilt insbesondere unter dem Gesichtspunkt, dass sich nach empirischen Erkenntnissen zur Wirksamkeit des der Geldwäsche zugrunde liegenden Konzepts es sich in der Praxis als weitgehend wirkungslos erwiesen hat. Die hieraus folgende, sich auf den symbolischen Charakter beschränkende Aufwertung der „Vortat“ des 8 299 StGB führt zu dem Dilemma, dass es dem Rechtsstaat fremd und für ihn nicht begründbar ist, aus symbolischen Gründen strafen zu dürfen. 21 | 22 | Mithin führt die Aufnahme des § 299 StGB in den Vortatenkatalog - auch unter der Einschrankung auf besondere Tatbegehungsweisen - zu Bedenken gegenüber dem Bestimmtheitsgrundsatz und dem Rechtsstaatsprinzip. Die Aufnahme der Norm in den Vortatenkatalog ist deshalb - auch gegen die „zwingenden“ Vorgaben in Art. 2 Abs. 3 des EU-Rahmenbeschlusses - wegen Verfassungswidrigkeit zu unterlassen. 23 | 24 | Mit freundlichen kollegialen Grüßen Vorstand der Rechtsanwaltskammer Köln 25 | 26 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean3.md: -------------------------------------------------------------------------------- 1 | Bundesrechtsanwaltskammer Littenstr. 9 2 | 3 | 10179 Berlin 4 | 5 | Köln, 11.08.2014 065-14brak01 L/B 6 | 7 | BRAK-Nr. 255/2014 8 | K I StGB § 299 9 | K I StGB §§ 331 ff. 10 | Entwurf eines Gesetzes zur Bekampfung der Korruption - Referentenentwurf des BMJV 11 | 12 | Sehr geehrte Damen und Herren Kolleginnen und Kollegen, sehr geehrter Herr Kollege Johnigk, 13 | 14 | Bezug nehmend auf Ihr Schreiben vom 18.06.2014 möchte der Vorstand der Rechtsanwaltskammer Köln zum Referentenentwurf des BMJV folgendes anmerken: 15 | 16 | 1; 17 | Der Referentenentwurf steht vor der Schwierigkeit, Vorgaben aus internationalen Abkommen umsetzen zu müssen, die selbst fragwürdig sind. Dies betrifft insbesondere die Tatbestandsausweitung der Norm des 8 299 StGB auf die Pflichtverletzung gegenüber dem Geschäftsherrn. Inwieweit es für eine international oder auch global geführte Sanktionierung von Korruption erforderlich sein soll, in § 299 StGB zusätzlich den Geschäftsherrn zu schützen, ist nicht ersichtlich. Eine Berührung internationaler Interessen bei fehlender Umsetzung des Geschäftsherrnmodells liegt fern, weil diese Interessen gerade durch das Wettbewerbsmodell geschützt werden. Die Tatbestandsausweitung hat mithin lediglich instrumentellen Charakter, dürfte aber noch im Rahmen des gesetzgeberischen Ermessensspielraums liegen, so dass verfassungsrechtliche Bedenken nicht erhoben werden können. Der Fehler für die unnötige Tatbestandsausweitung liegt mithin nicht im Referentenentwurf, sondern in der Vorarbeit zum Strafrechtsübereinkommen über Korruption des Europarats begründet. 18 | 19 | Die Aufnahme der Bestechung im geschäftlichen Verkehr in dem Vortatenkatalog des 8 261 StGB mag inzwischen zwingend sein (II, 3. der Begründung des Entwurfs). Hier ergeben sich allerdings verfassungsrechtliche Bedenken. 20 | 21 | Wir verweisen auf die Stellungnahme der BRAK zum Referentenentwurf eines Gesetzes zur Verbesserung der Bekämpfung der Geldwäsche und Steuerhinterziehung (BRAK-Stellungnahme-Nr. 34/2010). Dort ist zu Recht ausgeführt, die Gesetzgebungsgeschichte der Norm des 8 261 StGB lehre, dass bei deren Einführung Gesichtspunkte der Normenbestimmtheit und des Übermaßverbots gerade für die Aufstellung des Vortatenkatalogs eine entscheidende Rolle gespielt haben. Mit der seinerzeit vorgeschlagenen Ausweitung werde die enumerative Konstruktion des Katalogs praktisch aufgegeben (Blatt 4 der Stellungnahme). Dies gilt insbesondere unter dem Gesichtspunkt, dass sich nach empirischen Erkenntnissen zur Wirksamkeit des der Geldwäsche zugrunde liegenden Konzepts es sich in der Praxis als weitgehend wirkungslos erwiesen hat. Die hieraus folgende, sich auf den symbolischen Charakter beschränkende Aufwertung der „Vortat“ des 8 299 StGB führt zu dem Dilemma, dass es dem Rechtsstaat fremd und für ihn nicht begründbar ist, aus symbolischen Gründen strafen zu dürfen. 22 | 23 | Mithin führt die Aufnahme des § 299 StGB in den Vortatenkatalog - auch unter der Einschrankung auf besondere Tatbegehungsweisen - zu Bedenken gegenüber dem Bestimmtheitsgrundsatz und dem Rechtsstaatsprinzip. Die Aufnahme der Norm in den Vortatenkatalog ist deshalb - auch gegen die „zwingenden“ Vorgaben in Art. 2 Abs. 3 des EU-Rahmenbeschlusses - wegen Verfassungswidrigkeit zu unterlassen. 24 | 25 | Mit freundlichen kollegialen Grüßen Vorstand der Rechtsanwaltskammer Köln 26 | 27 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean4.md: -------------------------------------------------------------------------------- 1 | 29.3.2017 2 | 3 | Neue Richtervereinigung e.V. | Greifswalder Str. 4 | 10405 Berlin 4 | / 5 | 6 | te 7 | Bundesbüro: 8 | a . . 9 | Martina Reeßing, Bundesministerium der Justiz und für 10 | Leiterin des Bundesbüros Verbraucherschutz Greifswalder Str. 4 10405 Berlin Mohrenstraße 37 11 | Tel: 030 420223-49 12 | Fax: -50 13 | 11117 Berlin 14 | Mobil 0176 567 996 48 bb@neuerichter.de www.neuerichter.de 15 | 16 | VB 2 - 6100/61 - 5466/2017, dortiges Schreiben vom 14.3.2017 Referentenentwurf des Bundesministeriums der Justiz und für Verbraucherschutz 17 | 18 | Entwurf eines Gesetzes zur besseren Rechtsdurchsetzung in sozialen Netzwerken (NetzDG) 19 | 20 | Sehr geehrter Herr Dr. Meyer-Seitz, 21 | vielen Dank für die Gelegenheit zur Stellungnahme. 22 | 23 | Die Neue Richtervereinigung (nrv) begrüßt die Initiative, gegen Hasskriminalität, Verleumdungen und Fake News in sozialen Netzwerken strenger vorzugehen und damit das geltende Recht auch dort effektiver durchzusetzen. Aus Sicht der nrv geht es bei diesen Fragen nicht nur um einen wirksamen Persönlichkeitsschutz sondern auch um einen Schutz der freien Meinungsäußerung. Es geht also um Abwägungen zweier wichtiger Grundrechte und die dafür notwendigen materiell-rechtlichen und prozessualen Instrumente. 24 | 25 | Nach dem vorliegenden Entwurf würde es zunächst dem Anbieter von Telemediendiensten überlassen bleiben, die unbestimmten Rechtsbegriffe „rechtswidrige Inhalte“ und „offensichtlich rechtswidrige Inhalte“ in § 3 Abs. 2 NetzDG auszufüllen. Dabei kann und würde es aber nicht bleiben. Wie bei anderen unbestimmten Rechtsbegriffen auch werden Auseinandersetzungen im Rechtsverkehr zu gerichtlichen Verfahren führen und es ist dann 26 | 27 | Sprecherin und Sprecher des Vorstandes: 28 | 29 | Brigitte Kreuder-Sonnen, LG Lübeck, Am Burgfeld 7, 23568 Lübeck, Brigitte.Kreuder-Sonnen@neuerichter.de, Tel.: 0451/3711809 (d.) Carsten Löbbert, AG Lübeck, Am Burgfeld 7, 23568 Lübeck, Carsten.Loebbert@neuerichter.de, Tel. 0451/3711576 (d.) 30 | 31 | Weitere Mitglieder des Bundesvorstandes: Aufgabe der Rechtsprechung, unbestimmte Rechtsbegriffe näher auszugestalten. Wir stimmen dem Gesetzentwurf in seiner Grundannahme zu, dass es Aufgabe der Gerichte ist, in einem rechtförmigen, transparenten und unabhängigen Verfahren, die Abwägungen im Einzelfall zu treffen und in diesem Rahmen ggf. einheitliche und vorhersehbare Maßstäbe zu entwickeln. 32 | 33 | Damit das gelingen kann, ist es aber erforderlich, ein wirksames materiell-rechtliches und ein handhabbares prozessuales Instrumentarium zu haben. Insoweit bringt der Entwurf nach unserer Auffassung zwar Verbesserungen, bleibt aber in wesentlichen Punkten unzureichend. Der Gesetzentwurf versäumt es, eine Rechtslage zu schaffen, die in den hier relevanten Fragen eine angemessene Güterabwägung in einer modernen Mediengesellschaft ermöglicht. 34 | 35 | I. Die zivilrechtliche Seite 36 | 37 | Ein Kernpunkt des Entwurfes ist das in § 3 Abs. 2 NetzDG konzipierte Löschungsverfahren. Damit wird - aus unserer Sicht grundsätzlich richtig — ein zivilrechtliches Instrumentarium geschaffen, um Anbieter von Telemediendiensten zur Löschung zwingen zu können. In der bisherigen Form reicht es aber nicht aus, um im Rahmen der mittelbaren Drittwirkung der Grundrechte, für einen wirksamen und angemessenen Interessenausgleich zu sorgen: 38 | 39 | 1. Aus der Sicht der „Betroffenen“ geht es in erster Linie darum, Einträge mit falschen Tatsachenbehauptungen, Beschimpfungen, Beschuldigungen und Verleumdungen rasch aus den Netzwerken entfernt zu bekommen. Insoweit stellt die verpflichtende Einführung des in 40 | § 3 Abs. 2 NetzDG konzipierten Verfahrens grundsätzlich einen Fortschritt dar. Es ist sinnvoll, wie in $ 3 Abs. 2 Ziff. 2 NetzDG vorgesehen, offensichtlich rechtswidrige Inhalte innerhalb von 24 Stunden nach Eingang der Beschwerde zu löschen, andere Inhalte in einer Frist von 7 Tagen, $ 3 Abs. 2 Ziff. 3 NetzDG. Erst die längere Frist ermöglicht es, eine sinnvolle Kommunikation mit „Einstellern“ und „Beschwerdern“ zu betreiben. Selbstverständlich ist dabei auch die vorgesehene Benachrichtigung der Beteiligten, $ 3 Abs. 2 Ziff. 5 NetzDG. 41 | 42 | 2. Leider nicht durch das Gesetz geregelt wird aber, wie mit Meinungsverschiedenheiten und Streitigkeiten in dem Dreieckverhältnis Nutzer/Einsteller — Plattformbetreiber — Beschwerdeführer umgegangen werden soll. Genau in dieser Situation findet aber die Abwägung der verschieden Grundrechtspositionen statt und die Grundrechte entfalten die mittelbare Drittwirkung. 43 | Soweit der Gesetzentwurf schweigt, werden die allgemeinen Regelungen zur Anwendung kommen. Der aus der vorletzten Jahrhundertwende stammende 8 1004 BGB (mit allen Analogien) ist für die hier relevanten Fragen aber völlig unzureichend: Nicht erst seit der Entscheidung des LG Würzburg vom 7.3.2017 (11 O 2338/16) ist deutlich geworden, dass derzeit die (zivilrechtliche) Rechtsstellung von Betroffenen unzureichend ist. Die in diesem Zusammenhang wesentlichen Fragen, nämlich 44 | 45 | - 46 | 47 | ob soziale Netzwerke wie ,Host-Provider* (§ 10 TMG) zu behandeln sind - 48 | 49 | ob ein Betroffener jede einzelnen Fundstelle nachweisen oder das soziale Netzwerk auch ,proaktiv’ suchen muss 50 | 51 | - 52 | 53 | welche rechtliche Bedeutung „teilen“ und „liken“ haben müssen dringend geregelt werden. Dafür sollte der Gesetzgeber eine klare (gesetzliche) Anspruchsgrundlage für Betroffene gegen soziale Netzwerke schaffen auf Löschung, die die obigen Fragen regelt. Vertragliche Regelungen sind nicht ausreichend, da Betroffene nicht notwendiger Weise Verträge mit den Netzwerken haben müssen. 54 | 55 | Umgekehrt müssen Nutzer Schutzansprüche haben, falls Inhalte unberechtigt oder vorschnell gelöscht werden. Nur eine solche Ausgestaltung wird der Bedeutung der durch Art. 5 GG geschützten Grundrechte gerecht. 56 | Zu all dem verhält sich das NetzDG nicht. Es ist nicht einmal klar, ob $ 3 NetzDG als drittschützende Norm verstanden werden kann. Das ist insgesamt zu wenig. 57 | 58 | 3. Zudem muss auch der gerichtliche Rechtsschutz so ausgestaltet werden, dass eine rasche gerichtliche Entscheidung der ordentlichen Gerichte herbeigeführt werden kann. Richtigerweise liegt dem Gesetzentwurf die Annahme zugrunde, dass ein Streit im grundrechtsrelevanten Bereich letztlich durch die unabhängige Justiz entschieden werden muss. Dafür muss es dann aber auch eine effektive Verfahrensart geben. Das Verfahren auf Erlass einer einstweiligen Verfügung ist dafür grundsätzlich geeignet. Da es sich hier aber typischerweise um „Dreiecksverhältnisse“ handelt, sollten prozessuale Möglichkeiten erwogen werden, den „Dritten“ einzubeziehen. Damit die Gerichte die notwendigen Kompetenzen und Kapazitäten aufbauen können, sollten zudem Spezialzuständigkeiten vorgesehen werden, möglicherweise könnten solche Verfahren den Pressekammern der Landgerichte zugewiesen werden ($ 348 Abs. 1 Ziff. 21 ZPO). 59 | 60 | In diesem Zusammenhang sollte auch die Idee erwogen werden, bereits bestehende Strukturen zu nutzen: Wenn das Unterlassungsklagengesetz entsprechend ergänzt würde, etwa um einen $ 2c UKlagG, könnte ein Rechtsschutz effektiv erweitert werden. Die bisherige Handhabung, insbesondere durch den Bundesverband der Verbraucherschutzzentralen, zeigt, dass das Instrument einer Sammelklage durch eine qualifizierte Organisation sinnvoll und effektiv ist. Bereits ein Jahr nachdem $ 2 Absatz 2 Satz 1 Nummer 11 UKlagG ergänzt wurde, sind Verfahren zu den Obergerichten gelangt. 61 | 62 | ii. Die strafrechtliche Seite 63 | 64 | Neben dem zivilrechtlichen Bereich erfordert eine „effektive“ Durchsetzung der Normen und Werte auch, dass die Staatsanwaltschaften und Strafgerichte durch gründliche Ermittlungen und rechtsstaatliche Verfahren nicht nur einzelne Verstöße bestrafen, sondern auch generalpräventiv, also abschreckend wirken. Die aktuelle Praxis, z.B. im Datenschutz (Zusammenlegung personenbezogener Daten von WhatsApp und Facebook), zeigt, dass es an beweisbaren Sachverhalten bzw. gründlichen Ermittlungen oft fehlt. 65 | 1. Durch das Gesetz in der vorliegenden Fassung werden insoweit Doppelstrukturen geschaffen: Die $$ 86, 86a, 90, 90a, 111, 126, 130, 140, 166, 185 bis 187, 241 und 269 StGB - die auch im Entwurf genannt werden - bieten grundsätzlich ausreichende Grundlagen für Sanktionen. Hilfreich wäre aber eine Klarstellung, dass Anbieter von Telemediendiensten, die entsprechende Inhalte nicht löschen, auch als Mittäter oder Teilnehmer der genannten Delikte in Betracht kommen. Dass diejenigen, die z.B. als vertretungsberechtigtes Organ einer juristischen Person oder als vertretungsberechtigter Gesellschafter einer rechtsfähigen Personengesellschaft handeln, auch strafrechtlich verantwortlich sind, ergibt sich bereits aus $ 14 StGB; einer Sonderregelung für Anbieter von Telemediendiensten ist nicht erforderlich, eine Klarstellung würde’ reichen. 2. Die im Gesetzentwurf vorgesehene zusätzliche Bußgeldsanktion betrifft demgegenüber allgemeines organisatorisches Fehlverhalten und führt eine Verbandhaftung ein. Das mag sinnvoll sein, ersetzt aber nicht eine effektive, auch persönliche Haftung von handelnden Menschen. 66 | 67 | 3. Unklar ist in diesem Zusammenhang aber, was das in $ 4 Abs. 5 NetzDG vorgesehene Vorabprüfungsverfahren bewirken soll. Die Bußgeldtatbestände knüpfen gerade nicht daran an, dass der Anbieter die offensichtlich rechtswidrigen Inhalte (im Einzelfall) nicht löscht, sondern daran, dass er keine transparenten und effektiven Strukturen dafür zur Verfügung stellt. Welche Einzelentscheidung in den Strukturen schließlich getroffen werden, kann nicht Gegenstand eines Bußgeldverfahren sein, denn ein Tatbestand, der den Begriff der „offensichtlichen Rechtswidrigkeit“ verwendet, ist nicht hinreichend bestimmt i.S.v. Art 103 Abs. 2 GG und § 3 OWiG, um die Verurteilung zu einem Bußgeld zu tragen. Daran ändert es nichts, dass die Verwaltungsbehörde ein Gericht anrufen soll. Die gerichtliche Klarstellung kommt notwendigerweise nach dem vermeintlichen Verstoß. 68 | Wenn $ 4 Abs. 5 NetzDG so verstanden werden soll, dass vor jedem Bußgeldverfahren immer eine gerichtliche „Vorabentscheidung“ eingeholt werden muss, ist das bedenklich. Es ist schon unklar, um was für ein Verfahren es sich dabei handeln soll und inwieweit Betroffene und Dienstleister daran zu beteiligen sind. Zum anderen fragt sich, warum eine solche Vorabentscheidung notwendig ist. Denn gegen einen Bußgeldbescheid stünde den Dienstleistern ja immer der Rechtsschutz zu. Es ist schon dadurch gewährleistet, dass es letztlich den Gerichten überlassen bleibt, die Grenzen zu bestimmen. Ein Vorabentscheidungsverfahren würde das Bußgeldverfahren insgesamt „bürokratisch“ erschweren und die Gerichte mit Doppelverfahren belasten, ohne den Grundrechtsschutz zu verbessern. 69 | 70 | 4. Schließlich sollten auch hier bereits bestehende Regelungen bedacht werden: Eine Gewinnabschöpfung wie sie z.B. in $ 10 UWG geregelt ist, wäre jedenfalls ebenso effektiv wie die in 8 4 NetzDG vorgesehenen Bußgelder. Durch eine solche Ausgestaltung würde das Problem umgangen, dass die Bußgeldvorschriften nicht hinreichend bestimmt sind. 71 | 72 | Mit freundlichen Grüßen 73 | 74 | Carsten Löbbert 75 | 76 | Ruben Franzen, AG Eilenburg, Walther-Rathenau-Str. 9, 04838 Eilenburg, Tel.: 03423/654-330 (d.) 77 | Wilfried Hamm, Wilfried. Hamm@neuerichter.de, Kontakt über Bundesbiiro, Tel. 01575/8418000 78 | Werner Kannenberg, Special Chamber of the Supreme Court, Palace of Justice, Objekt A, Nekibe Keimendi Street, 10510 Hajvali/Kosovo, Tel.: 00377 4518 1246 (d.) 79 | Marianne Krause, AG Tempelhof-Kreuzberg, Möckernstr. 130, 10963 Berlin, Tel.: 030/90175256 (d.) 80 | Albert Lohmann, VG Gelsenkirchen, Bahnhofsvorplatz 3, 45879 Gelsenkirchen, Tel.: 0209/1701308 (d.) 81 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean5.md: -------------------------------------------------------------------------------- 1 | Zusammenschluss von Richterinnen und Richtern, 2 | Staatsanwältinnen und Staatsanwälten e.V. : Bundesvorstand 3 | 4 | 29.3.2017 5 | 6 | Neue Richtervereinigung e.V. | Greifswalder Str. 4 | 10405 Berlin 7 | / 8 | 9 | Bundesbüro: 10 | a . . 11 | Martina Reeßing, Verbraucherschutz Greifswalder Str. 4 Tel: 030 420223-49 12 | Fax: -50 13 | bb@neuerichter.de www.neuerichter.de 14 | 15 | VB 2 - 6100/61 - 5466/2017, dortiges Schreiben vom 14.3.2017 16 | 17 | (NetzDG) 18 | 19 | Sehr geehrter Herr Dr. Meyer-Seitz, 20 | vielen Dank für die Gelegenheit zur Stellungnahme. 21 | 22 | Die Neue Richtervereinigung (nrv) begrüßt die Initiative, gegen Hasskriminalität, Vergeltende Recht auch dort effektiver durchzusetzen. Aus Sicht der nrv geht es bei diesen der freien Meinungsäußerung. Es geht also um Abwägungen zweier wichtiger Grundrechte und die dafür notwendigen materiell-rechtlichen und prozessualen Instrumente. 23 | 24 | überlassen bleiben, die unbestimmten Rechtsbegriffe „rechtswidrige Inhalte“ und „offensichtlich rechtswidrige Inhalte“ in § 3 Abs. 2 NetzDG auszufüllen. Dabei kann und würde es aber nicht bleiben. Wie bei anderen unbestimmten Rechtsbegriffen auch werden 25 | 26 | Sprecherin und Sprecher des Vorstandes: 27 | 28 | Brigitte Kreuder-Sonnen, LG Lübeck, Am Burgfeld 7, 23568 Lübeck, Brigitte.Kreuder-Sonnen@neuerichter.de, Tel.: 0451/3711809 (d.) Carsten Löbbert, AG Lübeck, Am Burgfeld 7, 23568 Lübeck, Carsten.Loebbert@neuerichter.de, Tel. 0451/3711576 (d.) 29 | 30 | Weitere Mitglieder des Bundesvorstandes: 31 | 32 | Ruben Franzen, AG Eilenburg, Walther-Rathenau-Str. 9, 04838 Eilenburg, Tel.: 03423/654-330 (d.) 33 | Wilfried Hamm, Wilfried. Hamm@neuerichter.de, Kontakt über Bundesbiiro, Tel. 01575/8418000 34 | Werner Kannenberg, Special Chamber of the Supreme Court, Palace of Justice, Objekt A, Nekibe Keimendi Street, 10510 Hajvali/Kosovo, Tel.: 00377 4518 1246 (d.) 35 | Marianne Krause, AG Tempelhof-Kreuzberg, Möckernstr. 130, 10963 Berlin, Tel.: 030/90175256 (d.) 36 | Albert Lohmann, VG Gelsenkirchen, Bahnhofsvorplatz 3, 45879 Gelsenkirchen, Tel.: 0209/1701308 (d.) 37 | 38 | Aufgabe der Rechtsprechung, unbestimmte Rechtsbegriffe näher auszugestalten. Wir stimmen dem Gesetzentwurf in seiner Grundannahme zu, dass es Aufgabe der Gerichte ist, in einem rechtförmigen, transparenten und unabhängigen Verfahren, die Abwägungen im Einzelfall zu treffen und in diesem Rahmen ggf. einheitliche und vorhersehbare Maßstäbe zu entwickeln. 39 | 40 | Damit das gelingen kann, ist es aber erforderlich, ein wirksames materiell-rechtliches und ein handhabbares prozessuales Instrumentarium zu haben. Insoweit bringt der Entwurf nach unserer Auffassung zwar Verbesserungen, bleibt aber in wesentlichen Punkten unzureichend. Der Gesetzentwurf versäumt es, eine Rechtslage zu schaffen, die in den hier Mediengesellschaft ermöglicht. 41 | 42 | I. Die zivilrechtliche Seite 43 | 44 | Ein Kernpunkt des Entwurfes ist das in § 3 Abs. 2 NetzDG konzipierte Löschungsverfahren. Damit wird - aus unserer Sicht grundsätzlich richtig — ein zivilrechtliches Instrumentarium geschaffen, um Anbieter von Telemediendiensten zur Löschung zwingen zu können. In der bisherigen Form reicht es aber nicht aus, um im Rahmen der mittelbaren Drittwirkung der Grundrechte, für einen wirksamen und angemessenen Interessenausgleich zu sorgen: 45 | 46 | 1. Aus der Sicht der „Betroffenen“ geht es in erster Linie darum, Einträge mit falschen Tatsachenbehauptungen, Beschimpfungen, Beschuldigungen und Verleumdungen rasch aus den Netzwerken entfernt zu bekommen. Insoweit stellt die verpflichtende Einführung des in 47 | § 3 Abs. 2 NetzDG konzipierten Verfahrens grundsätzlich einen Fortschritt dar. Es ist sinnvoll, wie in $ 3 Abs. 2 Ziff. 2 NetzDG vorgesehen, offensichtlich rechtswidrige Inhalte innerhalb von 24 Stunden nach Eingang der Beschwerde zu löschen, andere Inhalte in einer Frist von 7 Tagen, $ 3 Abs. 2 Ziff. 3 NetzDG. Erst die längere Frist ermöglicht es, eine sinnvolle Kommunikation mit „Einstellern“ und „Beschwerdern“ zu betreiben. Selbstverständlich ist dabei auch die vorgesehene Benachrichtigung der Beteiligten, $ 3 Abs. 2 Ziff. 5 NetzDG. 48 | 49 | 2. Leider nicht durch das Gesetz geregelt wird aber, wie mit Meinungsverschiedenheiten und Streitigkeiten in dem Dreieckverhältnis Nutzer/Einsteller — Plattformbetreiber — Beschwerdeführer umgegangen werden soll. Genau in dieser Situation findet aber die mittelbare Drittwirkung. 50 | Soweit der Gesetzentwurf schweigt, werden die allgemeinen Regelungen zur Anwendung kommen. Der aus der vorletzten Jahrhundertwende stammende 8 1004 BGB (mit allen Analogien) ist für die hier relevanten Fragen aber völlig unzureichend: Nicht erst seit der Entscheidung des LG Würzburg vom 7.3.2017 (11 O 2338/16) ist deutlich geworden, dass derzeit die (zivilrechtliche) Rechtsstellung von Betroffenen unzureichend ist. Die in diesem Zusammenhang wesentlichen Fragen, nämlich 51 | 52 | - 53 | 54 | ob soziale Netzwerke wie ,Host-Provider* (§ 10 TMG) zu behandeln sind - 55 | 56 | auch ,proaktiv’ suchen muss 57 | 58 | - 59 | 60 | welche rechtliche Bedeutung „teilen“ und „liken“ haben müssen dringend geregelt werden. Dafür sollte der Gesetzgeber eine klare (gesetzliche) Anspruchsgrundlage für Betroffene gegen soziale Netzwerke schaffen auf Löschung, die die obigen Fragen regelt. Vertragliche Regelungen sind nicht ausreichend, da Betroffene nicht notwendiger Weise Verträge mit den Netzwerken haben müssen. 61 | 62 | Umgekehrt müssen Nutzer Schutzansprüche haben, falls Inhalte unberechtigt oder vorschnell gelöscht werden. Nur eine solche Ausgestaltung wird der Bedeutung der durch Art. 5 GG geschützten Grundrechte gerecht. 63 | Zu all dem verhält sich das NetzDG nicht. Es ist nicht einmal klar, ob $ 3 NetzDG als drittschützende Norm verstanden werden kann. Das ist insgesamt zu wenig. 64 | 65 | 3. Zudem muss auch der gerichtliche Rechtsschutz so ausgestaltet werden, dass eine rasche gerichtliche Entscheidung der ordentlichen Gerichte herbeigeführt werden kann. Richtigerweise liegt dem Gesetzentwurf die Annahme zugrunde, dass ein Streit im muss. Dafür muss es dann aber auch eine effektive Verfahrensart geben. Das Verfahren auf Erlass einer einstweiligen Verfügung ist dafür grundsätzlich geeignet. Da es sich hier aber typischerweise um „Dreiecksverhältnisse“ handelt, sollten prozessuale Möglichkeiten erwogen werden, den „Dritten“ einzubeziehen. Damit die Gerichte die notwendigen Kompetenzen und Kapazitäten aufbauen können, sollten zudem Spezialzuständigkeiten vorgesehen werden, möglicherweise könnten solche Verfahren den Pressekammern der Landgerichte zugewiesen werden ($ 348 Abs. 1 Ziff. 21 ZPO). 66 | 67 | In diesem Zusammenhang sollte auch die Idee erwogen werden, bereits bestehende Strukturen zu nutzen: Wenn das Unterlassungsklagengesetz entsprechend ergänzt würde, etwa um einen $ 2c UKlagG, könnte ein Rechtsschutz effektiv erweitert werden. Die bisherige Handhabung, insbesondere durch den Bundesverband der Verbraucherschutzzentralen, zeigt, dass das Instrument einer Sammelklage durch eine qualifizierte Organisation sinnvoll und effektiv ist. Bereits ein Jahr nachdem $ 2 Absatz 2 Satz 1 Nummer 11 UKlagG ergänzt wurde, sind Verfahren zu den Obergerichten gelangt. 68 | 69 | ii. Die strafrechtliche Seite 70 | 71 | Neben dem zivilrechtlichen Bereich erfordert eine „effektive“ Durchsetzung der Normen und Werte auch, dass die Staatsanwaltschaften und Strafgerichte durch gründliche Ermittlungen und rechtsstaatliche Verfahren nicht nur einzelne Verstöße bestrafen, sondern auch generalpräventiv, also abschreckend wirken. Die aktuelle Praxis, z.B. im Datenschutz (Zusammenlegung personenbezogener Daten von WhatsApp und Facebook), zeigt, dass es an beweisbaren Sachverhalten bzw. gründlichen Ermittlungen oft fehlt. 72 | 1. Durch das Gesetz in der vorliegenden Fassung werden insoweit Doppelstrukturen geschaffen: Die $$ 86, 86a, 90, 90a, 111, 126, 130, 140, 166, 185 bis 187, 241 und 269 StGB - die auch im Entwurf genannt werden - bieten grundsätzlich ausreichende Grundlagen für Sanktionen. Hilfreich wäre aber eine Klarstellung, dass Anbieter von Telemediendiensten, die entsprechende Inhalte nicht löschen, auch als Mittäter oder Teilnehmer der genannten Delikte in Betracht kommen. Dass diejenigen, die z.B. als Gesellschafter einer rechtsfähigen Personengesellschaft handeln, auch strafrechtlich verantwortlich sind, ergibt sich bereits aus $ 14 StGB; einer Sonderregelung für Anbieter von Telemediendiensten ist nicht erforderlich, eine Klarstellung würde’ reichen. 73 | 74 | die richtige Richtung; noch besser wäre es, einen inländischen Vertreter oder Bevollmächtigten i.S.v. &$ 14 StGB zu verlangen. 75 | 76 | 2. Die im Gesetzentwurf vorgesehene zusätzliche Bußgeldsanktion betrifft demgegenüber allgemeines organisatorisches Fehlverhalten und führt eine Verbandhaftung ein. Das mag sinnvoll sein, ersetzt aber nicht eine effektive, auch persönliche Haftung von handelnden Menschen. 77 | 78 | 3. Unklar ist in diesem Zusammenhang aber, was das in $ 4 Abs. 5 NetzDG vorgesehene Vorabprüfungsverfahren bewirken soll. Die Bußgeldtatbestände knüpfen gerade nicht daran an, dass der Anbieter die offensichtlich rechtswidrigen Inhalte (im Einzelfall) nicht löscht, sondern daran, dass er keine transparenten und effektiven Strukturen dafür zur Verfügung stellt. Welche Einzelentscheidung in den Strukturen schließlich getroffen werden, kann nicht Gegenstand eines Bußgeldverfahren sein, denn ein Tatbestand, der den Begriff der „offensichtlichen Rechtswidrigkeit“ verwendet, ist nicht hinreichend bestimmt i.S.v. Art 103 Abs. 2 GG und § 3 OWiG, um die Verurteilung zu einem Bußgeld zu tragen. Daran ändert es nichts, dass die Verwaltungsbehörde ein Gericht anrufen soll. Die gerichtliche Klarstellung kommt notwendigerweise nach dem vermeintlichen Verstoß. 79 | Wenn $ 4 Abs. 5 NetzDG so verstanden werden soll, dass vor jedem Bußgeldverfahren immer eine gerichtliche „Vorabentscheidung“ eingeholt werden muss, ist das bedenklich. Es ist schon unklar, um was für ein Verfahren es sich dabei handeln soll und inwieweit Betroffene und Dienstleister daran zu beteiligen sind. Zum anderen fragt sich, warum eine solche Vorabentscheidung notwendig ist. Denn gegen einen Bußgeldbescheid stünde den Dienstleistern ja immer der Rechtsschutz zu. Es ist schon dadurch gewährleistet, dass es letztlich den Gerichten überlassen bleibt, die Grenzen zu bestimmen. Ein Vorabentscheidungsverfahren würde das Bußgeldverfahren insgesamt „bürokratisch“ erschweren und die Gerichte mit Doppelverfahren belasten, ohne den Grundrechtsschutz zu verbessern. 80 | 81 | 4. Schließlich sollten auch hier bereits bestehende Regelungen bedacht werden: Eine Gewinnabschöpfung wie sie z.B. in $ 10 UWG geregelt ist, wäre jedenfalls ebenso effektiv wie die in 8 4 NetzDG vorgesehenen Bußgelder. Durch eine solche Ausgestaltung würde das Problem umgangen, dass die Bußgeldvorschriften nicht hinreichend bestimmt sind. 82 | 83 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean6.md: -------------------------------------------------------------------------------- 1 | Neue Richtervereinigung 2 | Zusammenschluss von Richterinnen und Richtern, 3 | Staatsanwältinnen und Staatsanwälten e.V. : Bundesvorstand 4 | 5 | Neue Richtervereinigung e.V. | Greifswalder Str. 4 | 10405 Berlin 6 | 7 | te a . . 8 | Martina Reeßing, 9 | Bundesministerium der Justiz und für Leiterin des Bundesbüros 10 | Verbraucherschutz Greifswalder Str. 4 11 | 10405 Berlin 12 | Mohrenstraße 37 13 | Tel: 030 420223-49 11117 Berlin Mobil 0176 567 996 48 14 | 15 | VB 2 - 6100/61 - 5466/2017, dortiges Schreiben vom 14.3.2017 Referentenentwurf des Bundesministeriums der Justiz und für Verbraucherschutz 16 | 17 | Entwurf eines Gesetzes zur besseren Rechtsdurchsetzung in sozialen Netzwerken 18 | 19 | Sehr geehrter Herr Dr. Meyer-Seitz, 20 | vielen Dank für die Gelegenheit zur Stellungnahme. 21 | 22 | Die Neue Richtervereinigung (nrv) begrüßt die Initiative, gegen Hasskriminalität, Verleumdungen und Fake News in sozialen Netzwerken strenger vorzugehen und damit das geltende Recht auch dort effektiver durchzusetzen. Aus Sicht der nrv geht es bei diesen Fragen nicht nur um einen wirksamen Persönlichkeitsschutz sondern auch um einen Schutz der freien Meinungsäußerung. Es geht also um Abwägungen zweier wichtiger Grundrechte und die dafür notwendigen materiell-rechtlichen und prozessualen Instrumente. 23 | 24 | Nach dem vorliegenden Entwurf würde es zunächst dem Anbieter von Telemediendiensten überlassen bleiben, die unbestimmten Rechtsbegriffe „rechtswidrige Inhalte“ und „offensichtlich rechtswidrige Inhalte“ in § 3 Abs. 2 NetzDG auszufüllen. Dabei kann und würde es aber nicht bleiben. Wie bei anderen unbestimmten Rechtsbegriffen auch werden Auseinandersetzungen im Rechtsverkehr zu gerichtlichen Verfahren führen und es ist dann 25 | 26 | Sprecherin und Sprecher des Vorstandes: 27 | 28 | Brigitte Kreuder-Sonnen, LG Lübeck, Am Burgfeld 7, 23568 Lübeck, Brigitte.Kreuder-Sonnen@neuerichter.de, Tel.: 0451/3711809 (d.) Carsten Löbbert, AG Lübeck, Am Burgfeld 7, 23568 Lübeck, Carsten.Loebbert@neuerichter.de, Tel. 0451/3711576 (d.) 29 | 30 | Weitere Mitglieder des Bundesvorstandes: 31 | 32 | Ruben Franzen, AG Eilenburg, Walther-Rathenau-Str. 9, 04838 Eilenburg, Tel.: 03423/654-330 (d.) Wilfried Hamm, Wilfried. Hamm@neuerichter.de, Kontakt über Bundesbiiro, Tel. 01575/8418000 Werner Kannenberg, Special Chamber of the Supreme Court, Palace of Justice, Objekt A, Nekibe Keimendi Street, 10510 Hajvali/Kosovo, Tel.: 00377 4518 1246 (d.) 33 | Marianne Krause, AG Tempelhof-Kreuzberg, Möckernstr. 130, 10963 Berlin, Tel.: 030/90175256 (d.) Albert Lohmann, VG Gelsenkirchen, Bahnhofsvorplatz 3, 45879 Gelsenkirchen, Tel.: 0209/1701308 (d.) 34 | 35 | Aufgabe der Rechtsprechung, unbestimmte Rechtsbegriffe näher auszugestalten. Wir stimmen dem Gesetzentwurf in seiner Grundannahme zu, dass es Aufgabe der Gerichte ist, in einem rechtförmigen, transparenten und unabhängigen Verfahren, die Abwägungen im Einzelfall zu treffen und in diesem Rahmen ggf. einheitliche und vorhersehbare Maßstäbe zu 36 | 37 | Damit das gelingen kann, ist es aber erforderlich, ein wirksames materiell-rechtliches und ein handhabbares prozessuales Instrumentarium zu haben. Insoweit bringt der Entwurf nach unserer Auffassung zwar Verbesserungen, bleibt aber in wesentlichen Punkten unzureichend. Der Gesetzentwurf versäumt es, eine Rechtslage zu schaffen, die in den hier relevanten Fragen eine angemessene Güterabwägung in einer modernen Mediengesellschaft ermöglicht. 38 | 39 | I. Die zivilrechtliche Seite 40 | 41 | Ein Kernpunkt des Entwurfes ist das in § 3 Abs. 2 NetzDG konzipierte Löschungsverfahren. Damit wird - aus unserer Sicht grundsätzlich richtig — ein zivilrechtliches Instrumentarium geschaffen, um Anbieter von Telemediendiensten zur Löschung zwingen zu können. In der bisherigen Form reicht es aber nicht aus, um im Rahmen der mittelbaren Drittwirkung der Grundrechte, für einen wirksamen und angemessenen Interessenausgleich zu sorgen: 42 | 43 | 1. Aus der Sicht der „Betroffenen“ geht es in erster Linie darum, Einträge mit falschen Tatsachenbehauptungen, Beschimpfungen, Beschuldigungen und Verleumdungen rasch aus den Netzwerken entfernt zu bekommen. Insoweit stellt die verpflichtende Einführung des in § 3 Abs. 2 NetzDG konzipierten Verfahrens grundsätzlich einen Fortschritt dar. Es ist sinnvoll, wie in $ 3 Abs. 2 Ziff. 2 NetzDG vorgesehen, offensichtlich rechtswidrige Inhalte innerhalb von 24 Stunden nach Eingang der Beschwerde zu löschen, andere Inhalte in einer Frist von 7 Tagen, $ 3 Abs. 2 Ziff. 3 NetzDG. Erst die längere Frist ermöglicht es, eine sinnvolle Kommunikation mit „Einstellern“ und „Beschwerdern“ zu betreiben. Selbstverständlich ist dabei auch die vorgesehene Benachrichtigung der Beteiligten, $ 3 Abs. 2 Ziff. 5 NetzDG. 44 | 45 | 2. Leider nicht durch das Gesetz geregelt wird aber, wie mit Meinungsverschiedenheiten und Streitigkeiten in dem Dreieckverhältnis Nutzer/Einsteller — Plattformbetreiber — Beschwerdeführer umgegangen werden soll. Genau in dieser Situation findet aber die Abwägung der verschieden Grundrechtspositionen statt und die Grundrechte entfalten die mittelbare Drittwirkung. 46 | Soweit der Gesetzentwurf schweigt, werden die allgemeinen Regelungen zur Anwendung kommen. Der aus der vorletzten Jahrhundertwende stammende 8 1004 BGB (mit allen Analogien) ist für die hier relevanten Fragen aber völlig unzureichend: Nicht erst seit der Entscheidung des LG Würzburg vom 7.3.2017 (11 O 2338/16) ist deutlich geworden, dass derzeit die (zivilrechtliche) Rechtsstellung von Betroffenen unzureichend ist. Die in diesem Zusammenhang wesentlichen Fragen, nämlich 47 | 48 | ob soziale Netzwerke wie ,Host-Provider* (§ 10 TMG) zu behandeln sind 49 | 50 | ob ein Betroffener jede einzelnen Fundstelle nachweisen oder das soziale Netzwerk auch ,proaktiv’ suchen muss 51 | 52 | welche rechtliche Bedeutung „teilen“ und „liken“ haben müssen dringend geregelt werden. Dafür sollte der Gesetzgeber eine klare (gesetzliche) Anspruchsgrundlage für Betroffene gegen soziale Netzwerke schaffen auf Löschung, die die obigen Fragen regelt. Vertragliche Regelungen sind nicht ausreichend, da Betroffene nicht notwendiger Weise Verträge mit den Netzwerken haben müssen. 53 | 54 | Umgekehrt müssen Nutzer Schutzansprüche haben, falls Inhalte unberechtigt oder vorschnell gelöscht werden. Nur eine solche Ausgestaltung wird der Bedeutung der durch Art. 5 GG geschützten Grundrechte gerecht. Zu all dem verhält sich das NetzDG nicht. Es ist nicht einmal klar, ob $ 3 NetzDG als drittschützende Norm verstanden werden kann. Das ist insgesamt zu wenig. 55 | 56 | 3. Zudem muss auch der gerichtliche Rechtsschutz so ausgestaltet werden, dass eine rasche gerichtliche Entscheidung der ordentlichen Gerichte herbeigeführt werden kann. Richtigerweise liegt dem Gesetzentwurf die Annahme zugrunde, dass ein Streit im grundrechtsrelevanten Bereich letztlich durch die unabhängige Justiz entschieden werden muss. Dafür muss es dann aber auch eine effektive Verfahrensart geben. Das Verfahren auf Erlass einer einstweiligen Verfügung ist dafür grundsätzlich geeignet. Da es sich hier aber typischerweise um „Dreiecksverhältnisse“ handelt, sollten prozessuale Möglichkeiten erwogen werden, den „Dritten“ einzubeziehen. Damit die Gerichte die notwendigen Kompetenzen und Kapazitäten aufbauen können, sollten zudem Spezialzuständigkeiten vorgesehen werden, möglicherweise könnten solche Verfahren den Pressekammern der Landgerichte zugewiesen werden ($ 348 Abs. 1 Ziff. 21 ZPO). 57 | 58 | In diesem Zusammenhang sollte auch die Idee erwogen werden, bereits bestehende Strukturen zu nutzen: Wenn das Unterlassungsklagengesetz entsprechend ergänzt würde, etwa um einen $ 2c UKlagG, könnte ein Rechtsschutz effektiv erweitert werden. Die bisherige Handhabung, insbesondere durch den Bundesverband der Verbraucherschutzzentralen, zeigt, dass das Instrument einer Sammelklage durch eine qualifizierte Organisation sinnvoll und effektiv ist. Bereits ein Jahr nachdem $ 2 Absatz 2 Satz 1 Nummer 11 UKlagG ergänzt wurde, sind Verfahren zu den Obergerichten gelangt. 59 | 60 | ii. Die strafrechtliche Seite 61 | 62 | Neben dem zivilrechtlichen Bereich erfordert eine „effektive“ Durchsetzung der Normen und Werte auch, dass die Staatsanwaltschaften und Strafgerichte durch gründliche Ermittlungen und rechtsstaatliche Verfahren nicht nur einzelne Verstöße bestrafen, sondern auch generalpräventiv, also abschreckend wirken. Die aktuelle Praxis, z.B. im Datenschutz (Zusammenlegung personenbezogener Daten von WhatsApp und Facebook), zeigt, dass es an beweisbaren Sachverhalten bzw. gründlichen Ermittlungen oft fehlt. 1. Durch das Gesetz in der vorliegenden Fassung werden insoweit Doppelstrukturen geschaffen: Die $$ 86, 86a, 90, 90a, 111, 126, 130, 140, 166, 185 bis 187, 241 und 269 StGB - die auch im Entwurf genannt werden - bieten grundsätzlich ausreichende Grundlagen für Sanktionen. Hilfreich wäre aber eine Klarstellung, dass Anbieter von Telemediendiensten, die entsprechende Inhalte nicht löschen, auch als Mittäter oder Teilnehmer der genannten Delikte in Betracht kommen. Dass diejenigen, die z.B. als vertretungsberechtigtes Organ einer juristischen Person oder als vertretungsberechtigter Gesellschafter einer rechtsfähigen Personengesellschaft handeln, auch strafrechtlich verantwortlich sind, ergibt sich bereits aus $ 14 StGB; einer Sonderregelung für Anbieter von Telemediendiensten ist nicht erforderlich, eine Klarstellung würde’ reichen. Der in § 5 Satz 1 NetzDG geforderte inländische Zustellungsbevollmächtigte ist ein Schritt in die richtige Richtung; noch besser wäre es, einen inländischen Vertreter oder Bevollmächtigten i.S.v. &$ 14 StGB zu verlangen. 63 | 64 | 2. Die im Gesetzentwurf vorgesehene zusätzliche Bußgeldsanktion betrifft demgegenüber allgemeines organisatorisches Fehlverhalten und führt eine Verbandhaftung ein. Das mag sinnvoll sein, ersetzt aber nicht eine effektive, auch persönliche Haftung von handelnden 65 | 66 | 3. Unklar ist in diesem Zusammenhang aber, was das in $ 4 Abs. 5 NetzDG vorgesehene Vorabprüfungsverfahren bewirken soll. Die Bußgeldtatbestände knüpfen gerade nicht daran an, dass der Anbieter die offensichtlich rechtswidrigen Inhalte (im Einzelfall) nicht löscht, sondern daran, dass er keine transparenten und effektiven Strukturen dafür zur Verfügung stellt. Welche Einzelentscheidung in den Strukturen schließlich getroffen werden, kann nicht Gegenstand eines Bußgeldverfahren sein, denn ein Tatbestand, der den Begriff der „offensichtlichen Rechtswidrigkeit“ verwendet, ist nicht hinreichend bestimmt i.S.v. Art 103 Abs. 2 GG und § 3 OWiG, um die Verurteilung zu einem Bußgeld zu tragen. Daran ändert es nichts, dass die Verwaltungsbehörde ein Gericht anrufen soll. Die gerichtliche Klarstellung kommt notwendigerweise nach dem vermeintlichen Verstoß. Wenn $ 4 Abs. 5 NetzDG so verstanden werden soll, dass vor jedem Bußgeldverfahren immer eine gerichtliche „Vorabentscheidung“ eingeholt werden muss, ist das bedenklich. Es ist schon unklar, um was für ein Verfahren es sich dabei handeln soll und inwieweit Betroffene und Dienstleister daran zu beteiligen sind. Zum anderen fragt sich, warum eine solche Vorabentscheidung notwendig ist. Denn gegen einen Bußgeldbescheid stünde den Dienstleistern ja immer der Rechtsschutz zu. Es ist schon dadurch gewährleistet, dass es letztlich den Gerichten überlassen bleibt, die Grenzen zu bestimmen. Ein Vorabentscheidungsverfahren würde das Bußgeldverfahren insgesamt „bürokratisch“ erschweren und die Gerichte mit Doppelverfahren belasten, ohne den Grundrechtsschutz zu 67 | 68 | 4. Schließlich sollten auch hier bereits bestehende Regelungen bedacht werden: Eine Gewinnabschöpfung wie sie z.B. in $ 10 UWG geregelt ist, wäre jedenfalls ebenso effektiv wie die in 8 4 NetzDG vorgesehenen Bußgelder. Durch eine solche Ausgestaltung würde das Problem umgangen, dass die Bußgeldvorschriften nicht hinreichend bestimmt sind. 69 | 70 | Mit freundlichen Grüßen 71 | 72 | Carsten Löbbert 73 | 74 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean7.md: -------------------------------------------------------------------------------- 1 | RECHTSANWALTSKAMMER KÖLN 2 | 3 | Bundesrechtsanwaltskammer 4 | Littenstr. 9 5 | 6 | 10179 Berlin 7 | 8 | K I StGB § 299 9 | K I StGB §§ 331 ff. Entwurf eines Gesetzes zur Bekampfung der Korruption - Referentenentwurf des BMJV 10 | 11 | Sehr geehrte Damen und Herren Kolleginnen und Kollegen, sehr geehrter Herr Kollege Johnigk, 12 | 13 | Bezug nehmend auf Ihr Schreiben vom 18.06.2014 möchte der Vorstand der Rechtsanwaltskammer Köln zum Referentenentwurf des BMJV folgendes anmerken: 14 | 15 | Der Referentenentwurf steht vor der Schwierigkeit, Vorgaben aus internationalen Abkommen umsetzen zu müssen, die selbst fragwürdig sind. Dies betrifft insbesondere die Tatbestandsausweitung der Norm des 8 299 StGB auf die Pflichtverletzung gegenüber dem Geschäftsherrn. Inwieweit es für eine international oder auch global geführte Sanktionierung von Korruption erforderlich sein soll, in § 299 StGB zusätzlich den Geschäftsherrn zu schützen, ist nicht ersichtlich. Eine Berührung internationaler Interessen bei fehlender Umsetzung des Geschäftsherrnmodells liegt fern, weil diese Interessen gerade durch das Wettbewerbsmodell geschützt werden. Die Tatbestandsausweitung hat mithin lediglich instrumentellen Charakter, dürfte aber noch im Rahmen des gesetzgeberischen Ermessensspielraums liegen, so dass verfassungsrechtliche Bedenken nicht erhoben werden können. Der Fehler für die unnötige Tatbestandsausweitung liegt mithin nicht im Referentenentwurf, sondern in der Vorarbeit zum Strafrechtsübereinkommen über Korruption des Europarats begründet. 16 | 17 | Die Aufnahme der Bestechung im geschäftlichen Verkehr in dem Vortatenkatalog des 8 261 StGB mag inzwischen zwingend sein (II, 3. der Begründung des Entwurfs). Hier ergeben sich allerdings verfassungsrechtliche Bedenken. 18 | 19 | Wir verweisen auf die Stellungnahme der BRAK zum Referentenentwurf eines Gesetzes zur Verbesserung der Bekämpfung der Geldwäsche und Steuerhinterziehung (BRAK-Stellungnahme-Nr. 34/2010). 20 | Dort ist zu Recht ausgeführt, die Gesetzgebungsgeschichte der Norm des 8 261 StGB lehre, dass bei deren Einführung Gesichtspunkte der Normenbestimmtheit und des Übermaßverbots gerade für die Aufstellung des Vortatenkatalogs eine entscheidende Rolle gespielt haben. Mit der seinerzeit vorgeschlagenen Ausweitung werde die enumerative Konstruktion des Katalogs praktisch aufgegeben (Blatt 4 der Stellungnahme). Dies gilt insbesondere unter dem Gesichtspunkt, dass sich nach empirischen Erkenntnissen zur Wirksamkeit des der Geldwäsche zugrunde liegenden Konzepts es sich in der Praxis als weitgehend wirkungslos erwiesen hat. Die hieraus folgende, sich auf den symbolischen Charakter beschränkende Aufwertung der „Vortat“ des 8 299 StGB führt zu dem Dilemma, dass es dem Rechtsstaat fremd und für ihn nicht begründbar ist, aus symbolischen Gründen strafen zu dürfen. 21 | 22 | Mithin führt die Aufnahme des § 299 StGB in den Vortatenkatalog - auch unter der Einschrankung auf besondere Tatbegehungsweisen - zu Bedenken gegenüber dem Bestimmtheitsgrundsatz und dem Rechtsstaatsprinzip. Die Aufnahme der Norm in den Vortatenkatalog ist deshalb - auch gegen die „zwingenden“ Vorgaben in Art. 2 Abs. 3 des EU-Rahmenbeschlusses - wegen Verfassungswidrigkeit zu unter- 23 | 24 | Mit freundlichen kollegialen Grüßen 25 | Vorstand der Rechtsanwaltskammer Köln 26 | 27 | RECHTSANWALTSKAMMER KÖLN - RIEHLER STRARE 30 - 50668 KÖLN TELEFON (0221) 97 30 10-0 28 | FAX (0221) 97 30 10-50 e-mail: kontakt@rak-koeln.de www.rak-koeln.de SPARKASSE KölnBonn 6 66 27 46 (BLZ 370 501 98) 29 | Latz 30 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean71.md: -------------------------------------------------------------------------------- 1 | Bundesrechtsanwaltskammer 2 | Littenstr. 9 3 | 4 | 10179 Berlin 5 | 6 | Köln, 11.08.2014 7 | 065-14brak01 L/B 8 | 9 | BRAK-Nr. 255/2014 10 | K I StGB § 299 11 | K I StGB §§ 331 ff. 12 | Entwurf eines Gesetzes zur Bekampfung der Korruption - Referentenentwurf des BMJV 13 | 14 | Sehr geehrte Damen und Herren Kolleginnen und Kollegen, 15 | sehr geehrter Herr Kollege Johnigk, 16 | Bezug nehmend auf Ihr Schreiben vom 18.06.2014 möchte der Vorstand der Rechtsanwaltskammer Köln zum Referentenentwurf des BMJV folgendes anmerken: 17 | 18 | 1; 19 | Der Referentenentwurf steht vor der Schwierigkeit, Vorgaben aus internationalen Abkommen umsetzen zu müssen, die selbst fragwürdig sind. Dies betrifft insbesondere die Tatbestandsausweitung der Norm des 8 299 StGB auf die Pflichtverletzung gegenüber dem Geschäftsherrn. Inwieweit es für eine international oder auch global geführte Sanktionierung von Korruption erforderlich sein soll, in § 299 StGB zusätzlich den Geschäftsherrn zu schützen, ist nicht ersichtlich. Eine Berührung internationaler Interessen bei fehlender Umsetzung des Geschäftsherrnmodells liegt fern, weil diese Interessen gerade durch das Wettbewerbsmodell geschützt werden. 20 | 21 | Die Tatbestandsausweitung hat mithin lediglich instrumentellen Charakter, dürfte aber noch im Rahmen des gesetzgeberischen Ermessensspielraums liegen, so dass verfassungsrechtliche Bedenken nicht erhoben werden können. Der Fehler für die unnötige Tatbestandsausweitung liegt mithin nicht im Referentenentwurf, sondern in der Vorarbeit zum Strafrechtsübereinkommen über Korruption des Europarats begründet. 22 | 23 | Die Aufnahme der Bestechung im geschäftlichen Verkehr in dem Vortatenkatalog des 8 261 StGB mag inzwischen zwingend sein (II, 3. der Begründung des Entwurfs). Hier ergeben sich allerdings verfassungsrechtliche Bedenken. 24 | 25 | Wir verweisen auf die Stellungnahme der BRAK zum Referentenentwurf eines Gesetzes zur Verbesserung der Bekämpfung der Geldwäsche und Steuerhinterziehung (BRAK-Stellungnahme-Nr. 34/2010). Dort ist zu Recht ausgeführt, die Gesetzgebungsgeschichte der Norm des 8 261 StGB lehre, dass bei deren Einführung Gesichtspunkte der Normenbestimmtheit und des Übermaßverbots gerade für die Aufstellung des Vortatenkatalogs eine entscheidende Rolle gespielt haben. Mit der seinerzeit vorgeschlagenen Ausweitung werde die enumerative Konstruktion des Katalogs praktisch aufgegeben (Blatt 4 der Stellungnahme). Dies gilt insbesondere unter dem Gesichtspunkt, dass sich nach empirischen Erkenntnissen zur Wirksamkeit des der Geldwäsche zugrunde liegenden Konzepts es sich in der Praxis als weitgehend wirkungslos erwiesen hat. Die hieraus folgende, sich auf den symbolischen Charakter beschränkende Aufwertung der „Vortat“ des 8 299 StGB führt zu dem Dilemma, dass es dem Rechtsstaat fremd und für ihn nicht begründbar ist, aus symbolischen Gründen strafen zu dürfen. 26 | 27 | Mithin führt die Aufnahme des § 299 StGB in den Vortatenkatalog - auch unter der Einschrankung auf besondere Tatbegehungsweisen - zu Bedenken gegenüber dem Bestimmtheitsgrundsatz und dem Rechtsstaatsprinzip. Die Aufnahme der Norm in den Vortatenkatalog ist deshalb - auch gegen die „zwingenden“ Vorgaben in Art. 2 Abs. 3 des EU-Rahmenbeschlusses - wegen Verfassungswidrigkeit zu unterlassen. 28 | 29 | Mit freundlichen kollegialen Grüßen 30 | Vorstand der Rechtsanwaltskammer Köln 31 | 32 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean8.md: -------------------------------------------------------------------------------- 1 | 29.3.2017 2 | 3 | Neue Richtervereinigung e.V. | Greifswalder Str. 4 | 10405 Berlin 4 | 5 | 6 | te 7 | Bundesbüro: 8 | a . . 9 | Martina Reeßing, Bundesministerium der Justiz und für 10 | Leiterin des Bundesbüros 11 | Verbraucherschutz Greifswalder Str. 4 12 | 10405 Berlin 13 | Mohrenstraße 37 14 | Tel: 030 420223-49 15 | Fax: -50 16 | 11117 Berlin 17 | Mobil 0176 567 996 48 18 | bb@neuerichter.de 19 | www.neuerichter.de 20 | 21 | VB 2 - 6100/61 - 5466/2017, dortiges Schreiben vom 14.3.2017 Referentenentwurf des Bundesministeriums der Justiz und für Verbraucherschutz 22 | 23 | Entwurf eines Gesetzes zur besseren Rechtsdurchsetzung in sozialen Netzwerken (NetzDG) 24 | 25 | Sehr geehrter Herr Dr. Meyer-Seitz, 26 | vielen Dank für die Gelegenheit zur Stellungnahme. 27 | 28 | Die Neue Richtervereinigung (nrv) begrüßt die Initiative, gegen Hasskriminalität, Verleumdungen und Fake News in sozialen Netzwerken strenger vorzugehen und damit das geltende Recht auch dort effektiver durchzusetzen. Aus Sicht der nrv geht es bei diesen Fragen nicht nur um einen wirksamen Persönlichkeitsschutz sondern auch um einen Schutz der freien Meinungsäußerung. Es geht also um Abwägungen zweier wichtiger Grundrechte und die dafür notwendigen materiell-rechtlichen und prozessualen Instrumente. 29 | 30 | Nach dem vorliegenden Entwurf würde es zunächst dem Anbieter von Telemediendiensten überlassen bleiben, die unbestimmten Rechtsbegriffe „rechtswidrige Inhalte“ und „offensichtlich rechtswidrige Inhalte“ in § 3 Abs. 2 NetzDG auszufüllen. Dabei kann und würde es aber nicht bleiben. Wie bei anderen unbestimmten Rechtsbegriffen auch werden Auseinandersetzungen im Rechtsverkehr zu gerichtlichen Verfahren führen und es ist dann 31 | 32 | Sprecherin und Sprecher des Vorstandes: 33 | 34 | Brigitte Kreuder-Sonnen, LG Lübeck, Am Burgfeld 7, 23568 Lübeck, Brigitte.Kreuder-Sonnen@neuerichter.de, Tel.: 0451/3711809 (d.) Carsten Löbbert, AG Lübeck, Am Burgfeld 7, 23568 Lübeck, Carsten.Loebbert@neuerichter.de, Tel. 0451/3711576 (d.) 35 | 36 | Weitere Mitglieder des Bundesvorstandes: 37 | 38 | Ruben Franzen, AG Eilenburg, Walther-Rathenau-Str. 9, 04838 Eilenburg, Tel.: 03423/654-330 (d.) 39 | Wilfried Hamm, Wilfried. Hamm@neuerichter.de, Kontakt über Bundesbiiro, Tel. 01575/8418000 40 | Werner Kannenberg, Special Chamber of the Supreme Court, Palace of Justice, Objekt A, Nekibe Keimendi Street, 10510 Hajvali/Kosovo, Tel.: 00377 4518 1246 (d.) 41 | Marianne Krause, AG Tempelhof-Kreuzberg, Möckernstr. 130, 10963 Berlin, Tel.: 030/90175256 (d.) 42 | Albert Lohmann, VG Gelsenkirchen, Bahnhofsvorplatz 3, 45879 Gelsenkirchen, Tel.: 0209/1701308 (d.) 43 | 44 | Aufgabe der Rechtsprechung, unbestimmte Rechtsbegriffe näher auszugestalten. Wir stimmen dem Gesetzentwurf in seiner Grundannahme zu, dass es Aufgabe der Gerichte ist, in einem rechtförmigen, transparenten und unabhängigen Verfahren, die Abwägungen im Einzelfall zu treffen und in diesem Rahmen ggf. einheitliche und vorhersehbare Maßstäbe zu entwickeln. 45 | 46 | Damit das gelingen kann, ist es aber erforderlich, ein wirksames materiell-rechtliches und ein handhabbares prozessuales Instrumentarium zu haben. Insoweit bringt der Entwurf nach unserer Auffassung zwar Verbesserungen, bleibt aber in wesentlichen Punkten unzureichend. Der Gesetzentwurf versäumt es, eine Rechtslage zu schaffen, die in den hier relevanten Fragen eine angemessene Güterabwägung in einer modernen Mediengesellschaft ermöglicht. 47 | 48 | I. Die zivilrechtliche Seite 49 | 50 | Ein Kernpunkt des Entwurfes ist das in § 3 Abs. 2 NetzDG konzipierte Löschungsverfahren. Damit wird - aus unserer Sicht grundsätzlich richtig — ein zivilrechtliches Instrumentarium geschaffen, um Anbieter von Telemediendiensten zur Löschung zwingen zu können. In der bisherigen Form reicht es aber nicht aus, um im Rahmen der mittelbaren Drittwirkung der Grundrechte, für einen wirksamen und angemessenen Interessenausgleich zu sorgen: 51 | 52 | 1. Aus der Sicht der „Betroffenen“ geht es in erster Linie darum, Einträge mit falschen Tatsachenbehauptungen, Beschimpfungen, Beschuldigungen und Verleumdungen rasch aus den Netzwerken entfernt zu bekommen. Insoweit stellt die verpflichtende Einführung des in 53 | § 3 Abs. 2 NetzDG konzipierten Verfahrens grundsätzlich einen Fortschritt dar. Es ist sinnvoll, wie in $ 3 Abs. 2 Ziff. 2 NetzDG vorgesehen, offensichtlich rechtswidrige Inhalte innerhalb von 24 Stunden nach Eingang der Beschwerde zu löschen, andere Inhalte in einer Frist von 7 Tagen, $ 3 Abs. 2 Ziff. 3 NetzDG. Erst die längere Frist ermöglicht es, eine sinnvolle Kommunikation mit „Einstellern“ und „Beschwerdern“ zu betreiben. Selbstverständlich ist dabei auch die vorgesehene Benachrichtigung der Beteiligten, $ 3 Abs. 2 Ziff. 5 NetzDG. 54 | 55 | 2. Leider nicht durch das Gesetz geregelt wird aber, wie mit Meinungsverschiedenheiten und Streitigkeiten in dem Dreieckverhältnis Nutzer/Einsteller — Plattformbetreiber — Beschwerdeführer umgegangen werden soll. Genau in dieser Situation findet aber die Abwägung der verschieden Grundrechtspositionen statt und die Grundrechte entfalten die mittelbare Drittwirkung. 56 | Soweit der Gesetzentwurf schweigt, werden die allgemeinen Regelungen zur Anwendung kommen. Der aus der vorletzten Jahrhundertwende stammende 8 1004 BGB (mit allen Analogien) ist für die hier relevanten Fragen aber völlig unzureichend: Nicht erst seit der Entscheidung des LG Würzburg vom 7.3.2017 (11 O 2338/16) ist deutlich geworden, dass derzeit die (zivilrechtliche) Rechtsstellung von Betroffenen unzureichend ist. Die in diesem Zusammenhang wesentlichen Fragen, nämlich 57 | 58 | ob soziale Netzwerke wie ,Host-Provider* (§ 10 TMG) zu behandeln sind 59 | 60 | ob ein Betroffener jede einzelnen Fundstelle nachweisen oder das soziale Netzwerk auch ,proaktiv’ suchen muss 61 | 62 | welche rechtliche Bedeutung „teilen“ und „liken“ haben müssen dringend geregelt werden. Dafür sollte der Gesetzgeber eine klare (gesetzliche) Anspruchsgrundlage für Betroffene gegen soziale Netzwerke schaffen auf Löschung, die die obigen Fragen regelt. Vertragliche Regelungen sind nicht ausreichend, da Betroffene nicht notwendiger Weise Verträge mit den Netzwerken haben müssen. 63 | 64 | Umgekehrt müssen Nutzer Schutzansprüche haben, falls Inhalte unberechtigt oder vorschnell gelöscht werden. Nur eine solche Ausgestaltung wird der Bedeutung der durch Art. 5 GG geschützten Grundrechte gerecht. 65 | Zu all dem verhält sich das NetzDG nicht. Es ist nicht einmal klar, ob $ 3 NetzDG als drittschützende Norm verstanden werden kann. Das ist insgesamt zu wenig. 66 | 67 | 3. Zudem muss auch der gerichtliche Rechtsschutz so ausgestaltet werden, dass eine rasche gerichtliche Entscheidung der ordentlichen Gerichte herbeigeführt werden kann. Richtigerweise liegt dem Gesetzentwurf die Annahme zugrunde, dass ein Streit im grundrechtsrelevanten Bereich letztlich durch die unabhängige Justiz entschieden werden muss. Dafür muss es dann aber auch eine effektive Verfahrensart geben. Das Verfahren auf Erlass einer einstweiligen Verfügung ist dafür grundsätzlich geeignet. Da es sich hier aber typischerweise um „Dreiecksverhältnisse“ handelt, sollten prozessuale Möglichkeiten erwogen werden, den „Dritten“ einzubeziehen. Damit die Gerichte die notwendigen Kompetenzen und Kapazitäten aufbauen können, sollten zudem Spezialzuständigkeiten vorgesehen werden, möglicherweise könnten solche Verfahren den Pressekammern der Landgerichte zugewiesen werden ($ 348 Abs. 1 Ziff. 21 ZPO). 68 | 69 | In diesem Zusammenhang sollte auch die Idee erwogen werden, bereits bestehende Strukturen zu nutzen: Wenn das Unterlassungsklagengesetz entsprechend ergänzt würde, etwa um einen $ 2c UKlagG, könnte ein Rechtsschutz effektiv erweitert werden. Die bisherige Handhabung, insbesondere durch den Bundesverband der Verbraucherschutzzentralen, zeigt, dass das Instrument einer Sammelklage durch eine qualifizierte Organisation sinnvoll und effektiv ist. Bereits ein Jahr nachdem $ 2 Absatz 2 Satz 1 Nummer 11 UKlagG ergänzt wurde, sind Verfahren zu den Obergerichten gelangt. 70 | 71 | ii. Die strafrechtliche Seite 72 | 73 | Neben dem zivilrechtlichen Bereich erfordert eine „effektive“ Durchsetzung der Normen und Werte auch, dass die Staatsanwaltschaften und Strafgerichte durch gründliche Ermittlungen und rechtsstaatliche Verfahren nicht nur einzelne Verstöße bestrafen, sondern auch generalpräventiv, also abschreckend wirken. Die aktuelle Praxis, z.B. im Datenschutz (Zusammenlegung personenbezogener Daten von WhatsApp und Facebook), zeigt, dass es an beweisbaren Sachverhalten bzw. gründlichen Ermittlungen oft fehlt. 74 | 1. Durch das Gesetz in der vorliegenden Fassung werden insoweit Doppelstrukturen geschaffen: Die $$ 86, 86a, 90, 90a, 111, 126, 130, 140, 166, 185 bis 187, 241 und 269 StGB - die auch im Entwurf genannt werden - bieten grundsätzlich ausreichende Grundlagen für Sanktionen. Hilfreich wäre aber eine Klarstellung, dass Anbieter von Telemediendiensten, die entsprechende Inhalte nicht löschen, auch als Mittäter oder Teilnehmer der genannten Delikte in Betracht kommen. Dass diejenigen, die z.B. als vertretungsberechtigtes Organ einer juristischen Person oder als vertretungsberechtigter Gesellschafter einer rechtsfähigen Personengesellschaft handeln, auch strafrechtlich verantwortlich sind, ergibt sich bereits aus $ 14 StGB; einer Sonderregelung für Anbieter von Telemediendiensten ist nicht erforderlich, eine Klarstellung würde’ reichen. 75 | 76 | 2. Die im Gesetzentwurf vorgesehene zusätzliche Bußgeldsanktion betrifft demgegenüber allgemeines organisatorisches Fehlverhalten und führt eine Verbandhaftung ein. Das mag sinnvoll sein, ersetzt aber nicht eine effektive, auch persönliche Haftung von handelnden Menschen. 77 | 78 | 3. Unklar ist in diesem Zusammenhang aber, was das in $ 4 Abs. 5 NetzDG vorgesehene Vorabprüfungsverfahren bewirken soll. Die Bußgeldtatbestände knüpfen gerade nicht daran an, dass der Anbieter die offensichtlich rechtswidrigen Inhalte (im Einzelfall) nicht löscht, sondern daran, dass er keine transparenten und effektiven Strukturen dafür zur Verfügung stellt. Welche Einzelentscheidung in den Strukturen schließlich getroffen werden, kann nicht Gegenstand eines Bußgeldverfahren sein, denn ein Tatbestand, der den Begriff der „offensichtlichen Rechtswidrigkeit“ verwendet, ist nicht hinreichend bestimmt i.S.v. Art 103 Abs. 2 GG und § 3 OWiG, um die Verurteilung zu einem Bußgeld zu tragen. Daran ändert es nichts, dass die Verwaltungsbehörde ein Gericht anrufen soll. Die gerichtliche Klarstellung kommt notwendigerweise nach dem vermeintlichen Verstoß. 79 | Wenn $ 4 Abs. 5 NetzDG so verstanden werden soll, dass vor jedem Bußgeldverfahren immer eine gerichtliche „Vorabentscheidung“ eingeholt werden muss, ist das bedenklich. Es ist schon unklar, um was für ein Verfahren es sich dabei handeln soll und inwieweit Betroffene und Dienstleister daran zu beteiligen sind. Zum anderen fragt sich, warum eine solche Vorabentscheidung notwendig ist. Denn gegen einen Bußgeldbescheid stünde den Dienstleistern ja immer der Rechtsschutz zu. Es ist schon dadurch gewährleistet, dass es letztlich den Gerichten überlassen bleibt, die Grenzen zu bestimmen. Ein Vorabentscheidungsverfahren würde das Bußgeldverfahren insgesamt „bürokratisch“ erschweren und die Gerichte mit Doppelverfahren belasten, ohne den Grundrechtsschutz zu verbessern. 80 | 81 | 4. Schließlich sollten auch hier bereits bestehende Regelungen bedacht werden: Eine Gewinnabschöpfung wie sie z.B. in $ 10 UWG geregelt ist, wäre jedenfalls ebenso effektiv wie die in 8 4 NetzDG vorgesehenen Bußgelder. Durch eine solche Ausgestaltung würde das Problem umgangen, dass die Bußgeldvorschriften nicht hinreichend bestimmt sind. 82 | 83 | Mit freundlichen Grüßen 84 | 85 | Carsten Löbbert 86 | 87 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/clean9.md: -------------------------------------------------------------------------------- 1 | Stellungnahme des Interessenverband Deutscher Schauspieler e.V. (IDS) zum Referentenentwurf eines Gesetzes zur verbesserten Durchsetzung des Anspruchs der Urheber und ausübenden Künstler auf angemessene Vergütung 2 | 3 | A. Zum Gesetzentwurf im Allgemeinen 4 | 5 | Ausgangspunkt des Gesetzentwurfes ist die gestörte Vertragsparität zwischen den Urhebern und ausübenden Künstlern auf der einen, und den Verwertern ihrer Werke auf der anderen Seite. 6 | 7 | Dieser Missstand, den wir innerhalb der Filmbranche als regelmäßig und gravierend relevant bestätigen können, hat zu grundsätzlich unangemessenen Honoraren und Vertragsbedingungen geführt und denjenigen Urhebern und ausübenden Künstlern, die ihre Rechte in Anspruch genommen oder dieses versucht haben, regelmäßig erhebliche berufliche Nachteile und immensen Aufwand bei der Durchsetzung ihrer Ansprüche zugemutet. 8 | 9 | Der IDS bedankt sich daher ausdrücklich dafür, dass das BMJV diese Missstände nicht nur erkannt und benannt, sondern sich auch das Ziel gesetzt hat, die Vertragsparität der Urheber und ausübenden Künstler zu stärken. 10 | 11 | Im Wesentlichen soll die individualrechtliche Stellung der Urheber und ausübenden Künstler gestärkt und das Recht der gemeinsamen Vergütungsregeln reformiert werden. 12 | 13 | Im Grundsatz begrüßen wir die beabsichtigten Lösungsvorschläge. 14 | 15 | Der IDS hält jedoch, zumal für die Gruppe der ausübenden Künstler, zu denen die von uns vertretenen Schauspieler gehören, eine weitergehende Verbesserung der individualrechtlichen Stellung der Urheber und insbesondere der ausübenden Künstler für erforderlich, um dem Ziel, zu angemessen Vergütungen und Vertragsbedingungen zu gelangen, entscheidend näher zu kommen. 16 | 17 | In Bezug auf das Recht der gemeinsamen Vergütungsregeln erscheint uns ein weiterreichender Regelungsbedarf zu bestehen, da die vorgesehenen Maßnahmen nicht sicher stellen, dass die Vertreter der Interessen der Urheber und ausübenden Künstler die ihnen zugedachten Aufgaben erfüllen können. 18 | 19 | Zu den vorgesehenen flankierenden Rechtsänderungen sollte die faktische Gleichstellung der ausübenden Künstler mit den Urhebern mit aufgenommen und die Schlechterstellung der Filmschaffenden beseitigt werden. 20 | 21 | B. Zu den einzelnen Vorschriften 22 | Zu 8 31 Abs. 5 UrhG (Einräumung von Nutzungsrechten) 23 | 24 | Werden Nutzungsrechte durch eine Vielzahl von in Verträgen vorformulierten Vertragsbedingungen über den von beiden Parteien zu Grunde gelegten Vertragszweck hinaus eingeräumt, ist die Bestimmung zur Einräumung unwirksam, wenn sie den Urheber entgegen den Geboten von Treu und Glauben unangemessen benachteiligt. Eine unangemessene Benachteiligung kann sich insbesondere daraus ergeben, dass, 25 | 26 | a) eine angemessene Beteiligung des Urhebers für jede Nutzung nicht vorgesehen ist, b) die Bestimmung nicht klar und verständlich ist 27 | c) die Bestimmung nicht-werknahe Nutzungen regelt 28 | 29 | (Abs. c) trägt dem Umstand Rechnung, dass nicht-werknahe Nutzungen in der Regel als überraschende Klausel anzusehen sind und oft auch hinsichtlich des Urheberpersönlichkeitsrechtes problematisch sind — z.B. Klammerteilauswertung, sonstige werkfremde Nutzungen) 30 | 31 | Zu $ 32 Abs. 2 Satz 3 UrhG (Angemessene Vergütung) - Ergänzung 32 | 33 | Eine Vergütung nach Satz 2 ist in der Regel nur dann angemessen, wenn der Urheber für mehrfache Nutzungen desselben Werkes oder Teile dieses Werks, oder für die Einräumung unterschiedlicher Nutzungsrechte hinsichtlich desselben Werks Anspruch auf jeweils gesonderte Vergütung hat. 34 | 35 | Zu § 32 Abs. d UrhG (Anspruch auf Auskunft und Rechenschaft) - Ergänzungen 36 | 37 | Jeder Werknutzer hat dem Urheber Auskunft über den Umfang der Werknutzung und die hieraus gezogenen Erträge und Vorteile zu erteilen sowie hierüber Rechenschaft abzulegen. Auskunft und Rechenschaft sind auf Verlangen des Urhebers mindestens einmal jährlich zu erteilen. Dem Urheber ist die erstmals geplante Nutzung hinsichtlich jeder Nutzungsart im Voraus mitzuteilen. Von den Sätzen 1 und 2 kann zum Nachteil des Urhebers nur durch eine Vereinbarung abgewichen werden, die auf einer gemeinsamen Vergütungsregel ($ 36) oder einem Tarifvertrag beruht. Eine solche Vereinbarung ist nur wirksam, wenn dem Urheber die Ansprüche aus den Sätzen 1 und 2 jedenfalls gegenüber einem Dritten vollumfänglich zustehen. 38 | 39 | (Da der Urheber in der Regel die Werknutzer nicht kennt bzw. nicht kennen muss, ist es zwingend erforderlich, dass er von jedem Werknutzer über ihm nicht bereits bekannte Nutzungen seiner Werke unterrichtet wird, um seine Ansprüche aus den Sätzen 1 und 2 geltend machen zu können. 40 | 41 | Vereinbarungen in gemeinsamen Vergütungsregeln oder Tarifverträgen dürfen den Urheber nicht schlechter stellen. Es muss daher geregelt werden, dass solche Vereinbarungen nur dann wirksam sind, wenn sie das Informationsbedürfnis der Urheber in gleicher Weise erfüllen. So ist es z.B. denkbar, dass die betroffenen Nutzergruppen eine zentrale Informationsstelle (Datenbank) einrichten, die die normierten Verpflichtungen gegenüber allen Urhebern und ausübenden Künstlern übernimmt. Es muss aber sicher gestellt werden, dass der Urheber dann seine Ansprüche aus Satz 1 und 2 gegenüber diesen Einrichtungen geltend machen kann) 42 | 43 | Zu § 32b UrhG (Zwingende Anwendung) - Ergänzung Die §§ 32, 32a und 32 d finden zwingende Anwendung, 44 | 45 | 1. wenn auf den Nutzungsvertrag mangels einer Rechtswahl deutsches Recht anzuwenden wäre oder 46 | 47 | 2. soweit Gegenstand des Vertrages maßgebliche Nutzungshandiungen im räumlichen Geltungsbereich des Gesetzes sind. 48 | 49 | Zu 8 36 (Gemeinsame Vergütungsregeln) - Ergänzung 50 | Abs.1 Satz 2 (neu): 51 | Werknutzer sind auch Dritte, für die der Vertragspartner des Urhebers das Werk herstellt. (typischer Fall: Auftragsproduktionen von Verleihern/Sendern/Mehrzahl von Sendern) 52 | 53 | Zu 8 36 (Gemeinsame Vergütungsregeln) — Ergänzung Abs.2 Satz 2 (neu): 54 | 55 | Eine Vereinigung von Werknutzern gilt im Sinne des Satz 1 als ermächtigt, wenn sie eigene Empfehlungen zu Urhebervergütungen erarbeitet oder sich in vergleichbarer Weise mit Urhebervergütungen befasst. 56 | 57 | Zu & 36 (Gemeinsame Vergütungsregeln) — Ergänzung Abs.5 (neu): 58 | 59 | Innerhalb von drei Monaten nach Zustellung der förmlichen Zustellung der Schlichtungsstelle, dass ein Einigungsvorschlag nicht angenommen worden ist, kann jede Partei bei dem nach $ 16 Abs. 4 Satz 1 des UrhWG (zukünftig $ 129 VGG) zuständigen OLG im ersten Rechtszug Antrag auf Prüfung der Angemessenheit der im Einigungsvorschlag vorgesehenen Mindestvergütung und anderen Mindestbedingungen stellen. 60 | 61 | Zu 8 36b (Unterlassungsanspruch bei Verstoß gegen gem. Vergütungsregeln) — Ergänzung Abs. 1: 62 | 63 | Wer in einem Vertrag mit einem Urheber eine Bestimmung verwendet, die zum Nachteil des Urhebers von gemeinsamen Vergütungsregeln abweicht, kann auf Beseitigung und Unterlassung in Anspruch genommen werden, wenn er 64 | 65 | 1. als Werknutzer die Vergütungsregeln selbst aufgestellt hat oder 66 | 67 | 2. Mitglied einer Vereinigung von Werknutzern ist, die die gemeinsamen Vergütungsregeln aufgestellt haben. 68 | 69 | Der Anspruch auf Unterlassung steht denjenigen Vereinigungen von Urhebern und Werknutzern und denjenigen einzelnen Werknutzern zu, die die gemeinsamen Vergütungsregeln aufgestellt haben oder für deren Mitglieder die gemeinsamen Vergütungsregeln gelten. 70 | 71 | (Es ist m.E. vertretbar, dass Urhebervereinigungen repräsentativ sein müssen, um Gemeinsame Vergütungsregeln aufzustellen. Dieses Erfordernis fehlt jedoch bei der Geltendmachung von Unterlassungsansprüchen und würde kleinere Verbände von Urhebern unangemessen benachteiligen und deren Mitglieder diskriminieren) 72 | 73 | Zu 8 36b (Unterlassungsanspruch bei Verstoß gegen gem. Vergütungsregeln) — Ergänzung Abs. 2: 74 | 75 | Auf das Verfahren ist § 8 Abs. 3 Nr. 1 und Nr. 2 sowie 8 12 Abs. 1,2, 4 und 5 des Gesetzes gegen den unlauteren Wettbewerb anzuwenden. Für die Bekanntmachung des Urteils gilt $ 103. 76 | 77 | Zu § 36c (Individualvertragl. Folgen des Verstoßes gegen gem. Vergütungsregeln) — Ergänzung Abs. 2 (neu): 78 | Die Verjährung des Anspruchs auf Zahlung einer gemeinsamen Vergütung wird gehemmt: 79 | 80 | 1. durch die Aufnahme von Verhandlungen zur Bestimmung der Angemessenheit von Vergütungen nach 8 36, solange diese Verhandlungen andauern und soweit die Parteien eines Nutzungsvertrages in dem Verfahren vertreten sind; oder 81 | 82 | 2. wenn die Parteien wechselseitig erklären, dass sie das Ergebnis von noch laufenden Vergütungsverhandlungen als verbindlich anerkennen werden; oder 83 | 84 | 3. wenn ein auch die Parteien des Nutzungsvertrages betreffendes Verfahren nach $ 36a Abs. 3 anhangig ist. 85 | 86 | Die §§ 203, 204 des Burgerlichen Gesetzbuches bleiben unberinhrt. (gleichlautend mit $ 32 Abs. 6 des Kölner Entwurfs) 87 | 88 | Zu 8 79 (Nutzungsrechte der ausübenden Künstler) 89 | Abs. 1: Streichung (des Abs. 1) 90 | Abs 2: Ergänzung 91 | Die 88 31 bis 42 und 43 sind entsprechend anzuwenden. 92 | 93 | (eine Ergänzung des § 79b Abs. 1 Satz 2 in Bezug auf die Hinzunahme von Verwertungseinrichtungen lehnt der IDS entschieden ab, da derartige Einrichtungen weitgehend der Kontrolle der Urheber und des Aufsichtsamtes entzogen sind). 94 | 95 | Zu § 89 Abs. 2 (Rechte am Filmwerk) — Neufassung Abs. 2 (neu): 96 | 97 | Der Urheber des Filmwerkes kann ein in Absatz 1 bezeichnetes Nutzungsrecht hinsichtlich der Verwertung des Filmwerkes im Voraus nur dem Filmhersteller oder einer Verwertungsgesellschaft einräumen. 98 | 99 | Zu & 92 (Ausübende Künstler) — Neufassung Abs. 2: 100 | 101 | Der ausübende Künstler kann ein in Absatz 1 bezeichnetes Nutzungsrecht hinsichtlich der Verwertung des Filmwerkes im Voraus nur dem Filmhersteller oder einer Verwertungsgesellschaft einräumen. 102 | 103 | C. Fazit 104 | 105 | Dem Gesetzentwurf liegt die erkennbare Absicht zu Grunde, die erkannten Defizite in Bezug auf die gestörte Vertragsparität zwischen Werknutzern und Urhebern und ausübenden Künstlern abzumildern. 106 | 107 | Der IDS sieht in den oben genannten Änderungsvorschlägen zusätzliche Schritte, um sich dem Ziel des Gesetzes noch etwas weiter anzunähern. 108 | 109 | Grundsätzlich vertritt der IDS die Auffassung, dass die urheberrechtlichen Ansprüche der Urheber und der ausübenden Künstler effektiv nur durch enge gesetzliche Vorgaben durchzusetzen sein werden, da sich in der Vergangenheit erwiesen hat, dass alle abtretbaren Rechte auch (weitgehend unentgeltlich) abgetreten werden müssen. 110 | 111 | Ergänzend hierzu halten wir bestimmte kollektivvertragliche Mechanismen für punktuell geeignet, um zu einer verbesserten Durchsetzung des Anspruchs der Urheber beizutragen. 112 | 113 | Es darf aber nicht aus dem Blick verloren werden, dass kollektivvertragliche Mechanismen nur dann zu Gunsten der betroffenen Urheber eingreifen können, wenn ihre Interessenvertretungen auf Augenhöhe mit ihren jeweiligen Verhandlungspartnern verhandeln können. 114 | 115 | In diesem Punkt sieht der IDS erhebliche Probleme, da sich auch Vereinigungen von Urhebern und ausübenden Künstlern der Einflussnahme der Gegenseite kaum entziehen können. 116 | 117 | „Blacklisting“ lässt sich ohne Weiteres auch auf Interessenvertreter besonders engagierter Verbände oder Gewerkschaften und deren Verbandsmitglieder anwenden. Ein Streikrecht gibt es faktisch nicht und Urheber und ausübende Künstler stehen im Bereich Film- und Fernsehen so gut wie nie in festen Arbeitsverhältnissen und genießen weitgehend keinen Kündigungsschutz. 118 | 119 | Es erscheint uns daher nicht sinnvoll, Verbänden und Gewerkschaften Aufgaben und Rechte zu übertragen, denen sie aus strukturellen Gründen nicht gewachsen sein können. 120 | 121 | Dies gilt insbesondere für die Wahrnehmung von Rechten, die ausschließlich Verwertungsgesellschaften vorbehalten sein sollten. Schon um Interessenkonflikte auszuschließen, sollten Verbände und Gewerkschaften bei der Bestimmung der angemessenen Vergütung mitwirken dürfen, die dem einzelnen Urheber bzw. ausübenden Künstler konkret zusteht, allerdings mit der Maßgabe, nicht bei der Wahrnehmung bzw. der Verteilung der Erlöse dieser Rechte involviert zu sein. 122 | 123 | Darüber hinaus würden wir es begrüßen, wenn im Urhebergesetz klar gestellt werden würde, dass eine gemeinsame Vergütungsregel für jeden Anspruchsberechtigten nachvollziehbar festlegen muss, welchen Anspruch er dem betroffenen Verwerter gegenüber besitzt. 124 | 125 | München, den 27. Dezember 2015 126 | 127 | Irina Wanka 128 | 1. Vorsitzende 129 | Interessenverband Deutscher Schauspieler e.V. (IDS) 130 | 131 | -------------------------------------------------------------------------------- /development/notebooks/05_new_tries/geo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 25, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from shapely.geometry import MultiPoint, box" 10 | ] 11 | }, 12 | { 13 | "cell_type": "code", 14 | "execution_count": 56, 15 | "metadata": {}, 16 | "outputs": [], 17 | "source": [ 18 | "def bbox(points):\n", 19 | " assert len(points) >= 4\n", 20 | " h = MultiPoint(points).convex_hull\n", 21 | " return box(*h.bounds)\n", 22 | "# new_points = convex_hull(*points).bounds\n", 23 | "# print(new_points)\n", 24 | "# return Polygon(new_points[:2], new_points[2:])" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 57, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "b1 = bbox([[0, 0], (1, 1), (1, 0), (0, 1)])" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 65, 39 | "metadata": {}, 40 | "outputs": [ 41 | { 42 | "data": { 43 | "text/plain": [ 44 | "1.0" 45 | ] 46 | }, 47 | "execution_count": 65, 48 | "metadata": {}, 49 | "output_type": "execute_result" 50 | } 51 | ], 52 | "source": [ 53 | "b1.area" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 59, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "b2 = bbox([[0, 0], (1, 0.5), (1, 0), (0, 0.5)])" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 64, 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "data": { 72 | "text/plain": [ 73 | "0.5" 74 | ] 75 | }, 76 | "execution_count": 64, 77 | "metadata": {}, 78 | "output_type": "execute_result" 79 | } 80 | ], 81 | "source": [ 82 | "b2.area" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 63, 88 | "metadata": {}, 89 | "outputs": [ 90 | { 91 | "data": { 92 | "text/plain": [ 93 | "0.5" 94 | ] 95 | }, 96 | "execution_count": 63, 97 | "metadata": {}, 98 | "output_type": "execute_result" 99 | } 100 | ], 101 | "source": [ 102 | "b1.intersection(b2).area" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 85, 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "def sim_bbox(e1, e2):\n", 112 | " b1 = bbox(e1)\n", 113 | " b2 = bbox(e2)\n", 114 | " shared_area = b1.intersection(b2).area\n", 115 | " return shared_area / max(b1.area, b2.area)" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 86, 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "data": { 125 | "text/plain": [ 126 | "0.5" 127 | ] 128 | }, 129 | "execution_count": 86, 130 | "metadata": {}, 131 | "output_type": "execute_result" 132 | } 133 | ], 134 | "source": [ 135 | "sim_bbox([[0, 0], (1, 1), (1, 1), (0, 1)], [[0, 0], (1, 0.5), (1, 0), (0, 0.5)])" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": null, 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [] 151 | } 152 | ], 153 | "metadata": { 154 | "kernelspec": { 155 | "display_name": "Python 3", 156 | "language": "python", 157 | "name": "python3" 158 | }, 159 | "language_info": { 160 | "codemirror_mode": { 161 | "name": "ipython", 162 | "version": 3 163 | }, 164 | "file_extension": ".py", 165 | "mimetype": "text/x-python", 166 | "name": "python", 167 | "nbconvert_exporter": "python", 168 | "pygments_lexer": "ipython3", 169 | "version": "3.8.4" 170 | } 171 | }, 172 | "nbformat": 4, 173 | "nbformat_minor": 4 174 | } 175 | -------------------------------------------------------------------------------- /development/notebooks/07_pd3f_api/Untitled.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 8, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import time\n", 10 | "\n", 11 | "import requests" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 14, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "files = {'pdf': ('test.pdf', open('/Users/user/Downloads/3kg5LtLygF4qdurCZPdt5b02628_04292015_Stellungnahme_Gravenbrucher_Kreis_RefE_Anfechtungsgesetz_dragged.pdf'))}" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 15, 26 | "metadata": {}, 27 | "outputs": [ 28 | { 29 | "ename": "UnicodeDecodeError", 30 | "evalue": "'utf-8' codec can't decode byte 0xbf in position 10: invalid start byte", 31 | "output_type": "error", 32 | "traceback": [ 33 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 34 | "\u001b[0;31mUnicodeDecodeError\u001b[0m Traceback (most recent call last)", 35 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mresponse\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrequests\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpost\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'http://localhost:1616'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfiles\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mfiles\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0;34m'lang'\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;34m'de'\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 36 | "\u001b[0;32m~/Library/Caches/pypoetry/virtualenvs/pdddf-rsXREmUU-py3.8/lib/python3.8/site-packages/requests/api.py\u001b[0m in \u001b[0;36mpost\u001b[0;34m(url, data, json, **kwargs)\u001b[0m\n\u001b[1;32m 117\u001b[0m \"\"\"\n\u001b[1;32m 118\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 119\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mrequest\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'post'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mjson\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 120\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 121\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", 37 | "\u001b[0;32m~/Library/Caches/pypoetry/virtualenvs/pdddf-rsXREmUU-py3.8/lib/python3.8/site-packages/requests/api.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(method, url, **kwargs)\u001b[0m\n\u001b[1;32m 59\u001b[0m \u001b[0;31m# cases, and look like a memory leak in others.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 60\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0msessions\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mSession\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0msession\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 61\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 62\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", 38 | "\u001b[0;32m~/Library/Caches/pypoetry/virtualenvs/pdddf-rsXREmUU-py3.8/lib/python3.8/site-packages/requests/sessions.py\u001b[0m in \u001b[0;36mrequest\u001b[0;34m(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)\u001b[0m\n\u001b[1;32m 514\u001b[0m \u001b[0mhooks\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mhooks\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 515\u001b[0m )\n\u001b[0;32m--> 516\u001b[0;31m \u001b[0mprep\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprepare_request\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreq\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 517\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 518\u001b[0m \u001b[0mproxies\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mproxies\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 39 | "\u001b[0;32m~/Library/Caches/pypoetry/virtualenvs/pdddf-rsXREmUU-py3.8/lib/python3.8/site-packages/requests/sessions.py\u001b[0m in \u001b[0;36mprepare_request\u001b[0;34m(self, request)\u001b[0m\n\u001b[1;32m 447\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 448\u001b[0m \u001b[0mp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mPreparedRequest\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 449\u001b[0;31m p.prepare(\n\u001b[0m\u001b[1;32m 450\u001b[0m \u001b[0mmethod\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmethod\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 451\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrequest\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0murl\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 40 | "\u001b[0;32m~/Library/Caches/pypoetry/virtualenvs/pdddf-rsXREmUU-py3.8/lib/python3.8/site-packages/requests/models.py\u001b[0m in \u001b[0;36mprepare\u001b[0;34m(self, method, url, headers, files, data, params, auth, cookies, hooks, json)\u001b[0m\n\u001b[1;32m 315\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprepare_headers\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheaders\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 316\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprepare_cookies\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcookies\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 317\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprepare_body\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfiles\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 318\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mprepare_auth\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mauth\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0murl\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 319\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", 41 | "\u001b[0;32m~/Library/Caches/pypoetry/virtualenvs/pdddf-rsXREmUU-py3.8/lib/python3.8/site-packages/requests/models.py\u001b[0m in \u001b[0;36mprepare_body\u001b[0;34m(self, data, files, json)\u001b[0m\n\u001b[1;32m 503\u001b[0m \u001b[0;31m# Multi-part file uploads.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 504\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mfiles\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 505\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mbody\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontent_type\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_encode_files\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfiles\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 506\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 507\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 42 | "\u001b[0;32m~/Library/Caches/pypoetry/virtualenvs/pdddf-rsXREmUU-py3.8/lib/python3.8/site-packages/requests/models.py\u001b[0m in \u001b[0;36m_encode_files\u001b[0;34m(files, data)\u001b[0m\n\u001b[1;32m 157\u001b[0m \u001b[0mfdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfp\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 158\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mhasattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'read'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 159\u001b[0;31m \u001b[0mfdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 160\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mfp\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 161\u001b[0m \u001b[0;32mcontinue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 43 | "\u001b[0;32m/usr/local/Cellar/python@3.8/3.8.4/Frameworks/Python.framework/Versions/3.8/lib/python3.8/codecs.py\u001b[0m in \u001b[0;36mdecode\u001b[0;34m(self, input, final)\u001b[0m\n\u001b[1;32m 320\u001b[0m \u001b[0;31m# decode input (taking the buffer into account)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuffer\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0minput\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 322\u001b[0;31m \u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconsumed\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_buffer_decode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0merrors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfinal\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 323\u001b[0m \u001b[0;31m# keep undecoded input until the next call\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 324\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuffer\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mconsumed\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 44 | "\u001b[0;31mUnicodeDecodeError\u001b[0m: 'utf-8' codec can't decode byte 0xbf in position 10: invalid start byte" 45 | ] 46 | } 47 | ], 48 | "source": [ 49 | "response = requests.post('http://localhost:1616', files=files, data={'lang': 'de'})" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 16, 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "id = response.json()['id']" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 12, 64 | "metadata": {}, 65 | "outputs": [ 66 | { 67 | "data": { 68 | "text/plain": [ 69 | "'LsWd4fxRWueYH4wwUZwmhg'" 70 | ] 71 | }, 72 | "execution_count": 12, 73 | "metadata": {}, 74 | "output_type": "execute_result" 75 | } 76 | ], 77 | "source": [ 78 | "id" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 13, 84 | "metadata": {}, 85 | "outputs": [ 86 | { 87 | "name": "stdout", 88 | "output_type": "stream", 89 | "text": [ 90 | "waiting...\n", 91 | "waiting...\n", 92 | "waiting...\n", 93 | "waiting...\n", 94 | "waiting...\n", 95 | "waiting...\n", 96 | "waiting...\n", 97 | "waiting...\n", 98 | "waiting...\n", 99 | "waiting...\n", 100 | "waiting...\n", 101 | "waiting...\n", 102 | "waiting...\n", 103 | "waiting...\n", 104 | "waiting...\n", 105 | "waiting...\n", 106 | "waiting...\n", 107 | "waiting...\n", 108 | "waiting...\n", 109 | "Gravenbrucher Kreis\n", 110 | "\n", 111 | "Stellungnahme des Gravenbrucher Kreis zum Referentenentwurf eines Gesetzes zur Verbesserung der Rechtssicherheit bei Anfechtungen nach der Insolvenzordnung und nach dem Anfechtungsgesetz vom 16.03.2015\n", 112 | "\n", 113 | "I. Vorbemerkung\n", 114 | "\n", 115 | "Der Gravenbrucher Kreis hatte sich bereits unter dem 5. August 2014 umfangreich zu einer möglichen Reform des Insolvenzanfechtungsrechts positioniert. Bevor auf die Einzelheiten des nunmehr zur Stellungnahme vorgelegten Referentenentwurfs eingegangen wird, sollen die wichtigsten Punkte unseres Papiers vom 5. August 2014 vorab nochmals aufgegriffen werden:\n", 116 | "\n", 117 | "1. Bedeutung des Anfechtungsrechts im System der Insolvenzordnung\n", 118 | "\n", 119 | "Das Recht der Insolvenzanfechtung stellt ein wesentliches Fundament für die Funktionsfähigkeit der Insolvenzordnung dar und erfüllt zentrale ordnungspolitische Funktionen. Es liegt auf der Hand, dass insbesondere bestimmte sozial inadäquate Verhaltensweisen des Schuldners in einer späteren Insolvenz rückgängig gemacht werden müssen, um dem Prinzip der Gläubigergleichbehandlung zu seiner Durchsetzung zu verhelfen.\n", 120 | "\n", 121 | "Darüber hinaus können die Anfechtungsvorschriften eine frühzeitige Insolvenzantragstellung sicherstellen. Gerät ein Unternehmen in die Krise, müssen sich seine Vertragspartner darauf einstellen, möglicherweise zu einem späteren Zeitpunkt Insolvenzanfechtungsansprüchen ausgesetzt zu sein. Ist dieses Risiko hoch, werden sie möglicherweise keine Geschäfte mehr mit dem Krisenunternehmen machen oder selbst einen Insolvenzantrag stellen. Die Anfechtungsvorschriften müssen insoweit angemessen sein, als sie eine Fortführung von Unternehmen, die auch außerhalb eines Insolvenzverfahrens eine Sanierungsaussicht haben, nicht behindern. Umgekehrt darf gut informierten oder durchsetzungsstarken\n", 122 | "\n", 123 | "Gläubigern kein übermäßiger Anreiz gesetzt werden, mit einem Unter-\n", 124 | "\n", 125 | "SPRECHER\n", 126 | "\n", 127 | "RA Prof. Dr. Lucas F. Fiéther Franzosenweg 20\n", 128 | "06112 Halle\n", 129 | "Tel +49 (0)345 21222-0\n", 130 | "Fax +49 (0)345 21222-395\n", 131 | "\n", 132 | "www.gravenbrucher-kreis.de\n", 133 | "gk@ floether-wissing.de\n", 134 | "\n", 135 | "AKTIVE MITGLTEDER:\n", 136 | "\n", 137 | "RA Prof. Dr. Siegfried Beck\n", 138 | "RA > Axel W. Bierbach\n", 139 | "RA Joachim Exner\n", 140 | "RA Udo Feser\n", 141 | "RA Prof, Dr. Lucas F. Flöther RA Dr. Michael Ü, Frege\n", 142 | "WP StB Arndt Geiwitz\n", 143 | "RA WR StB OLtmar Hermann\n", 144 | "RA Tobias tloefer\n", 145 | "RA Dr. Michael Jaffé\n", 146 | "RA Dr. Frank Kebekus\n", 147 | "RA Dr. Bruno M, Kübler\n", 148 | "RA Prof. Dr. Rolf Dieter Minning KA Dr, Jörg Nerlich\n", 149 | "RA Horst Piepenburg\n", 150 | "RA Michael Pluta\n", 151 | "RA Dr. Andreas Ringstmeier\n", 152 | "RA Christopher Seagon\n", 153 | "RA Dr. Sven-Holger Undritz\n", 154 | "RA Rüdiger Wienberg\n", 155 | "\n", 156 | "PRSSIVE MITGLIEDER:\n", 157 | "\n", 158 | "RAin Barbara Beutler\n", 159 | "RA Joachim G. Brandenburg\n", 160 | "RA Dr. Volker Grub\n", 161 | "RA Horst M. Johike\n", 162 | "RA Heinrich Müller-Feyen\n", 163 | "RA Dr. Wolfgang Petereit\n", 164 | "RA flans-F. Runkel\n", 165 | "WP StB Werner Schneider\n", 166 | "RA Dr. Gerd Gustav Weiland\n", 167 | "RA Dr. Jobst Wellensiek\n", 168 | "\n", 169 | "N\n", 170 | "Gravenbrucher Kr\n", 171 | "\n", 172 | "nehmen, das sich in der schweren Krise befindet, weiter in einer Weise Geschäfte zu machen, die die anderen Gläubiger, die schlechter informiert oder weniger durchsetzungsstark sind, benachteiligen (vgl. Kirchhof, MüKo-InsO, Vorb. 88 129 ff., Rn. 2). Die Anfechtungsvorschriften dienen insoweit auch dem Schutz der „schwachen“ Gläubiger, wie etwa der Arbeitnehmer des Insolvenzschuldners.\n", 173 | "\n", 174 | "Schließlich darf man nicht übersehen, dass sämtliche ungesicherten Gläubiger des Unternehmens von den Anfechtungsvorschriften profitieren. Jede Schwächung der Anfechtungsvorschriften führt dazu, dass die ungesicherten Gläubiger, zu denen beispielsweise auch die Arbeitnehmer oder die Finanzverwaltung und andere öffentliche Stellen gehören, eine schlechtere Quote erhalten.\n", 175 | "\n", 176 | "2. Regelungsbedarf\n", 177 | "\n", 178 | "Ungeachtet der hieraus folgenden positiven Implikationen des geltenden Anfechtungsrechts für die Ziele der Insolvenzordnung, allem voran dem Grundsatz der Gläubigergleichbehandlung, kann der Gravenbrucher Kreis nachvollziehen, dass die derzeitige gesetzliche Regelung in Einzelfällen zu Ergebnissen führen kann, die die Beteiligten unangemessen belasten. Dies gilt vor allem für solche Rechtsbeziehungen, die auch in Kenntnis der Krise fortgesetzt werden, um das Überleben des in Schieflage befindlichen Geschäftspartners zu sichern.\n", 179 | "\n", 180 | "Il. Einschätzung des Gesetzesentwurfs\n", 181 | "\n", 182 | "Dies vorausgeschickt, begrüßt der Gravenbrucher Kreis den vorgelegten Gesetzesentwurf und unterstützt das Anliegen, die aktuell bestehenden Regelungsdefizite durch maßvolle Korrekturen des geltenden Rechts zu beseitigen.\n", 183 | "\n", 184 | "Der Gravenbrucher Kreis nimmt dabei zur Kenntnis, dass ein Großteil der auch in seinem Positionspapier vom 5. August 2014 genannten Aspekte zur Reform des Insolvenzanfechtungsrechts aufgegriffen wurde. Das Insolvenzanfechtungsrecht als tragende Säule des geltenden Insolvenzrechts bleibt damit erhalten.\n", 185 | "\n", 186 | "Gravenbrucher Kr\n", 187 | "\n", 188 | "Nachfolgend beschränkt sich der Gravenbrucher Kreis auf die wenigen aus unserer Sicht kritischen Punkte. Im Übrigen sollten die neuen Regelungen nach einer gewissen Zeit, beispielsweise fünf Jahre nach Inkrafttreten der avisierten Reform, anhand der bis dahin aufgetretenen praktischen Erfahrungen nochmals auf den Prüfstand gestellt werden, um ggf. erforderliche Anpassungen zu diskutieren.\n", 189 | "\n", 190 | "Im Einzelnen:\n", 191 | "\n", 192 | "Änderung von § 133 InsO:\n", 193 | "\n", 194 | "„(1) Anfechtbar ist eine Rechtshandlung, die der Schuldner in den letzten zehn Jahren vor dem Antrag auf Eröffnung des Insolvenzverfahrens oder nach diesem Antrag mit dem Vorsatz, seine Gläubiger unangemessen zu benachteiligen, vorgenommen hat, wenn der andere Teil zur Zeit der Handlung den Vorsatz des Schuldners kannte. Diese-Kenntnis-wird-vermu-ann Sar oe\n", 195 | "\n", 196 | "1. für eine Leistung des Schuldners unmittelbar eine gleichwertige Gegenleistung in sein Vermögen gelangt, die zur Fortführung seines Unternehmens oder zur Sicherung seines Lebensbedarfs erforderlich ist, oder\n", 197 | "\n", 198 | "2. die Rechtshandlung Bestandteil eines ernsthaften Sanierungsversuchs ist.\n", 199 | "\n", 200 | "\n" 201 | ] 202 | } 203 | ], 204 | "source": [ 205 | "while True:\n", 206 | " r = requests.get(f\"http://localhost:1616/update/{id}\")\n", 207 | " j = r.json()\n", 208 | " if 'text' in j:\n", 209 | " break\n", 210 | " print('waiting...')\n", 211 | " # print(j['log'])\n", 212 | " time.sleep(1)\n", 213 | "print(j['text'])" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": null, 226 | "metadata": {}, 227 | "outputs": [], 228 | "source": [] 229 | } 230 | ], 231 | "metadata": { 232 | "kernelspec": { 233 | "display_name": "Python 3", 234 | "language": "python", 235 | "name": "python3" 236 | }, 237 | "language_info": { 238 | "codemirror_mode": { 239 | "name": "ipython", 240 | "version": 3 241 | }, 242 | "file_extension": ".py", 243 | "mimetype": "text/x-python", 244 | "name": "python", 245 | "nbconvert_exporter": "python", 246 | "pygments_lexer": "ipython3", 247 | "version": "3.8.4" 248 | } 249 | }, 250 | "nbformat": 4, 251 | "nbformat_minor": 4 252 | } 253 | -------------------------------------------------------------------------------- /development/notebooks/08_debugging/idx_page_bug.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "%reload_ext autoreload\n", 10 | "%autoreload 2" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 2, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "import logging\n", 20 | "import sys" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 3, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 4, 35 | "metadata": {}, 36 | "outputs": [ 37 | { 38 | "name": "stdout", 39 | "output_type": "stream", 40 | "text": [ 41 | "INFO:transformers.file_utils:PyTorch version 1.6.0 available.\n", 42 | "DEBUG:shapely.geos:Found GEOS DLL: , using it.\n", 43 | "DEBUG:shapely.geos:Trying `CDLL(/usr/lib/libc.dylib)`\n", 44 | "DEBUG:shapely.geos:Library path: '/usr/lib/libc.dylib'\n", 45 | "DEBUG:shapely.geos:DLL: \n" 46 | ] 47 | } 48 | ], 49 | "source": [ 50 | "import pd3f" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 11, 56 | "metadata": {}, 57 | "outputs": [ 58 | { 59 | "name": "stdout", 60 | "output_type": "stream", 61 | "text": [ 62 | "INFO:pd3f.parsr_wrapper:sending PDF to Parsr\n", 63 | "DEBUG:pd3f.parsr_wrapper:{'version': 0.9, 'extractor': {'pdf': 'pdfminer', 'ocr': 'tesseract', 'language': ['deu']}, 'cleaner': ['drawing-detection', ['image-detection', {'ocrImages': False}], 'out-of-page-removal', ['whitespace-removal', {'minWidth': 0}], ['redundancy-detection', {'minOverlap': 0.5}], ['header-footer-detection', {'ignorePages': [], 'maxMarginPercentage': 15}], 'words-to-line-new', ['reading-order-detection', {'minVerticalGapWidth': 5, 'minColumnWidthInPagePercent': 15}], ['lines-to-paragraph', {'tolerance': 0.25}], 'page-number-detection', 'hierarchy-detection'], 'output': {'granularity': 'word', 'includeMarginals': True, 'includeDrawings': True, 'formats': {'json': True, 'text': False, 'csv': True, 'markdown': False, 'pdf': False, 'simpleJson': False}}}\n", 64 | "DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:3001\n", 65 | "DEBUG:urllib3.connectionpool:http://localhost:3001 \"POST /api/v1/document HTTP/1.1\" 202 30\n", 66 | "> Polling server for the job 440f5a6edf4dfb0288bbc55e758950...\n", 67 | "DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:3001\n", 68 | "DEBUG:urllib3.connectionpool:http://localhost:3001 \"GET /api/v1/queue/440f5a6edf4dfb0288bbc55e758950 HTTP/1.1\" 200 109\n", 69 | ">> Progress percentage: 0\n", 70 | "DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:3001\n", 71 | "DEBUG:urllib3.connectionpool:http://localhost:3001 \"GET /api/v1/queue/440f5a6edf4dfb0288bbc55e758950 HTTP/1.1\" 200 109\n", 72 | ">> Progress percentage: 0\n", 73 | "DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:3001\n", 74 | "DEBUG:urllib3.connectionpool:http://localhost:3001 \"GET /api/v1/queue/440f5a6edf4dfb0288bbc55e758950 HTTP/1.1\" 200 318\n", 75 | ">> Progress percentage: 0\n", 76 | "DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:3001\n", 77 | "DEBUG:urllib3.connectionpool:http://localhost:3001 \"GET /api/v1/queue/440f5a6edf4dfb0288bbc55e758950 HTTP/1.1\" 200 247\n", 78 | ">> Progress percentage: 0\n", 79 | "DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:3001\n", 80 | "DEBUG:urllib3.connectionpool:http://localhost:3001 \"GET /api/v1/queue/440f5a6edf4dfb0288bbc55e758950 HTTP/1.1\" 201 257\n", 81 | ">> Job done!\n", 82 | "INFO:pd3f.parsr_wrapper:got response from Parsr\n", 83 | "DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:3001\n", 84 | "DEBUG:urllib3.connectionpool:http://localhost:3001 \"GET /api/v1/json/440f5a6edf4dfb0288bbc55e758950 HTTP/1.1\" 200 174940\n", 85 | "INFO:pd3f.doc_info:media line width: 271.91\n", 86 | "INFO:pd3f.doc_info:median line height: 8.28\n", 87 | "INFO:pd3f.doc_info:median line space: 8.91999999999999\n", 88 | "INFO:pd3f.doc_info:counter width: [(409.33, 2), (409.1, 2), (409.4, 2), (409.87, 2), (409.79, 2)]\n", 89 | "INFO:pd3f.doc_info:counter height: [(8.28, 49), (7.92, 24), (8.64, 8), (9, 5), (6.12, 4)]\n", 90 | "INFO:pd3f.doc_info:counter lineheight: [(8.999999999999973, 6), (8.640000000000017, 5), (9.000000000000016, 3), (9.00000000000003, 3), (9.080000000000014, 3)]\n", 91 | "DEBUG:pd3f.doc_info:is it a body para?\n", 92 | "DEBUG:pd3f.doc_info:is it a body para?\n", 93 | "DEBUG:pd3f.doc_info:is it a body para?\n", 94 | "DEBUG:pd3f.doc_info:is it a body para?\n", 95 | "DEBUG:pd3f.doc_info:is it a body para?\n", 96 | "DEBUG:pd3f.doc_info:is it a body para?\n", 97 | "DEBUG:pd3f.doc_info:is it a body para?\n", 98 | "DEBUG:pd3f.doc_info:is it a body para?\n", 99 | "INFO:pd3f.export:export page #0\n", 100 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['Berlin,']['29.', 'Juni', '2020']\n", 101 | "DEBUG:pd3f.export:adding newline here ['Berlin,\\n']\n", 102 | "DEBUG:pd3f.export:No next line, but adding space ['29.', 'Juni', '2020']\n", 103 | "DEBUG:pd3f.export:last line, not adding space\n", 104 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['Hausruf:']['9330']\n", 105 | "DEBUG:pd3f.export:adding newline here ['Hausruf:\\n']\n", 106 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 5.684341886080802e-14 avg space: 0.0 ['9330']\n", 107 | "DEBUG:pd3f.export:adding newline here ['9330\\n']\n", 108 | "DEBUG:pd3f.export:testing the lines: \n", 109 | "DEBUG:pd3f.export:['Vorlage_DPR_Kündigung_Anerkennungsvertrag_Ili'] ['A3_v6_KabRef+Stn.docx']\n", 110 | "DEBUG:pd3f.export:adding newline here ['Vorlage_DPR_Kündigung_Anerkennungsvertrag_Ili\\n']\n", 111 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 89.29 avg space: 0.0 ['A3_v6_KabRef+Stn.docx']\n", 112 | "DEBUG:pd3f.export:adding newline here ['A3_v6_KabRef+Stn.docx\\n']\n", 113 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['Referat:']['Referatsleiter:', 'Herr', 'Dr.', 'Eichholz']\n", 114 | "DEBUG:pd3f.export:adding newline here ['Referat:\\n']\n", 115 | "DEBUG:pd3f.export:testing the lines: \n", 116 | "DEBUG:pd3f.export:['Referatsleiter:', 'Herr', 'Dr.', 'Eichholz'] ['Referent:', 'Herr', 'Urban']\n", 117 | "DEBUG:pd3f.export:adding newline here ['Referatsleiter:', 'Herr', 'Dr.', 'Eichholz\\n']\n", 118 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 14.393333333333354 avg space: 13.616666666666665 ['Referent:', 'Herr', 'Urban']\n", 119 | "DEBUG:pd3f.export:adding newline here ['Referent:', 'Herr', 'Urban\\n']\n", 120 | "DEBUG:pd3f.export:adding newline here ['k', 'Io', 'Bilanzkontrollverfahren\\n']\n", 121 | "DEBUG:pd3f.export:adding newline here ['=.', '®', 'A\\n']\n", 122 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 100.83299999999998 avg space: 3.2569999999999935 ['stelle', 'für', 'Rechnungslegung', 'DPR', 'e.', 'V.', 'vom', '30.', 'März', '2005']\n", 123 | "DEBUG:pd3f.export:adding newline here ['stelle', 'für', 'Rechnungslegung', 'DPR', 'e.', 'V.', 'vom', '30.', 'März', '2005\\n']\n", 124 | "DEBUG:pd3f.export:adding newline here ['Bezug:\\n']\n", 125 | "DEBUG:pd3f.export:lines should be seperated\n", 126 | "DEBUG:pd3f.export:['Verfügung', 'von', 'Frau', 'Staatssekretärin', 'vom', '25./26.', 'Juni', '2020'] ['E']\n", 127 | "DEBUG:pd3f.export:adding newline here ['Verfügung', 'von', 'Frau', 'Staatssekretärin', 'vom', '25./26.', 'Juni', '2020\\n']\n", 128 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 312.53 avg space: 0.0 ['E']\n", 129 | "DEBUG:pd3f.export:adding newline here ['E\\n']\n", 130 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['Über']['Herrn', 'UAL', 'Ill', 'A', 'elektronisch', 'gezeichnet', 'am', '29.6.2020', '-', 'i.V.', 'Herr', 'Dr.', 'Wichard']\n", 131 | "DEBUG:pd3f.export:adding newline here ['Über\\n']\n", 132 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 14.995833333333337 avg space: 2.2341666666666717 ['Herrn', 'AL', 'Ill', 'elektronisch', 'gezeichnet', 'am', '29.6.2020', '-', 'i.V,', 'Herr', 'Dr.', 'Wichard']\n", 133 | "DEBUG:pd3f.export:adding newline here ['Herrn', 'AL', 'Ill', 'elektronisch', 'gezeichnet', 'am', '29.6.2020', '-', 'i.V,', 'Herr', 'Dr.', 'Wichard\\n']\n", 134 | "DEBUG:pd3f.export:testing the lines: \n", 135 | "DEBUG:pd3f.export:['das', 'Kabinettreferat'] ['och', '-']\n", 136 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 66.17000000000002 avg space: 1.8100000000000023 ['och', '-']\n", 137 | "DEBUG:pd3f.export:adding newline here ['och', '-\\n']\n", 138 | "DEBUG:pd3f.export:adding newline here ['elektr.', 'gez.', '29.06.2020', '-', 'Dr.', 'Frantzi-\\n']\n", 139 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 255.4 avg space: 0.0 ['hof']\n", 140 | "DEBUG:pd3f.export:adding newline here ['hof\\n']\n", 141 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['Frau', 'Ministerin', 'IN', '%.b.']['mit', 'der', 'Bitte', 'um', 'Billigung', 'und', 'Zeichnung', 'des', 'Schreibens']\n", 142 | "DEBUG:pd3f.export:adding newline here ['Frau', 'Ministerin', 'IN', '%.b.\\n']\n", 143 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 190.84333333333333 avg space: 2.8466666666666547 ['zu', 'Il.', 'vorgelegt.']\n", 144 | "DEBUG:pd3f.export:adding newline here ['zu', 'Il.', 'vorgelegt.\\n']\n", 145 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 252.55 avg space: 0.0 ['ten.']\n", 146 | "DEBUG:pd3f.export:adding newline here ['ten.\\n']\n", 147 | "INFO:pd3f.export:export page #1\n", 148 | "DEBUG:pd3f.export:adding newline here ['Das', 'Referat', 'Presse', 'hat', '(elektr.)', 'Abdruck', 'erhalten.\\n']\n", 149 | "DEBUG:pd3f.export:adding newline here ['Vermerk:\\n']\n", 150 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 394.87 avg space: 0.0 ['ten.']\n", 151 | "DEBUG:pd3f.export:adding newline here ['ten.\\n']\n", 152 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 386.15999999999997 avg space: 0.0 ['chen.']\n", 153 | "DEBUG:pd3f.export:adding newline here ['chen.\\n']\n", 154 | "DEBUG:pd3f.export:adding newline here ['Die', 'Prüfstelle', 'für', 'Rechnungslegung', 'und', 'das', 'Bilanzkontrollverfahren\\n']\n", 155 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 209.36200000000002 avg space: 3.0780000000000003 ['ten', 'durch', 'Vertrag', 'anerkennen', '(Prüfstelle).']\n", 156 | "DEBUG:pd3f.export:adding newline here ['ten', 'durch', 'Vertrag', 'anerkennen', '(Prüfstelle).\\n']\n", 157 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 235.9 avg space: 2.009999999999998 ['senen', 'Rechnungslegungsstandards.']\n", 158 | "DEBUG:pd3f.export:adding newline here ['senen', 'Rechnungslegungsstandards.\\n']\n", 159 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 386.75 avg space: 0.0 ['prüft.']\n", 160 | "DEBUG:pd3f.export:adding newline here ['prüft.\\n']\n", 161 | "DEBUG:pd3f.export:No next line, but adding space ['Unterlagen', 'verlangen,', 'ist', 'ihr', 'das', 'Betreten', 'von', 'Geschäftsräumen', 'zu', 'gestatten', 'und', 'kann']\n", 162 | "DEBUG:pd3f.export:last line, not adding space\n", 163 | "INFO:pd3f.export:export page #2\n", 164 | "DEBUG:pd3f.export:adding newline here ['-3-\\n']\n", 165 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 380.06 avg space: 0.0 ['macht.']\n", 166 | "DEBUG:pd3f.export:adding newline here ['macht.\\n']\n", 167 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['punkten', 'für', 'einen', 'Verstoß', 'gegen', 'Rechnungslegungsvorschriften)', 'erst', 'prüfen,', 'wenn']['-', 'das', 'Unternehmen', '(anders', 'als', 'im', 'praktischen', 'Regelfall)', 'nicht', 'freiwillig', 'an', 'der', 'Prüfung']\n", 168 | "DEBUG:pd3f.export:adding newline here ['punkten', 'für', 'einen', 'Verstoß', 'gegen', 'Rechnungslegungsvorschriften)', 'erst', 'prüfen,', 'wenn\\n']\n", 169 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['durch', 'die', 'DPR', 'mitwirkt,']['-', 'das', 'Unternehmen', 'mit', 'dem', 'Prüfergebnis', 'der', 'DPR', 'nicht', 'einverstanden', 'ist', 'oder']\n", 170 | "DEBUG:pd3f.export:adding newline here ['durch', 'die', 'DPR', 'mitwirkt,\\n']\n", 171 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['-', 'das', 'Unternehmen', 'mit', 'dem', 'Prüfergebnis', 'der', 'DPR', 'nicht', 'einverstanden', 'ist', 'oder']['-', 'erhebliche', 'Zweifel', 'an', 'der', 'Richtigkeit', 'des', 'Prüfergebnisses', 'der', 'DPR', 'oder', 'der', 'ord-']\n", 172 | "DEBUG:pd3f.export:adding newline here ['-', 'das', 'Unternehmen', 'mit', 'dem', 'Prüfergebnis', 'der', 'DPR', 'nicht', 'einverstanden', 'ist', 'oder\\n']\n", 173 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['WpHG).']['Auch', 'wenn', 'diese', 'Voraussetzungen', 'nicht', 'vorliegen,', 'darf', 'die', 'BaFin', 'die', 'Prüfung', 'jederzeit']\n", 174 | "DEBUG:pd3f.export:adding newline here ['WpHG).\\n']\n", 175 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 13.770833333333325 avg space: 2.83916666666666 ['und', 'die', 'Prüfungen-denselben', 'Gegenstand', 'betreffen', '($', '108', 'Absatz', '1', 'Satz', '4', 'WpHG).']\n", 176 | "DEBUG:pd3f.export:adding newline here ['und', 'die', 'Prüfungen-denselben', 'Gegenstand', 'betreffen', '($', '108', 'Absatz', '1', 'Satz', '4', 'WpHG).\\n']\n", 177 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 258.10749999999996 avg space: 2.682500000000001 ['ist', 'deshalb', 'derzeit', '»zweistufig«.']\n", 178 | "DEBUG:pd3f.export:adding newline here ['ist', 'deshalb', 'derzeit', '»zweistufig«.\\n']\n", 179 | "DEBUG:pd3f.export:adding newline here ['Kündigung', 'des', 'Anerkennungsvertrags', 'mit', 'der', 'DPR\\n']\n", 180 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 306.1333333333333 avg space: 2.4966666666666604 ['aus', 'wichtigem', 'Grund.']\n", 181 | "DEBUG:pd3f.export:adding newline here ['aus', 'wichtigem', 'Grund.\\n']\n", 182 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 43.291818181818186 avg space: 3.328181818181817 ['verstoßen', 'hat.', 'Dafür', 'liegen', 'keine', 'Hinweise', 'vor.', 'BMF', 'teilt', 'diese', 'Einschätzung.']\n", 183 | "DEBUG:pd3f.export:adding newline here ['verstoßen', 'hat.', 'Dafür', 'liegen', 'keine', 'Hinweise', 'vor.', 'BMF', 'teilt', 'diese', 'Einschätzung.\\n']\n", 184 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 374.25 avg space: 0.0 ['werden.']\n", 185 | "DEBUG:pd3f.export:adding newline here ['werden.\\n']\n", 186 | "INFO:pd3f.export:export page #3\n", 187 | "DEBUG:pd3f.export:adding newline here ['-4-\\n']\n", 188 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 228.4933333333333 avg space: 2.4066666666666663 ['eines', 'Kündigungsschreibens', 'gebeten.']\n", 189 | "DEBUG:pd3f.export:adding newline here ['eines', 'Kündigungsschreibens', 'gebeten.\\n']\n", 190 | "INFO:pd3f.export:export page #4\n", 191 | "DEBUG:pd3f.export:adding newline here ['Umsetzung', 'der', 'Kündigung\\n']\n", 192 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 36.317272727272744 avg space: 3.5427272727272676 ['nehmens', 'bedarf.', 'Das', 'Einvernehmen', 'zur', 'Kündigung', 'hat', 'BMF', 'erteilt', '(Anlage', '3).']\n", 193 | "DEBUG:pd3f.export:adding newline here ['nehmens', 'bedarf.', 'Das', 'Einvernehmen', 'zur', 'Kündigung', 'hat', 'BMF', 'erteilt', '(Anlage', '3).\\n']\n", 194 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 227.21666666666667 avg space: 3.0733333333333315 ['dings', '(vgl.', '$', '26', 'Absatz', '2', 'Satz', '2', 'BGB).']\n", 195 | "DEBUG:pd3f.export:adding newline here ['dings', '(vgl.', '$', '26', 'Absatz', '2', 'Satz', '2', 'BGB).\\n']\n", 196 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 279.37200000000007 avg space: 2.947999999999996 ['am', '30.', 'Juni', '2020', 'zugehen.']\n", 197 | "DEBUG:pd3f.export:adding newline here ['am', '30.', 'Juni', '2020', 'zugehen.\\n']\n", 198 | "INFO:pd3f.export:export page #5\n", 199 | "DEBUG:pd3f.export:adding newline here ['-7-\\n']\n", 200 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 356.65 avg space: 0.0 ['übermitteln.']\n", 201 | "DEBUG:pd3f.export:adding newline here ['übermitteln.\\n']\n", 202 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 57.41333333333333 avg space: 3.4666666666666655 ['geschlagen,', 'dass', 'Frau', 'Ministerin', 'das', 'Kündigungsschreiben', 'zu', 'Il.', 'zeichnet.']\n", 203 | "DEBUG:pd3f.export:adding newline here ['geschlagen,', 'dass', 'Frau', 'Ministerin', 'das', 'Kündigungsschreiben', 'zu', 'Il.', 'zeichnet.\\n']\n", 204 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 183.59571428571428 avg space: 2.8542857142857088 ['des', 'beigefügten', 'Entwurfs', '(Anlage', '5)', 'zu', 'billigen.']\n", 205 | "DEBUG:pd3f.export:adding newline here ['des', 'beigefügten', 'Entwurfs', '(Anlage', '5)', 'zu', 'billigen.\\n']\n", 206 | "DEBUG:pd3f.export:adding newline here ['Schreiben\\n']\n", 207 | "INFO:pd3f.export:export page #6\n", 208 | "DEBUG:pd3f.export:adding newline here ['IV.\\n']\n", 209 | "DEBUG:pd3f.export:adding newline here ['v.\\n']\n", 210 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['6']['VI.']\n", 211 | "DEBUG:pd3f.export:adding newline here ['6\\n']\n", 212 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['VI.']['Ww.über']\n", 213 | "DEBUG:pd3f.export:adding newline here ['VI.\\n']\n", 214 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['Ww.über']['Herrn', 'Herrn', 'AL', 'UAL', 'll', 'III', 'A', '?', '}', '/', '+']\n", 215 | "DEBUG:pd3f.export:adding newline here ['Ww.über\\n']\n", 216 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 65.52199999999999 avg space: 3.5180000000000007 ['in', 'Referat', 'Ill', 'A', '3.']\n", 217 | "DEBUG:pd3f.export:adding newline here ['in', 'Referat', 'Ill', 'A', '3.\\n']\n", 218 | "DEBUG:pd3f.export:testing the lines: \n", 219 | "DEBUG:pd3f.export:['Dr.', 'Eichholz', 'Urban'] ['elektronisch', 'gezeichnet']\n", 220 | "DEBUG:pd3f.export:adding newline here ['Dr.', 'Eichholz', 'Urban\\n']\n", 221 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['elektronisch', 'gezeichnet']['am', '29.6.2020', '-', 'Herr', 'Dr.', 'Eichholz']\n", 222 | "DEBUG:pd3f.export:adding newline here ['elektronisch', 'gezeichnet\\n']\n", 223 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 98.51833333333335 avg space: 2.411666666666657 ['am', '29.6.2020', '-', 'Herr', 'Dr.', 'Eichholz']\n", 224 | "DEBUG:pd3f.export:adding newline here ['am', '29.6.2020', '-', 'Herr', 'Dr.', 'Eichholz\\n']\n", 225 | "DEBUG:pd3f.export:testing the lines: \n", 226 | "DEBUG:pd3f.export:['elektronisch', 'gezeichnet'] ['am', '29.6.2020', '—', 'Herr', 'Urban']\n", 227 | "DEBUG:pd3f.export:adding newline here ['elektronisch', 'gezeichnet\\n']\n", 228 | "DEBUG:pd3f.export:No next line, but adding space ['am', '29.6.2020', '—', 'Herr', 'Urban']\n", 229 | "DEBUG:pd3f.export:last line, not adding space\n", 230 | "DEBUG:pd3f.export:There is enough space on the lext for the next word. So adding a linebreak between ['47']['27.', 'Kom', '(deln,', '2.K&.']\n", 231 | "DEBUG:pd3f.export:adding newline here ['47\\n']\n", 232 | "DEBUG:pd3f.export:lines should be seperated\n", 233 | "DEBUG:pd3f.export:['27.', 'Kom', '(deln,', '2.K&.'] ['2.', '4A']\n", 234 | "DEBUG:pd3f.export:adding newline here ['27.', 'Kom', '(deln,', '2.K&.\\n']\n", 235 | "DEBUG:pd3f.export:No next line, but adding \\n, avail space: 86.02499999999996 avg space: 8.955000000000013 ['2.', '4A']\n", 236 | "DEBUG:pd3f.export:adding newline here ['2.', '4A\\n']\n", 237 | "DEBUG:pd3f.export:adding newline here ['72\\n']\n" 238 | ] 239 | } 240 | ], 241 | "source": [ 242 | "output= pd3f.extract(\n", 243 | " '/Users/user/Desktop/sw1.pdf',\n", 244 | " )" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": null, 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [] 253 | } 254 | ], 255 | "metadata": { 256 | "kernelspec": { 257 | "display_name": "Python 3", 258 | "language": "python", 259 | "name": "python3" 260 | }, 261 | "language_info": { 262 | "codemirror_mode": { 263 | "name": "ipython", 264 | "version": 3 265 | }, 266 | "file_extension": ".py", 267 | "mimetype": "text/x-python", 268 | "name": "python", 269 | "nbconvert_exporter": "python", 270 | "pygments_lexer": "ipython3", 271 | "version": "3.8.4" 272 | } 273 | }, 274 | "nbformat": 4, 275 | "nbformat_minor": 4 276 | } 277 | -------------------------------------------------------------------------------- /development/notes/01_notes.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | ## First 4 | 5 | https://github.com/axa-group/Parsr 6 | 7 | https://www.heise.de/ct/artikel/OCR-Software-zur-Digitalisierung-ausgedruckter-Dokumente-4270739.html 8 | 9 | https://academy.datawrapper.de/article/135-how-to-extract-data-out-of-pdfs 10 | 11 | https://opensource.com/article/18/7/textricator 12 | 13 | https://www.quora.com/Which-is-the-best-document-processing-software-to-extract-PDF-data 14 | 15 | https://www.heise.de/ct/artikel/FAQ-Portable-Document-Format-4564849.html 16 | 17 | PDFs 18 | 19 | https://gini.net/de/technologie/ 20 | 21 | http://okfnlabs.org/blog/2016/04/19/pdf-tools-extract-text-and-data-from-pdfs.html 22 | 23 | https://blog.safe.com/2016/10/ocr-for-fme-now-i-know-my-abc/ 24 | 25 | https://openresearchsoftware.metajnl.com/articles/10.5334/jors.164/ 26 | 27 | https://openresearchsoftware.metajnl.com/articles/10.5334/jors.164/ 28 | 29 | https://github.com/tmbdev/ocropy 30 | 31 | https://www.microsoft.com/developerblog/2018/05/07/handwriting-detection-and-recognition-in-scanned-documents-using-azure-ml-package-computer-vision-azure-cognitive-services-ocr/ 32 | 33 | https://www.learnopencv.com/deep-learning-based-text-recognition-ocr-using-tesseract-and-opencv/ 34 | 35 | https://tabula.technology/ 36 | 37 | ## Text Cleaning / Validation 38 | 39 | https://github.com/textpipe/textpipe 40 | 41 | https://github.com/veraPDF/veraPDF-pdfbox-validation 42 | 43 | https://www.linux-community.de/ausgaben/linuxuser/2017/05/genau-durchleuchtet/ 44 | 45 | https://github.com/democracyworks/pdf-validator 46 | 47 | https://wiki.dnb.de/pages/viewpage.action?pageId=125418723 48 | 49 | https://www.heise.de/ix/meldung/Open-Source-PDF-Validator-veraPDF-ein-Meilenstein-fuer-die-PDF-Industrie-3594460.html 50 | 51 | ## Stellungnamen zu Referentenentwürfen 52 | 53 | https://stellungnah.me/ 54 | 55 | https://github.com/okfde/stellungnahme 56 | 57 | https://github.com/okfde/stellungnahme/issues/1 58 | 59 | https://www.krankenkassen-direkt.de/news/pr/mitteilung.pl?id=2104032 60 | 61 | https://rsw.beck.de/aktuell/meldung/bundesregierung-will-transparenz-in-gesetzgebung-fortsetzen 62 | 63 | ## Extracting 64 | 65 | https://www.filingdb.com/pdf-text-extraction 66 | 67 | https://groups.google.com/forum/#!topic/alaveteli-users/Qr6EEFPMS_4 68 | 69 | ## Repair PDFs 70 | 71 | https://github.com/NicolasBernaerts/ubuntu-scripts/blob/master/pdf/pdf-repair 72 | 73 | 74 | [Check out my blog post](https://johannesfilter.com/python-and-pdf-a-review-of-existing-tools/) -------------------------------------------------------------------------------- /development/notes/02_notes.md: -------------------------------------------------------------------------------- 1 | Konkret: 2 | Das mit dem LM score checken 3 | 4 | etwas besser 5 | 6 | 7 | https://github.com/hpanwar08/detectron2 8 | 9 | https://github.com/GNOME/ocrfeeder 10 | 11 | https://github.com/OCR4all/LAREX 12 | 13 | 14 | 1. 15 | 2. 16 | 3. Idea: Improve Text output 17 | 18 | Improve Page Deteciton 19 | 20 | Seiten/Rand Bemerkungen entfernen oder extra, nicht in MarkDown 21 | 22 | 23 | Annahme: Header/Foot funktioniert 24 | 25 | Erste Dachte ich: ich arbeite auf bin aber dann: es gibt viel zu viele unterschiedliche Format 26 | 27 | also arbeite ich auf Paragraphen Basis 28 | 29 | 30 | TODO: 31 | - finalize PDF-Scripts (README, better documentation, support linux and OSX, tests?) 32 | 33 | 34 | was mit Fußnoten? Einsetzen? 35 | 36 | pdf.js ist besser als 37 | 38 | PDF js ist besser im erkennen von Wörtern, aber schlechter insgesamt 39 | 40 | so verlor zum Beispiel XX mehrere Zeichen, zudem ware die Reihenfolge falsch 41 | 42 | simpler algorithm: check ob in pdf.js als word richtig gemacht wird 43 | 44 | most similiar paragraph: > 45 | 46 | 47 | https://nanonets.com/blog/deep-learning-ocr/ 48 | 49 | https://medium.com/capital-one-tech/learning-to-read-computer-vision-methods-for-extracting-text-from-images-2ffcdae11594 50 | 51 | https://nanonets.com/blog/deep-learning-ocr/ 52 | 53 | https://github.com/argman/EAST 54 | 55 | https://github.com/michaelgfalk/clean-ocr 56 | 57 | 58 | 59 | # RANDOm 60 | 61 | 62 | https://okfnlabs.org/blog/2016/04/19/pdf-tools-extract-text-and-data-from-pdfs.html 63 | 64 | https://labs.imaginea.com/pdf-to-text-extraction/ 65 | 66 | https://aws.amazon.com/textract/ 67 | 68 | https://aws.amazon.com/textract/pricing/ 69 | 70 | https://github.com/OCR4all/OCR4all 71 | 72 | https://hub.docker.com/r/gkmr/pdf-tools/dockerfile 73 | -------------------------------------------------------------------------------- /development/notes/03_notes.md: -------------------------------------------------------------------------------- 1 | https://pytorch.org/get-started/previous-versions/ 2 | 3 | 4 | # CUDA 10.0 5 | conda install pytorch==1.2.0 torchvision==0.4.0 cudatoolkit=10.0 -c pytorch 6 | 7 | 8 | 9 | forward loss here 10 | 11 | https://github.com/flairNLP/flair/blob/1d44abf35f1122d1e146c9a219b2cb5f98b41b40/flair/nn.py 12 | 13 | 14 | Ddd ideas 15 | 16 | Login only email 17 | 18 | Only sent results via email 19 | 20 | 21 | https://python-markdown.github.io/extensions/footnotes/ 22 | 23 | 24 | 25 | 26 | 27 | https://bitbucket.org/tiedemann/pdf2xml/src/master/ 28 | 29 | 30 | 31 | ## Footenotes 32 | 33 | https://github.com/markdown-it/markdown-it-footnote 34 | 35 | Pull all Footnotes to the bottom 36 | 37 | ## Other Language Models 38 | 39 | Using Flair's language model is pretty time-intensive 40 | 41 | 42 | Use a word-based language model, e.g. huggingface transformer 43 | 44 | 45 | Or using my old ULMFIT for german model? Or train a new model? 46 | 47 | Pro: re-using old model, training a new model takes too long. 48 | 49 | 50 | ## Related Work 51 | 52 | Improved Text Extraction from PDF Documents for Large-Scale Natural Language Processing 53 | 54 | https://dl.acm.org/doi/10.1007/978-3-642-54906-9_9 55 | 56 | 57 | 58 | 59 | 60 | Do not work on headlines right now, parsr is improving the detection with ML. Kinda broken right now. -------------------------------------------------------------------------------- /development/notes/04_data.md: -------------------------------------------------------------------------------- 1 | # Description of Training Data 2 | 3 | 1. Download "Stellungnahmen zu Referententwürfen" from the [BMJV](https://www.bmjv.de/SiteGlobals/Forms/Suche/Gesetzgebungsverfahrensuche_Formular.html?resultsPerPage=25), around 02.04.2022 4 | 2. Prepend filenames with numbers 5 | 3. OCRd for German and English 6 | 4. Sort / group by language, only consider German 7 | 5. Redo two OCR that were not broken, text extracted 8 | 9 | -------------------------------------------------------------------------------- /development/notes/05_notes.md: -------------------------------------------------------------------------------- 1 | # 2 | 3 | TODO: Flask micro service based on DDD 4 | 5 | Docker Image 6 | 7 | Optin to Force CUDA? -------------------------------------------------------------------------------- /development/notes/06_blogpost.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: project (tragen wir ein) 3 | title: 'pd3f: PDF zu Fließtext mit Maschinellem Lernen 4 | image: /path/to/Beitragfsfoto-DDD_1500.jpg 5 | video: (tragen wir ein) 6 | tags: (tragen wir ein) 7 | authors: Johannes Filter 8 | summary: Durch lange Wörter im Deutschen sind aus PDF extrahierte Texte mit Zeilenumbrüchen zerstückelt. 'pd3f' rekonstruiert mithilfe von Maschinellem Lernen den ursprünglichen Fließtext. 9 | --- 10 | 11 | # PDF zu Fließtext mit Maschinellem Lernen 12 | 13 | > Durch lange Wörter im Deutschen sind aus PDF extrahierte Texte mit Zeilenumbrüchen zerstückelt. 14 | > 'pd3f' rekonstruiert mithilfe von Maschinellem Lernen den ursprünglichen Fließtext. 15 | 16 | PDFs sind für Menschen gemacht und nicht für Maschinen. 17 | Das führt dazu, dass wir sie lesen können, aber Maschinen Probleme damit haben, Text zu extrahieren. 18 | Dies ist jedoch notwendig, um z. B. große Mengen von PDFs im Rahmen journalistischer Recherchen auszuwerten. 19 | Auch Personen mit Seheinschränkungen sind darauf angewiesen, dass Computer ihnen Texte vorlesen. 20 | Für die bereits erfolgte oder geplante Digitalisierung deutscher Behörden müssen zudem große Aktenbestände digitalisiert werden. 21 | 22 | ## Text extrahieren mit 'pd3f' 23 | 24 | Im Rahmen der Prototype-Fund-Förderung von "DDD: Deutsche Dokumente Digitalisieren" ist die Software-Lösung 'pd3f' entstanden, um "guten" Text aus PDF zu rekonstruieren - 25 | “gut” bedeutet hier, dass der ursprüngliche Text ohne unnötige Zeilenumbrüche wiederhergestellt werden kann. 26 | Aus dem zerstückelten Text im PDF wird somit wieder ein digitaler Fließtext. 27 | Im Deutschen gibt es viele lange Wörter und deswegen die Besonderheit, dass Wörter am Zeilenende getrennt werden. 28 | Bei einer üblichen Textextraktion werden getrennte Wörter jedoch nicht wieder zusammengefügt. 29 | Damit kann das ursprüngliche Wort z. B. nicht mehr per Suche gefunden werden. 30 | Auch weiterführende Anwendungen, wie z. B. die automatisierte Erkennung von Eigennamen (Named-Entity Recognition), werden erschwert. 31 | 32 | ## Automatisierte Texterkennung 33 | 34 | Texterkennung auf gescannten Dokumenten (Optical Character Recognition/OCR) erfolgt schon heute zufriedenstellend mit Open-Source-Lösungen. 35 | Es ist aber aufgrund des veralteten Portable Document Format (PDF) weiterhin schwierig, die Wörter aus einem PDF zu einem Text zusammenfassen. 36 | Das Format folgt der Idee des Druckens, weshalb Fließtext darin nicht als Text dargestellt wird. 37 | Stattdessen wird teilweise jeder einzelne Buchstabe als Zeichen kodiert und ihm eine Position (x- und y-Wert für Höhe und Breite) auf dem (virtuellen) Blatt Papier zugewiesen. 38 | 39 | ![pdf_buchstaben.jpg]() 40 | 41 | Um aus diesem Buchstabensalat nutzen bestehen Tools simple Annahmen, um Buchstaben zu Wörter zusammen zu setzen. 42 | Also Buchstaben werden zu Wörtern zusammengefasst, wenn sie vertikal ungefähr auf der gleichen Linie sind und mir nur wenigen Millimetern Abstand zueinander entfernt sind. 43 | Aus Wörtern müssen wieder ganze Zeilen und diese Zeilen anschließend zu Paragraphen zusammengefasst werden. 44 | Das ist ohnehin eine schwierige Aufgabe, da es für den Satz eines Textes nahezu endlose Möglichkeiten gibt. 45 | Das Open-Source-Tool [Parsr](https://github.com/axa-group/Parsr) des französischen Versicherungskonzerns Axa sorgt bereits für Besserung, denn es zerlegt relativ erfolgreich ein PDF in Zeilen und Absätze. 46 | Das Tool ist wenige Monate vor Start der Projektförderung erschienen und erwies sich in der Projektphase als nützlich. 47 | Unsere Software 'pd3f' füttert zunächst das PDF in Parsr und nutzt die Ausgabe von Parsr, um darauf aufbauend guten Text wiederherzustellen. 48 | 49 | ## Zeilenumbrüche entfernen 50 | 51 | Worttrennung an Zeilenumbrüchen zu entfernen, ist eigentlich eine einfache Aufgabe: Alle Wörter mit einem "-" am Zeilenende werden mit dem Wort auf der darauffolgenden Zeile zusammengefügt wie in diesem Beispiel. 52 | 53 | > ... die Bedeutung der finan- 54 | > 55 | > ziellen Interessen der Union ... 56 | 57 | Das Wort "finanziellen" entspricht dem ursprünglichen Text. 58 | 59 | Es gibt aber auch "-" am Zeilenenden, das nicht entfernt werden darf, weil es Bestandteil des Worter ist: 60 | 61 | > Auch andere EU- 62 | > 63 | > Staaten, wie bspw. Polen, ... 64 | 65 | Um an dieser Stelle weiterzukommen, braucht es mehr Verständnis über die deutsche Sprache. 66 | Hier kommt Maschinelles Lernen in Form von Sprachmodellen zum Einsatz. 67 | 68 | ## Was sind Sprachmodelle? (Language Models) 69 | 70 | Bei Sprachmodellen geht es darum, dass ein Computerprogramm neue Wörter auf der Basis bereits genutzter Wörter lernt. 71 | Zum Einsatz kommen Sprachmodelle z. B. auf Smartphones bei der Autovervollständigung. 72 | Sprachmodelle verinnerlichen die Charakteristiken der deutschen Sprache und können vorhersagen, welche Wörter oder Buchstaben wahrscheinlich als nächstes kommen. 73 | So kommt nach "Sehr geehrte" wahrscheinlich "Frau" als nächstes. 74 | 75 | Solche Sprachmodelle operieren auf ganzen Wörtern oder auch nur auf Buchstaben (um nachfolgende Buchstaben zu erraten). 76 | 77 | ## Texte reparieren mit 'dehyphen' 78 | 79 | Eine Unterkomponente von 'pd3f' ist das Softwarepaket 'dehyphen', welches ebenfalls im Rahmen der Förderung entstand. 80 | Es benutzt Sprachmodelle, um zu entscheiden ob ein "-" am Zeilenende entfernt werden sollte oder nicht. 81 | Die Grundidee ist dabei eine Berechnung darüber, welche die wahrscheinliche Möglichkeit ist, zwei Zeilen zu verbinden. 82 | 83 | > Auch andere EU- 84 | > 85 | > Staaten, wie bspw. Polen, ... 86 | 87 | Bei diesem Beispiel kommt 'dehyphen' zum richtigen Ergebnis: "EU-Staaten" ist korrekt, nicht "EUStaaten". 88 | 'dehyphen' kann als Modul von anderen Software-Entwickler*innen einfach wiederverwendet werden. 89 | 90 | ## Datenverarbeitungs-Pipeline 'pd3f' 91 | 92 | Das Hauptergebnis der Förderung ist 'pd3f': Eine komplette Anwendung und eine Datenverarbeitungs-Pipeline für PDFs. 93 | Mit ihrer Hilfe können (deutsche) Dokumente digitalisiert werden. 94 | Auf einem gescannten Dokument wird der Text automatisiert gescannt, dann mithilfe von Parsr der Text in Wörter, Zeilen und Absätze unterteilt. 95 | Anschließend wird mithilfe von 'dehyphen' guter Text extrahiert. 96 | Anbei eine schematische Auflistung der benutzten Komponenten. 97 | 98 | ![flow.jpg]() 99 | 100 | [Zur Demo von 'pd3f'](https://demo.pd3f.com) 101 | 102 | Der Fokus liegt auf der deutschen Sprache, deren besonderes Charakteristikum lange Wörter sind, 'pd3f' kann aber auch für andere Sprachen angewandt werden. 103 | Es ist aktuell auch für Englisch, Spanisch und Französisch verfügbar. 104 | 105 | Wer nach dieser kleinen Einführung mehr über 'pd3f' erfahren möchte, dem sei die ausführliche Dokumentation zum Quellcode empfohlen. 106 | Der Code wird online stehen und weiter gepflegt. 107 | Die Hauptfunktionalitäten von 'pd3f' sind zusätzlich in einem eigenen Python-Paket gebündelt, sodass auch hier eine einfach weitere Verwendung möglich ist. 108 | 109 | ## Weitere Arbeit 110 | 111 | Da Dokumente in so vielen unterschiedlichen Formen vorkommen, funktioniert 'pd3f' noch nicht für alle PDFs. 112 | Gerade bei schlecht gescannten PDFs ist der extrahierte Text zudem noch stark verbesserungsbedürftig. 113 | Es ist unwahrscheinlich, dass 'pd3f' jemals für alle PDFs funktionieren wird, doch es wird weiterhin gearbeitet, die Resultate, z. B. für mehrspaltige Dokumente, zu verbessern. 114 | 115 | Was noch fehlt, ist eine systematische Evaluation der Textextrakte von 'pd3f'. 116 | Diese wird voraussichtlich im September 2020 erfolgen. 117 | 118 | Ich danke dem Prototype Fund und dem DLR-Projektträger für die Betreuung des Projekts, Ame Elliott und Eileen Wagner von [Simply Secure](https://simplysecure.org/) für das Coaching, Simon Wörpel für sein Feedback, und dem BMBF für die finanzielle Förderung. 119 | 120 | Website: 121 | Twitter: 122 | GitHub: 123 | 124 | von Johannes Filter 125 | 126 | Webseite: 127 | Twitter: 128 | 129 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | parsr: 5 | image: axarev/parsr:v1.2.2 6 | ports: 7 | - "3001:3001" 8 | 9 | parsr_ui: 10 | image: axarev/parsr-ui-localhost:v1.2.2 11 | ports: 12 | - "8080:80" 13 | -------------------------------------------------------------------------------- /pd3f/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.4.0" 2 | 3 | import logging 4 | from logging import NullHandler 5 | 6 | from .export import Export, extract 7 | from .parsr_wrapper import run_parsr 8 | 9 | logging.getLogger(__name__).addHandler(NullHandler()) 10 | -------------------------------------------------------------------------------- /pd3f/dehyphen_wrapper.py: -------------------------------------------------------------------------------- 1 | """Interaction with `dehyphen`, cache results 2 | """ 3 | 4 | 5 | from functools import lru_cache 6 | 7 | from joblib import Memory 8 | 9 | from dehyphen import FlairScorer 10 | 11 | 12 | # cache max 100mb 13 | memory = Memory( 14 | "~/.cache/pd3f/dehyphen", verbose=0, compress=5, bytes_limit=100 * 1000 * 1000 15 | ) 16 | 17 | 18 | scorer = None 19 | 20 | 21 | def get_scorer(lang): 22 | """Simple singleton to avoid re-initialization of the language model. 23 | """ 24 | global scorer 25 | if scorer is None: 26 | # simplify Flair's naming of models 27 | if lang.endswith("-fast"): 28 | scorer = FlairScorer(lang=lang[:-5], fast=True) 29 | else: 30 | scorer = FlairScorer(lang=lang) 31 | return scorer 32 | 33 | 34 | @memory.cache 35 | def dehyphen_paragraph(lines, lang): 36 | scorer = get_scorer(lang) 37 | return scorer.dehyphen_paragraph(lines) 38 | 39 | 40 | @memory.cache 41 | def is_split_paragraph(p1, p2, lang): 42 | scorer = get_scorer(lang) 43 | return scorer.is_split_paragraph(p1, p2) 44 | 45 | 46 | @memory.cache 47 | def newline_or_not(l1, l2, lang): 48 | """Decide whether to add a newline or not. 49 | """ 50 | # Flair does not work with only one char, thus this special case 51 | if len(l1) == 1 and len(l1[0]) == 1: 52 | return True 53 | if len(l2) == 1 and len(l2[0]) == 1: 54 | return False 55 | scorer = get_scorer(lang) 56 | 57 | texts = [l1, l2, l1 + " " + l2] 58 | scores = scorer.score(texts) 59 | best_score_idx = scores.index(min(scores)) 60 | return best_score_idx != 2 61 | 62 | 63 | @lru_cache 64 | def single_score(text, lang): 65 | scorer = get_scorer(lang) 66 | # Flair does not work with only one char, thus this special case 67 | if len(text) == 1: 68 | return float("inf") 69 | return scorer.score([text])[0] 70 | -------------------------------------------------------------------------------- /pd3f/doc_info.py: -------------------------------------------------------------------------------- 1 | """Statistics and information about document elements. 2 | """ 3 | 4 | import logging 5 | from collections import Counter 6 | from statistics import median 7 | 8 | from textdistance import jaccard 9 | from cleantext import clean, fix_bad_unicode 10 | 11 | from .dehyphen_wrapper import single_score 12 | from .geometry import sim_bbox 13 | from .utils import flatten 14 | 15 | logger = logging.getLogger(__name__) 16 | 17 | 18 | def avg_word_space(line): 19 | """Average word space on a line, util for words / lines 20 | 21 | src: https://github.com/axa-group/Parsr/blob/69e6b9bf33f1cc43d5a87d428cedf1132ccc48e8/server/src/types/DocumentRepresentation/Paragraph.ts#L460 22 | """ 23 | 24 | def calc_margins(index, word): 25 | if index > 0: 26 | return word["box"]["l"] - ( 27 | line["content"][index - 1]["box"]["l"] 28 | + line["content"][index - 1]["box"]["w"] 29 | ) 30 | return 0 31 | 32 | margins = [calc_margins(i, w) for i, w in enumerate(line["content"])] 33 | return sum(margins) / len(margins) 34 | 35 | 36 | def roughly_same_font(f1, f2): 37 | # unreliable 38 | assert f1["sizeUnit"] == "px" 39 | assert f2["sizeUnit"] == "px" 40 | return abs(f1["size"] - f2["size"]) < max(f1["size"], f2["size"]) * 0.2 41 | 42 | 43 | def extract_elements(outer_element, element_type): 44 | def traverse(element): 45 | if type(element) is dict: 46 | if "type" in element and element["type"] == element_type: 47 | return element 48 | if "content" in element: 49 | return traverse(element["content"]) 50 | return None 51 | if type(element) is list: 52 | return [traverse(e) for e in element] 53 | 54 | return [ 55 | x for x in flatten(traverse(outer_element), keep_dict=True) if x is not None 56 | ] 57 | 58 | 59 | def font_stats(outer_element): 60 | return [x["font"] for x in extract_elements(outer_element, "word")] 61 | 62 | 63 | def most_used_font(element): 64 | return Counter(font_stats(element)).most_common(1)[0][0] 65 | 66 | 67 | def get_lineheight(l1, l2): 68 | # l1 or l2 can be the upper line 69 | if l2["box"]["t"] < l1["box"]["t"]: 70 | l1, l2 = l2, l1 71 | dif = l2["box"]["t"] - l1["box"]["t"] - l1["box"]["h"] 72 | # it may happen that the lines are on the same 73 | return dif if dif > 0 else None 74 | 75 | 76 | def median_from_counter(c): 77 | data = [] 78 | for value, count in c.most_common(): 79 | data += [value] * count 80 | return median(data) 81 | 82 | 83 | def only_text(es): 84 | r = [] 85 | for e in es: 86 | for x in extract_elements(e, "word"): 87 | r.append(x["content"].strip()) 88 | return fix_bad_unicode(" ".join(r)) 89 | 90 | 91 | def only_points(es): 92 | r = [] 93 | for e in es: 94 | b = e["box"] 95 | r.append((b["t"], b["l"])) 96 | r.append((b["t"] + b["h"], b["l"])) 97 | r.append((b["t"], b["l"] + b["w"])) 98 | r.append((b["t"] + b["h"], b["l"] + b["w"])) 99 | return r 100 | 101 | 102 | def super_similiar(es1, es2, sim_factor=0.8, sim_box=0.6): 103 | """Check if two elements are super similiar by text (Jaccad) and visually (compare bbox). 104 | """ 105 | text1 = only_text(es1) 106 | text2 = only_text(es2) 107 | 108 | points1 = only_points(es1) 109 | points2 = only_points(es2) 110 | 111 | if min(len(points1), len(points2)) < 4: 112 | return False 113 | 114 | logger.debug("points") 115 | logger.debug(points1) 116 | logger.debug(points2) 117 | 118 | j_sim = jaccard(text1, text2) 119 | b_sim = sim_bbox(points1, points2) 120 | 121 | logger.debug(f"footer/header sims {j_sim} {b_sim}") 122 | 123 | return j_sim > sim_factor and b_sim > sim_box 124 | 125 | 126 | def remove_duplicates(page_items, lang): 127 | results = [page_items[0]] 128 | for elements in page_items[1:]: 129 | cool = True 130 | for r in results: 131 | if len(r) == 0: 132 | continue 133 | # only choose the best first one? 134 | if super_similiar(r, elements): 135 | logger.debug("items are super similiar") 136 | if single_score(only_text(r), lang) <= single_score( 137 | only_text(elements), lang 138 | ): 139 | logger.debug( 140 | "okay, skipping here, the previous one got better / same score" 141 | ) 142 | cool = False 143 | break 144 | else: 145 | logger.debug("removing previous one, this is better") 146 | results.remove(r) 147 | 148 | if cool: 149 | results.append(elements) 150 | else: 151 | results.append([]) 152 | return results 153 | 154 | 155 | def remove_page_number_header_footer(page_items): 156 | """Rough check to remove elements with text such as `Seite $NUM von $NUM` or just `$NUM`. 157 | 158 | TODO: Make it work if the pager number is part of a bigger header/footer. And also consider the language. 159 | """ 160 | texts = [ 161 | clean(only_text(x), replace_with_number="", no_punct=True) 162 | .replace("seite", "") 163 | .replace("von", "") 164 | for x in page_items 165 | ] 166 | 167 | results = [] 168 | for idx, x in enumerate(page_items): 169 | if texts[idx].strip() != "": 170 | results.append(x) 171 | return results 172 | 173 | 174 | def calc_line_space(lines): 175 | if len(lines) <= 1: 176 | return [] 177 | lineheights = [] 178 | for i, _ in enumerate(lines[:-1]): 179 | if (x := get_lineheight(lines[i], lines[i + 1])) is not None: 180 | lineheights.append(x) 181 | return lineheights 182 | 183 | 184 | class DocumentInfo: 185 | def __init__(self, input_data) -> None: 186 | self.input_data = input_data 187 | 188 | # needs to be done first 189 | self.element_order_page() 190 | self.document_font_stats() 191 | self.document_paragraph_stats() 192 | 193 | # free memory (code should have been re-written but whatehver) 194 | del self.input_data 195 | 196 | def document_paragraph_stats(self): 197 | """ 198 | """ 199 | 200 | self.counter_width = Counter() 201 | self.counter_height = Counter() 202 | self.counter_lineheight = Counter() 203 | self.counter_line_left = Counter() 204 | 205 | for n_page, p in enumerate(self.input_data["pages"]): 206 | for e in p["elements"]: 207 | lis = extract_elements(e, "line") 208 | for x in lis: 209 | x["idx_page"] = n_page 210 | self.id_to_elem[x["id"]] = x 211 | 212 | self.counter_width.update([x["box"]["w"] for x in lis]) 213 | self.counter_height.update([x["box"]["h"] for x in lis]) 214 | self.counter_lineheight.update(calc_line_space(lis)) 215 | self.counter_line_left.update([x["box"]["l"] for x in lis]) 216 | 217 | if ( 218 | min( 219 | map( 220 | len, 221 | [ 222 | self.counter_width, 223 | self.counter_height, 224 | self.counter_lineheight, 225 | self.counter_line_left, 226 | ], 227 | ) 228 | ) 229 | == 0 230 | ): 231 | raise ValueError( 232 | "Something is wrong with the document. Is the text in the PDF broken (copy the text out of the doc and see how it looks)?" 233 | ) 234 | 235 | self.median_line_width = median_from_counter(self.counter_width) 236 | self.median_line_height = median_from_counter(self.counter_height) 237 | # line space: line height 238 | self.median_line_space = median_from_counter(self.counter_lineheight) 239 | self.median_line_left = median_from_counter(self.counter_line_left) 240 | 241 | logger.info(f"media line width: {self.median_line_width}") 242 | logger.info(f"median line height: {self.median_line_height}") 243 | logger.info(f"median line space: {self.median_line_space}") 244 | logger.info(f"counter width: {self.counter_width.most_common(5)}") 245 | logger.info(f"counter height: {self.counter_height.most_common(5)}") 246 | logger.info(f"counter lineheight: {self.counter_lineheight.most_common(5)}") 247 | 248 | def document_font_stats(self): 249 | """Get statistics about font usage in the document 250 | """ 251 | c = Counter() 252 | for p in self.input_data["pages"]: 253 | for e in p["elements"]: 254 | c.update(font_stats(e)) 255 | 256 | if len(c) == 0: 257 | raise ValueError( 258 | "Something is wrong with the document. Is the text in the PDF broken (copy the text out of the doc and see how it looks)?" 259 | ) 260 | 261 | self.body_font = c.most_common(1)[0][0] 262 | self.font_counter = c 263 | self.font_info = {} 264 | for x in self.input_data["fonts"]: 265 | self.font_info[x["id"]] = x 266 | assert x["sizeUnit"] == "px" 267 | 268 | def seperate_lines(self, l1, l2, factor=0.5): 269 | lh = get_lineheight(l1, l2) 270 | if lh is None: 271 | return False 272 | # space between lines can only be + 0.5x the body lineheight 273 | return ((lh - self.median_line_space) / self.median_line_space) > factor 274 | 275 | def on_same_page(self, e1, e2): 276 | """Check if both elements are on the same page 277 | """ 278 | return ( 279 | self.id_to_elem[e1["id"]]["idx_page"] 280 | == self.id_to_elem[e2["id"]]["idx_page"] 281 | ) 282 | 283 | def element_order_page(self): 284 | """Save the order of paragraphes for each page, exclude header / footer 285 | """ 286 | self.order_page = [] 287 | self.id_to_elem = {} 288 | for idx_page, p in enumerate(self.input_data["pages"]): 289 | per_page = [] 290 | for e in p["elements"]: 291 | # not all elements are included here 292 | e["idx_page"] = idx_page 293 | self.id_to_elem[e["id"]] = e 294 | 295 | if not e["type"] in ("paragraph", "heading"): 296 | continue 297 | if "isHeader" in e["properties"] and e["properties"]["isHeader"]: 298 | continue 299 | if "isFooter" in e["properties"] and e["properties"]["isFooter"]: 300 | continue 301 | 302 | per_page.append(e["id"]) 303 | self.order_page.append(per_page) 304 | 305 | def is_body_paragrah(self, para): 306 | lines = extract_elements(para, "line") 307 | w_lines = [x["box"]["w"] for x in lines] 308 | h_lines = [x["box"]["h"] for x in lines] 309 | l_lines = [x["box"]["l"] for x in lines] 310 | 311 | logger.debug("is it a body para?") 312 | if abs(self.median_line_width - max(w_lines)) > 5: 313 | return False 314 | 315 | if abs(self.median_line_height - median(h_lines)) > 2: 316 | return False 317 | 318 | if abs(self.median_line_left - median(l_lines)) > 5: 319 | return False 320 | logger.debug("yes!") 321 | return True 322 | -------------------------------------------------------------------------------- /pd3f/doc_output.py: -------------------------------------------------------------------------------- 1 | """Document represenation after extraction stuff from parsr 2 | """ 3 | 4 | import logging 5 | import re 6 | 7 | from .dehyphen_wrapper import is_split_paragraph 8 | from .string_utils import strip_spaces_line_end 9 | from .utils import flatten 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | 14 | class DocumentOutput: 15 | def __init__(self, data, header, footer, order, lang): 16 | self.data = data or [] 17 | self.header = header or [] 18 | self.footer = footer or [] 19 | self.order = order or [] 20 | self.lang = lang 21 | self.merged_elements = {} 22 | 23 | def __getitem__(self, key): 24 | return self.data[key] 25 | 26 | def __setitem__(self, key, value): 27 | self.data[key] = value 28 | 29 | def get_element(self, elem_id): 30 | """Returns element from the data. Returns `None` if the element is not part out of the output anymore. 31 | """ 32 | if elem_id in self.merged_elements: 33 | elem_id = self.merged_elements[elem_id] 34 | result = list(filter(lambda x: x.id == elem_id, self)) 35 | 36 | # `result` may be empty if the elem was port of footer / header and is gone now (due to dudeplication) 37 | if len(result) == 1: 38 | return result[0] 39 | return None 40 | 41 | def get_first_of_type_on_page(self, find_types, idx_page): 42 | for ele_id in self.order[idx_page]: 43 | ele = self.get_element(ele_id) 44 | if ele is None: 45 | continue 46 | if ele.type in find_types: 47 | return ele 48 | return None 49 | 50 | def get_last_of_type_on_page(self, find_types, idx_page): 51 | for ele_id in reversed(self.order[idx_page]): 52 | ele = self.get_element(ele_id) 53 | if ele is None: 54 | continue 55 | if ele.type in find_types: 56 | return ele 57 | return None 58 | 59 | def reverse_page_break(self): 60 | """join paragraphs that were split between pages 61 | 62 | gets complicated when footnotes are not re-ordered 63 | """ 64 | for idx, page in enumerate(self.order[:-1]): 65 | logger.info(f"reversing page break page #{idx}") 66 | last_element = self.get_last_of_type_on_page(("body", "heading"), idx) 67 | next_element = self.get_first_of_type_on_page(("body", "heading"), idx + 1) 68 | 69 | if last_element is None or next_element is None: 70 | logger.debug("some element is none, cannot test") 71 | continue 72 | 73 | if last_element.type == "heading" or next_element.type == "heading": 74 | logger.debug("some element is a header, cannot test") 75 | continue 76 | 77 | # It cannot contain newlines 78 | if last_element.ends_newline: 79 | logger.debug( 80 | f"the last element has ends with a newline. Do not try to join with the next one." 81 | ) 82 | continue 83 | 84 | fixed = is_split_paragraph(last_element, next_element, self.lang) 85 | if fixed is None: 86 | logger.debug("looks like a split paragraph") 87 | continue 88 | 89 | logger.debug("joining the following paragraphs") 90 | logger.debug(f"{last_element}\n{next_element}\n{fixed}") 91 | 92 | # set new paragraph 93 | self[self.data.index(last_element)] = fixed 94 | self.data.remove(next_element) 95 | self.merged_elements[next_element.id] = last_element.id 96 | 97 | def reorder_footnotes(self): 98 | new_data, all_footsnotes = [], [] 99 | for element in self: 100 | if element.type == "footnotes": 101 | all_footsnotes.append(element) 102 | else: 103 | new_data.append(element) 104 | 105 | self.data = new_data + all_footsnotes 106 | 107 | def markdown(self): 108 | return self.text(markdown=True) 109 | 110 | def text(self, markdown=False): 111 | txt = "" 112 | 113 | txt += "\n\n".join([str(x) for x in flatten(self.header)]) 114 | 115 | for element in self: 116 | if markdown and element.type == "heading": 117 | # prepend dashes 118 | txt += "#" * element.level + " " 119 | txt += str(element) 120 | 121 | txt += "\n\n".join([str(x) for x in flatten(self.footer)]) 122 | 123 | # hotfix, there are sometimes too many newlines 124 | txt = re.sub(r"(\n){3,}", "\n\n", txt) 125 | return txt 126 | 127 | 128 | class Element: 129 | def __init__( 130 | self, 131 | element_type, 132 | lines, 133 | element_id, 134 | idx_page=None, 135 | num_newlines=0, 136 | level=None, 137 | ends_newline=None, 138 | ): 139 | assert element_type in ("body", "heading", "footnotes") 140 | self.type = element_type 141 | self.lines = lines 142 | self.id = element_id 143 | self.level = level 144 | self.idx_page = idx_page 145 | self.num_newlines = num_newlines 146 | self.ends_newline = ends_newline 147 | 148 | for x in lines: 149 | assert len(x) > 0 150 | 151 | def __getitem__(self, key): 152 | return self.lines[key] 153 | 154 | def __str__(self): 155 | if self.type == "footnotes": 156 | # In some cases, there were unnesary spaces added befor newlines. 157 | # So this should generally not happend in the first place. 158 | fixed_lines = [strip_spaces_line_end(" ".join(line)) for line in self.lines] 159 | return "".join(fixed_lines) + "\n" 160 | 161 | return "".join([" ".join(line) for line in self.lines]) + "\n\n" 162 | 163 | def __add__(self, other_element): 164 | assert self.type == other_element.type 165 | self.lines += other_element.lines 166 | return self 167 | 168 | def __len__(self): 169 | return len(self.lines) 170 | -------------------------------------------------------------------------------- /pd3f/geometry.py: -------------------------------------------------------------------------------- 1 | """Compare geometric shapes 2 | """ 3 | 4 | from shapely.geometry import MultiPoint, box 5 | 6 | 7 | def bbox(points): 8 | assert len(points) >= 4 9 | h = MultiPoint(points).convex_hull 10 | return box(*h.bounds) 11 | 12 | 13 | def sim_bbox(e1, e2): 14 | b1 = bbox(e1) 15 | b2 = bbox(e2) 16 | shared_area = b1.intersection(b2).area 17 | return shared_area / max(b1.area, b2.area) 18 | -------------------------------------------------------------------------------- /pd3f/parsr_wrapper.py: -------------------------------------------------------------------------------- 1 | """Wrapper to interaction with parsr (using parsr's Python client) 2 | """ 3 | 4 | import importlib.resources 5 | import json 6 | import logging 7 | import tempfile 8 | from pathlib import Path 9 | 10 | from parsr_client import ParsrClient as client 11 | 12 | from .utils import update_dict, write_dict 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | 17 | def setup_config(config, adjust_cleaner_config, check_tables, fast): 18 | """ 19 | 20 | """ 21 | # update base config of parsr 22 | with importlib.resources.path("pd3f", "pd3fConfig.json") as cfg_path: 23 | jdata = json.loads(cfg_path.read_text()) 24 | jdata = update_dict(jdata, config) 25 | 26 | # Update parsr cleaner config since it's more complicated. 27 | # The cleaner consists of a pipeline, so we first have to find the matching module. 28 | # Then update its configuration. 29 | for new_cl in adjust_cleaner_config: 30 | for idx, cl in enumerate(jdata["cleaner"]): 31 | if type(cl) != list: 32 | continue 33 | if cl[0] != new_cl[0]: 34 | continue 35 | jdata["cleaner"][idx] = [cl[0], {**cl[1], **new_cl[1]}] 36 | 37 | if not check_tables: 38 | jdata["cleaner"] = [ 39 | x 40 | for x in jdata["cleaner"] 41 | if type(x) is str or "table-detection" not in x[0] 42 | ] 43 | 44 | if fast: 45 | jdata["cleaner"] = [ 46 | x 47 | for x in jdata["cleaner"] 48 | if type(x) is str and x != "drawing-detection" or x[0] != "image-detection" 49 | ] 50 | return jdata 51 | 52 | 53 | def run_parsr( 54 | file_path, 55 | out_dir=None, 56 | config={}, 57 | adjust_cleaner_config=[], 58 | text=False, 59 | markdown=False, 60 | check_tables=False, 61 | fast=False, 62 | parsr_location="localhost:3001", 63 | **kwargs, 64 | ): 65 | """Wrapper to interact with parsr (using parsr's Python client) 66 | """ 67 | parsr = client(parsr_location) 68 | 69 | parsr_config = setup_config(config, adjust_cleaner_config, check_tables, fast) 70 | 71 | with tempfile.NamedTemporaryFile(mode="w+") as tmp_config: 72 | json.dump(parsr_config, tmp_config) 73 | tmp_config.flush() # persist 74 | 75 | # TODO: when upgrading to v3.2, use file_path and config_path 76 | logger.info("sending PDF to Parsr") 77 | 78 | logger.debug(parsr_config) 79 | 80 | parsr.send_document( 81 | file=file_path, 82 | config=tmp_config.name, 83 | wait_till_finished=True, 84 | save_request_id=True, 85 | silent=False, 86 | ) 87 | 88 | logger.info("got response from Parsr") 89 | 90 | tables = [] 91 | if check_tables: 92 | for page, table in parsr.get_tables_info(): 93 | # table gets returned as panda df 94 | tables.append(parsr.get_table(page=page, table=table)) 95 | 96 | if not out_dir is None: 97 | out_dir = Path(out_dir) / Path(file_path).stem 98 | out_dir.mkdir(exist_ok=True, parents=True) 99 | 100 | if text: 101 | (out_dir / "text.txt").write_text(parsr.get_text()) 102 | 103 | if markdown: 104 | (out_dir / "text.md").write_text(parsr.get_markdown()) 105 | 106 | if check_tables: 107 | for idx, t in enumerate(tables): 108 | (out_dir / f"table_{idx}.csv").write_text(t.to_csv()) 109 | 110 | write_dict(parsr.get_json(), out_dir / "data.json") 111 | 112 | if not check_tables: 113 | return parsr.get_json(), None 114 | return parsr.get_json(), [x.to_csv() for x in tables] 115 | 116 | -------------------------------------------------------------------------------- /pd3f/pd3fConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0.9, 3 | "extractor": { 4 | "pdf": "pdfminer", 5 | "ocr": "tesseract", 6 | "language": ["deu"] 7 | }, 8 | "cleaner": [ 9 | "drawing-detection", 10 | [ 11 | "image-detection", 12 | { 13 | "ocrImages": false 14 | } 15 | ], 16 | "out-of-page-removal", 17 | [ 18 | "whitespace-removal", 19 | { 20 | "minWidth": 0 21 | } 22 | ], 23 | [ 24 | "redundancy-detection", 25 | { 26 | "minOverlap": 0.5 27 | } 28 | ], 29 | [ 30 | "table-detection", 31 | { 32 | "runConfig": [ 33 | { 34 | "pages": [], 35 | "flavor": "lattice" 36 | } 37 | ] 38 | } 39 | ], 40 | [ 41 | "table-detection-2", 42 | { 43 | "runConfig": [ 44 | { 45 | "pages": [] 46 | } 47 | ] 48 | } 49 | ], 50 | [ 51 | "header-footer-detection", 52 | { 53 | "ignorePages": [], 54 | "maxMarginPercentage": 15 55 | } 56 | ], 57 | "words-to-line-new", 58 | [ 59 | "reading-order-detection", 60 | { 61 | "minVerticalGapWidth": 5, 62 | "minColumnWidthInPagePercent": 15 63 | } 64 | ], 65 | [ 66 | "lines-to-paragraph", 67 | { 68 | "tolerance": 0.25 69 | } 70 | ], 71 | "page-number-detection", 72 | "hierarchy-detection" 73 | ], 74 | "output": { 75 | "granularity": "word", 76 | "includeMarginals": true, 77 | "includeDrawings": true, 78 | "formats": { 79 | "json": true, 80 | "text": false, 81 | "csv": true, 82 | "markdown": false, 83 | "pdf": false, 84 | "simpleJson": false 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /pd3f/string_utils.py: -------------------------------------------------------------------------------- 1 | def strip_spaces_line_end(text): 2 | """Removing spaces at the end of a line. 3 | 4 | For instance in this string `the text \n` the space is removed. 5 | While `the text\n` the string is unchanged. 6 | """ 7 | if len(text) < 2: 8 | return text 9 | if text[-1] == "\n": 10 | if text[-2] == " ": 11 | return strip_spaces_line_end(text[:-2]) + "\n" 12 | return text 13 | -------------------------------------------------------------------------------- /pd3f/utils.py: -------------------------------------------------------------------------------- 1 | """Utility functions 2 | """ 3 | 4 | import json 5 | from collections import Iterable 6 | from collections.abc import Mapping 7 | 8 | 9 | def update_dict(d, u): 10 | """Add u's keys to d. Override key in d if existing. 11 | Similar to JavaScript's Object.assign (but changes d inplace as well). 12 | """ 13 | 14 | for k, v in u.items(): 15 | if isinstance(v, Mapping): 16 | d[k] = update_dict(d.get(k, {}), v) 17 | else: 18 | d[k] = v 19 | return d 20 | 21 | 22 | def write_dict(d, fn): 23 | """Write dict to disk 24 | """ 25 | if not type(fn) == str: 26 | fn = str(fn) 27 | with open(fn, "w", encoding="utf-8") as f: 28 | json.dump(d, f, ensure_ascii=False, indent=4) 29 | 30 | 31 | def flatten(items, keep_dict=False): 32 | """Yield items from any nested iterable; see Reference. 33 | # https://stackoverflow.com/a/40857703/4028896 34 | 35 | keep_dict: do not flatten dicts 36 | """ 37 | if items is None: 38 | return 39 | if keep_dict and isinstance(items, Mapping): 40 | yield items 41 | else: 42 | for x in items: 43 | if ( 44 | isinstance(x, Iterable) 45 | and not isinstance(x, (str, bytes)) 46 | and (not keep_dict or not isinstance(x, Mapping)) 47 | ): 48 | for sub_x in flatten(x, keep_dict): 49 | yield sub_x 50 | else: 51 | yield x 52 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "pd3f" 3 | version = "0.4.0" 4 | description = "Reconstruct the original continuous text from PDFs with language models" 5 | license = "AGPL-3.0-only" 6 | authors = ["Johannes Filter "] 7 | repository = "https://github.com/pd3f/pd3f-core" 8 | keywords = ["pdf", "nlp", "german", "text-extraction"] 9 | classifiers = [ 10 | "Programming Language :: Python :: 3.8", 11 | "License :: OSI Approved :: GNU Affero General Public License v3", 12 | "Topic :: Scientific/Engineering :: Information Analysis", 13 | "Topic :: Scientific/Engineering :: Artificial Intelligence", 14 | ] 15 | readme = "README.md" 16 | 17 | [tool.poetry.dependencies] 18 | python = "^3.8" 19 | parsr-client = "3.1" 20 | joblib = "*" 21 | clean-text = { version = "*", extras = [ "gpl" ] } 22 | dehyphen = "^0.3.0" 23 | textdistance = "*" 24 | shapely = "*" 25 | 26 | 27 | [tool.poetry.dev-dependencies] 28 | pytest = "^5.2" 29 | jupyterlab = "*" 30 | black = {version = "^19.10b0", allow-prereleases = true} 31 | pdoc3 = "^0.9.2" 32 | 33 | 34 | [build-system] 35 | requires = ["poetry>=0.12"] 36 | build-backend = "poetry.masonry.api" 37 | -------------------------------------------------------------------------------- /scripts/remote_parsr.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | set -x 4 | 5 | # set ssh config 6 | remote_destination=$REMOTE_WORKSTATION 7 | 8 | parsr_version="v1.2.2" 9 | parsr_ui_version="v1.2.2" 10 | 11 | if [ "$1" = "stop" ]; then 12 | ssh $remote_destination "docker stop parsr-api; docker stop parsr-ui" || true 13 | killall ssh 14 | else 15 | ssh -f $remote_destination "nohup docker run --name parsr-api --rm -p 3001:3001 axarev/parsr:$parsr_version > /dev/null 2>&1 &" 16 | ssh -f $remote_destination "nohup docker run --name parsr-ui --rm -t -p 8080:80 axarev/parsr-ui-localhost:$parsr_ui_version > /dev/null 2>&1 &" 17 | ssh -f -N -L 8080:localhost:8080 $remote_destination 18 | ssh -f -N -L 3001:localhost:3001 $remote_destination 19 | fi 20 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pd3f/pd3f-core/00730834e23f387cbe079ef8ddec9483160d6a7f/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_data/00001/00001_112018_FU_Berlin_Richtlinie_2017_1371.pdf: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d2ffddce46833eeb9694e178916f1def91bc5720d3351b7bfd5bef912ff2b4bc 3 | size 430055 4 | -------------------------------------------------------------------------------- /tests/test_data/00001/00001_112018_FU_Berlin_Richtlinie_2017_1371_fast_experimental.txt: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:79c2a71aa5558cbd0924e859f839022c19e1c4424761212f64b40504fb48b2dc 3 | size 41530 4 | -------------------------------------------------------------------------------- /tests/test_data/00004/00004_09212018_bstbk_Unwandlungsgesetz.pdf: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0a5b53a761deac62e7d04630d739027270c195eac6faf46790c8cb68cbc03e7d 3 | size 90688 4 | -------------------------------------------------------------------------------- /tests/test_data/00004/00004_09212018_bstbk_Unwandlungsgesetz_fast_experimental.txt: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:03b7c702757cfbc0333df2cc553527c14a003e19f5f7aaa381a97107b001e80a 3 | size 5299 4 | -------------------------------------------------------------------------------- /tests/test_data/00020/00020_08112014_Stellungnahme_RAK_Koeln_RefE_Bekaempfung_Korruption.pdf: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:30dd8fda9f16c0cd5406a7332f8ece81a6ed61a41607a3df936dab61cdf0dd78 3 | size 645718 4 | -------------------------------------------------------------------------------- /tests/test_data/00020/00020_08112014_Stellungnahme_RAK_Koeln_RefE_Bekaempfung_Korruption_fast_experimental.txt: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:741b9d2ade51ed0e635efdce3ae28e7041a2055f923b75d33cecd4f2a03569a5 3 | size 3310 4 | -------------------------------------------------------------------------------- /tests/test_geometry.py: -------------------------------------------------------------------------------- 1 | from pd3f.geometry import * 2 | 3 | 4 | def test_geometry(): 5 | assert ( 6 | sim_bbox([[0, 0], (1, 1), (1, 1), (0, 1)], [[0, 0], (1, 0.5), (1, 0), (0, 0.5)]) 7 | == 0.5 8 | ) 9 | 10 | assert ( 11 | sim_bbox([[0, 0], (1, 1), (1, 1), (0, 1)], [[0, 0], (1, 1), (1, 0), (0, 1)]) 12 | == 1 13 | ) 14 | 15 | assert ( 16 | sim_bbox([[0, 0], (1, 1), (1, 1), (0, 1)], [[5, 5], (1, 1), (1, 5), (5, 1)]) 17 | == 0 18 | ) 19 | -------------------------------------------------------------------------------- /tests/test_pdfs.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from pd3f import extract 4 | 5 | 6 | def test_fast_experimental(): 7 | 8 | for dir in Path("tests/test_data").glob("*"): 9 | if not dir.is_dir(): 10 | continue 11 | pdf_path = next(dir.glob("*.pdf")) 12 | 13 | # strip to remove newlines of test files (because files should end with an empty line) 14 | ouput_text = next(dir.glob("*fast_experimental.txt")).read_text().strip() 15 | 16 | text, _ = extract( 17 | str(pdf_path), 18 | tables=False, 19 | experimental=True, 20 | lang="multi-v0-fast", 21 | fast=True, 22 | ) 23 | 24 | assert text.strip() == ouput_text 25 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | from pd3f.utils import * 2 | 3 | 4 | def test_flatten(): 5 | assert list(flatten([["a"], [[["b"]]]])) == ["a", "b"] 6 | assert list(flatten([["a"], [[["b"]]]], keep_dict=True)) == ["a", "b"] 7 | assert list(flatten([["a"], [[[{"b": "c"}]]]], keep_dict=True)) == ["a", {"b": "c"}] 8 | 9 | 10 | def test_update_dict(): 11 | target = {"a": 1, "b": 2} 12 | source = {"b": 4, "c": 5} 13 | 14 | assert update_dict(target, source) == {"a": 1, "b": 4, "c": 5} 15 | 16 | assert update_dict(target, {"b": [[{"d": 5}]]}) == {"a": 1, "b": [[{"d": 5}]], "c": 5} 17 | --------------------------------------------------------------------------------