mosaic-python-client 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mosaic_python_client-0.2.0/LICENSE +21 -0
- mosaic_python_client-0.2.0/PKG-INFO +290 -0
- mosaic_python_client-0.2.0/README.md +259 -0
- mosaic_python_client-0.2.0/mosaic_client/__init__.py +17 -0
- mosaic_python_client-0.2.0/mosaic_client/epix/__init__.py +115 -0
- mosaic_python_client-0.2.0/mosaic_client/epix/client.py +772 -0
- mosaic_python_client-0.2.0/mosaic_client/epix/models.py +487 -0
- mosaic_python_client-0.2.0/mosaic_client/epix/schemas.py +379 -0
- mosaic_python_client-0.2.0/mosaic_client/gpas/__init__.py +13 -0
- mosaic_python_client-0.2.0/mosaic_client/gpas/client.py +342 -0
- mosaic_python_client-0.2.0/mosaic_client/gpas/models.py +70 -0
- mosaic_python_client-0.2.0/mosaic_client/gpas/schemas.py +53 -0
- mosaic_python_client-0.2.0/mosaic_client/helpers.py +115 -0
- mosaic_python_client-0.2.0/pyproject.toml +64 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 University Medical Center Leipzig, Dept. Medical Data Science
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mosaic-python-client
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Zeep client for interacting with the SOAP interfaces provided by E-PIX and gPAS of the MOSAIC suite by the THS Greifswald.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: client,soap,e-pix,epix,gpas,mosaic,zeep
|
|
8
|
+
Author: Maximilian Jugl
|
|
9
|
+
Requires-Python: >=3.11,<4
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Healthcare Industry
|
|
13
|
+
Classifier: Intended Audience :: Information Technology
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Natural Language :: English
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Dist: marshmallow (>=4.3.1,<5.0.0)
|
|
27
|
+
Requires-Dist: zeep (>=4.3.3,<5.0.0)
|
|
28
|
+
Project-URL: Repository, https://github.com/ul-mds/mosaic-python-client
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
[](https://pypi.org/project/mosaic-python-client/)
|
|
32
|
+
[](https://pypi.org/project/mosaic-python-client/)
|
|
33
|
+

|
|
34
|
+
[](https://pypi.org/project/mosaic-python-client/)
|
|
35
|
+
[](https://conventionalcommits.org)
|
|
36
|
+
|
|
37
|
+
# MOSAIC Client
|
|
38
|
+
|
|
39
|
+
The `mosaic_client` library provides wrappers around the SOAP (**S**imple **O**bject **A**ccess **P**rotocol) interfaces
|
|
40
|
+
of E-PIX and gPAS by the [THS Greifswald](https://www.ths-greifswald.de/en/projekte/mosaic-project/).
|
|
41
|
+
The main entrypoints are `mosaic_client.EPIXClient` and `mosaic_client.GPASClient`, which are classes that simply take
|
|
42
|
+
the URLs to the WSDL endpoints of their respective services and expose functions to interact with these services.
|
|
43
|
+
Both client classes are implemented as [Zeep](https://docs.python-zeep.org/en/master/) clients while validation is
|
|
44
|
+
leveraged by [marshmallow](https://marshmallow.readthedocs.io/en/latest/).
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
To install the client, Python 3.11 or higher is required.
|
|
49
|
+
|
|
50
|
+
```shell
|
|
51
|
+
pip install mosaic-python-client
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Getting started
|
|
55
|
+
|
|
56
|
+
Both E-PIX and gPAS client can be either instantiated by passing the WSDL URLs as strings or by passing your own
|
|
57
|
+
`zeep.Client` instance.
|
|
58
|
+
This section briefly demonstrates the usage of both clients.
|
|
59
|
+
For more information, have a look at the clients available methods and the respective docstrings.
|
|
60
|
+
|
|
61
|
+
### E-PIX client
|
|
62
|
+
|
|
63
|
+
As a very first step, we need to instantiate the client.
|
|
64
|
+
`EPIXClient` expects a WSDL URL for the regular E-PIX service which enables operations like requesting an MPI
|
|
65
|
+
(**M**aster **P**atient **I**ndex) or deactivating/deleting identities and a WSDL URL for the management service which
|
|
66
|
+
leverages the management of domains.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from mosaic_client import EPIXClient
|
|
70
|
+
|
|
71
|
+
epix = EPIXClient(
|
|
72
|
+
client="http://localhost:8081/epix/epixService?wsdl",
|
|
73
|
+
management_client="http://localhost:8081/epix/epixManagementService?wsdl",
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
To be able to request an MPI, we first need to create a data domain where the identity we want an MPI for is saved.
|
|
78
|
+
We name that new data domain `default`.
|
|
79
|
+
Note that E-PIX comes with a data source named `dummy_safe_source` and an identifier domain named `MPI` by default.
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from mosaic_client import EPIXClient
|
|
83
|
+
from mosaic_client.epix import Domain
|
|
84
|
+
|
|
85
|
+
epix = EPIXClient(
|
|
86
|
+
client="http://localhost:8081/epix/epixService?wsdl",
|
|
87
|
+
management_client="http://localhost:8081/epix/epixManagementService?wsdl",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
epix.add_domain(
|
|
91
|
+
domain=Domain(
|
|
92
|
+
name="default",
|
|
93
|
+
label="default",
|
|
94
|
+
mpi_domain=epix.get_identifier_domain(identifier_domain_name="MPI"),
|
|
95
|
+
safe_source=epix.get_source(source_name="dummy_safe_source"),
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Now, it is possible to request an MPI for an identity.
|
|
101
|
+
The default configuration of a data domain assumes that first and last name, gender and birthdate are required for new
|
|
102
|
+
identities.
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from dataclasses import asdict
|
|
106
|
+
import datetime
|
|
107
|
+
import json
|
|
108
|
+
from mosaic_client import EPIXClient
|
|
109
|
+
from mosaic_client.epix import Identity
|
|
110
|
+
|
|
111
|
+
epix = EPIXClient(
|
|
112
|
+
client="http://localhost:8081/epix/epixService?wsdl",
|
|
113
|
+
management_client="http://localhost:8081/epix/epixManagementService?wsdl",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
mpi_response = epix.request_mpi(
|
|
117
|
+
domain_name="default",
|
|
118
|
+
source_name="dummy_safe_source",
|
|
119
|
+
identity=Identity(
|
|
120
|
+
first_name="Foo",
|
|
121
|
+
last_name="Bar",
|
|
122
|
+
gender="f",
|
|
123
|
+
birth_date=datetime.datetime(1970, 1, 1, tzinfo=datetime.UTC),
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
print(json.dumps(asdict(mpi_response), indent=2, default=str))
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
{
|
|
132
|
+
"match_status": "NO_MATCH",
|
|
133
|
+
"person": {
|
|
134
|
+
"deactivated": false,
|
|
135
|
+
"mpi_id": {
|
|
136
|
+
"value": "1001000000011",
|
|
137
|
+
"identifier_domain": {
|
|
138
|
+
"name": "MPI",
|
|
139
|
+
"label": "MPI",
|
|
140
|
+
"oid": "1.2.276.0.76.3.1.132.1.1.1",
|
|
141
|
+
"description": null,
|
|
142
|
+
"entry_date": "2026-09-29 15:57:00.492000+02:00",
|
|
143
|
+
"update_date": "2026-09-29 15:57:00.492000+02:00"
|
|
144
|
+
},
|
|
145
|
+
"entry_date": "2026-09-29 15:57:43.496000+02:00",
|
|
146
|
+
"description": "generated MPI id",
|
|
147
|
+
"fresh": false
|
|
148
|
+
},
|
|
149
|
+
"person_created": "2026-09-29 15:57:43.496000+02:00",
|
|
150
|
+
"person_id": 1,
|
|
151
|
+
"person_last_edited": "2026-09-29 15:57:43.496000+02:00",
|
|
152
|
+
"other_identities": [],
|
|
153
|
+
"reference_identity": {
|
|
154
|
+
"birth_date": "1970-01-01 01:00:00+01:00",
|
|
155
|
+
"birth_place": null,
|
|
156
|
+
"civil_status": null,
|
|
157
|
+
"degree": null,
|
|
158
|
+
"external_date": null,
|
|
159
|
+
"first_name": "Foo",
|
|
160
|
+
"gender": "F",
|
|
161
|
+
"identifiers": [],
|
|
162
|
+
"last_name": "Bar",
|
|
163
|
+
"middle_name": null,
|
|
164
|
+
"mother_tongue": null,
|
|
165
|
+
"mothers_maiden_name": null,
|
|
166
|
+
"nationality": null,
|
|
167
|
+
"vital_status": null,
|
|
168
|
+
"death_date": null,
|
|
169
|
+
"prefix": null,
|
|
170
|
+
"race": null,
|
|
171
|
+
"religion": null,
|
|
172
|
+
"suffix": null,
|
|
173
|
+
"value_1": null,
|
|
174
|
+
"value_2": null,
|
|
175
|
+
"value_3": null,
|
|
176
|
+
"value_4": null,
|
|
177
|
+
"value_5": null,
|
|
178
|
+
"value_6": null,
|
|
179
|
+
"value_7": null,
|
|
180
|
+
"value_8": null,
|
|
181
|
+
"value_9": null,
|
|
182
|
+
"value_10": null,
|
|
183
|
+
"contacts": [],
|
|
184
|
+
"deactivated": false,
|
|
185
|
+
"identity_created": "2026-09-29 15:57:43.496000+02:00",
|
|
186
|
+
"identity_id": 1,
|
|
187
|
+
"identity_last_edited": "2026-09-29 15:57:43.496000+02:00",
|
|
188
|
+
"identity_version": 1,
|
|
189
|
+
"person_id": 1,
|
|
190
|
+
"source": {
|
|
191
|
+
"name": "dummy_safe_source",
|
|
192
|
+
"description": "dummy because of the default-property \"safe_source\" in table domain",
|
|
193
|
+
"label": "dummy_safe_source",
|
|
194
|
+
"entry_date": "2026-09-29 15:57:00.531000+02:00",
|
|
195
|
+
"update_date": "2026-09-29 15:57:00.531000+02:00"
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
"domain_name": "default"
|
|
199
|
+
},
|
|
200
|
+
"mpi_error_code": null
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### gPAS client
|
|
205
|
+
|
|
206
|
+
As before, we first need to instantiate the client.
|
|
207
|
+
`GPASClient` expects a WSDL URL for the regular gPAS service which enables operations like creating pseudonyms and a
|
|
208
|
+
WSDL URL for the domain service which leverages the management of domains.
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
from mosaic_client import GPASClient
|
|
212
|
+
|
|
213
|
+
gpas = GPASClient(
|
|
214
|
+
client="http://localhost:8080/gpas/gpasService?wsdl",
|
|
215
|
+
domain_client="http://localhost:8080/gpas/DomainService?wsdl",
|
|
216
|
+
)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
To be able to create pseudonyms, we first need to create a new domain since pseudonyms are organized in domains.
|
|
220
|
+
We name that new domain `default`.
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from mosaic_client import GPASClient
|
|
224
|
+
from mosaic_client.gpas import Domain
|
|
225
|
+
|
|
226
|
+
gpas = GPASClient(
|
|
227
|
+
client="http://localhost:8080/gpas/gpasService?wsdl",
|
|
228
|
+
domain_client="http://localhost:8080/gpas/DomainService?wsdl",
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
gpas.add_domain(domain=Domain(name="default", label="default"))
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Now, we are able to create a new pseudonym for the value `value123`.
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
from mosaic_client import GPASClient
|
|
238
|
+
|
|
239
|
+
gpas = GPASClient(
|
|
240
|
+
client="http://localhost:8080/gpas/gpasService?wsdl",
|
|
241
|
+
domain_client="http://localhost:8080/gpas/DomainService?wsdl",
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
pseudonym = gpas.get_or_create_pseudonym_for(domain_name="default", value="value123")
|
|
245
|
+
|
|
246
|
+
print(f"Pseudonym: {pseudonym}")
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
```text
|
|
250
|
+
Pseudonym: 199799437
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Running tests
|
|
254
|
+
|
|
255
|
+
This library implements its tests via [pytest](https://docs.pytest.org/en/stable/).
|
|
256
|
+
In order to run integration tests, a running instance of E-PIX and gPAS are needed.
|
|
257
|
+
The first option is to spin up the services independently and direct pytest to it.
|
|
258
|
+
Have a look at the provided docker compose file `./tests/docker/docker-compose.yml` for a quick solution.
|
|
259
|
+
For more sophisticated deployments, please read the documentation of
|
|
260
|
+
[E-PIX](https://www.ths-greifswald.de/forscher/e-pix/) and [gPAS](https://www.ths-greifswald.de/forscher/gpas/).
|
|
261
|
+
Alternatively, pytest can start Docker test-containers for the duration of the test run.
|
|
262
|
+
Since containers are started and stopped for each run individually, such a test run takes more time.
|
|
263
|
+
|
|
264
|
+
The following table shows all available options to configure pytest.
|
|
265
|
+
|
|
266
|
+
| **Environment variable** | **Description** | **Default** |
|
|
267
|
+
|----------------------------------------------|-----------------------------------------------------|-------------|
|
|
268
|
+
| PYTEST_USE_TESTCONTAINERS | Whether pytest should use test-containers or not | 0 |
|
|
269
|
+
| PYTEST_EPIX_WSDL_URL<sup>1)</sup> | WSDL URL for the E-PIX service | |
|
|
270
|
+
| PYTEST_EPIX_MANAGEMENT_WSDL_URL<sup>1)</sup> | WSDL URL for the E-PIX management service | |
|
|
271
|
+
| PYTEST_EPIX_IMAGE_TAG<sup>2)</sup> | E-PIX image tag that is used for the test-container | latest |
|
|
272
|
+
| PYTEST_GPAS_WSDL_URL<sup>1)</sup> | WSDL URL for the gPAS service | |
|
|
273
|
+
| PYTEST_GPAS_DOMAIN_WSDL_URL<sup>1)</sup> | WSDL URL for the gPAS domain service | |
|
|
274
|
+
| PYTEST_GPAS_IMAGE_TAG<sup>2)</sup> | gPAS image tag that is used for the test-container | latest |
|
|
275
|
+
|
|
276
|
+
<sup>1)</sup> Only needed, if `PYTEST_USE_TESTCONTAINERS` is set to `0`.<br>
|
|
277
|
+
<sup>2)</sup> Only used, if `PYTEST_USE_TESTCONTAINERS` is set to `1`.
|
|
278
|
+
|
|
279
|
+
It is possible to define these variables in a `.env.test` file.
|
|
280
|
+
The `.env.example` file provides a template.
|
|
281
|
+
You can copy the content of `.env.example` to directly get started with pytest using test-containers.
|
|
282
|
+
|
|
283
|
+
```shell
|
|
284
|
+
cp .env.example .env.test
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## License
|
|
288
|
+
|
|
289
|
+
MIT.
|
|
290
|
+
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
[](https://pypi.org/project/mosaic-python-client/)
|
|
2
|
+
[](https://pypi.org/project/mosaic-python-client/)
|
|
3
|
+

|
|
4
|
+
[](https://pypi.org/project/mosaic-python-client/)
|
|
5
|
+
[](https://conventionalcommits.org)
|
|
6
|
+
|
|
7
|
+
# MOSAIC Client
|
|
8
|
+
|
|
9
|
+
The `mosaic_client` library provides wrappers around the SOAP (**S**imple **O**bject **A**ccess **P**rotocol) interfaces
|
|
10
|
+
of E-PIX and gPAS by the [THS Greifswald](https://www.ths-greifswald.de/en/projekte/mosaic-project/).
|
|
11
|
+
The main entrypoints are `mosaic_client.EPIXClient` and `mosaic_client.GPASClient`, which are classes that simply take
|
|
12
|
+
the URLs to the WSDL endpoints of their respective services and expose functions to interact with these services.
|
|
13
|
+
Both client classes are implemented as [Zeep](https://docs.python-zeep.org/en/master/) clients while validation is
|
|
14
|
+
leveraged by [marshmallow](https://marshmallow.readthedocs.io/en/latest/).
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
To install the client, Python 3.11 or higher is required.
|
|
19
|
+
|
|
20
|
+
```shell
|
|
21
|
+
pip install mosaic-python-client
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Getting started
|
|
25
|
+
|
|
26
|
+
Both E-PIX and gPAS client can be either instantiated by passing the WSDL URLs as strings or by passing your own
|
|
27
|
+
`zeep.Client` instance.
|
|
28
|
+
This section briefly demonstrates the usage of both clients.
|
|
29
|
+
For more information, have a look at the clients available methods and the respective docstrings.
|
|
30
|
+
|
|
31
|
+
### E-PIX client
|
|
32
|
+
|
|
33
|
+
As a very first step, we need to instantiate the client.
|
|
34
|
+
`EPIXClient` expects a WSDL URL for the regular E-PIX service which enables operations like requesting an MPI
|
|
35
|
+
(**M**aster **P**atient **I**ndex) or deactivating/deleting identities and a WSDL URL for the management service which
|
|
36
|
+
leverages the management of domains.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from mosaic_client import EPIXClient
|
|
40
|
+
|
|
41
|
+
epix = EPIXClient(
|
|
42
|
+
client="http://localhost:8081/epix/epixService?wsdl",
|
|
43
|
+
management_client="http://localhost:8081/epix/epixManagementService?wsdl",
|
|
44
|
+
)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
To be able to request an MPI, we first need to create a data domain where the identity we want an MPI for is saved.
|
|
48
|
+
We name that new data domain `default`.
|
|
49
|
+
Note that E-PIX comes with a data source named `dummy_safe_source` and an identifier domain named `MPI` by default.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from mosaic_client import EPIXClient
|
|
53
|
+
from mosaic_client.epix import Domain
|
|
54
|
+
|
|
55
|
+
epix = EPIXClient(
|
|
56
|
+
client="http://localhost:8081/epix/epixService?wsdl",
|
|
57
|
+
management_client="http://localhost:8081/epix/epixManagementService?wsdl",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
epix.add_domain(
|
|
61
|
+
domain=Domain(
|
|
62
|
+
name="default",
|
|
63
|
+
label="default",
|
|
64
|
+
mpi_domain=epix.get_identifier_domain(identifier_domain_name="MPI"),
|
|
65
|
+
safe_source=epix.get_source(source_name="dummy_safe_source"),
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Now, it is possible to request an MPI for an identity.
|
|
71
|
+
The default configuration of a data domain assumes that first and last name, gender and birthdate are required for new
|
|
72
|
+
identities.
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from dataclasses import asdict
|
|
76
|
+
import datetime
|
|
77
|
+
import json
|
|
78
|
+
from mosaic_client import EPIXClient
|
|
79
|
+
from mosaic_client.epix import Identity
|
|
80
|
+
|
|
81
|
+
epix = EPIXClient(
|
|
82
|
+
client="http://localhost:8081/epix/epixService?wsdl",
|
|
83
|
+
management_client="http://localhost:8081/epix/epixManagementService?wsdl",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
mpi_response = epix.request_mpi(
|
|
87
|
+
domain_name="default",
|
|
88
|
+
source_name="dummy_safe_source",
|
|
89
|
+
identity=Identity(
|
|
90
|
+
first_name="Foo",
|
|
91
|
+
last_name="Bar",
|
|
92
|
+
gender="f",
|
|
93
|
+
birth_date=datetime.datetime(1970, 1, 1, tzinfo=datetime.UTC),
|
|
94
|
+
),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
print(json.dumps(asdict(mpi_response), indent=2, default=str))
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
{
|
|
102
|
+
"match_status": "NO_MATCH",
|
|
103
|
+
"person": {
|
|
104
|
+
"deactivated": false,
|
|
105
|
+
"mpi_id": {
|
|
106
|
+
"value": "1001000000011",
|
|
107
|
+
"identifier_domain": {
|
|
108
|
+
"name": "MPI",
|
|
109
|
+
"label": "MPI",
|
|
110
|
+
"oid": "1.2.276.0.76.3.1.132.1.1.1",
|
|
111
|
+
"description": null,
|
|
112
|
+
"entry_date": "2026-09-29 15:57:00.492000+02:00",
|
|
113
|
+
"update_date": "2026-09-29 15:57:00.492000+02:00"
|
|
114
|
+
},
|
|
115
|
+
"entry_date": "2026-09-29 15:57:43.496000+02:00",
|
|
116
|
+
"description": "generated MPI id",
|
|
117
|
+
"fresh": false
|
|
118
|
+
},
|
|
119
|
+
"person_created": "2026-09-29 15:57:43.496000+02:00",
|
|
120
|
+
"person_id": 1,
|
|
121
|
+
"person_last_edited": "2026-09-29 15:57:43.496000+02:00",
|
|
122
|
+
"other_identities": [],
|
|
123
|
+
"reference_identity": {
|
|
124
|
+
"birth_date": "1970-01-01 01:00:00+01:00",
|
|
125
|
+
"birth_place": null,
|
|
126
|
+
"civil_status": null,
|
|
127
|
+
"degree": null,
|
|
128
|
+
"external_date": null,
|
|
129
|
+
"first_name": "Foo",
|
|
130
|
+
"gender": "F",
|
|
131
|
+
"identifiers": [],
|
|
132
|
+
"last_name": "Bar",
|
|
133
|
+
"middle_name": null,
|
|
134
|
+
"mother_tongue": null,
|
|
135
|
+
"mothers_maiden_name": null,
|
|
136
|
+
"nationality": null,
|
|
137
|
+
"vital_status": null,
|
|
138
|
+
"death_date": null,
|
|
139
|
+
"prefix": null,
|
|
140
|
+
"race": null,
|
|
141
|
+
"religion": null,
|
|
142
|
+
"suffix": null,
|
|
143
|
+
"value_1": null,
|
|
144
|
+
"value_2": null,
|
|
145
|
+
"value_3": null,
|
|
146
|
+
"value_4": null,
|
|
147
|
+
"value_5": null,
|
|
148
|
+
"value_6": null,
|
|
149
|
+
"value_7": null,
|
|
150
|
+
"value_8": null,
|
|
151
|
+
"value_9": null,
|
|
152
|
+
"value_10": null,
|
|
153
|
+
"contacts": [],
|
|
154
|
+
"deactivated": false,
|
|
155
|
+
"identity_created": "2026-09-29 15:57:43.496000+02:00",
|
|
156
|
+
"identity_id": 1,
|
|
157
|
+
"identity_last_edited": "2026-09-29 15:57:43.496000+02:00",
|
|
158
|
+
"identity_version": 1,
|
|
159
|
+
"person_id": 1,
|
|
160
|
+
"source": {
|
|
161
|
+
"name": "dummy_safe_source",
|
|
162
|
+
"description": "dummy because of the default-property \"safe_source\" in table domain",
|
|
163
|
+
"label": "dummy_safe_source",
|
|
164
|
+
"entry_date": "2026-09-29 15:57:00.531000+02:00",
|
|
165
|
+
"update_date": "2026-09-29 15:57:00.531000+02:00"
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"domain_name": "default"
|
|
169
|
+
},
|
|
170
|
+
"mpi_error_code": null
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### gPAS client
|
|
175
|
+
|
|
176
|
+
As before, we first need to instantiate the client.
|
|
177
|
+
`GPASClient` expects a WSDL URL for the regular gPAS service which enables operations like creating pseudonyms and a
|
|
178
|
+
WSDL URL for the domain service which leverages the management of domains.
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from mosaic_client import GPASClient
|
|
182
|
+
|
|
183
|
+
gpas = GPASClient(
|
|
184
|
+
client="http://localhost:8080/gpas/gpasService?wsdl",
|
|
185
|
+
domain_client="http://localhost:8080/gpas/DomainService?wsdl",
|
|
186
|
+
)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
To be able to create pseudonyms, we first need to create a new domain since pseudonyms are organized in domains.
|
|
190
|
+
We name that new domain `default`.
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from mosaic_client import GPASClient
|
|
194
|
+
from mosaic_client.gpas import Domain
|
|
195
|
+
|
|
196
|
+
gpas = GPASClient(
|
|
197
|
+
client="http://localhost:8080/gpas/gpasService?wsdl",
|
|
198
|
+
domain_client="http://localhost:8080/gpas/DomainService?wsdl",
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
gpas.add_domain(domain=Domain(name="default", label="default"))
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Now, we are able to create a new pseudonym for the value `value123`.
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from mosaic_client import GPASClient
|
|
208
|
+
|
|
209
|
+
gpas = GPASClient(
|
|
210
|
+
client="http://localhost:8080/gpas/gpasService?wsdl",
|
|
211
|
+
domain_client="http://localhost:8080/gpas/DomainService?wsdl",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
pseudonym = gpas.get_or_create_pseudonym_for(domain_name="default", value="value123")
|
|
215
|
+
|
|
216
|
+
print(f"Pseudonym: {pseudonym}")
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
Pseudonym: 199799437
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Running tests
|
|
224
|
+
|
|
225
|
+
This library implements its tests via [pytest](https://docs.pytest.org/en/stable/).
|
|
226
|
+
In order to run integration tests, a running instance of E-PIX and gPAS are needed.
|
|
227
|
+
The first option is to spin up the services independently and direct pytest to it.
|
|
228
|
+
Have a look at the provided docker compose file `./tests/docker/docker-compose.yml` for a quick solution.
|
|
229
|
+
For more sophisticated deployments, please read the documentation of
|
|
230
|
+
[E-PIX](https://www.ths-greifswald.de/forscher/e-pix/) and [gPAS](https://www.ths-greifswald.de/forscher/gpas/).
|
|
231
|
+
Alternatively, pytest can start Docker test-containers for the duration of the test run.
|
|
232
|
+
Since containers are started and stopped for each run individually, such a test run takes more time.
|
|
233
|
+
|
|
234
|
+
The following table shows all available options to configure pytest.
|
|
235
|
+
|
|
236
|
+
| **Environment variable** | **Description** | **Default** |
|
|
237
|
+
|----------------------------------------------|-----------------------------------------------------|-------------|
|
|
238
|
+
| PYTEST_USE_TESTCONTAINERS | Whether pytest should use test-containers or not | 0 |
|
|
239
|
+
| PYTEST_EPIX_WSDL_URL<sup>1)</sup> | WSDL URL for the E-PIX service | |
|
|
240
|
+
| PYTEST_EPIX_MANAGEMENT_WSDL_URL<sup>1)</sup> | WSDL URL for the E-PIX management service | |
|
|
241
|
+
| PYTEST_EPIX_IMAGE_TAG<sup>2)</sup> | E-PIX image tag that is used for the test-container | latest |
|
|
242
|
+
| PYTEST_GPAS_WSDL_URL<sup>1)</sup> | WSDL URL for the gPAS service | |
|
|
243
|
+
| PYTEST_GPAS_DOMAIN_WSDL_URL<sup>1)</sup> | WSDL URL for the gPAS domain service | |
|
|
244
|
+
| PYTEST_GPAS_IMAGE_TAG<sup>2)</sup> | gPAS image tag that is used for the test-container | latest |
|
|
245
|
+
|
|
246
|
+
<sup>1)</sup> Only needed, if `PYTEST_USE_TESTCONTAINERS` is set to `0`.<br>
|
|
247
|
+
<sup>2)</sup> Only used, if `PYTEST_USE_TESTCONTAINERS` is set to `1`.
|
|
248
|
+
|
|
249
|
+
It is possible to define these variables in a `.env.test` file.
|
|
250
|
+
The `.env.example` file provides a template.
|
|
251
|
+
You can copy the content of `.env.example` to directly get started with pytest using test-containers.
|
|
252
|
+
|
|
253
|
+
```shell
|
|
254
|
+
cp .env.example .env.test
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## License
|
|
258
|
+
|
|
259
|
+
MIT.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains functions for working with the SOAP interfaces provided by E-PIX and gPAS - services
|
|
3
|
+
provided as part of the MOSAIC suite by the THS Greifswald.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from mosaic_client import epix, gpas
|
|
7
|
+
from mosaic_client.epix import EPIXClient
|
|
8
|
+
from mosaic_client.gpas import GPASClient
|
|
9
|
+
from mosaic_client.helpers import WSDLClient
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"EPIXClient",
|
|
13
|
+
"GPASClient",
|
|
14
|
+
"WSDLClient",
|
|
15
|
+
"epix",
|
|
16
|
+
"gpas",
|
|
17
|
+
]
|