Skip to content

Client(endpoint, token)

Creates a new MedConB client.

Parameters:

Name Type Description Default
endpoint str

URL of the MedConB API. E.g. https://api.medconb.example.com/graphql/

required
token str

Authorization token.

required
Source code in medconb_client.py
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
def __init__(
    self,
    endpoint: str,
    token: str,
):
    """
    Creates a new MedConB client.

    Args:
        endpoint (str): URL of the MedConB API. E.g. https://api.medconb.example.com/graphql/
        token (str): Authorization token.
    """
    self.endpoint = endpoint
    self.token = token
    self.transport = RequestsHTTPTransport(
        url=self.endpoint,
        headers={"Authorization": f"Bearer {self.token}"},
        retries=3,
    )
    self.client = GQLClient(
        transport=self.transport,
        fetch_schema_from_transport=True,
        execute_timeout=30,
    )

get_codelist(codelist_id, with_description=False)

Retrieves the codelist by ID from the API and parses the data into the python data structures.

It mirrors the logic of the export and current understanding of transient codesets: Transient codesets are the current version and should be used if they exist. (At some point the API might change to reflect that default behaviour better as it might be a bit confusing atm.)

Source code in medconb_client.py
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
def get_codelist(
    self, codelist_id: str, with_description: bool = False
) -> "Codelist":
    """
    Retrieves the codelist by ID from the API and parses
    the data into the python data structures.

    It mirrors the logic of the export and current understanding
    of transient codesets:
    Transient codesets are the current version and should be used
    if they exist. (At some point the API might change to reflect
    that default behaviour better as it might be a bit confusing
    atm.)
    """
    query = gql(
        _GQL_QUERY_CODELST
        if with_description
        else _GQL_QUERY_CODELST_NO_DESCRIPTION
    )

    with self.client as session:
        result = session.execute(query, variable_values={"codelistID": codelist_id})
        codelist_data = result["codelist"]

        css = codelist_data["codesets"]
        tcss = codelist_data["transientCodesets"]
        codesets: Codesets = Codesets()

        if tcss is None:
            tcss = css

        for cs in tcss:
            codesets.append(
                Codeset(
                    ontology=cs["ontology"]["name"],
                    codes=[
                        (
                            c["code"],
                            c["description"] if with_description else "",
                        )
                        for c in cs["codes"]
                    ],
                )
            )

        return Codelist(
            id=codelist_data["id"],
            name=codelist_data["name"],
            description=codelist_data.get("description"),
            codesets=codesets,
        )

get_codelist_by_name(*, codelist_name, codelist_collection_name=None, phenotype_collection_name=None, phenotype_name=None)

get_codelist_by_name(
    *, codelist_name: str, codelist_collection_name: str
)
get_codelist_by_name(
    *,
    codelist_name: str,
    phenotype_collection_name: str,
    phenotype_name: str
)

Retrieves a Codelist by its name.

Use the arguments codelist_name with either:

  • codelist_collection_name or
  • phenotype_collection_name and phenotype_name

Parameters:

Name Type Description Default
codelist_name str

Name of the codelist

required
codelist_collection_name str

Name of the codelist collection

None
phenotype_collection_name str

Name of the phenotype collection

None
phenotype_name str

Name of the phenotype

None
Source code in medconb_client.py
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
def get_codelist_by_name(
    self,
    *,
    codelist_name,
    codelist_collection_name=None,
    phenotype_collection_name=None,
    phenotype_name=None,
) -> "Codelist":
    """
    Retrieves a Codelist by its name.

    Use the arguments `codelist_name` with either:

    - `codelist_collection_name` or
    - `phenotype_collection_name` and `phenotype_name`

    Args:
        codelist_name (str): Name of the codelist
        codelist_collection_name (str, optional): Name of the codelist collection
        phenotype_collection_name (str, optional): Name of the phenotype collection
        phenotype_name (str, optional): Name of the phenotype
    """
    # codelist_collection_name = kwargs.get("codelist_collection_name")
    # codelist_name = kwargs.get("codelist_name")
    # phenotype_collection_name = kwargs.get("phenotype_collection_name")
    # phenotype_name = kwargs.get("phenotype_name")

    if codelist_name is None:
        raise ValueError("Invalid arguments: codelist_name is required")

    mode = None

    if codelist_collection_name is not None:
        mode = "collection"
    elif phenotype_collection_name is not None and phenotype_name is not None:
        mode = "phenotype"
    else:
        raise ValueError(
            "Invalid arguments: Specify either codelist_collection_name or"
            " phenotype_collection_name and phenotype_name"
        )

    candidates = self._search_codelist(codelist_name)
    matches = []

    if mode == "collection":
        matches = self._filter_codelist_in_collection(
            candidates, codelist_collection_name
        )
    else:
        matches = self._filter_codelist_in_phenotype(
            candidates, phenotype_collection_name, phenotype_name
        )

    if len(matches) > 1:
        raise ValueError(
            "The codelist can not be retrieved because the name is ambiguous"
        )

    if len(matches) == 0:
        raise ValueError(
            "The codelist can not be retrieved because it was not found"
        )

    return self.get_codelist(matches[0])

get_workspace()

Retrieves a listing of all collections and their codelists/pheontypes within the workspace.

Returns:

Name Type Description
Workspace Workspace

Workspace object containing id and name of all codelists and phenotypes.

Example

For a detailed example, see Examples.

>>> workspace = client.get_workspace()
>>> print(workspace)
Workspace(
    collections=[
        Collection(
            id="ff755b3a-8f93-43a2-bb8f-2ee435e28938",
            name="ATTR CM Library",
            description="...",
            referenceID="...",
            itemType="Codelist",
            items=[
                CodelistInfo(id="...", name="..."),
                CodelistInfo(id="...", name="..."),
                ...
            ],
            ownerID="...",
            locked=False,
            visibility="Private",
        ),
        ...
    ],
    shared=[
        Collection(...),
    ],
)

Source code in medconb_client.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
def get_workspace(self) -> "Workspace":
    """
    Retrieves a listing of all collections and their codelists/pheontypes
    within the workspace.

    Returns:
        Workspace: Workspace object containing id and name of all codelists and phenotypes.

    Example:
        For a detailed example, see [Examples](/examples#list-all-collections-in-your-workspace).
        ```ipython
        >>> workspace = client.get_workspace()
        >>> print(workspace)
        Workspace(
            collections=[
                Collection(
                    id="ff755b3a-8f93-43a2-bb8f-2ee435e28938",
                    name="ATTR CM Library",
                    description="...",
                    referenceID="...",
                    itemType="Codelist",
                    items=[
                        CodelistInfo(id="...", name="..."),
                        CodelistInfo(id="...", name="..."),
                        ...
                    ],
                    ownerID="...",
                    locked=False,
                    visibility="Private",
                ),
                ...
            ],
            shared=[
                Collection(...),
            ],
        )
        ```
    """
    query = gql(_GQL_QUERY_WORKSPACE)

    with self.client as session:
        result = session.execute(query)
        workspace_data = result["self"]["workspace"]
        return Workspace(**workspace_data)

search_public_codelists(query)

Searches the public marketplace for codelists.

Parameters:

Name Type Description Default
query str

A query string similar to a google search.

required

Returns:

Type Description
list[CodelistInfo]

list[CodelistInfo]: List of codelists that match the search.

All search is case-insensitive.

The search by default searches name and description for the search terms. By using "name:search-term" you can search a specific field (name in this case). To only consider exact matches of a word, use "name:'blood'". This will find results like "Blood Infusion", but not "bloody nose".

Source code in medconb_client.py
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
def search_public_codelists(self, query: str) -> list[CodelistInfo]:
    """
    Searches the public marketplace for codelists.

    Args:
        query (str): A query string similar to a google search.

    Returns:
        list[CodelistInfo]: List of codelists that match the search.

    All search is case-insensitive.

    The search by default searches name and description for the
    search terms. By using "name:search-term" you can search
    a specific field (name in this case).
    To only consider exact matches of a word, use
    "name:'blood'". This will find results like "Blood Infusion",
    but not "bloody nose".
    """
    gql_query = gql(_GQL_QUERY_SEARCH_CODELIST)

    query_str = f"{query} visibility:'public'"

    with self.client as session:
        result = session.execute(gql_query, variable_values={"query": query_str})
        items = result["searchEntities"]["items"]
        res = [CodelistInfo(id=i["id"], name=i["name"]) for i in items]
        return res

Codelist(id, name, description, codesets) dataclass

Codelist is a codelist as defined in MedConB.

Attributes:

Name Type Description
id str

ID of the codelist.

name str

Name of the codelist.

description str

Description of the codelist.

codesets Codesets

List of codesets in the codelist.

to_pandas()

Convert the codelists codesets to a pandas DataFrame.

Returns:

Type Description

pd.DataFrame: The codesets as a DataFrame.

Source code in medconb_client.py
101
102
103
104
105
106
107
108
def to_pandas(self):
    """
    Convert the codelists codesets to a pandas DataFrame.

    Returns:
        pd.DataFrame: The codesets as a DataFrame.
    """
    return self.codesets.to_pandas()

CodelistInfo

Bases: BaseModel

Basic information on a codelist as defined in MedConB.

Attributes:

Name Type Description
id str

ID of the codelist.

name str

Name of the codelist.

Codeset(ontology, codes) dataclass

Codeset is a codeset as defined in MedConB.

Attributes:

Name Type Description
ontology str

Ontology which the codes belong to

codes list[tuple[str, str]]

List of codes, each represented as a tuple of code and description

Codesets

Bases: UserList['Codeset']

Codesets is a list of codesets as defined in MedConB.

It's just a thin wrapper, so we can offer to_pandas.

to_pandas()

Convert the codesets to a pandas DataFrame.

Returns:

Type Description

pd.DataFrame: The codesets as a DataFrame.

Source code in medconb_client.py
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
def to_pandas(self):
    """
    Convert the codesets to a pandas DataFrame.

    Returns:
        pd.DataFrame: The codesets as a DataFrame.
    """
    rows = []
    for codeset in self.data:
        for code, description in codeset.codes:
            rows.append(
                {
                    "ontology": codeset.ontology,
                    "code": code,
                    "description": description,
                }
            )

    return pd.DataFrame(rows)

Collection

Bases: BaseModel

Collection as defined in MedConB.

Attributes:

Name Type Description
id str

ID of the collection.

name str

Name of the collection.

description str

Description of the collection.

referenceID str

ID of the collection this one was copied from.

itemType str

Type of the collections items.

items list[CodelistInfo | PhenotypeInfo]

Basic information on the items in this collection.

ownerID str

ID of the owner of the collection.

locked bool

Whether the collection is locked.

visibility str

Visibility of the collection. Can be "own", "shared" or "public".

PhenotypeInfo

Bases: BaseModel

Basic information on a phenotype as defined in MedConB.

Attributes:

Name Type Description
id str

ID of the phenotype.

name str

Name of the phenotype.

SearchMatchingType

Bases: Enum

Types of matching to use when searching the public marketplace.

Enum Members:

Enum Name Description
EXACT Search for an exact match.
SUBSTRING Search for a substring match.

Workspace

Bases: BaseModel

Workspace as defined in MedConB.

Attributes:

Name Type Description
collections list[Collection]

List of collections in the workspace owned by the user.

shared list[Collection]

List of collections in the workspace shared with the user.