diff --git a/.vscode/launch.json b/.vscode/launch.json index 851776c..58c4668 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,29 +8,31 @@ "name": "Generate eRP", "type": "debugpy", "request": "launch", - "program": "${workspaceFolder}/main.py", + "program": "main.py", "console": "integratedTerminal", "justMyCode": true, "args": [ "--project-dir", - "projects/erp", + "../../projects/erp", "--html", "--json" - ] + ], + "cwd": "${workspaceFolder}/service/src", }, { "name": "Generate ApoVZD", "type": "debugpy", "request": "launch", - "program": "${workspaceFolder}/main.py", + "program": "main.py", "console": "integratedTerminal", "justMyCode": true, "args": [ "--project-dir", - "projects/apovzd", + "../../projects/apovzd", "--html", "--json" - ] + ], + "cwd": "${workspaceFolder}/service/src", }, { "name": "Flask App eRP", @@ -39,8 +41,9 @@ "program": "server.py", "args": [ "--project-dir", - "projects/erp" + "../../projects/erp" ], + "cwd": "${workspaceFolder}/service/src", "jinja": true, "justMyCode": true }, diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 18820ca..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -black -Jinja2 -Flask~=3.0 -flask-swagger~=0.2.14 -flask-cors -PyYAML diff --git a/rest/requests.http b/rest/requests.http index 9ee540e..0ad2835 100644 --- a/rest/requests.http +++ b/rest/requests.http @@ -10,6 +10,11 @@ GET http://{{host}}/spec ### +# @name getclassifications +GET http://{{host}}/classification + +### + # @name getmappings GET http://{{host}}/mappings @@ -73,3 +78,71 @@ Content-Type: application/json { "fixed": "{{fixed}}" } + +### + +# @name post_copy_from +POST http://{{host}}/mapping/{{mapping_id}}/field/{{field_id}}/action +Content-Type: application/json + +{ + "action": "copy_from", + "target": "{{targetdiff}}" +} + +### + + +# @name post_copy_to +POST http://{{host}}/mapping/{{mapping_id}}/field/{{field_id}}/action +Content-Type: application/json + +{ + "action": "copy_to", + "target": "{{targetdiff}}" +} + +### + +# @name post_fixed + +POST http://{{host}}/mapping/{{mapping_id}}/field/{{field_id}}/action +Content-Type: application/json + +{ + "action": "fixed", + "value": "fixed-value" +} + +### + +# @name post_use + +POST http://{{host}}/mapping/{{mapping_id}}/field/{{field_id}}/action +Content-Type: application/json + +{ + "action": "use" +} + +### + +# @name post_not_use + +POST http://{{host}}/mapping/{{mapping_id}}/field/{{field_id}}/action +Content-Type: application/json + +{ + "action": "not_use" +} + +### + +# @name post_empty + +POST http://{{host}}/mapping/{{mapping_id}}/field/{{field_id}}/action +Content-Type: application/json + +{ + "action": "empty" +} diff --git a/service/README.md b/service/README.md new file mode 100644 index 0000000..6e5b7bf --- /dev/null +++ b/service/README.md @@ -0,0 +1,15 @@ +# Structure Comparer Service + +## Develop + +Install with + +```bash +pip install --editable . +``` + +Run all tests with + +```bash +pytest +``` diff --git a/service/poetry.lock b/service/poetry.lock new file mode 100644 index 0000000..dbaee04 --- /dev/null +++ b/service/poetry.lock @@ -0,0 +1,360 @@ +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. + +[[package]] +name = "black" +version = "24.4.2" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.8" +files = [ + {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"}, + {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"}, + {file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"}, + {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"}, + {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"}, + {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"}, + {file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"}, + {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"}, + {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"}, + {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"}, + {file = "black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"}, + {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"}, + {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"}, + {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"}, + {file = "black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"}, + {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"}, + {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"}, + {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"}, + {file = "black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"}, + {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"}, + {file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"}, + {file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "blinker" +version = "1.8.2" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, + {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "flask" +version = "3.0.3" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, + {file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "flask-cors" +version = "4.0.1" +description = "A Flask extension adding a decorator for CORS support" +optional = false +python-versions = "*" +files = [ + {file = "Flask_Cors-4.0.1-py2.py3-none-any.whl", hash = "sha256:f2a704e4458665580c074b714c4627dd5a306b333deb9074d0b1794dfa2fb677"}, + {file = "flask_cors-4.0.1.tar.gz", hash = "sha256:eeb69b342142fdbf4766ad99357a7f3876a2ceb77689dc10ff912aac06c389e4"}, +] + +[package.dependencies] +Flask = ">=0.9" + +[[package]] +name = "flask-swagger" +version = "0.2.14" +description = "Extract swagger specs from your flask project" +optional = false +python-versions = "*" +files = [ + {file = "flask-swagger-0.2.14.tar.gz", hash = "sha256:b4085f5bc36df4c20b6548cd1413adc9cf35719b0f0695367cd542065145294d"}, + {file = "flask_swagger-0.2.14-py2-none-any.whl", hash = "sha256:3caddb1311388eafc86f82f8e64ba386a5df6b84e5f16dfae19ca08173eba216"}, +] + +[package.dependencies] +Flask = ">=0.10" +PyYAML = ">=5.1" + +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.2.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"}, + {file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] +type = ["mypy (>=1.8)"] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "werkzeug" +version = "3.0.3" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8"}, + {file = "werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "23cd19d5cff2b40215123d7e526ddbe778e9817b3320480c74961906dd60df7d" diff --git a/service/pyproject.toml b/service/pyproject.toml new file mode 100644 index 0000000..3a0f7fa --- /dev/null +++ b/service/pyproject.toml @@ -0,0 +1,22 @@ +[tool.poetry] +name = "structure-comparer" +version = "0.1.0" +description = "" +authors = ["Alexander Essenwanger "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.12" +jinja2 = "^3.1.4" +flask = ">=3.0,<4.0" +flask-swagger = ">=0.2.14,<0.3.0" +flask-cors = "^4.0.1" +pyyaml = "^6.0.1" + + +[tool.poetry.group.dev.dependencies] +black = "^24.4.2" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/main.py b/service/src/main.py similarity index 100% rename from main.py rename to service/src/main.py diff --git a/mapper.py b/service/src/mapper.py similarity index 100% rename from mapper.py rename to service/src/mapper.py diff --git a/server.py b/service/src/server.py similarity index 72% rename from server.py rename to service/src/server.py index 878d4c0..63056ac 100644 --- a/server.py +++ b/service/src/server.py @@ -6,11 +6,13 @@ from structure_comparer.serve import ( + get_classifications_int, get_mapping_fields_int, get_mapping_int, get_mappings_int, init_project, post_mapping_field_int, + post_mapping_classification_int, ) @@ -19,7 +21,6 @@ def create_app(project_dir: Path): app = Flask(__name__) CORS(app, origins="http://localhost:4200") - # project config project = init_project(project_dir) setattr(app, "project", project) @@ -28,6 +29,26 @@ def create_app(project_dir: Path): def hello_world(): return "

Hello, World!

" + @app.route("/classification", methods=["GET"]) + def get_classifications(): + """ + Get all classifications + --- + produces: + - application/json + responses: + 200: + description: Classifications + schema: + required: + - classifications + properties: + classifications: + type: array + items: string + """ + return get_classifications_int() + @app.route("/mappings", methods=["GET"]) def get_mappings(): """ @@ -101,6 +122,10 @@ def get_mapping(id: str): type: string classification: type: string + classifications_allowed + type: array + items: + string extension: type: string extra: @@ -252,6 +277,75 @@ def post_mapping_field(mapping_id: str, field_id: str): return "", 200 + @app.route( + "/mapping//field//classification", methods=["POST"] + ) + def post_mapping_classification(mapping_id: str, field_id: str): + """ + Post a manual classification for a field + Overrides the default action of a field. `action` that should set for the field, `target` is the target of copy action and `value` may be a fixed value. + --- + consumes: + - application/json + parameters: + - in: path + name: mapping_id + type: string + required: true + description: The id of the mapping + - in: path + name: field_id + type: string + required: true + description: The id of the field + - in: body + name: body + schema: + required: + - action + properties: + action: + type: string + enum: + - copy_from + - copy_to + - fixed + - use + - not_use + - empty + description: Which action should be performed + target: + type: string + description: Field that is targetted (for copy actions) + value: + type: string + description: The fixed value + responses: + 200: + description: The field was updated + 400: + description: There was something wrong with the request + schema: + properties: + error: + type: string + description: An error message + 404: + description: Mapping or field not found + """ + try: + result = post_mapping_classification_int( + app.project, mapping_id, field_id, request.get_json() + ) + except ValueError as e: + error = {"error": str(e)} + return jsonify(error), 400 + else: + if result is None: + return "", 404 + + return "", 200 + @app.route("/spec", methods=["GET"]) def spec(): swag = swagger(app) diff --git a/structure_comparer/__init__.py b/service/src/structure_comparer/__init__.py similarity index 100% rename from structure_comparer/__init__.py rename to service/src/structure_comparer/__init__.py diff --git a/structure_comparer/classification.py b/service/src/structure_comparer/classification.py similarity index 100% rename from structure_comparer/classification.py rename to service/src/structure_comparer/classification.py diff --git a/structure_comparer/compare.py b/service/src/structure_comparer/compare.py similarity index 83% rename from structure_comparer/compare.py rename to service/src/structure_comparer/compare.py index a09849b..50af203 100644 --- a/structure_comparer/compare.py +++ b/service/src/structure_comparer/compare.py @@ -73,6 +73,14 @@ def compare_profile(profile_map: ProfileMap) -> Comparison: classifications and remarks. """ + # Iterate over all mappings (each entry are mapping to the same profile) + comparison = generate_comparison(profile_map) + fill_classification_remark(comparison) + + return comparison + + +def generate_comparison(profile_map: ProfileMap) -> Comparison: # Iterate over all mappings (each entry are mapping to the same profile) comparison = Comparison() @@ -112,11 +120,42 @@ def compare_profile(profile_map: ProfileMap) -> Comparison: # Add remarks and classifications for each field for field in comparison.fields.values(): - _classify_remark_field(field, source_profiles, target_profile, comparison) + _fill_allowed_classifications(field, source_profiles, target_profile) return comparison +def fill_classification_remark(comparison: Comparison): + for field in comparison.fields.values(): + _classify_remark_field( + field, comparison.source_profiles, comparison.target_profile, comparison + ) + + +def _fill_allowed_classifications( + field: ComparisonField, source_profiles: List[str], target_profile: str +): + allowed = set([c for c in Classification]) + + any_source_present = any( + [field.profiles[profile].present for profile in source_profiles] + ) + target_present = field.profiles[target_profile].present + + if not any_source_present: + allowed -= set( + [Classification.USE, Classification.NOT_USE, Classification.COPY_TO] + ) + else: + allowed -= set([Classification.EMPTY]) + if not target_present: + allowed -= set( + [Classification.USE, Classification.EMPTY, Classification.COPY_FROM] + ) + + field.classifications_allowed = list(allowed) + + def _classify_remark_field( field: ComparisonField, source_profiles: List[str], diff --git a/structure_comparer/consts.py b/service/src/structure_comparer/consts.py similarity index 100% rename from structure_comparer/consts.py rename to service/src/structure_comparer/consts.py diff --git a/structure_comparer/data/__init__.py b/service/src/structure_comparer/data/__init__.py similarity index 100% rename from structure_comparer/data/__init__.py rename to service/src/structure_comparer/data/__init__.py diff --git a/structure_comparer/data/comparison.py b/service/src/structure_comparer/data/comparison.py similarity index 82% rename from structure_comparer/data/comparison.py rename to service/src/structure_comparer/data/comparison.py index 331e10c..0c1ef52 100644 --- a/structure_comparer/data/comparison.py +++ b/service/src/structure_comparer/data/comparison.py @@ -18,6 +18,7 @@ class ComparisonField: extra: str profiles: Dict[str, ProfileField] remark: str + classifications_allowed: List[Classification] def __init__(self, name: str, id: str) -> None: self.name: str = name @@ -27,6 +28,7 @@ def __init__(self, name: str, id: str) -> None: self.profiles = {} self.remark = None self.id = id + self.classifications_allowed = [] def dict(self) -> dict: result = { @@ -35,6 +37,7 @@ def dict(self) -> dict: "remark": self.remark, "id": self.id, "name": self.name, + "classifications_allowed": [c.value for c in self.classifications_allowed], } if self.extension: @@ -68,3 +71,10 @@ def dict(self) -> dict: "target_profile": self.target_profile, "fields": [field.dict() for field in self.fields.values()], } + + +def get_field_by_id(comparison: Comparison, field_id: str) -> ComparisonField | None: + for field in comparison.fields.values(): + if field.id == field_id: + return field + return None diff --git a/structure_comparer/data/profile.py b/service/src/structure_comparer/data/profile.py similarity index 100% rename from structure_comparer/data/profile.py rename to service/src/structure_comparer/data/profile.py diff --git a/structure_comparer/files/style.css b/service/src/structure_comparer/files/style.css similarity index 100% rename from structure_comparer/files/style.css rename to service/src/structure_comparer/files/style.css diff --git a/structure_comparer/files/template.html.j2 b/service/src/structure_comparer/files/template.html.j2 similarity index 100% rename from structure_comparer/files/template.html.j2 rename to service/src/structure_comparer/files/template.html.j2 diff --git a/structure_comparer/helpers.py b/service/src/structure_comparer/helpers.py similarity index 100% rename from structure_comparer/helpers.py rename to service/src/structure_comparer/helpers.py diff --git a/structure_comparer/manual_entries.py b/service/src/structure_comparer/manual_entries.py similarity index 100% rename from structure_comparer/manual_entries.py rename to service/src/structure_comparer/manual_entries.py diff --git a/structure_comparer/results_dict.py b/service/src/structure_comparer/results_dict.py similarity index 100% rename from structure_comparer/results_dict.py rename to service/src/structure_comparer/results_dict.py diff --git a/structure_comparer/results_html.py b/service/src/structure_comparer/results_html.py similarity index 100% rename from structure_comparer/results_html.py rename to service/src/structure_comparer/results_html.py diff --git a/structure_comparer/serve.py b/service/src/structure_comparer/serve.py similarity index 51% rename from structure_comparer/serve.py rename to service/src/structure_comparer/serve.py index 27727be..560d2c0 100644 --- a/structure_comparer/serve.py +++ b/service/src/structure_comparer/serve.py @@ -1,16 +1,19 @@ -from collections import OrderedDict import json from pathlib import Path from uuid import uuid4 from .classification import Classification -from .data.comparison import Comparison +from .data.comparison import Comparison, get_field_by_id from .manual_entries import ( MANUAL_ENTRIES, MANUAL_ENTRIES_CLASSIFICATION, MANUAL_ENTRIES_EXTRA, ) -from .compare import compare_profile, load_profiles as _load_profiles +from .compare import ( + load_profiles as _load_profiles, + generate_comparison, + fill_classification_remark, +) def init_project(project_dir: Path): @@ -40,27 +43,31 @@ def read_manual_entries(project): def load_profiles(project): profile_maps = _load_profiles(project.profiles_to_compare_list, project.data_dir) - project.profiles_to_compare = { - str(uuid4()): entry for entry in profile_maps.values() + project.comparisons = { + str(uuid4()): generate_comparison(entry) for entry in profile_maps.values() } +def get_classifications_int(): + return {"classifications": [c.value for c in Classification]} + + def get_mappings_int(project): return { "mappings": [ {"id": id, "name": profile_map.name, "url": f"/mapping/{id}"} - for id, profile_map in project.profiles_to_compare.items() + for id, profile_map in project.comparisons.items() ] } def get_mapping_int(project, id: str): - profile_map = project.profiles_to_compare.get(id) + comparison = project.comparisons.get(id) - if not profile_map: + if not comparison: return None - comparison = compare_profile(profile_map) + fill_classification_remark(comparison) result = comparison.dict() result["id"] = id @@ -69,12 +76,12 @@ def get_mapping_int(project, id: str): def get_mapping_fields_int(project, id: str): - profile_map = project.profiles_to_compare.get(id) + comparison = project.comparisons.get(id) - if not profile_map: + if not comparison: return None - comparison = compare_profile(profile_map) + fill_classification_remark(comparison) result = {"id": id} result["fields"] = [ @@ -85,13 +92,13 @@ def get_mapping_fields_int(project, id: str): def post_mapping_field_int(project, mapping_id: str, field_id: str, content: dict): - profile_map = project.profiles_to_compare.get(mapping_id) + comparison = project.comparisons.get(mapping_id) - if not profile_map: + if not comparison: return None # Easiest way to get the fields - comparison = compare_profile(profile_map) + fill_classification_remark(comparison) name = _get_field_by_id(field_id, comparison) @@ -120,7 +127,7 @@ def post_mapping_field_int(project, mapping_id: str, field_id: str, content: dic } else: # If entry is mapped to itself, simply mark it as "use" - if target := content.get("target") and target == field_id: + if (target := content.get("target")) and target == field_id: MANUAL_ENTRIES[name] = {MANUAL_ENTRIES_CLASSIFICATION: Classification.USE} # If mapped to nothing, mark it as "ignore" @@ -144,7 +151,77 @@ def post_mapping_field_int(project, mapping_id: str, field_id: str, content: dic return True -def _get_field_by_id(field_id: str, comparison: Comparison) -> str | None: +def post_mapping_classification_int( + project, mapping_id: str, field_id: str, content: dict +): + comparison = project.comparisons.get(mapping_id) + + if not comparison: + return None + + # Easiest way to get the fields + fill_classification_remark(comparison) + + field = get_field_by_id(comparison, field_id) + + if field is None: + return None + + action = Classification(content.get("action")) + + # Check if action is allowed for this field + if action not in field.classifications_allowed: + raise ValueError( + f"action '{action.value}' not allowed for this field, allowed: {', '.join([field.value for field in field.classifications_allowed])}" + ) + + # Build the entry that should be created/updated + manual_entry = {MANUAL_ENTRIES_CLASSIFICATION: action} + if action == Classification.COPY_FROM or action == Classification.COPY_TO: + if target_id := content.get("target"): + target = get_field_by_id(comparison, target_id) + + if target is None: + raise ValueError("'target' does not exists") + + manual_entry[MANUAL_ENTRIES_EXTRA] = target.name + else: + raise ValueError("field 'target' missing") + elif action == Classification.FIXED: + if fixed := content.get("fixed"): + manual_entry[MANUAL_ENTRIES_EXTRA] = fixed + else: + raise ValueError("field 'fixed' missing") + + # Clean up possible manual entry this was copied from before + if ( + field.name in MANUAL_ENTRIES.entries + and MANUAL_ENTRIES_EXTRA in MANUAL_ENTRIES[field.name] + ): + del MANUAL_ENTRIES.entries[MANUAL_ENTRIES[field.name][MANUAL_ENTRIES_EXTRA]] + + # Apply the manual entry + MANUAL_ENTRIES[field.name] = manual_entry + + # Handle the partner entry for copy actions + if action == Classification.COPY_FROM: + MANUAL_ENTRIES[target.name] = { + MANUAL_ENTRIES_CLASSIFICATION: Classification.COPY_TO, + MANUAL_ENTRIES_EXTRA: field.name, + } + elif action == Classification.COPY_TO: + MANUAL_ENTRIES[target.name] = { + MANUAL_ENTRIES_CLASSIFICATION: Classification.COPY_FROM, + MANUAL_ENTRIES_EXTRA: field.name, + } + + # Save the changes + MANUAL_ENTRIES.write() + + return True + + +def _get_field_by_id(field_id: str, comparison: Comparison) -> Comparison | None: for field in comparison.fields.values(): if field.id == field_id: return field.name diff --git a/service/tests/test_compare.py b/service/tests/test_compare.py new file mode 100644 index 0000000..7426f91 --- /dev/null +++ b/service/tests/test_compare.py @@ -0,0 +1,70 @@ +from structure_comparer.compare import _fill_allowed_classifications +from structure_comparer.classification import Classification +from structure_comparer.data.comparison import ComparisonField, ProfileField + +PROFILE_SOURCE1 = "source1" +PROFILE_SOURCE2 = "source2" +PROFILE_TARGET = "target" + + +def test_fill_allowed_classifications_all_present(): + sources = [PROFILE_SOURCE1, PROFILE_SOURCE2] + target = PROFILE_TARGET + + field = ComparisonField("field", "1") + field.profiles = { + PROFILE_SOURCE1: ProfileField(PROFILE_SOURCE1, True), + PROFILE_SOURCE2: ProfileField(PROFILE_SOURCE2, True), + PROFILE_TARGET: ProfileField(PROFILE_TARGET, True), + } + + _fill_allowed_classifications(field, sources, target) + + assert Classification.USE in field.classifications_allowed + assert Classification.NOT_USE in field.classifications_allowed + assert Classification.COPY_FROM in field.classifications_allowed + assert Classification.COPY_TO in field.classifications_allowed + + assert Classification.EMPTY not in field.classifications_allowed + + +def test_fill_allowed_classifications_target_present(): + sources = [PROFILE_SOURCE1, PROFILE_SOURCE2] + target = PROFILE_TARGET + + field = ComparisonField("field", "1") + field.profiles = { + PROFILE_SOURCE1: ProfileField(PROFILE_SOURCE1, False), + PROFILE_SOURCE2: ProfileField(PROFILE_SOURCE2, False), + PROFILE_TARGET: ProfileField(PROFILE_TARGET, True), + } + + _fill_allowed_classifications(field, sources, target) + + assert Classification.COPY_FROM in field.classifications_allowed + assert Classification.EMPTY in field.classifications_allowed + + assert Classification.USE not in field.classifications_allowed + assert Classification.NOT_USE not in field.classifications_allowed + assert Classification.COPY_TO not in field.classifications_allowed + + +def test_fill_allowed_classifications_source_present(): + sources = [PROFILE_SOURCE1, PROFILE_SOURCE2] + target = PROFILE_TARGET + + field = ComparisonField("field", "1") + field.profiles = { + PROFILE_SOURCE1: ProfileField(PROFILE_SOURCE1, True), + PROFILE_SOURCE2: ProfileField(PROFILE_SOURCE2, True), + PROFILE_TARGET: ProfileField(PROFILE_TARGET, False), + } + + _fill_allowed_classifications(field, sources, target) + + assert Classification.NOT_USE in field.classifications_allowed + assert Classification.COPY_TO in field.classifications_allowed + + assert Classification.USE not in field.classifications_allowed + assert Classification.COPY_FROM not in field.classifications_allowed + assert Classification.EMPTY not in field.classifications_allowed