diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2bcfc365..08842170 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,42 +3,42 @@
Thanks for considering making an addition to this project! These contributing guidelines should help make your life easier.
Before starting, some things to consider:
-* For larger features, it would be helpful to get in touch first (through issue/email)
+* For larger features, it would be helpful to get in touch first (through issue/email).
* A lot of the logic is in `docdeid`, please consider making a PR there for things that are not specific to `deduce`.
-* `deduce` is a rule-based de-identifier
-* In case you would like to see any rules added/removed/changed, a decent substantiation (with examples) of the potential improvement is useful
+* `deduce` is a rule-based de-identifier.
+* In case you would like to see any rules added/removed/changed, a decent substantiation (with examples) of the potential improvement is useful.
## Setting up the environment
-* This project uses poetry for package management. Install it with ```pip install poetry```
-* Set up the environment is easy, just use ```poetry install```
+* This project uses poetry for package management. Install it with ``pip install poetry``.
+* Setting up the environment is easy, just use ``poetry install``.
* The makefile contains some useful commands when developing:
- * `make format` formats the package code
- * `make lint` runs the linters (check the output)
- * `make clean` removes build/test artifacts, etc
+ * `make format` formats the package code;
+ * `make lint` runs the linters (check the output);
+ * `make clean` removes build/test artifacts, etc.
* And for docs:
- * `make build-docs` builds the docs
+ * `make build-docs` builds the docs.
-## Runing the tests
+## Running the tests
```bash
-pytest .
+poetry run pytest .
```
-## PR checlist
+## PR checklist
-* Verify that tests are passing
-* Verify that tests are updated/added according to changes
-* Run the formatters (`make format`)
-* Run the linters (`make lint`)
-* Add a section to the changelog
-* Add a description to your PR
+* Verify that tests are passing.
+* Verify that tests are updated/added according to changes.
+* Run the formatters (`make format`).
+* Run the linters (`make lint`).
+* Add a section to the changelog.
+* Add a description to your PR.
If all the steps above are followed, this ensures a quick review and release of your contribution.
## Releasing
* Readthedocs has a webhook connected to pushes on the main branch. It will trigger and update automatically.
-* Create a [release on github](https://github.com/vmenger/docdeid/releases/new), create a tag with the right version, manually copy and paste from the changelog
-* Build pipeline and release to PyPi trigger automatically on release
+* Create a [release on Github](https://github.com/vmenger/docdeid/releases/new), create a tag with the right version, manually copy and paste from the changelog.
+* Build pipeline and release to PyPI trigger automatically on release.
-Any other questions/issues not covered here? Please just get in touch!
\ No newline at end of file
+Any other questions/issues not covered here? Please just get in touch!
diff --git a/README.md b/README.md
index 160502e8..33ec1be3 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,18 @@

[](https://github.com/psf/black)
+# About this fork
+
+This is Matěj Korvas's fork of the original Deduce tool, available at
+https://github.com/vmenger/deduce, forked on 2024-02-29. The latest version
+available here has some extra or different functionality on top of the
+original tool -- which is maybe obvious but the license requires me to
+state it clearly.
+
+Use at your own risk.
+
+Original Readme documentation follows.
+
# deduce
> Deduce 3.0.0 is out! It is way more accurate, and faster too. It's fully backward compatible, but some functionality is scheduled for removal, read more about it here: [docs/migrating-to-v3](https://deduce.readthedocs.io/en/latest/migrating.html)
@@ -141,4 +153,4 @@ For setting up the dev environment and contributing guidelines, see: [docs/contr
## License
-This project is licensed under the GNU General Public License v3.0 - see the [LICENSE.md](LICENSE.md) file for details
\ No newline at end of file
+This project is licensed under the GNU General Public License v3.0 - see the [LICENSE.md](LICENSE.md) file for details
diff --git a/base_config.json b/base_config.json
index d0f37871..bf1c3522 100644
--- a/base_config.json
+++ b/base_config.json
@@ -14,7 +14,7 @@
"redactor_close_char": "]",
"annotators": {
"prefix_with_initial": {
- "annotator_type": "deduce.annotator.TokenPatternAnnotator",
+ "annotator_type": "docdeid.process.SequenceAnnotator",
"group": "names",
"args": {
"tag": "prefix+initiaal",
@@ -37,7 +37,7 @@
}
},
"prefix_with_interfix": {
- "annotator_type": "deduce.annotator.TokenPatternAnnotator",
+ "annotator_type": "docdeid.process.SequenceAnnotator",
"group": "names",
"args": {
"tag": "prefix+interfix+naam",
@@ -56,7 +56,7 @@
}
},
"prefix_with_name": {
- "annotator_type": "deduce.annotator.TokenPatternAnnotator",
+ "annotator_type": "docdeid.process.SequenceAnnotator",
"group": "names",
"args": {
"tag": "prefix+naam",
@@ -79,7 +79,7 @@
}
},
"interfix_with_name": {
- "annotator_type": "deduce.annotator.TokenPatternAnnotator",
+ "annotator_type": "docdeid.process.SequenceAnnotator",
"group": "names",
"args": {
"tag": "interfix+achternaam",
@@ -102,7 +102,7 @@
}
},
"initial_with_name": {
- "annotator_type": "deduce.annotator.TokenPatternAnnotator",
+ "annotator_type": "docdeid.process.SequenceAnnotator",
"group": "names",
"args": {
"tag": "initiaal+naam",
@@ -128,7 +128,7 @@
}
},
"initial_interfix": {
- "annotator_type": "deduce.annotator.TokenPatternAnnotator",
+ "annotator_type": "docdeid.process.SequenceAnnotator",
"group": "names",
"args": {
"tag": "initiaal+interfix+naam",
@@ -177,6 +177,32 @@
"args": {
"iterative": true,
"pattern": [
+ {
+ "name": "patient_left",
+ "direction": "left",
+ "pre_tag": [
+ "achternaam_patient"
+ ],
+ "tag": "voornaam_patient+achternaam_patient",
+ "pattern": [
+ {
+ "tag": "vornaam_patient"
+ }
+ ]
+ },
+ {
+ "name": "patient_right",
+ "direction": "right",
+ "pre_tag": [
+ "voornaam_patient"
+ ],
+ "tag": "voornaam_patient+achternaam_patient",
+ "pattern": [
+ {
+ "tag": "achternaam_patient"
+ }
+ ]
+ },
{
"name": "interfix_right",
"direction": "right",
@@ -295,11 +321,7 @@
"skip": ["."],
"pattern": [
{
- "and": [
- {
- "lookup": "prefix"
- }
- ]
+ "lookup": "prefix"
}
]
}
@@ -325,7 +347,7 @@
}
},
"street_pattern": {
- "annotator_type": "deduce.annotator.TokenPatternAnnotator",
+ "annotator_type": "docdeid.process.SequenceAnnotator",
"group": "locations",
"args": {
"pattern": [
diff --git a/deduce/annotation_processor.py b/deduce/annotation_processor.py
index de482f8a..ab73494c 100644
--- a/deduce/annotation_processor.py
+++ b/deduce/annotation_processor.py
@@ -6,9 +6,16 @@
class DeduceMergeAdjacentAnnotations(dd.process.MergeAdjacentAnnotations):
- """Merge adjacent tags, according to deduce logic: adjacent annotations with mixed
- patient/person tags are replaced with a patient annotation, in other cases only
- annotations with equal tags are considered adjacent."""
+ """
+ Merges adjacent tags, according to Deduce logic:
+
+ - adjacent annotations with mixed patient/person tags are replaced
+ with the "persoon" annotation;
+ - adjacent annotations with patient tags of which one is the surname
+ are replaced with the "patient" annotation; and
+ - adjacent annotations with other patient tags are replaced with
+ the "part_of_patient" annotation.
+ """
def _tags_match(self, left_tag: str, right_tag: str) -> bool:
"""
@@ -23,10 +30,15 @@ def _tags_match(self, left_tag: str, right_tag: str) -> bool:
``True`` if tags match, ``False`` otherwise.
"""
- return (left_tag == right_tag) or {left_tag, right_tag} == {
- "patient",
- "persoon",
- }
+ patient_part = [tag.endswith("_patient") for tag in (left_tag, right_tag)]
+ # FIXME Ideally, we should be first looking for a `*_patient` tag in
+ # both directions and only failing that, merge with an adjacent
+ # "persoon" tag.
+ return (
+ left_tag == right_tag
+ or all(patient_part)
+ or (patient_part[0] and right_tag == "persoon")
+ )
def _adjacent_annotations_replacement(
self,
@@ -37,14 +49,22 @@ def _adjacent_annotations_replacement(
"""
Replace two annotations that have equal tags with a new annotation.
- If one of the two annotations has the patient tag, the new annotation will also
- be tagged patient. In other cases, the tags are already equal.
+ If one of the two annotations has the "patient" tag (and the other is either
+ "patient" or "persoon"), the other annotation will be used. In other cases, the
+ tags are always equal.
"""
- if left_annotation.tag != right_annotation.tag:
- replacement_tag = "patient"
- else:
- replacement_tag = left_annotation.tag
+ ltag = left_annotation.tag
+ rtag = right_annotation.tag
+ replacement_tag = (
+ ltag
+ if ltag == rtag
+ else "persoon"
+ if rtag == "persoon"
+ else "patient"
+ if any(tag.startswith("achternaam") for tag in (ltag, rtag))
+ else "part_of_patient"
+ )
return dd.Annotation(
text=text[left_annotation.start_char : right_annotation.end_char],
@@ -59,20 +79,28 @@ class PersonAnnotationConverter(dd.process.AnnotationProcessor):
Responsible for processing the annotations produced by all name annotators (regular
and context-based).
- Any overlap with annotations that are contain "pseudo" in their tag are removed, as
- are those annotations. Then resolves overlap between remaining annotations, and maps
- the tags to either "patient" or "persoon", based on whether "patient" is in the tag
- (e.g. voornaam_patient => patient, achternaam_onbekend => persoon).
+ Any overlap with annotations that contain "pseudo" in their tag is removed, as are
+ those annotations. Then resolves overlap between remaining annotations, and maps the
+ tags to either "patient" or "persoon", based on whether "patient" is in all
+ constituent tags (e.g. voornaam_patient+achternaam_patient => patient,
+ achternaam_onbekend => persoon).
"""
def __init__(self) -> None:
- def map_tag_to_prio(tag: str) -> int:
- if "pseudo" in tag:
- return 0
- if "patient" in tag:
- return 1
-
- return 2
+ def map_tag_to_prio(tag: str) -> (int, int, int):
+ """
+ Maps from the tag of a mention to its priority. The lower, the higher
+ priority.
+
+ The return value is a tuple of:
+ 1. Is this a pseudo tag? If it is, it's a priority.
+ 2. How many subtags does the tag have? The more, the higher priority.
+ 3. Is this a patient tag? If it is, it's a priority.
+ """
+ is_pseudo = "pseudo" in tag
+ num_subtags = tag.count("+") + 1
+ is_patient = tag.count("patient") == num_subtags
+ return (-int(is_pseudo), -num_subtags, -int(is_patient))
self._overlap_resolver = dd.process.OverlapResolver(
sort_by=("tag", "length"),
@@ -89,15 +117,30 @@ def process_annotations(
annotations, text=text
)
- return dd.AnnotationSet(
+ real_annos = (
+ anno
+ for anno in new_annotations
+ if "pseudo" not in anno.tag and anno.text.strip()
+ )
+ with_patient = (
dd.Annotation(
- text=annotation.text,
- start_char=annotation.start_char,
- end_char=annotation.end_char,
- tag="patient" if "patient" in annotation.tag else "persoon",
+ text=anno.text,
+ start_char=anno.start_char,
+ end_char=anno.end_char,
+ tag=PersonAnnotationConverter._resolve_tag(anno.tag),
)
- for annotation in new_annotations
- if ("pseudo" not in annotation.tag and len(annotation.text.strip()) != 0)
+ for anno in real_annos
+ )
+ return dd.AnnotationSet(with_patient)
+
+ @classmethod
+ def _resolve_tag(cls, tag: str) -> str:
+ if "+" not in tag:
+ return tag if "patient" in tag else "persoon"
+ return (
+ "patient"
+ if all("patient" in part for part in tag.split("+"))
+ else "persoon"
)
@@ -114,7 +157,7 @@ def process_annotations(
class CleanAnnotationTag(dd.process.AnnotationProcessor):
- """Cleans annotation tags based on the corresponding mapping."""
+ """Renames tags using a mapping."""
def __init__(self, tag_map: dict[str, str]) -> None:
self.tag_map = tag_map
diff --git a/deduce/annotator.py b/deduce/annotator.py
index 788bb546..166d9f87 100644
--- a/deduce/annotator.py
+++ b/deduce/annotator.py
@@ -2,234 +2,37 @@
import re
import warnings
-from typing import Literal, Optional
+from collections import defaultdict
+from collections.abc import Iterable
+from dataclasses import dataclass
+from itertools import islice
+from typing import Optional
import docdeid as dd
-from docdeid import Annotation, Document, Tokenizer
-from docdeid.process import RegexpAnnotator
-
from deduce.utils import str_match
+from docdeid import Annotation, Document, Token, Tokenizer
+from docdeid.direction import Direction
+from docdeid.process import Annotator, RegexpAnnotator
+from docdeid.process.annotator import (
+ as_token_pattern,
+ SequenceAnnotator,
+ SequencePattern,
+)
warnings.simplefilter(action="default")
-_DIRECTION_MAP = {
- "left": {
- "attr": "previous",
- "order": reversed,
- "start_token": lambda annotation: annotation.start_token,
- },
- "right": {
- "attr": "next",
- "order": lambda pattern: pattern,
- "start_token": lambda annotation: annotation.end_token,
- },
-}
-
-
-class _PatternPositionMatcher: # pylint: disable=R0903
- """Checks if a token matches against a single pattern."""
-
- @classmethod
- def match(cls, pattern_position: dict, **kwargs) -> bool: # pylint: disable=R0911
- """
- Matches a pattern position (a dict with one key). Other information should be
- presented as kwargs.
-
- Args:
- pattern_position: A dictionary with a single key, e.g. {'is_initial': True}
- kwargs: Any other information, like the token or ds
-
- Returns:
- True if the pattern position matches, false otherwise.
- """
-
- if len(pattern_position) > 1:
- raise ValueError(
- f"Cannot parse token pattern ({pattern_position}) with more than 1 key"
- )
-
- func, value = next(iter(pattern_position.items()))
-
- if func == "equal":
- return kwargs.get("token").text == value
- if func == "re_match":
- return re.match(value, kwargs.get("token").text) is not None
- if func == "is_initials":
- return (
- len(kwargs.get("token").text) <= 4
- and kwargs.get("token").text.isupper()
- ) == value
- if func == "like_name":
- return (
- len(kwargs.get("token").text) >= 3
- and kwargs.get("token").text.istitle()
- and not any(ch.isdigit() for ch in kwargs.get("token").text)
- ) == value
- if func == "lookup":
- return kwargs.get("token").text in kwargs.get("ds")[value]
- if func == "neg_lookup":
- return kwargs.get("token").text not in kwargs.get("ds")[value]
- if func == "and":
- return all(
- _PatternPositionMatcher.match(pattern_position=x, **kwargs)
- for x in value
- )
- if func == "or":
- return any(
- _PatternPositionMatcher.match(pattern_position=x, **kwargs)
- for x in value
- )
-
- raise NotImplementedError(f"No known logic for pattern {func}")
-
-class TokenPatternAnnotator(dd.process.Annotator):
+@dataclass
+class ContextPattern:
"""
- Annotates based on token patterns, which should be provided as a list of dicts. Each
- position in the list denotes a token position, e.g.: [{'is_initial': True},
- {'like_name': True}] matches sequences of two tokens, where the first one is an
- initial, and the second one is like a name.
-
- Arguments:
- pattern: The pattern
- ds: Any datastructures, that can be used for lookup or other logic
- skip: Any string values that should be skipped in matching (e.g. periods)
+ Pattern for matching a sequence of tokens anchored on a certain starting tag.
"""
+ pre_tag: Optional[set[str]]
+ tag: str
+ seq_pattern: SequencePattern
- def __init__(
- self,
- pattern: list[dict],
- *args,
- ds: Optional[dd.ds.DsCollection] = None,
- skip: Optional[list[str]] = None,
- **kwargs,
- ) -> None:
- self.pattern = pattern
- self.ds = ds
- self.skip = set(skip or [])
-
- self._start_words = None
- self._matching_pipeline = None
-
- if len(self.pattern) > 0 and "lookup" in self.pattern[0]:
-
- if self.ds is None:
- raise RuntimeError(
- "Created pattern with lookup in TokenPatternAnnotator, but "
- "no lookup structures provided."
- )
-
- lookup_list = self.ds[self.pattern[0]["lookup"]]
-
- if not isinstance(lookup_list, dd.ds.LookupSet):
- raise ValueError(
- f"Expected a LookupSet, but got a " f"{type(lookup_list)}."
- )
-
- self._start_words = lookup_list.items()
- self._matching_pipeline = lookup_list.matching_pipeline
-
- super().__init__(*args, **kwargs)
-
- @staticmethod
- def _get_chained_token(
- token: dd.Token, attr: str, skip: set[str]
- ) -> Optional[dd.Token]:
- while True:
- token = getattr(token, attr)()
-
- if token is None or token.text not in skip:
- break
-
- return token
-
- def _match_sequence( # pylint: disable=R0913
- self,
- text: str,
- pattern: list[dict],
- start_token: dd.tokenizer.Token,
- direction: Literal["left", "right"] = "right",
- skip: Optional[set[str]] = None,
- ) -> Optional[dd.Annotation]:
- """
- Sequentially match a pattern against a specified start_token.
-
- Args:
- text: The original document text.
- pattern: The pattern to match.
- start_token: The start token to match.
- direction: The direction to match, choice of "left" or "right".
- skip: Any string values that should be skipped in matching.
-
- Returns:
- An Annotation if matching is possible, None otherwise.
- """
-
- skip = skip or set()
-
- attr = _DIRECTION_MAP[direction]["attr"]
- pattern = _DIRECTION_MAP[direction]["order"](pattern)
-
- current_token = start_token
- end_token = start_token
-
- for pattern_position in pattern:
- if current_token is None or not _PatternPositionMatcher.match(
- pattern_position=pattern_position, token=current_token, ds=self.ds
- ):
- return None
-
- end_token = current_token
- current_token = self._get_chained_token(current_token, attr, skip)
-
- start_token, end_token = _DIRECTION_MAP[direction]["order"](
- (start_token, end_token)
- )
-
- return dd.Annotation(
- text=text[start_token.start_char : end_token.end_char],
- start_char=start_token.start_char,
- end_char=end_token.end_char,
- tag=self.tag,
- priority=self.priority,
- start_token=start_token,
- end_token=end_token,
- )
-
- def annotate(self, doc: dd.Document) -> list[dd.Annotation]:
- """
- Annotate the document, by matching the pattern against all tokens.
-
- Args:
- doc: The document being processed.
-
- Returns:
- A list of Annotation.
- """
- annotations = []
-
- tokens = doc.get_tokens()
-
- if self._start_words is not None:
- tokens = tokens.token_lookup(
- lookup_values=self._start_words,
- matching_pipeline=self._matching_pipeline,
- )
-
- for token in tokens:
-
- annotation = self._match_sequence(
- doc.text, self.pattern, token, direction="right", skip=self.skip
- )
-
- if annotation is not None:
- annotations.append(annotation)
-
- return annotations
-
-
-class ContextAnnotator(TokenPatternAnnotator):
+class ContextAnnotator(Annotator):
"""
Extends existing annotations to the left or right, based on specified patterns.
@@ -241,96 +44,123 @@ class ContextAnnotator(TokenPatternAnnotator):
def __init__(
self,
+ pattern: list[dict], # TODO Rename to "patterns" or similar.
*args,
ds: Optional[dd.ds.DsCollection] = None,
iterative: bool = True,
**kwargs,
) -> None:
+ self.ds = ds
self.iterative = iterative
- super().__init__(*args, **kwargs, ds=ds, tag="_")
+ self._patterns = [
+ ContextPattern(pat['pre_tag'],
+ pat['tag'],
+ SequencePattern(
+ Direction.from_string(pat.get('direction', "right")),
+ set(pat.get('skip', ())),
+ [as_token_pattern(it) for it in pat['pattern']]))
+ for pat in pattern
+ ]
+ super().__init__(*args, **kwargs, tag='_') # XXX Not sure why exactly '_'.
def _apply_context_pattern(
- self, text: str, annotations: dd.AnnotationSet, context_pattern: dict
+ self,
+ doc: Document,
+ context_pattern: ContextPattern,
+ orig_annos: dd.AnnotationSet,
) -> dd.AnnotationSet:
- direction = context_pattern["direction"]
- skip = set(context_pattern.get("skip", []))
-
- for annotation in annotations.copy():
+ # TODO Maybe we should index all annotations here, not just the `new` ones.
+ annos_by_token = doc.annos_by_token(orig_annos)
- tag = list(_DIRECTION_MAP[direction]["order"](annotation.tag.split("+")))[
- -1
- ]
-
- if tag not in context_pattern["pre_tag"]:
- continue
-
- attr = _DIRECTION_MAP[direction]["attr"]
- start_token = self._get_chained_token(
- _DIRECTION_MAP[direction]["start_token"](annotation), attr, skip
- )
- new_annotation = self._match_sequence(
- text,
- context_pattern["pattern"],
- start_token,
- direction=direction,
- skip=skip,
- )
-
- if new_annotation:
- left_ann, right_ann = _DIRECTION_MAP[direction]["order"](
- (annotation, new_annotation)
- )
-
- merged_annotation = dd.Annotation(
- text=text[left_ann.start_char : right_ann.end_char],
- start_char=left_ann.start_char,
- end_char=right_ann.end_char,
- start_token=left_ann.start_token,
- end_token=right_ann.end_token,
- tag=context_pattern["tag"].format(tag=annotation.tag),
- priority=annotation.priority,
- )
-
- annotations.remove(annotation)
- annotations.add(merged_annotation)
+ return dd.AnnotationSet(
+ self._maybe_merge_anno(anno, context_pattern, doc, annos_by_token)
+ for anno in orig_annos
+ )
- return annotations
+ def _maybe_merge_anno(
+ self,
+ annotation: Annotation,
+ context_pattern: ContextPattern,
+ doc: Document,
+ annos_by_token: defaultdict[str, Iterable[Annotation]],
+ ) -> Annotation:
+
+ dir_ = context_pattern.seq_pattern.direction
+ tag = list(dir_.iter(annotation.tag.split("+")))[-1]
+
+ if tag not in context_pattern.pre_tag:
+ return annotation
+
+ last_anno_token = (annotation.end_token if dir_ is Direction.RIGHT else
+ annotation.start_token)
+ skip = context_pattern.seq_pattern.skip
+ following_tokens = islice(last_anno_token.iter_to(dir_), 1, None)
+ nonskip_tokens = (token for token in following_tokens
+ if token.text not in skip)
+ try:
+ start_token = next(nonskip_tokens)
+ except StopIteration:
+ return annotation
+
+ new_annotation = self._match_sequence(doc,
+ context_pattern.seq_pattern,
+ start_token,
+ annos_by_token,
+ self.ds)
+
+ if not new_annotation:
+ return annotation
+
+ left_ann, right_ann = dir_.iter((annotation, new_annotation))
+
+ return Annotation(
+ text=doc.text[left_ann.start_char : right_ann.end_char],
+ start_char=left_ann.start_char,
+ end_char=right_ann.end_char,
+ start_token=left_ann.start_token,
+ end_token=right_ann.end_token,
+ tag=context_pattern.tag.format(tag=annotation.tag),
+ priority=annotation.priority,
+ )
- def _annotate(self, text: str, annotations: dd.AnnotationSet) -> dd.AnnotationSet:
+ def _get_annotations(
+ self, doc: Document, orig_annos: Optional[dd.AnnotationSet] = None
+ ) -> dd.AnnotationSet:
"""
- Does the annotation, by calling _apply_context_pattern, and then optionally
- recursing. Also keeps track of the (un)changed annotations, so they are not
- repeatedly processed.
+ Computes the annotation for `doc` and returns it.
+
+ Does this by calling _apply_context_pattern and then optionally recursing.
+ Also keeps track of the (un)changed annotations, so they are not repeatedly
+ processed.
Args:
- text: The input text.
- annotations: The input annotations.
+ doc: The input document.
+ orig_annos: Current set of annotations. If `None`, `doc.annotations` will
+ be consulted.
Returns:
An extended set of annotations, based on the patterns provided.
"""
- original_annotations = annotations.copy()
+ if orig_annos is None:
+ orig_annos = doc.annotations
+ annotations = dd.AnnotationSet(orig_annos.copy())
- for context_pattern in self.pattern:
- annotations = self._apply_context_pattern(
- text, annotations, context_pattern
- )
+ for context_pattern in self._patterns:
+ annotations = self._apply_context_pattern(doc, context_pattern, annotations)
- if self.iterative:
-
- changed = dd.AnnotationSet(annotations.difference(original_annotations))
+ if self.iterative and (new := dd.AnnotationSet(annotations - orig_annos)):
+ # XXX Are we sure that other annotations than `new` don't matter anymore
+ # to the operation of the `_get_annotations` method?
annotations = dd.AnnotationSet(
- annotations.intersection(original_annotations)
+ (annotations - new) |
+ self._get_annotations(doc, dd.AnnotationSet(annotations | new))
)
- if changed:
- annotations.update(self._annotate(text, changed))
-
return annotations
- def annotate(self, doc: dd.Document) -> list[dd.Annotation]:
+ def annotate(self, doc: Document) -> list[Annotation]:
"""
Wrapper for annotating.
@@ -341,7 +171,7 @@ def annotate(self, doc: dd.Document) -> list[dd.Annotation]:
An empty list, as annotations are modified and not added.
"""
- doc.annotations = self._annotate(doc.text, doc.annotations)
+ doc.annotations = self._get_annotations(doc)
return []
@@ -364,8 +194,8 @@ def __init__(self, tokenizer: Tokenizer, *args, **kwargs) -> None:
@staticmethod
def _match_first_names(
- doc: dd.Document, token: dd.Token
- ) -> Optional[tuple[dd.Token, dd.Token]]:
+ doc: Document, token: Token
+ ) -> Optional[tuple[Token, Token]]:
for first_name in doc.metadata["patient"].first_names:
@@ -379,8 +209,8 @@ def _match_first_names(
@staticmethod
def _match_initial_from_name(
- doc: dd.Document, token: dd.Token
- ) -> Optional[tuple[dd.Token, dd.Token]]:
+ doc: Document, token: Token
+ ) -> Optional[tuple[Token, Token]]:
for _, first_name in enumerate(doc.metadata["patient"].first_names):
if str_match(token.text, first_name[0]):
@@ -394,16 +224,14 @@ def _match_initial_from_name(
return None
@staticmethod
- def _match_initials(
- doc: dd.Document, token: dd.Token
- ) -> Optional[tuple[dd.Token, dd.Token]]:
+ def _match_initials(doc: Document, token: Token) -> Optional[tuple[Token, Token]]:
if str_match(token.text, doc.metadata["patient"].initials):
return token, token
return None
- def next_with_skip(self, token: dd.Token) -> Optional[dd.Token]:
+ def next_with_skip(self, token: Token) -> Optional[Token]:
"""Find the next token, while skipping certain punctuation."""
while True:
@@ -415,8 +243,8 @@ def next_with_skip(self, token: dd.Token) -> Optional[dd.Token]:
return token
def _match_surname(
- self, doc: dd.Document, token: dd.Token
- ) -> Optional[tuple[dd.Token, dd.Token]]:
+ self, doc: Document, token: Token
+ ) -> Optional[tuple[Token, Token]]:
if doc.metadata["surname_pattern"] is None:
doc.metadata["surname_pattern"] = self.tokenizer.tokenize(
@@ -484,7 +312,7 @@ def annotate(self, doc: Document) -> list[Annotation]:
start_token, end_token = match
annotations.append(
- dd.Annotation(
+ Annotation(
text=doc.text[start_token.start_char : end_token.end_char],
start_char=start_token.start_char,
end_char=end_token.end_char,
@@ -671,7 +499,7 @@ def annotate(self, doc: Document) -> list[Annotation]:
class PhoneNumberAnnotator(dd.process.Annotator):
"""
Annotates phone numbers, based on a regexp and min and max number of digits.
- Additionally employs some logic like detecting parentheses and hyphens.
+ Additionally, employs some logic like detecting parentheses and hyphens.
Args:
phone_regexp: The regexp to detect phone numbers.
@@ -737,3 +565,9 @@ def annotate(self, doc: Document) -> list[Annotation]:
)
return annotations
+
+
+# TODO Drop this. It's confusing given the other annotator of the same name defined
+# in Docdeid.
+# For sake of backward compatibility:
+TokenPatternAnnotator = SequenceAnnotator
diff --git a/deduce/deduce.py b/deduce/deduce.py
index 3bd1356f..496b0afb 100644
--- a/deduce/deduce.py
+++ b/deduce/deduce.py
@@ -6,10 +6,13 @@
import logging
import os
import sys
+import warnings
from pathlib import Path
from typing import Any, Optional, Union
import docdeid as dd
+
+from docdeid.ds import LookupSet, LookupTrie
from frozendict import frozendict
from deduce import utils
@@ -27,6 +30,8 @@
__version__ = importlib.metadata.version(__package__ or __name__)
+from deduce.utils import ensure_path
+
_BASE_PATH = Path(os.path.dirname(__file__)).parent
_LOOKUP_LIST_PATH = _BASE_PATH / "deduce" / "data" / "lookup"
_BASE_CONFIG_FILE = _BASE_PATH / "base_config.json"
@@ -37,7 +42,7 @@
class Deduce(dd.DocDeid): # pylint: disable=R0903
"""
- Main class for de-identifiation.
+ Main class for de-identification.
Inherits from ``docdeid.DocDeid``, and as such, most information on deidentifying
text with a Deduce object is available there.
@@ -51,7 +56,9 @@ class Deduce(dd.DocDeid): # pylint: disable=R0903
are overwritten, and other defaults are kept. When `load_base_config` is
set to `False`, no defaults are loaded and only configuration from `config`
is applied.
- looup_data_path: The path to look for lookup data, by default included in
+ config_file: (Deprecated!) Same as `config` but it's expected to be a `str`
+ holding the path to the config JSON file.
+ lookup_data_path: The path to look for lookup data, by default included in
the package. If you want to make changes to source files, it's recommended
to copy the source data and pointing deduce to this folder with this
argument.
@@ -60,25 +67,43 @@ class Deduce(dd.DocDeid): # pylint: disable=R0903
the same as the lookup list path.
build_lookup_structs: Will always reload and rebuild lookup structs rather than
using the cache when this is set to `True`.
+ save_lookup_structs: Should the lookup structures be pickled (cached)
+ upon loading? Default: `True`.
"""
def __init__( # pylint: disable=R0913
self,
load_base_config: bool = True,
config: Optional[Union[str, dict]] = None,
+ config_file: Optional[str] = None,
lookup_data_path: Union[str, Path] = _LOOKUP_LIST_PATH,
cache_path: Optional[Union[str, Path]] = _LOOKUP_LIST_PATH,
build_lookup_structs: bool = False,
+ save_lookup_structs: bool = True,
) -> None:
super().__init__()
+ if config_file is not None:
+
+ warnings.warn(
+ "The config_file keyword is deprecated, please use config "
+ "instead, which accepts both filenames and dictionaries.",
+ DeprecationWarning,
+ )
+
+ config = config_file
+
+ logging.info("Going to init config.")
self.config = self._initialize_config(
load_base_config=load_base_config, user_config=config
)
- self.lookup_data_path = self._initialize_path_or_str(lookup_data_path)
- self.cache_path = self._initialize_path_or_str(cache_path)
+ self.lookup_data_path = ensure_path(lookup_data_path)
+ self.cache_path = ensure_path(cache_path)
+
+ logging.info("Going to init tokenizers.")
self.tokenizers = {"default": self._initialize_tokenizer(self.lookup_data_path)}
+ logging.debug("Done initing tokenizers.")
self.lookup_structs = get_lookup_structs(
lookup_path=self.lookup_data_path,
@@ -86,10 +111,13 @@ def __init__( # pylint: disable=R0913
tokenizer=self.tokenizers["default"],
deduce_version=__version__,
build=build_lookup_structs,
+ save_cache=save_lookup_structs,
)
+ logging.info("Done loading lookup structs.")
extras = {"tokenizer": self.tokenizers["default"], "ds": self.lookup_structs}
+ logging.info("Going to load the Deduce processor.")
self.processors = _DeduceProcessorLoader().load(
config=self.config, extras=extras
)
@@ -123,13 +151,6 @@ def _initialize_config(
return frozendict(config)
- @staticmethod
- def _initialize_path_or_str(path: Union[str, Path]) -> Path:
- if isinstance(path, str):
- path = Path(path)
-
- return path
-
@staticmethod
def _initialize_tokenizer(lookup_data_path: Path) -> dd.Tokenizer:
raw_itemsets = load_raw_itemsets(
@@ -151,25 +172,33 @@ class _DeduceProcessorLoader: # pylint: disable=R0903
@staticmethod
def _get_multi_token_annotator(args: dict, extras: dict) -> dd.process.Annotator:
- lookup_struct = extras["ds"][args["lookup_values"]]
+ lookup_struct = extras["ds"][args.pop("lookup_values")]
+
+ if isinstance(lookup_struct, LookupTrie):
+ return dd.process.MultiTokenTrieAnnotator(trie=lookup_struct, **args)
+ elif isinstance(lookup_struct, LookupSet):
+ try:
+ tokenizer = args["tokenizer"]
+ except KeyError:
+ # This indicates an error in the code, not in configuration, as
+ # the "tokenizer" key is always added to `extras` where `extras` is
+ # defined -- in `Deduce.__init__`.
+ # pylint: disable=W0707
+ raise ValueError(
+ "When constructing a MultiTokenLookupAnnotator from a LookupSet, "
+ "a tokenizer must be given."
+ )
- if isinstance(lookup_struct, dd.ds.LookupSet):
- args.update(
- lookup_values=lookup_struct.items(),
+ return dd.process.MultiTokenLookupAnnotator(
+ lookup_values=lookup_struct,
matching_pipeline=lookup_struct.matching_pipeline,
- tokenizer=extras["tokenizer]"],
- )
- elif isinstance(lookup_struct, dd.ds.LookupTrie):
- args.update(trie=lookup_struct)
- del args["lookup_values"]
+ tokenizer=tokenizer)
else:
raise ValueError(
f"Don't know how to present lookup structure with type "
f"{type(lookup_struct)} to MultiTokenLookupAnnotator"
)
- return dd.process.MultiTokenLookupAnnotator(**args)
-
@staticmethod
def _get_annotator_from_class(
annotator_type: str, args: dict, extras: dict
@@ -283,6 +312,21 @@ def _load_post_processors(
),
)
+ post_group.add_processor(
+ "patient_cleaner",
+ CleanAnnotationTag(
+ tag_map={
+ "voornaam_patient": "patient",
+ "initiaal_patient": "patient",
+ "achternaam_patient": "patient",
+ "part_of_patient": "persoon",
+ # TODO We should probably merge this new "person"
+ # annotation with neighbouring annotations in yet another
+ # postprocessing step.
+ }
+ ),
+ )
+
post_group.add_processor(
"redactor",
DeduceRedactor(
diff --git a/deduce/lookup_struct_loader.py b/deduce/lookup_struct_loader.py
index 7d1f642d..01105927 100644
--- a/deduce/lookup_struct_loader.py
+++ b/deduce/lookup_struct_loader.py
@@ -145,13 +145,14 @@ def load_street_lookup(
) -> dd.ds.LookupTrie:
"""Load street LookupTrie."""
- street = dd.ds.LookupSet()
+ street = dd.ds.LookupSet(matching_pipeline=[dd.str.LowercaseTail()])
street.add_items_from_iterable(
raw_itemsets["street"],
cleaning_pipeline=[
dd.str.StripString(),
dd.str.FilterByLength(min_len=4),
+ dd.str.LowercaseTail(),
],
)
diff --git a/deduce/lookup_structs.py b/deduce/lookup_structs.py
index fa89ecac..5b54b110 100644
--- a/deduce/lookup_structs.py
+++ b/deduce/lookup_structs.py
@@ -61,6 +61,7 @@ def load_raw_itemset(path: Path) -> set[str]:
The raw items, as a set of strings.
"""
+ logging.info("Loading itemset %s...", path)
items = optional_load_items(path / "items.txt")
exceptions = optional_load_items(path / "exceptions.txt")
@@ -84,8 +85,10 @@ def load_raw_itemset(path: Path) -> set[str]:
transform_config = optional_load_json(path / "transform.json")
if transform_config is not None:
+ logging.info("Applying transformation to %s...", path)
items = apply_transform(items, transform_config)
+ logging.info("Done loading %s.", path)
return items
@@ -98,7 +101,7 @@ def load_raw_itemsets(base_path: Path, subdirs: list[str]) -> dict[str, set[str]
subdirs: The lists to load.
Returns:
- The raw itemsetes, represented as a dictionary mapping the name of the
+ The raw itemsets, represented as a dictionary mapping the name of the
lookup list to a set of strings.
"""
@@ -223,12 +226,14 @@ def get_lookup_structs( # pylint: disable=R0913
"""
+ logging.debug("lookup_path = %s", lookup_path)
if not build:
lookup_structs = load_lookup_structs_from_cache(
cache_path=cache_path, deduce_version=deduce_version
)
if lookup_structs is not None:
+ logging.info("Loaded lookup structs from the cache.")
return lookup_structs
logging.info(
@@ -249,16 +254,20 @@ def get_lookup_structs( # pylint: disable=R0913
)
for name in defaults:
+ logging.info("Adding the %s defaults...", name)
lookup_set = dd.ds.LookupSet()
lookup_set.add_items_from_iterable(base_items[name])
lookup_structs[name] = lookup_set
for name, set_init_function in _LOOKUP_SET_LOADERS.items():
+ logging.info("Initing the %s set...", name)
lookup_structs[name] = set_init_function(base_items)
for name, trie_init_function in _LOOKUP_TRIE_LOADERS.items():
+ logging.info("Initing the %s trie...", name)
lookup_structs[name] = trie_init_function(base_items, tokenizer)
+ logging.info("Going to cache lookup structs.")
if save_cache:
cache_lookup_structs(
lookup_structs=lookup_structs,
diff --git a/deduce/utils.py b/deduce/utils.py
index b8822abd..1801ac55 100644
--- a/deduce/utils.py
+++ b/deduce/utils.py
@@ -3,14 +3,22 @@
import json
import re
from pathlib import Path
-from typing import Optional
+from typing import Optional, Union
import docdeid as dd
from docdeid import Tokenizer
+from docdeid.str import LowercaseTail
from rapidfuzz.distance import DamerauLevenshtein
+_TITLECASER = LowercaseTail()
-def str_match(str_1: str, str_2: str, max_edit_distance: Optional[int] = None) -> bool:
+
+def str_match(
+ str_1: str,
+ str_2: str,
+ max_edit_distance: Optional[int] = None,
+ titlecase: bool = True,
+) -> bool:
"""
Match two strings, potentially in a fuzzy way.
@@ -23,13 +31,18 @@ def str_match(str_1: str, str_2: str, max_edit_distance: Optional[int] = None) -
Returns:
``True`` if the strings match, ``False`` otherwise.
"""
+ norm_1, norm_2 = (
+ (_TITLECASER.process(str_1), _TITLECASER.process(str_2))
+ if titlecase
+ else (str_1, str_2)
+ )
if max_edit_distance is not None:
return (
- DamerauLevenshtein.distance(str_1, str_2, score_cutoff=max_edit_distance)
+ DamerauLevenshtein.distance(norm_1, norm_2, score_cutoff=max_edit_distance)
<= max_edit_distance
)
- return str_1 == str_2
+ return norm_1 == norm_2
def class_for_name(module_name: str, class_name: str) -> type:
@@ -54,8 +67,8 @@ def initialize_class(cls: type, args: dict, extras: dict) -> object:
items in extras are passed to the class initializer if they are present.
Args:
- cls: The class to initialze.
- args: The arguments to pass to the initalizer.
+ cls: The class to initialize.
+ args: The arguments to pass to the initializer.
extras: A superset of arguments that should be passed to the initializer.
Will be checked against the class.
@@ -123,7 +136,7 @@ def repl_segments(s: str, matches: list[tuple]) -> list[list[str]]:
(5, 8, ["Mr.", "Meester"]).
Returns:
- A list of options that together segement the entire string, e.g. [["Prof.",
+ A list of options that together segment the entire string, e.g. [["Prof.",
"Professor"], [" "], ["Meester", "Mr."], [" Lievenslaan"]].
"""
@@ -213,6 +226,23 @@ def apply_transform(items: set[str], transform_config: dict) -> set[str]:
to_add = []
for item in items:
+ # FIXME Why _add_ the result of `str_variations` rather than
+ # replace the original item? In most cases, manual effort was
+ # exerted to include also the original string in
+ # the replacements, however some transformations do not include
+ # it (e.g. for "(?<=\\()Ut(?=\\))", the surrounding parens are
+ # always dropped). I guess that these transformations do not
+ # include the original version because it's supposed to be
+ # dropped. Or if the original version ("(Ut)" in this case) was
+ # supposed to be kept, by not including it explicitly yet
+ # _adding_ all variations to the set of terms, the net effect is
+ # that just all _other_ transformations within the string will
+ # be excluded in the version that keeps the original "(Ut)".
+ #
+ # We should either avoid combining the result of `str_variations`
+ # with the original set, `{item}`, or _always_ apply the void
+ # transformation so as to save effort in writing
+ # the `transform.json` configs and prevent subtle bugs.
to_add += str_variations(item, transform)
items.update(to_add)
@@ -281,3 +311,8 @@ def lookup_set_to_trie(
trie.add_item([token.text for token in tokenizer.tokenize(item)])
return trie
+
+
+def ensure_path(path_or_str: Union[str, Path]) -> Path:
+ """Casts the argument as a `Path` if it's not a `Path` already."""
+ return path_or_str if isinstance(path_or_str, Path) else Path(path_or_str)
diff --git a/docs/source/tutorial.md b/docs/source/tutorial.md
index 69b894aa..d2084925 100644
--- a/docs/source/tutorial.md
+++ b/docs/source/tutorial.md
@@ -57,14 +57,14 @@ It's possible to add, remove, apply subsets, or to implement custom annotators,
In addition to annotators, a `docdeid` de-identifier contains annotation processors, which do some operation to the set of annotations generated previously, and redactors, which take the annotation and replace them in the text. Other processors included in `deduce` are listed below:
-| **Name** | **Group** | **Description** |
-|-----------------------------|-----------------|-------------------------------------------------------------------------------------------------------|
-| person_annotation_converter | names | Maps name tags to either PERSON or PATIENT, and removes overlap with 'pseudo_name'. |
-| remove_street_tags | locations | Removes any matched street names that are not followed by a housenumber |
-| clean_street_tags | locations | Cleans up street tags, e.g. straat+huisnummer -> locatie |
-| overlap_resolver | post_processing | Makes sure overlap among annotations is resolved. |
-| merge_adjacent_annotations | post_processing | If there are any adjacent annotations with the same tag, they are merged into a single annotation. |
-| redactor | post_processing | Takes care of replacing the annotated PHIs with `[TAG]` (e.g. `[LOCATION-1]`, `[DATE-2]`) |
+| **Name** | **Group** | **Description** |
+|-----------------------------|-----------------|-------------------------------------------------------------------------------------------------------------------------------|
+| person_annotation_converter | names | Collapses competing annotations for the same span to either "persoon" or "\*patient", and removes overlap with 'pseudo_name'. |
+| remove_street_tags | locations | Removes any matched street names that are not followed by a housenumber. |
+| clean_street_tags | locations | Renames compound street tags, e.g. "straat+huisnummer", to "locatie". |
+| overlap_resolver | post_processing | Makes sure overlap among annotations is resolved. |
+| merge_adjacent_annotations | post_processing | If there are any adjacent annotations with the same tag, they are merged into a single annotation. |
+| redactor | post_processing | Takes care of replacing the annotated PHIs with `[TAG]` (e.g. `[LOCATION-1]`, `[DATE-2]`). |
### Lookup sets
diff --git a/poetry.lock b/poetry.lock
index 4d0faf9a..c319928a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,25 +1,25 @@
-# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "alabaster"
-version = "0.7.13"
-description = "A configurable sidebar-enabled Sphinx theme"
+version = "0.7.16"
+description = "A light, configurable Sphinx theme"
optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.9"
files = [
- {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"},
- {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"},
+ {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"},
+ {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"},
]
[[package]]
name = "astroid"
-version = "3.0.1"
+version = "3.2.3"
description = "An abstract syntax tree for Python with inference support."
optional = false
python-versions = ">=3.8.0"
files = [
- {file = "astroid-3.0.1-py3-none-any.whl", hash = "sha256:7d5895c9825e18079c5aeac0572bc2e4c83205c95d416e0b4fee8bc361d2d9ca"},
- {file = "astroid-3.0.1.tar.gz", hash = "sha256:86b0bb7d7da0be1a7c4aedb7974e391b32d4ed89e33de6ed6902b4b15c97577e"},
+ {file = "astroid-3.2.3-py3-none-any.whl", hash = "sha256:3eae9ea67c11c858cdd2c91337d2e816bd019ac897ca07d7b346ac10105fceb3"},
+ {file = "astroid-3.2.3.tar.gz", hash = "sha256:7099b5a60985529d8d46858befa103b82d0d05a5a5e8b816b5303ed96075e1d9"},
]
[package.dependencies]
@@ -27,36 +27,34 @@ typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""}
[[package]]
name = "attrs"
-version = "23.1.0"
+version = "23.2.0"
description = "Classes Without Boilerplate"
optional = false
python-versions = ">=3.7"
files = [
- {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"},
- {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"},
+ {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
+ {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
]
[package.extras]
cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
-dev = ["attrs[docs,tests]", "pre-commit"]
+dev = ["attrs[tests]", "pre-commit"]
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
tests = ["attrs[tests-no-zope]", "zope-interface"]
-tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
+tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
[[package]]
name = "babel"
-version = "2.13.1"
+version = "2.15.0"
description = "Internationalization utilities"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "Babel-2.13.1-py3-none-any.whl", hash = "sha256:7077a4984b02b6727ac10f1f7294484f737443d7e2e66c5e4380e41a3ae0b4ed"},
- {file = "Babel-2.13.1.tar.gz", hash = "sha256:33e0952d7dd6374af8dbf6768cc4ddf3ccfefc244f9986d4074704f2fbd18900"},
+ {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"},
+ {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"},
]
-[package.dependencies]
-setuptools = {version = "*", markers = "python_version >= \"3.12\""}
-
[package.extras]
dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
@@ -97,13 +95,13 @@ uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "certifi"
-version = "2023.11.17"
+version = "2024.7.4"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
- {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"},
- {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
+ {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
+ {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
]
[[package]]
@@ -232,63 +230,63 @@ files = [
[[package]]
name = "coverage"
-version = "7.3.2"
+version = "7.6.0"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "coverage-7.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d872145f3a3231a5f20fd48500274d7df222e291d90baa2026cc5152b7ce86bf"},
- {file = "coverage-7.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:310b3bb9c91ea66d59c53fa4989f57d2436e08f18fb2f421a1b0b6b8cc7fffda"},
- {file = "coverage-7.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f47d39359e2c3779c5331fc740cf4bce6d9d680a7b4b4ead97056a0ae07cb49a"},
- {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa72dbaf2c2068404b9870d93436e6d23addd8bbe9295f49cbca83f6e278179c"},
- {file = "coverage-7.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:beaa5c1b4777f03fc63dfd2a6bd820f73f036bfb10e925fce067b00a340d0f3f"},
- {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dbc1b46b92186cc8074fee9d9fbb97a9dd06c6cbbef391c2f59d80eabdf0faa6"},
- {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:315a989e861031334d7bee1f9113c8770472db2ac484e5b8c3173428360a9148"},
- {file = "coverage-7.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d1bc430677773397f64a5c88cb522ea43175ff16f8bfcc89d467d974cb2274f9"},
- {file = "coverage-7.3.2-cp310-cp310-win32.whl", hash = "sha256:a889ae02f43aa45032afe364c8ae84ad3c54828c2faa44f3bfcafecb5c96b02f"},
- {file = "coverage-7.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c0ba320de3fb8c6ec16e0be17ee1d3d69adcda99406c43c0409cb5c41788a611"},
- {file = "coverage-7.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ac8c802fa29843a72d32ec56d0ca792ad15a302b28ca6203389afe21f8fa062c"},
- {file = "coverage-7.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89a937174104339e3a3ffcf9f446c00e3a806c28b1841c63edb2b369310fd074"},
- {file = "coverage-7.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e267e9e2b574a176ddb983399dec325a80dbe161f1a32715c780b5d14b5f583a"},
- {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2443cbda35df0d35dcfb9bf8f3c02c57c1d6111169e3c85fc1fcc05e0c9f39a3"},
- {file = "coverage-7.3.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4175e10cc8dda0265653e8714b3174430b07c1dca8957f4966cbd6c2b1b8065a"},
- {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf38419fb1a347aaf63481c00f0bdc86889d9fbf3f25109cf96c26b403fda1"},
- {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5c913b556a116b8d5f6ef834038ba983834d887d82187c8f73dec21049abd65c"},
- {file = "coverage-7.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1981f785239e4e39e6444c63a98da3a1db8e971cb9ceb50a945ba6296b43f312"},
- {file = "coverage-7.3.2-cp311-cp311-win32.whl", hash = "sha256:43668cabd5ca8258f5954f27a3aaf78757e6acf13c17604d89648ecc0cc66640"},
- {file = "coverage-7.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10c39c0452bf6e694511c901426d6b5ac005acc0f78ff265dbe36bf81f808a2"},
- {file = "coverage-7.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4cbae1051ab791debecc4a5dcc4a1ff45fc27b91b9aee165c8a27514dd160836"},
- {file = "coverage-7.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12d15ab5833a997716d76f2ac1e4b4d536814fc213c85ca72756c19e5a6b3d63"},
- {file = "coverage-7.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c7bba973ebee5e56fe9251300c00f1579652587a9f4a5ed8404b15a0471f216"},
- {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe494faa90ce6381770746077243231e0b83ff3f17069d748f645617cefe19d4"},
- {file = "coverage-7.3.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6e9589bd04d0461a417562649522575d8752904d35c12907d8c9dfeba588faf"},
- {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d51ac2a26f71da1b57f2dc81d0e108b6ab177e7d30e774db90675467c847bbdf"},
- {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:99b89d9f76070237975b315b3d5f4d6956ae354a4c92ac2388a5695516e47c84"},
- {file = "coverage-7.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fa28e909776dc69efb6ed975a63691bc8172b64ff357e663a1bb06ff3c9b589a"},
- {file = "coverage-7.3.2-cp312-cp312-win32.whl", hash = "sha256:289fe43bf45a575e3ab10b26d7b6f2ddb9ee2dba447499f5401cfb5ecb8196bb"},
- {file = "coverage-7.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7dbc3ed60e8659bc59b6b304b43ff9c3ed858da2839c78b804973f613d3e92ed"},
- {file = "coverage-7.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f94b734214ea6a36fe16e96a70d941af80ff3bfd716c141300d95ebc85339738"},
- {file = "coverage-7.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:af3d828d2c1cbae52d34bdbb22fcd94d1ce715d95f1a012354a75e5913f1bda2"},
- {file = "coverage-7.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:630b13e3036e13c7adc480ca42fa7afc2a5d938081d28e20903cf7fd687872e2"},
- {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9eacf273e885b02a0273bb3a2170f30e2d53a6d53b72dbe02d6701b5296101c"},
- {file = "coverage-7.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f17966e861ff97305e0801134e69db33b143bbfb36436efb9cfff6ec7b2fd9"},
- {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b4275802d16882cf9c8b3d057a0839acb07ee9379fa2749eca54efbce1535b82"},
- {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:72c0cfa5250f483181e677ebc97133ea1ab3eb68645e494775deb6a7f6f83901"},
- {file = "coverage-7.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cb536f0dcd14149425996821a168f6e269d7dcd2c273a8bff8201e79f5104e76"},
- {file = "coverage-7.3.2-cp38-cp38-win32.whl", hash = "sha256:307adb8bd3abe389a471e649038a71b4eb13bfd6b7dd9a129fa856f5c695cf92"},
- {file = "coverage-7.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:88ed2c30a49ea81ea3b7f172e0269c182a44c236eb394718f976239892c0a27a"},
- {file = "coverage-7.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b631c92dfe601adf8f5ebc7fc13ced6bb6e9609b19d9a8cd59fa47c4186ad1ce"},
- {file = "coverage-7.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d3d9df4051c4a7d13036524b66ecf7a7537d14c18a384043f30a303b146164e9"},
- {file = "coverage-7.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7363d3b6a1119ef05015959ca24a9afc0ea8a02c687fe7e2d557705375c01f"},
- {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f11cc3c967a09d3695d2a6f03fb3e6236622b93be7a4b5dc09166a861be6d25"},
- {file = "coverage-7.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:149de1d2401ae4655c436a3dced6dd153f4c3309f599c3d4bd97ab172eaf02d9"},
- {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3a4006916aa6fee7cd38db3bfc95aa9c54ebb4ffbfc47c677c8bba949ceba0a6"},
- {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9028a3871280110d6e1aa2df1afd5ef003bab5fb1ef421d6dc748ae1c8ef2ebc"},
- {file = "coverage-7.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9f805d62aec8eb92bab5b61c0f07329275b6f41c97d80e847b03eb894f38d083"},
- {file = "coverage-7.3.2-cp39-cp39-win32.whl", hash = "sha256:d1c88ec1a7ff4ebca0219f5b1ef863451d828cccf889c173e1253aa84b1e07ce"},
- {file = "coverage-7.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4767da59464bb593c07afceaddea61b154136300881844768037fd5e859353f"},
- {file = "coverage-7.3.2-pp38.pp39.pp310-none-any.whl", hash = "sha256:ae97af89f0fbf373400970c0a21eef5aa941ffeed90aee43650b81f7d7f47637"},
- {file = "coverage-7.3.2.tar.gz", hash = "sha256:be32ad29341b0170e795ca590e1c07e81fc061cb5b10c74ce7203491484404ef"},
+ {file = "coverage-7.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dff044f661f59dace805eedb4a7404c573b6ff0cdba4a524141bc63d7be5c7fd"},
+ {file = "coverage-7.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a8659fd33ee9e6ca03950cfdcdf271d645cf681609153f218826dd9805ab585c"},
+ {file = "coverage-7.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7792f0ab20df8071d669d929c75c97fecfa6bcab82c10ee4adb91c7a54055463"},
+ {file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4b3cd1ca7cd73d229487fa5caca9e4bc1f0bca96526b922d61053ea751fe791"},
+ {file = "coverage-7.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7e128f85c0b419907d1f38e616c4f1e9f1d1b37a7949f44df9a73d5da5cd53c"},
+ {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a94925102c89247530ae1dab7dc02c690942566f22e189cbd53579b0693c0783"},
+ {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dcd070b5b585b50e6617e8972f3fbbee786afca71b1936ac06257f7e178f00f6"},
+ {file = "coverage-7.6.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d50a252b23b9b4dfeefc1f663c568a221092cbaded20a05a11665d0dbec9b8fb"},
+ {file = "coverage-7.6.0-cp310-cp310-win32.whl", hash = "sha256:0e7b27d04131c46e6894f23a4ae186a6a2207209a05df5b6ad4caee6d54a222c"},
+ {file = "coverage-7.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dece71673b3187c86226c3ca793c5f891f9fc3d8aa183f2e3653da18566169"},
+ {file = "coverage-7.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7b525ab52ce18c57ae232ba6f7010297a87ced82a2383b1afd238849c1ff933"},
+ {file = "coverage-7.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bea27c4269234e06f621f3fac3925f56ff34bc14521484b8f66a580aacc2e7d"},
+ {file = "coverage-7.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed8d1d1821ba5fc88d4a4f45387b65de52382fa3ef1f0115a4f7a20cdfab0e94"},
+ {file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c322ef2bbe15057bc4bf132b525b7e3f7206f071799eb8aa6ad1940bcf5fb1"},
+ {file = "coverage-7.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03cafe82c1b32b770a29fd6de923625ccac3185a54a5e66606da26d105f37dac"},
+ {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d1b923fc4a40c5832be4f35a5dab0e5ff89cddf83bb4174499e02ea089daf57"},
+ {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4b03741e70fb811d1a9a1d75355cf391f274ed85847f4b78e35459899f57af4d"},
+ {file = "coverage-7.6.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a73d18625f6a8a1cbb11eadc1d03929f9510f4131879288e3f7922097a429f63"},
+ {file = "coverage-7.6.0-cp311-cp311-win32.whl", hash = "sha256:65fa405b837060db569a61ec368b74688f429b32fa47a8929a7a2f9b47183713"},
+ {file = "coverage-7.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:6379688fb4cfa921ae349c76eb1a9ab26b65f32b03d46bb0eed841fd4cb6afb1"},
+ {file = "coverage-7.6.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f7db0b6ae1f96ae41afe626095149ecd1b212b424626175a6633c2999eaad45b"},
+ {file = "coverage-7.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bbdf9a72403110a3bdae77948b8011f644571311c2fb35ee15f0f10a8fc082e8"},
+ {file = "coverage-7.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc44bf0315268e253bf563f3560e6c004efe38f76db03a1558274a6e04bf5d5"},
+ {file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da8549d17489cd52f85a9829d0e1d91059359b3c54a26f28bec2c5d369524807"},
+ {file = "coverage-7.6.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0086cd4fc71b7d485ac93ca4239c8f75732c2ae3ba83f6be1c9be59d9e2c6382"},
+ {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fad32ee9b27350687035cb5fdf9145bc9cf0a094a9577d43e909948ebcfa27b"},
+ {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:044a0985a4f25b335882b0966625270a8d9db3d3409ddc49a4eb00b0ef5e8cee"},
+ {file = "coverage-7.6.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:76d5f82213aa78098b9b964ea89de4617e70e0d43e97900c2778a50856dac605"},
+ {file = "coverage-7.6.0-cp312-cp312-win32.whl", hash = "sha256:3c59105f8d58ce500f348c5b56163a4113a440dad6daa2294b5052a10db866da"},
+ {file = "coverage-7.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:ca5d79cfdae420a1d52bf177de4bc2289c321d6c961ae321503b2ca59c17ae67"},
+ {file = "coverage-7.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d39bd10f0ae453554798b125d2f39884290c480f56e8a02ba7a6ed552005243b"},
+ {file = "coverage-7.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:beb08e8508e53a568811016e59f3234d29c2583f6b6e28572f0954a6b4f7e03d"},
+ {file = "coverage-7.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2e16f4cd2bc4d88ba30ca2d3bbf2f21f00f382cf4e1ce3b1ddc96c634bc48ca"},
+ {file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6616d1c9bf1e3faea78711ee42a8b972367d82ceae233ec0ac61cc7fec09fa6b"},
+ {file = "coverage-7.6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad4567d6c334c46046d1c4c20024de2a1c3abc626817ae21ae3da600f5779b44"},
+ {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d17c6a415d68cfe1091d3296ba5749d3d8696e42c37fca5d4860c5bf7b729f03"},
+ {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9146579352d7b5f6412735d0f203bbd8d00113a680b66565e205bc605ef81bc6"},
+ {file = "coverage-7.6.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cdab02a0a941af190df8782aafc591ef3ad08824f97850b015c8c6a8b3877b0b"},
+ {file = "coverage-7.6.0-cp38-cp38-win32.whl", hash = "sha256:df423f351b162a702c053d5dddc0fc0ef9a9e27ea3f449781ace5f906b664428"},
+ {file = "coverage-7.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:f2501d60d7497fd55e391f423f965bbe9e650e9ffc3c627d5f0ac516026000b8"},
+ {file = "coverage-7.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7221f9ac9dad9492cecab6f676b3eaf9185141539d5c9689d13fd6b0d7de840c"},
+ {file = "coverage-7.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ddaaa91bfc4477d2871442bbf30a125e8fe6b05da8a0015507bfbf4718228ab2"},
+ {file = "coverage-7.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4cbe651f3904e28f3a55d6f371203049034b4ddbce65a54527a3f189ca3b390"},
+ {file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:831b476d79408ab6ccfadaaf199906c833f02fdb32c9ab907b1d4aa0713cfa3b"},
+ {file = "coverage-7.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46c3d091059ad0b9c59d1034de74a7f36dcfa7f6d3bde782c49deb42438f2450"},
+ {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4d5fae0a22dc86259dee66f2cc6c1d3e490c4a1214d7daa2a93d07491c5c04b6"},
+ {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:07ed352205574aad067482e53dd606926afebcb5590653121063fbf4e2175166"},
+ {file = "coverage-7.6.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:49c76cdfa13015c4560702574bad67f0e15ca5a2872c6a125f6327ead2b731dd"},
+ {file = "coverage-7.6.0-cp39-cp39-win32.whl", hash = "sha256:482855914928c8175735a2a59c8dc5806cf7d8f032e4820d52e845d1f731dca2"},
+ {file = "coverage-7.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:543ef9179bc55edfd895154a51792b01c017c87af0ebaae092720152e19e42ca"},
+ {file = "coverage-7.6.0-pp38.pp39.pp310-none-any.whl", hash = "sha256:6fe885135c8a479d3e37a7aae61cbd3a0fb2deccb4dda3c25f92a49189f766d6"},
+ {file = "coverage-7.6.0.tar.gz", hash = "sha256:289cc803fa1dc901f84701ac10c9ee873619320f2f9aff38794db4a4a0268d51"},
]
[package.dependencies]
@@ -297,19 +295,37 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1
[package.extras]
toml = ["tomli"]
+[[package]]
+name = "deprecated"
+version = "1.2.14"
+description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"},
+ {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"},
+]
+
+[package.dependencies]
+wrapt = ">=1.10,<2"
+
+[package.extras]
+dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
+
[[package]]
name = "dill"
-version = "0.3.7"
+version = "0.3.8"
description = "serialize all of Python"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"},
- {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"},
+ {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"},
+ {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"},
]
[package.extras]
graph = ["objgraph (>=1.7.2)"]
+profile = ["gprof2dot (>=2022.7.29)"]
[[package]]
name = "docdeid"
@@ -357,27 +373,30 @@ files = [
[[package]]
name = "emoji"
-version = "2.9.0"
+version = "2.12.1"
description = "Emoji for Python"
optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = ">=3.7"
files = [
- {file = "emoji-2.9.0-py2.py3-none-any.whl", hash = "sha256:17b0d53e1d9f787307a4c65aa19badb0a1ffdbc89b3a3cd851fc77821cdaced2"},
- {file = "emoji-2.9.0.tar.gz", hash = "sha256:5f4a15b7caa9c67fc11be9d90a822e3fa26aeb4e5b7bd2ded754b394d9c47869"},
+ {file = "emoji-2.12.1-py3-none-any.whl", hash = "sha256:a00d62173bdadc2510967a381810101624a2f0986145b8da0cffa42e29430235"},
+ {file = "emoji-2.12.1.tar.gz", hash = "sha256:4aa0488817691aa58d83764b6c209f8a27c0b3ab3f89d1b8dceca1a62e4973eb"},
]
+[package.dependencies]
+typing-extensions = ">=4.7.0"
+
[package.extras]
-dev = ["coverage", "coveralls", "pytest"]
+dev = ["coverage", "pytest (>=7.4.4)"]
[[package]]
name = "exceptiongroup"
-version = "1.2.0"
+version = "1.2.1"
description = "Backport of PEP 654 (exception groups)"
optional = false
python-versions = ">=3.7"
files = [
- {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
- {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
+ {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"},
+ {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"},
]
[package.extras]
@@ -401,13 +420,13 @@ pyflakes = ">=2.5.0,<2.6.0"
[[package]]
name = "flake8-annotations"
-version = "3.0.1"
+version = "3.1.1"
description = "Flake8 Type Annotation Checks"
optional = false
python-versions = ">=3.8.1"
files = [
- {file = "flake8_annotations-3.0.1-py3-none-any.whl", hash = "sha256:af78e3216ad800d7e144745ece6df706c81b3255290cbf870e54879d495e8ade"},
- {file = "flake8_annotations-3.0.1.tar.gz", hash = "sha256:ff37375e71e3b83f2a5a04d443c41e2c407de557a884f3300a7fa32f3c41cb0a"},
+ {file = "flake8_annotations-3.1.1-py3-none-any.whl", hash = "sha256:102935bdcbfa714759a152aeb07b14aee343fc0b6f7c55ad16968ce3e0e91a8a"},
+ {file = "flake8_annotations-3.1.1.tar.gz", hash = "sha256:6c98968ccc6bdc0581d363bf147a87df2f01d0d078264b2da805799d911cf5fe"},
]
[package.dependencies]
@@ -433,59 +452,55 @@ dev = ["pyTest", "pyTest-cov"]
[[package]]
name = "frozendict"
-version = "2.3.10"
+version = "2.4.4"
description = "A simple immutable dictionary"
optional = false
python-versions = ">=3.6"
files = [
- {file = "frozendict-2.3.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df2d2afa5af41bfa09dc9d5a8e6d73ae39b677a8572200c65a5ea353387ffccd"},
- {file = "frozendict-2.3.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b10df7f5d8637b1af319434f99dc25ca6f5537e28b293e4c405ebfb4bf9581fa"},
- {file = "frozendict-2.3.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da22a3e873f365f97445c49afc1e6d5198ed6d172f3efaf0e9fde0edcca3cea1"},
- {file = "frozendict-2.3.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89218738e2122b50bf8a0444083dbe2de280402e9c2ef0929c0db0f93ff11271"},
- {file = "frozendict-2.3.10-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aa11add43a71fd47523fbd011be5cc011df79e25ec0b0339fc0d728623aaa7ec"},
- {file = "frozendict-2.3.10-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:af267bd6d98cbc10580105dc76f28f7156856fa48a5bbcadd40edb85f93657ae"},
- {file = "frozendict-2.3.10-cp310-cp310-win_amd64.whl", hash = "sha256:c112024df64b8926a315d7e36b860967fcad8aae0c592b9f117589391373e893"},
- {file = "frozendict-2.3.10-cp310-cp310-win_arm64.whl", hash = "sha256:a0065db2bc76628853dd620bd08c1ca44ad0b711e92e89b4156493153add6f9d"},
- {file = "frozendict-2.3.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:93634af5a6d71762aebc7d78bdce92890b7e612588faf887c9eaf752dc7ccdb1"},
- {file = "frozendict-2.3.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b4d05e231dc1a2ec874f847fd7348cbee469555468efb875a89994ecde31a81"},
- {file = "frozendict-2.3.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d40d0644f19365fc6cc428db31c0f113fa550bd15920262f9d77ccf6556d87b"},
- {file = "frozendict-2.3.10-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:12b40526219f9583b30690011288bca4d6cce8724cda96b3c3ab08b67c5a7f09"},
- {file = "frozendict-2.3.10-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:6b552fffeba8e41b43ce10cc0fc467e048a7c9a71ae3241057510342132555b9"},
- {file = "frozendict-2.3.10-cp36-cp36m-win_amd64.whl", hash = "sha256:07208e4718cb70aa259ac886c19b96a4aad1cf00e9199f211746f738951bbf7c"},
- {file = "frozendict-2.3.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e8bec6d11f7254e405290cb1b081caffa0c18b6aa779130da9a546349c56be83"},
- {file = "frozendict-2.3.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b089c7e8c95d8b043e82e7da26e165f4220d7310efaad5e94445db7e3bc8321e"},
- {file = "frozendict-2.3.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08a5829d708657c9d5ad58f4a7e4baa73a3d57290f9613bdd909d481fc203a3a"},
- {file = "frozendict-2.3.10-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1c015852dacf144dbeadf203673d8c714f788fcc2b810a36504994b3c4f5a436"},
- {file = "frozendict-2.3.10-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bb9f15a5ed924be2b1cb3654b7ea3b7bae265ff39e2b5784d42bd4a6e1353e45"},
- {file = "frozendict-2.3.10-cp37-cp37m-win_amd64.whl", hash = "sha256:809bb9c6c657bded925710a309bb2a2350bdbfdc9371df427f1a93cb8ab7ec3e"},
- {file = "frozendict-2.3.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ff7a9cca3a3a1e584349e859d028388bd96a5475f76721471b73797472c6db17"},
- {file = "frozendict-2.3.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cdd496933ddb428f3854bea9ffdce0245bb27c27909f663ad396409fb4dffb5"},
- {file = "frozendict-2.3.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9df392b655fadaa0174c1923e6205b30ad1ccca248e8e146e63a8147a355ee01"},
- {file = "frozendict-2.3.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7901828700f36fe12486705afe7afc5583434390c8f69b5419de1b6c566fb00d"},
- {file = "frozendict-2.3.10-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c9aa28ce48d848ee520409533fd0254de4caf025c5cf1b9f27c98c1dd8cf90aa"},
- {file = "frozendict-2.3.10-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0856af4f5b4288b2270e0b74078fad5cbaf4f799326b82183865f6f367008b2c"},
- {file = "frozendict-2.3.10-cp38-cp38-win_amd64.whl", hash = "sha256:ac41c671ff33cbefc0f06c4b2a630d18ab59f5256f45f57d5632252ae4a8c07a"},
- {file = "frozendict-2.3.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:893205dc5a4e5c4b24e5822ceb21ef14fed8ca4afae7ac688e2fc24294c85225"},
- {file = "frozendict-2.3.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e78c5ac5d71f3b73f07ff9d9e3cc32dfbf7954f2c57c2d0e1fe8f1600e980b40"},
- {file = "frozendict-2.3.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c4ca4cc42bc30b20476616411d4b49aae6084760b99251f1cbdfed879ae53ea"},
- {file = "frozendict-2.3.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c865962216f7cfd6dac8693f4de431a9d98a7225185ff23613ecd10c42423adc"},
- {file = "frozendict-2.3.10-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:99b2f47b292cc4d68f6679918e8e9e6dc5e816924d8369d07018be56b93fb20f"},
- {file = "frozendict-2.3.10-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e7abf4539b73c8e5680dd2fdbd19ca4fc3e2b2f3666f80f022217839bb859fd"},
- {file = "frozendict-2.3.10-cp39-cp39-win_amd64.whl", hash = "sha256:901e774629fc63f84d24b5e46b59de1eed22392ee98b7f92e694a127d541edac"},
- {file = "frozendict-2.3.10-cp39-cp39-win_arm64.whl", hash = "sha256:6f8681c0ffe92be9aba40c9b9960c48f0ae7f6ea585af2b93fc9542cc3865969"},
- {file = "frozendict-2.3.10-py3-none-any.whl", hash = "sha256:66cded65f144393b4226bda9fe9ac2f42451d2d603e8a486015744bb566a7008"},
- {file = "frozendict-2.3.10.tar.gz", hash = "sha256:aadc83510ce82751a0bb3575231f778bc37cbb373f5f05a52b888e26cbb92f79"},
+ {file = "frozendict-2.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4a59578d47b3949437519b5c39a016a6116b9e787bb19289e333faae81462e59"},
+ {file = "frozendict-2.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12a342e439aef28ccec533f0253ea53d75fe9102bd6ea928ff530e76eac38906"},
+ {file = "frozendict-2.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f79c26dff10ce11dad3b3627c89bb2e87b9dd5958c2b24325f16a23019b8b94"},
+ {file = "frozendict-2.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2bd009cf4fc47972838a91e9b83654dc9a095dc4f2bb3a37c3f3124c8a364543"},
+ {file = "frozendict-2.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:87ebcde21565a14fe039672c25550060d6f6d88cf1f339beac094c3b10004eb0"},
+ {file = "frozendict-2.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:fefeb700bc7eb8b4c2dc48704e4221860d254c8989fb53488540bc44e44a1ac2"},
+ {file = "frozendict-2.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:4297d694eb600efa429769125a6f910ec02b85606f22f178bafbee309e7d3ec7"},
+ {file = "frozendict-2.4.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:812ab17522ba13637826e65454115a914c2da538356e85f43ecea069813e4b33"},
+ {file = "frozendict-2.4.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fee9420475bb6ff357000092aa9990c2f6182b2bab15764330f4ad7de2eae49"},
+ {file = "frozendict-2.4.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3148062675536724502c6344d7c485dd4667fdf7980ca9bd05e338ccc0c4471e"},
+ {file = "frozendict-2.4.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:78c94991944dd33c5376f720228e5b252ee67faf3bac50ef381adc9e51e90d9d"},
+ {file = "frozendict-2.4.4-cp36-cp36m-win_amd64.whl", hash = "sha256:1697793b5f62b416c0fc1d94638ec91ed3aa4ab277f6affa3a95216ecb3af170"},
+ {file = "frozendict-2.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:199a4d32194f3afed6258de7e317054155bc9519252b568d9cfffde7e4d834e5"},
+ {file = "frozendict-2.4.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85375ec6e979e6373bffb4f54576a68bf7497c350861d20686ccae38aab69c0a"},
+ {file = "frozendict-2.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2d8536e068d6bf281f23fa835ac07747fb0f8851879dd189e9709f9567408b4d"},
+ {file = "frozendict-2.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:259528ba6b56fa051bc996f1c4d8b57e30d6dd3bc2f27441891b04babc4b5e73"},
+ {file = "frozendict-2.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:07c3a5dee8bbb84cba770e273cdbf2c87c8e035903af8f781292d72583416801"},
+ {file = "frozendict-2.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6874fec816b37b6eb5795b00e0574cba261bf59723e2de607a195d5edaff0786"},
+ {file = "frozendict-2.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8f92425686323a950337da4b75b4c17a3327b831df8c881df24038d560640d4"},
+ {file = "frozendict-2.4.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d58d9a8d9e49662c6dafbea5e641f97decdb3d6ccd76e55e79818415362ba25"},
+ {file = "frozendict-2.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:93a7b19afb429cbf99d56faf436b45ef2fa8fe9aca89c49eb1610c3bd85f1760"},
+ {file = "frozendict-2.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2b70b431e3a72d410a2cdf1497b3aba2f553635e0c0f657ce311d841bf8273b6"},
+ {file = "frozendict-2.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:e1b941132d79ce72d562a13341d38fc217bc1ee24d8c35a20d754e79ff99e038"},
+ {file = "frozendict-2.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc2228874eacae390e63fd4f2bb513b3144066a977dc192163c9f6c7f6de6474"},
+ {file = "frozendict-2.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63aa49f1919af7d45fb8fd5dec4c0859bc09f46880bd6297c79bb2db2969b63d"},
+ {file = "frozendict-2.4.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6bf9260018d653f3cab9bd147bd8592bf98a5c6e338be0491ced3c196c034a3"},
+ {file = "frozendict-2.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6eb716e6a6d693c03b1d53280a1947716129f5ef9bcdd061db5c17dea44b80fe"},
+ {file = "frozendict-2.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d13b4310db337f4d2103867c5a05090b22bc4d50ca842093779ef541ea9c9eea"},
+ {file = "frozendict-2.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:b3b967d5065872e27b06f785a80c0ed0a45d1f7c9b85223da05358e734d858ca"},
+ {file = "frozendict-2.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:4ae8d05c8d0b6134bfb6bfb369d5fa0c4df21eabb5ca7f645af95fdc6689678e"},
+ {file = "frozendict-2.4.4-py311-none-any.whl", hash = "sha256:705efca8d74d3facbb6ace80ab3afdd28eb8a237bfb4063ed89996b024bc443d"},
+ {file = "frozendict-2.4.4-py312-none-any.whl", hash = "sha256:d9647563e76adb05b7cde2172403123380871360a114f546b4ae1704510801e5"},
+ {file = "frozendict-2.4.4.tar.gz", hash = "sha256:3f7c031b26e4ee6a3f786ceb5e3abf1181c4ade92dce1f847da26ea2c96008c7"},
]
[[package]]
name = "idna"
-version = "3.6"
+version = "3.7"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.5"
files = [
- {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
- {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
+ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
+ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
]
[[package]]
@@ -501,22 +516,22 @@ files = [
[[package]]
name = "importlib-metadata"
-version = "6.9.0"
+version = "8.0.0"
description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
files = [
- {file = "importlib_metadata-6.9.0-py3-none-any.whl", hash = "sha256:1c8dc6839ddc9771412596926f24cb5a553bbd40624ee2c7e55e531542bed3b8"},
- {file = "importlib_metadata-6.9.0.tar.gz", hash = "sha256:e8acb523c335a91822674e149b46c0399ec4d328c4d1f6e49c273da5ff0201b9"},
+ {file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"},
+ {file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"},
]
[package.dependencies]
zipp = ">=0.5"
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
+test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"]
[[package]]
name = "iniconfig"
@@ -531,30 +546,27 @@ files = [
[[package]]
name = "isort"
-version = "5.12.0"
+version = "5.13.2"
description = "A Python utility / library to sort Python imports."
optional = false
python-versions = ">=3.8.0"
files = [
- {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
- {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
+ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
+ {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
]
[package.extras]
-colors = ["colorama (>=0.4.3)"]
-pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
-plugins = ["setuptools"]
-requirements-deprecated-finder = ["pip-api", "pipreqs"]
+colors = ["colorama (>=0.4.6)"]
[[package]]
name = "jinja2"
-version = "3.1.2"
+version = "3.1.4"
description = "A very fast and expressive template engine."
optional = false
python-versions = ">=3.7"
files = [
- {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
- {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
+ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"},
+ {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"},
]
[package.dependencies]
@@ -599,71 +611,71 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]]
name = "markupsafe"
-version = "2.1.3"
+version = "2.1.5"
description = "Safely add untrusted strings to HTML/XML markup."
optional = false
python-versions = ">=3.7"
files = [
- {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"},
- {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"},
- {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"},
- {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"},
- {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"},
- {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"},
- {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"},
- {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"},
- {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"},
- {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"},
- {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"},
- {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"},
- {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"},
- {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"},
- {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"},
- {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"},
- {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"},
- {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
- {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
- {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
- {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"},
- {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"},
- {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"},
- {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"},
- {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"},
- {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"},
- {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"},
- {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"},
- {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"},
- {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"},
- {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
- {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
- {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
- {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"},
- {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"},
- {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"},
- {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"},
- {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"},
- {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"},
- {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"},
- {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"},
- {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"},
- {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"},
- {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"},
- {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"},
- {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"},
- {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"},
- {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"},
- {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"},
- {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"},
- {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"},
- {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"},
- {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"},
- {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"},
- {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"},
- {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"},
- {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"},
- {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"},
- {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"},
- {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
+ {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]]
@@ -746,95 +758,96 @@ testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=6,<7)", "pytest-cov",
[[package]]
name = "numpy"
-version = "1.26.2"
+version = "1.26.4"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.9"
files = [
- {file = "numpy-1.26.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3703fc9258a4a122d17043e57b35e5ef1c5a5837c3db8be396c82e04c1cf9b0f"},
- {file = "numpy-1.26.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cc392fdcbd21d4be6ae1bb4475a03ce3b025cd49a9be5345d76d7585aea69440"},
- {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36340109af8da8805d8851ef1d74761b3b88e81a9bd80b290bbfed61bd2b4f75"},
- {file = "numpy-1.26.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc008217145b3d77abd3e4d5ef586e3bdfba8fe17940769f8aa09b99e856c00"},
- {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ced40d4e9e18242f70dd02d739e44698df3dcb010d31f495ff00a31ef6014fe"},
- {file = "numpy-1.26.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b272d4cecc32c9e19911891446b72e986157e6a1809b7b56518b4f3755267523"},
- {file = "numpy-1.26.2-cp310-cp310-win32.whl", hash = "sha256:22f8fc02fdbc829e7a8c578dd8d2e15a9074b630d4da29cda483337e300e3ee9"},
- {file = "numpy-1.26.2-cp310-cp310-win_amd64.whl", hash = "sha256:26c9d33f8e8b846d5a65dd068c14e04018d05533b348d9eaeef6c1bd787f9919"},
- {file = "numpy-1.26.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b96e7b9c624ef3ae2ae0e04fa9b460f6b9f17ad8b4bec6d7756510f1f6c0c841"},
- {file = "numpy-1.26.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa18428111fb9a591d7a9cc1b48150097ba6a7e8299fb56bdf574df650e7d1f1"},
- {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06fa1ed84aa60ea6ef9f91ba57b5ed963c3729534e6e54055fc151fad0423f0a"},
- {file = "numpy-1.26.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96ca5482c3dbdd051bcd1fce8034603d6ebfc125a7bd59f55b40d8f5d246832b"},
- {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:854ab91a2906ef29dc3925a064fcd365c7b4da743f84b123002f6139bcb3f8a7"},
- {file = "numpy-1.26.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f43740ab089277d403aa07567be138fc2a89d4d9892d113b76153e0e412409f8"},
- {file = "numpy-1.26.2-cp311-cp311-win32.whl", hash = "sha256:a2bbc29fcb1771cd7b7425f98b05307776a6baf43035d3b80c4b0f29e9545186"},
- {file = "numpy-1.26.2-cp311-cp311-win_amd64.whl", hash = "sha256:2b3fca8a5b00184828d12b073af4d0fc5fdd94b1632c2477526f6bd7842d700d"},
- {file = "numpy-1.26.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a4cd6ed4a339c21f1d1b0fdf13426cb3b284555c27ac2f156dfdaaa7e16bfab0"},
- {file = "numpy-1.26.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5d5244aabd6ed7f312268b9247be47343a654ebea52a60f002dc70c769048e75"},
- {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a3cdb4d9c70e6b8c0814239ead47da00934666f668426fc6e94cce869e13fd7"},
- {file = "numpy-1.26.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa317b2325f7aa0a9471663e6093c210cb2ae9c0ad824732b307d2c51983d5b6"},
- {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:174a8880739c16c925799c018f3f55b8130c1f7c8e75ab0a6fa9d41cab092fd6"},
- {file = "numpy-1.26.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f79b231bf5c16b1f39c7f4875e1ded36abee1591e98742b05d8a0fb55d8a3eec"},
- {file = "numpy-1.26.2-cp312-cp312-win32.whl", hash = "sha256:4a06263321dfd3598cacb252f51e521a8cb4b6df471bb12a7ee5cbab20ea9167"},
- {file = "numpy-1.26.2-cp312-cp312-win_amd64.whl", hash = "sha256:b04f5dc6b3efdaab541f7857351aac359e6ae3c126e2edb376929bd3b7f92d7e"},
- {file = "numpy-1.26.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4eb8df4bf8d3d90d091e0146f6c28492b0be84da3e409ebef54349f71ed271ef"},
- {file = "numpy-1.26.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a13860fdcd95de7cf58bd6f8bc5a5ef81c0b0625eb2c9a783948847abbef2c2"},
- {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64308ebc366a8ed63fd0bf426b6a9468060962f1a4339ab1074c228fa6ade8e3"},
- {file = "numpy-1.26.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baf8aab04a2c0e859da118f0b38617e5ee65d75b83795055fb66c0d5e9e9b818"},
- {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d73a3abcac238250091b11caef9ad12413dab01669511779bc9b29261dd50210"},
- {file = "numpy-1.26.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b361d369fc7e5e1714cf827b731ca32bff8d411212fccd29ad98ad622449cc36"},
- {file = "numpy-1.26.2-cp39-cp39-win32.whl", hash = "sha256:bd3f0091e845164a20bd5a326860c840fe2af79fa12e0469a12768a3ec578d80"},
- {file = "numpy-1.26.2-cp39-cp39-win_amd64.whl", hash = "sha256:2beef57fb031dcc0dc8fa4fe297a742027b954949cabb52a2a376c144e5e6060"},
- {file = "numpy-1.26.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1cc3d5029a30fb5f06704ad6b23b35e11309491c999838c31f124fee32107c79"},
- {file = "numpy-1.26.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94cc3c222bb9fb5a12e334d0479b97bb2df446fbe622b470928f5284ffca3f8d"},
- {file = "numpy-1.26.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe6b44fb8fcdf7eda4ef4461b97b3f63c466b27ab151bec2366db8b197387841"},
- {file = "numpy-1.26.2.tar.gz", hash = "sha256:f65738447676ab5777f11e6bbbdb8ce11b785e105f690bc45966574816b6d3ea"},
+ {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
+ {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"},
+ {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"},
+ {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"},
+ {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"},
+ {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"},
+ {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"},
+ {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"},
+ {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"},
+ {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"},
+ {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"},
+ {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"},
+ {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"},
+ {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"},
+ {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"},
+ {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"},
+ {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"},
+ {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"},
+ {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"},
+ {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"},
+ {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"},
+ {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"},
+ {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"},
+ {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"},
+ {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"},
+ {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"},
+ {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"},
+ {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"},
+ {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"},
+ {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"},
+ {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"},
+ {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"},
+ {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"},
+ {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"},
+ {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"},
+ {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"},
]
[[package]]
name = "packaging"
-version = "23.2"
+version = "24.1"
description = "Core utilities for Python packages"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
- {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
+ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
+ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
]
[[package]]
name = "pathspec"
-version = "0.11.2"
+version = "0.12.1"
description = "Utility library for gitignore style pattern matching of file paths."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
- {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
+ {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.0.0"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+version = "4.2.2"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"},
- {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"},
+ {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
+ {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
]
[package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
+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 = "pluggy"
-version = "1.3.0"
+version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
- {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
+ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
+ {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
@@ -865,39 +878,38 @@ files = [
[[package]]
name = "pygments"
-version = "2.17.2"
+version = "2.18.0"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
- {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
+ {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
+ {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
]
[package.extras]
-plugins = ["importlib-metadata"]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "pylint"
-version = "3.0.2"
+version = "3.2.5"
description = "python code static checker"
optional = false
python-versions = ">=3.8.0"
files = [
- {file = "pylint-3.0.2-py3-none-any.whl", hash = "sha256:60ed5f3a9ff8b61839ff0348b3624ceeb9e6c2a92c514d81c9cc273da3b6bcda"},
- {file = "pylint-3.0.2.tar.gz", hash = "sha256:0d4c286ef6d2f66c8bfb527a7f8a629009e42c99707dec821a03e1b51a4c1496"},
+ {file = "pylint-3.2.5-py3-none-any.whl", hash = "sha256:32cd6c042b5004b8e857d727708720c54a676d1e22917cf1a2df9b4d4868abd6"},
+ {file = "pylint-3.2.5.tar.gz", hash = "sha256:e9b7171e242dcc6ebd0aaa7540481d1a72860748a0a7816b8fe6cf6c80a6fe7e"},
]
[package.dependencies]
-astroid = ">=3.0.1,<=3.1.0-dev0"
+astroid = ">=3.2.2,<=3.3.0-dev0"
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
dill = [
{version = ">=0.2", markers = "python_version < \"3.11\""},
- {version = ">=0.3.6", markers = "python_version >= \"3.11\""},
{version = ">=0.3.7", markers = "python_version >= \"3.12\""},
+ {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""},
]
-isort = ">=4.2.5,<6"
+isort = ">=4.2.5,<5.13.0 || >5.13.0,<6"
mccabe = ">=0.6,<0.8"
platformdirs = ">=2.2.0"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
@@ -910,13 +922,13 @@ testutils = ["gitpython (>3)"]
[[package]]
name = "pytest"
-version = "7.4.3"
+version = "7.4.4"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.7"
files = [
- {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"},
- {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"},
+ {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
+ {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
]
[package.dependencies]
@@ -948,6 +960,20 @@ pytest = ">=4.6"
[package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
+[[package]]
+name = "pytest-datadir"
+version = "1.5.0"
+description = "pytest plugin for test data directories and files"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pytest-datadir-1.5.0.tar.gz", hash = "sha256:1617ed92f9afda0c877e4eac91904b5f779d24ba8f5e438752e3ae39d8d2ee3f"},
+ {file = "pytest_datadir-1.5.0-py3-none-any.whl", hash = "sha256:34adf361bcc7b37961bbc1dfa8d25a4829e778bab461703c38a5c50ca9c36dc8"},
+]
+
+[package.dependencies]
+pytest = ">=5.0"
+
[[package]]
name = "pyyaml"
version = "6.0.1"
@@ -973,6 +999,7 @@ files = [
{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"},
@@ -1226,13 +1253,13 @@ files = [
[[package]]
name = "requests"
-version = "2.31.0"
+version = "2.32.3"
description = "Python HTTP for Humans."
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
- {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
+ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
+ {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
]
[package.dependencies]
@@ -1245,22 +1272,6 @@ urllib3 = ">=1.21.1,<3"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
-[[package]]
-name = "setuptools"
-version = "69.0.2"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"},
- {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"},
-]
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
-
[[package]]
name = "snowballstemmer"
version = "2.2.0"
@@ -1309,56 +1320,50 @@ test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"]
[[package]]
name = "sphinxcontrib-applehelp"
-version = "1.0.7"
+version = "1.0.8"
description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books"
optional = false
python-versions = ">=3.9"
files = [
- {file = "sphinxcontrib_applehelp-1.0.7-py3-none-any.whl", hash = "sha256:094c4d56209d1734e7d252f6e0b3ccc090bd52ee56807a5d9315b19c122ab15d"},
- {file = "sphinxcontrib_applehelp-1.0.7.tar.gz", hash = "sha256:39fdc8d762d33b01a7d8f026a3b7d71563ea3b72787d5f00ad8465bd9d6dfbfa"},
+ {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"},
+ {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"},
]
-[package.dependencies]
-Sphinx = ">=5"
-
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
+standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-devhelp"
-version = "1.0.5"
+version = "1.0.6"
description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents"
optional = false
python-versions = ">=3.9"
files = [
- {file = "sphinxcontrib_devhelp-1.0.5-py3-none-any.whl", hash = "sha256:fe8009aed765188f08fcaadbb3ea0d90ce8ae2d76710b7e29ea7d047177dae2f"},
- {file = "sphinxcontrib_devhelp-1.0.5.tar.gz", hash = "sha256:63b41e0d38207ca40ebbeabcf4d8e51f76c03e78cd61abe118cf4435c73d4212"},
+ {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"},
+ {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"},
]
-[package.dependencies]
-Sphinx = ">=5"
-
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
+standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-htmlhelp"
-version = "2.0.4"
+version = "2.0.5"
description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
optional = false
python-versions = ">=3.9"
files = [
- {file = "sphinxcontrib_htmlhelp-2.0.4-py3-none-any.whl", hash = "sha256:8001661c077a73c29beaf4a79968d0726103c5605e27db92b9ebed8bab1359e9"},
- {file = "sphinxcontrib_htmlhelp-2.0.4.tar.gz", hash = "sha256:6c26a118a05b76000738429b724a0568dbde5b72391a688577da08f11891092a"},
+ {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"},
+ {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"},
]
-[package.dependencies]
-Sphinx = ">=5"
-
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
+standalone = ["Sphinx (>=5)"]
test = ["html5lib", "pytest"]
[[package]]
@@ -1377,38 +1382,34 @@ test = ["flake8", "mypy", "pytest"]
[[package]]
name = "sphinxcontrib-qthelp"
-version = "1.0.6"
+version = "1.0.7"
description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents"
optional = false
python-versions = ">=3.9"
files = [
- {file = "sphinxcontrib_qthelp-1.0.6-py3-none-any.whl", hash = "sha256:bf76886ee7470b934e363da7a954ea2825650013d367728588732c7350f49ea4"},
- {file = "sphinxcontrib_qthelp-1.0.6.tar.gz", hash = "sha256:62b9d1a186ab7f5ee3356d906f648cacb7a6bdb94d201ee7adf26db55092982d"},
+ {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"},
+ {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"},
]
-[package.dependencies]
-Sphinx = ">=5"
-
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
+standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
name = "sphinxcontrib-serializinghtml"
-version = "1.1.9"
+version = "1.1.10"
description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)"
optional = false
python-versions = ">=3.9"
files = [
- {file = "sphinxcontrib_serializinghtml-1.1.9-py3-none-any.whl", hash = "sha256:9b36e503703ff04f20e9675771df105e58aa029cfcbc23b8ed716019b7416ae1"},
- {file = "sphinxcontrib_serializinghtml-1.1.9.tar.gz", hash = "sha256:0c64ff898339e1fac29abd2bf5f11078f3ec413cfe9c046d3120d7ca65530b54"},
+ {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"},
+ {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"},
]
-[package.dependencies]
-Sphinx = ">=5"
-
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
+standalone = ["Sphinx (>=5)"]
test = ["pytest"]
[[package]]
@@ -1435,24 +1436,24 @@ files = [
[[package]]
name = "tomlkit"
-version = "0.12.3"
+version = "0.13.0"
description = "Style preserving TOML library"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "tomlkit-0.12.3-py3-none-any.whl", hash = "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba"},
- {file = "tomlkit-0.12.3.tar.gz", hash = "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4"},
+ {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"},
+ {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"},
]
[[package]]
name = "typing-extensions"
-version = "4.8.0"
+version = "4.12.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
- {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"},
- {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"},
+ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
+ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
[[package]]
@@ -1467,36 +1468,116 @@ files = [
[[package]]
name = "urllib3"
-version = "2.1.0"
+version = "2.2.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.8"
files = [
- {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"},
- {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"},
+ {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
+ {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
+[[package]]
+name = "wrapt"
+version = "1.16.0"
+description = "Module for decorators, wrappers and monkey patching."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
+ {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
+ {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
+ {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
+ {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
+ {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
+ {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
+ {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
+ {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
+ {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
+ {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
+ {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
+ {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
+ {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
+ {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
+ {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
+ {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
+ {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
+ {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
+ {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
+ {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
+ {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
+ {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
+ {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
+ {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
+ {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
+ {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
+ {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
+ {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
+ {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
+ {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
+ {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
+ {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
+ {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
+ {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
+ {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
+ {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
+ {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
+ {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
+ {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
+ {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
+ {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
+ {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
+ {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
+ {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
+ {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
+ {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
+ {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
+ {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
+ {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
+ {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
+ {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
+ {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
+ {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
+ {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
+ {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
+ {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
+ {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
+ {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
+ {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
+ {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
+ {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
+ {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
+ {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
+ {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
+ {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
+ {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
+ {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
+ {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
+ {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
+]
+
[[package]]
name = "zipp"
-version = "3.17.0"
+version = "3.19.2"
description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.8"
files = [
- {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"},
- {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"},
+ {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"},
+ {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"},
]
[package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
-testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"]
+doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.9"
-content-hash = "0aa47f24ef708b11b56e5a478275eabb54bc44670ce7aa5fa2f30f0b39d1ce66"
+content-hash = "844fe1fe2b44afc59d7722df7f26dfd388660265eb48e81b1066c095850cb3e6"
diff --git a/pyproject.toml b/pyproject.toml
index ba06e034..3343da68 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -26,10 +26,12 @@ author = "Vincent Menger"
[tool.poetry.dependencies]
python = "^3.9"
-rapidfuzz = "^2.11.1"
docdeid = "1.0.0"
+rapidfuzz = "^2.11.1"
regex = "^2022.9.13"
frozendict = "^2.3.10"
+deprecated = "^1.2.14"
+pytest-datadir = "^1.5.0"
[tool.poetry.group.dev]
optional = false
diff --git a/tests/conftest.py b/tests/conftest.py
deleted file mode 100644
index e7139230..00000000
--- a/tests/conftest.py
+++ /dev/null
@@ -1,8 +0,0 @@
-import pytest
-
-from deduce import Deduce
-
-
-@pytest.fixture(scope="session")
-def model():
- return Deduce(build_lookup_structs=True)
diff --git a/tests/pipeline/data/lookup/src/institutions/lst_healthcare_institution/exceptions.txt b/tests/pipeline/data/lookup/src/institutions/lst_healthcare_institution/exceptions.txt
new file mode 100644
index 00000000..8441a19d
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/institutions/lst_healthcare_institution/exceptions.txt
@@ -0,0 +1,3 @@
+Oktober
+PSOAS
+Prisma
diff --git a/tests/pipeline/data/lookup/src/institutions/lst_healthcare_institution/items.txt b/tests/pipeline/data/lookup/src/institutions/lst_healthcare_institution/items.txt
new file mode 100644
index 00000000..1aece757
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/institutions/lst_healthcare_institution/items.txt
@@ -0,0 +1,3 @@
+De SeT Residentie
+De Zorggroep, Woonzorgcentrum Bosscherhof
+Oktober
diff --git a/tests/pipeline/data/lookup/src/institutions/lst_hospital/items.txt b/tests/pipeline/data/lookup/src/institutions/lst_hospital/items.txt
new file mode 100644
index 00000000..65fc1cf9
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/institutions/lst_hospital/items.txt
@@ -0,0 +1,5 @@
+Weesperplein Ziekenhuis
+De Stadsmaten
+Sint Vincentius Ziekenhuis
+Wilhelmina Kinder
+Zaans Medisch Centrum
diff --git a/tests/pipeline/data/lookup/src/institutions/lst_hospital_abbr/items.txt b/tests/pipeline/data/lookup/src/institutions/lst_hospital_abbr/items.txt
new file mode 100644
index 00000000..f32bd3b0
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/institutions/lst_hospital_abbr/items.txt
@@ -0,0 +1,2 @@
+MUMC
+UMCU
diff --git a/tests/pipeline/data/lookup/src/locations/lst_placename/lst_municipality/items.txt b/tests/pipeline/data/lookup/src/locations/lst_placename/lst_municipality/items.txt
new file mode 100644
index 00000000..101dadfc
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/locations/lst_placename/lst_municipality/items.txt
@@ -0,0 +1,2 @@
+Hengelo
+Utrecht
diff --git a/tests/pipeline/data/lookup/src/locations/lst_placename/lst_province/items.txt b/tests/pipeline/data/lookup/src/locations/lst_placename/lst_province/items.txt
new file mode 100644
index 00000000..45804100
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/locations/lst_placename/lst_province/items.txt
@@ -0,0 +1,2 @@
+Friesland
+Utrecht
diff --git a/tests/pipeline/data/lookup/src/locations/lst_placename/lst_region/items.txt b/tests/pipeline/data/lookup/src/locations/lst_placename/lst_region/items.txt
new file mode 100644
index 00000000..0afab38a
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/locations/lst_placename/lst_region/items.txt
@@ -0,0 +1,5 @@
+Oostergo
+Prins Alexanderpolder
+Rijk Van Nijmegen
+Noordenveld
+Rottum
diff --git a/tests/pipeline/data/lookup/src/locations/lst_placename/lst_residence/exceptions.txt b/tests/pipeline/data/lookup/src/locations/lst_placename/lst_residence/exceptions.txt
new file mode 100644
index 00000000..504b7724
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/locations/lst_placename/lst_residence/exceptions.txt
@@ -0,0 +1,5 @@
+Raar
+Smal
+Zittend
+Brand
+Ie
diff --git a/tests/pipeline/data/lookup/src/locations/lst_placename/lst_residence/items.txt b/tests/pipeline/data/lookup/src/locations/lst_placename/lst_residence/items.txt
new file mode 100644
index 00000000..1486dde6
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/locations/lst_placename/lst_residence/items.txt
@@ -0,0 +1,2 @@
+Felland
+Utrecht
diff --git a/tests/pipeline/data/lookup/src/locations/lst_street/exceptions.txt b/tests/pipeline/data/lookup/src/locations/lst_street/exceptions.txt
new file mode 100644
index 00000000..4cb5b7ab
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/locations/lst_street/exceptions.txt
@@ -0,0 +1,5 @@
+Segment
+Generaal
+Berg
+Vrij
+Oost
diff --git a/tests/pipeline/data/lookup/src/locations/lst_street/items.txt b/tests/pipeline/data/lookup/src/locations/lst_street/items.txt
new file mode 100644
index 00000000..d7e8904c
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/locations/lst_street/items.txt
@@ -0,0 +1,3 @@
+Blauw Druifje
+IJsweg
+Visser
diff --git a/tests/pipeline/data/lookup/src/locations/lst_street/streets_bag.txt b/tests/pipeline/data/lookup/src/locations/lst_street/streets_bag.txt
new file mode 100644
index 00000000..73533e51
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/locations/lst_street/streets_bag.txt
@@ -0,0 +1,3 @@
+Blauw druifje
+IJsweg
+Visser
diff --git a/tests/pipeline/data/lookup/src/names/lst_first_name/exceptions.txt b/tests/pipeline/data/lookup/src/names/lst_first_name/exceptions.txt
new file mode 100644
index 00000000..5c793052
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/names/lst_first_name/exceptions.txt
@@ -0,0 +1,5 @@
+Lung
+Ad
+Ace
+Man
+Heino
diff --git a/tests/pipeline/data/lookup/src/names/lst_first_name/items.txt b/tests/pipeline/data/lookup/src/names/lst_first_name/items.txt
new file mode 100644
index 00000000..8bf097e6
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/names/lst_first_name/items.txt
@@ -0,0 +1,4 @@
+Annes
+Jan
+Jansen
+Peter
\ No newline at end of file
diff --git a/tests/pipeline/data/lookup/src/names/lst_initial/items.txt b/tests/pipeline/data/lookup/src/names/lst_initial/items.txt
new file mode 100644
index 00000000..b4925874
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/names/lst_initial/items.txt
@@ -0,0 +1,5 @@
+P
+Š
+Ñ
+A
+Ã
diff --git a/tests/pipeline/data/lookup/src/names/lst_interfix/items.txt b/tests/pipeline/data/lookup/src/names/lst_interfix/items.txt
new file mode 100644
index 00000000..a00f7851
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/names/lst_interfix/items.txt
@@ -0,0 +1,7 @@
+aan de
+de
+in 't
+in het
+uit de
+van den
+von
\ No newline at end of file
diff --git a/tests/pipeline/data/lookup/src/names/lst_interfix_surname/exceptions.txt b/tests/pipeline/data/lookup/src/names/lst_interfix_surname/exceptions.txt
new file mode 100644
index 00000000..87b8f311
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/names/lst_interfix_surname/exceptions.txt
@@ -0,0 +1,3 @@
+Amersfoort
+Utrecht
+Veenendaal
diff --git a/tests/pipeline/data/lookup/src/names/lst_interfix_surname/items.txt b/tests/pipeline/data/lookup/src/names/lst_interfix_surname/items.txt
new file mode 100644
index 00000000..da71be6d
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/names/lst_interfix_surname/items.txt
@@ -0,0 +1,3 @@
+Olst
+Utrecht
+Visser
diff --git a/tests/pipeline/data/lookup/src/names/lst_prefix/items.txt b/tests/pipeline/data/lookup/src/names/lst_prefix/items.txt
new file mode 100644
index 00000000..74c2f9a2
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/names/lst_prefix/items.txt
@@ -0,0 +1,5 @@
+de weledelgeleerde
+ing.
+mr.
+dr.h.c.
+mevr.
diff --git a/tests/pipeline/data/lookup/src/names/lst_surname/exceptions.txt b/tests/pipeline/data/lookup/src/names/lst_surname/exceptions.txt
new file mode 100644
index 00000000..4f5351e9
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/names/lst_surname/exceptions.txt
@@ -0,0 +1,5 @@
+Oost
+Tel
+Klein
+Broer
+Lang
diff --git a/tests/pipeline/data/lookup/src/names/lst_surname/items.txt b/tests/pipeline/data/lookup/src/names/lst_surname/items.txt
new file mode 100644
index 00000000..97509bb7
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/names/lst_surname/items.txt
@@ -0,0 +1,6 @@
+Jansen
+Killaars
+Peter
+Visser
+Quervain
+de Quervain
\ No newline at end of file
diff --git a/tests/pipeline/data/lookup/src/whitelist/lst_common_word/exceptions.txt b/tests/pipeline/data/lookup/src/whitelist/lst_common_word/exceptions.txt
new file mode 100644
index 00000000..3a2cb72d
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/whitelist/lst_common_word/exceptions.txt
@@ -0,0 +1,5 @@
+boos
+zondag
+hemel
+bel
+helder
diff --git a/tests/pipeline/data/lookup/src/whitelist/lst_common_word/items.txt b/tests/pipeline/data/lookup/src/whitelist/lst_common_word/items.txt
new file mode 100644
index 00000000..615e97f5
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/whitelist/lst_common_word/items.txt
@@ -0,0 +1,6 @@
+oog
+oktober
+raam
+soms
+we
+wijs
diff --git a/tests/pipeline/data/lookup/src/whitelist/lst_eponymous_disease/items.txt b/tests/pipeline/data/lookup/src/whitelist/lst_eponymous_disease/items.txt
new file mode 100644
index 00000000..569997d0
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/whitelist/lst_eponymous_disease/items.txt
@@ -0,0 +1,5 @@
+Gerbec-Morgagni-Adams-Stokes
+Non-Hodgkin
+Diamond-Blackfan
+Baller-Gerold
+Alpers-Huttenlocher
diff --git a/tests/pipeline/data/lookup/src/whitelist/lst_eponymous_disease/lst_eponymous_single/items.txt b/tests/pipeline/data/lookup/src/whitelist/lst_eponymous_disease/lst_eponymous_single/items.txt
new file mode 100644
index 00000000..4ce5d915
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/whitelist/lst_eponymous_disease/lst_eponymous_single/items.txt
@@ -0,0 +1,6 @@
+ziekte van Eales
+ziekte van Glanzmann
+Krabbe ziekte
+Dupuytren ziekte
+ziekte van Laron
+de Quervain ziekte
\ No newline at end of file
diff --git a/tests/pipeline/data/lookup/src/whitelist/lst_medical_term/items.txt b/tests/pipeline/data/lookup/src/whitelist/lst_medical_term/items.txt
new file mode 100644
index 00000000..5e56f639
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/whitelist/lst_medical_term/items.txt
@@ -0,0 +1,5 @@
+mdl
+kinetic
+functies
+zetpil
+schaambeen
diff --git a/tests/pipeline/data/lookup/src/whitelist/lst_stop_word/items.txt b/tests/pipeline/data/lookup/src/whitelist/lst_stop_word/items.txt
new file mode 100644
index 00000000..bc5fa458
--- /dev/null
+++ b/tests/pipeline/data/lookup/src/whitelist/lst_stop_word/items.txt
@@ -0,0 +1,5 @@
+nu
+zonder
+doen
+kan
+een
diff --git a/tests/pipeline/test_deduce.py b/tests/pipeline/test_deduce.py
index 3bc80771..b1136619 100644
--- a/tests/pipeline/test_deduce.py
+++ b/tests/pipeline/test_deduce.py
@@ -1,59 +1,79 @@
import docdeid as dd
+import pytest
+from deduce import Deduce
from deduce.person import Person
text = (
"betreft: Jan Jansen, bsn 111222333, patnr 000334433. De patient J. Jansen is 64 "
- "jaar oud en woonachtig in Utrecht. Hij werd op 10 oktober 2018 door arts "
- "Peter de Visser ontslagen van de kliniek van het UMCU. Voor nazorg kan hij "
- "worden bereikt via j.JNSEN.123@gmail.com of (06)12345678."
+ "jaar oud en woonachtig in Utrecht, IJSWEG 10r. Hij werd op 10 oktober 2018 door "
+ "arts Peter de Visser ontslagen van de kliniek van het UMCU. Voor nazorg kan hij "
+ "worden bereikt via j.JNSEN.123@gmail.com of (06)12345678. "
+ "Vader, Peter Jansen, 104 jr, woont ook in Utrecht. Met collegiale groeten, "
+ "Jan de Visser."
+ # FIXME "aan de" is joined to one token (due to "lst_interfix/items.txt"),
+ # preventing "de Quervain ziekte" from matching. Furthermore, when I
+ # managed to get this term censored, the "aan" word was censored, too.
+ # Use a simple whitespace/punctuation-based tokenizer for that annotator
+ # to fix this issue.
+ # " De patient lijdt aan de Quervain ziekte."
)
+@pytest.fixture
+def model(shared_datadir):
+ return Deduce(
+ save_lookup_structs=False,
+ build_lookup_structs=True,
+ lookup_data_path=shared_datadir / "lookup",
+ )
+
+
class TestDeduce:
def test_annotate(self, model):
metadata = {"patient": Person(first_names=["Jan"], surname="Jansen")}
doc = model.deidentify(text, metadata=metadata)
- expected_annotations = dd.AnnotationSet(
- [
- dd.Annotation(
- text="(06)12345678",
- start_char=272,
- end_char=284,
- tag="telefoonnummer",
- ),
- dd.Annotation(text="111222333", start_char=25, end_char=34, tag="bsn"),
- dd.Annotation(
- text="Peter de Visser", start_char=153, end_char=168, tag="persoon"
- ),
- dd.Annotation(
- text="j.JNSEN.123@gmail.com",
- start_char=247,
- end_char=268,
- tag="emailadres",
- ),
- dd.Annotation(
- text="J. Jansen", start_char=64, end_char=73, tag="patient"
- ),
- dd.Annotation(
- text="Jan Jansen", start_char=9, end_char=19, tag="patient"
- ),
- dd.Annotation(
- text="10 oktober 2018", start_char=127, end_char=142, tag="datum"
- ),
- dd.Annotation(text="64", start_char=77, end_char=79, tag="leeftijd"),
- dd.Annotation(text="000334433", start_char=42, end_char=51, tag="id"),
- dd.Annotation(
- text="Utrecht", start_char=106, end_char=113, tag="locatie"
- ),
- dd.Annotation(
- text="UMCU", start_char=202, end_char=206, tag="ziekenhuis"
- ),
- ]
- )
+ expected_annotations = {
+ dd.Annotation(
+ text="(06)12345678",
+ start_char=284,
+ end_char=296,
+ tag="telefoonnummer",
+ ),
+ dd.Annotation(text="111222333", start_char=25, end_char=34, tag="bsn"),
+ dd.Annotation(
+ text="Peter de Visser", start_char=165, end_char=180, tag="persoon"
+ ),
+ dd.Annotation(
+ text="j.JNSEN.123@gmail.com",
+ start_char=259,
+ end_char=280,
+ tag="emailadres",
+ ),
+ dd.Annotation(text="J. Jansen", start_char=64, end_char=73, tag="patient"),
+ dd.Annotation(text="Jan Jansen", start_char=9, end_char=19, tag="patient"),
+ dd.Annotation(
+ text="10 oktober 2018", start_char=139, end_char=154, tag="datum"
+ ),
+ dd.Annotation(text="64", start_char=77, end_char=79, tag="leeftijd"),
+ dd.Annotation(text="000334433", start_char=42, end_char=51, tag="id"),
+ dd.Annotation(text="Utrecht", start_char=106, end_char=113, tag="locatie"),
+ dd.Annotation(
+ text="IJSWEG 10r", start_char=115, end_char=125, tag="locatie"
+ ),
+ dd.Annotation(text="UMCU", start_char=214, end_char=218, tag="ziekenhuis"),
+ dd.Annotation(
+ text="Peter Jansen", start_char=305, end_char=317, tag="persoon"
+ ),
+ dd.Annotation(text="104", start_char=319, end_char=322, tag="leeftijd"),
+ dd.Annotation(text="Utrecht", start_char=340, end_char=347, tag="locatie"),
+ dd.Annotation(
+ text="Jan de Visser", start_char=373, end_char=386, tag="persoon"
+ ),
+ }
- assert doc.annotations == set(expected_annotations)
+ assert set(doc.annotations) == expected_annotations
def test_deidentify(self, model):
metadata = {"patient": Person(first_names=["Jan"], surname="Jansen")}
@@ -61,10 +81,16 @@ def test_deidentify(self, model):
expected_deidentified = (
"betreft: [PATIENT], bsn [BSN-1], patnr [ID-1]. De patient [PATIENT] is "
- "[LEEFTIJD-1] jaar oud en woonachtig in [LOCATIE-1]. Hij werd op "
- "[DATUM-1] door arts [PERSOON-1] ontslagen van de kliniek van het "
+ "[LEEFTIJD-1] jaar oud en woonachtig in [LOCATIE-1], [LOCATIE-2]. Hij werd "
+ "op [DATUM-1] door arts [PERSOON-1] ontslagen van de kliniek van het "
"[ZIEKENHUIS-1]. Voor nazorg kan hij worden bereikt via [EMAILADRES-1] "
- "of [TELEFOONNUMMER-1]."
+ "of [TELEFOONNUMMER-1]. Vader, [PERSOON-2], [LEEFTIJD-2] jr, woont "
+ # XXX Btw, if we wanted more perfect security, we should
+ # not give away whether two mentions of age (or street or
+ # anything) were equal before deidentification or not.
+ # Concretely, it shouldn't matter whether LEEFTIJD-1 is the same
+ # as LEEFTIJD-2.
+ "ook in [LOCATIE-1]. Met collegiale groeten, [PERSOON-3]."
)
assert doc.deidentified_text == expected_deidentified
@@ -77,11 +103,35 @@ def test_annotate_intext(self, model):
"betreft: Jan Jansen, bsn 111222333, "
"patnr 000334433. De patient J. Jansen is "
"64 jaar oud en woonachtig in Utrecht"
- ". Hij werd op 10 oktober 2018 door arts "
- "Peter de Visser ontslagen van de kliniek van het "
- "UMCU. Voor nazorg kan hij worden bereikt "
- "via j.JNSEN.123@gmail.com of "
+ ", IJSWEG 10r. Hij werd op 10 "
+ "oktober 2018 door arts Peter de "
+ "Visser ontslagen van de kliniek van het "
+ "UMCU. Voor nazorg kan hij worden "
+ "bereikt via j.JNSEN.123@gmail.com of "
"(06)12345678."
+ # " De patient lijdt aan de Quervain ziekte."
+ " Vader, Peter Jansen, "
+ "104 jr, woont ook in "
+ "Utrecht. Met collegiale groeten, "
+ "Jan de Visser."
)
assert dd.utils.annotate_intext(doc) == expected_intext_annotated
+
+ def test_patient_2(self, model):
+ metadata = {"patient": Person(first_names=["Jan"], surname="Jansen")}
+ doc = (
+ "Lorem ipsum JANSEN sit amet, Peter Jansen adipiscing elit. "
+ "Curabitur J. Jansen sapien, J. P. Jansen a vestibulum quis, "
+ "facilisis vel J Jansen. Jan de Visser iaculis gravida nulla. "
+ "Etiam quis Jan van den Jansen."
+ )
+ want = (
+ "Lorem ipsum [PATIENT] sit amet, [PERSOON-1] adipiscing elit. "
+ "Curabitur [PATIENT] sapien, [PERSOON-2] a vestibulum quis, "
+ "facilisis vel [PATIENT]. [PERSOON-3] iaculis gravida nulla. "
+ "Etiam quis [PERSOON-4]."
+ )
+
+ deid = model.deidentify(doc, metadata=metadata)
+ assert deid.deidentified_text == want
diff --git a/tests/data/regression_cases/ages.json b/tests/regression/data/ages.json
similarity index 100%
rename from tests/data/regression_cases/ages.json
rename to tests/regression/data/ages.json
diff --git a/tests/data/regression_cases/dates.json b/tests/regression/data/dates.json
similarity index 100%
rename from tests/data/regression_cases/dates.json
rename to tests/regression/data/dates.json
diff --git a/tests/data/regression_cases/emails.json b/tests/regression/data/emails.json
similarity index 100%
rename from tests/data/regression_cases/emails.json
rename to tests/regression/data/emails.json
diff --git a/tests/data/regression_cases/identifiers.json b/tests/regression/data/identifiers.json
similarity index 100%
rename from tests/data/regression_cases/identifiers.json
rename to tests/regression/data/identifiers.json
diff --git a/tests/data/regression_cases/institutions.json b/tests/regression/data/institutions.json
similarity index 100%
rename from tests/data/regression_cases/institutions.json
rename to tests/regression/data/institutions.json
diff --git a/tests/data/regression_cases/locations.json b/tests/regression/data/locations.json
similarity index 100%
rename from tests/data/regression_cases/locations.json
rename to tests/regression/data/locations.json
diff --git a/tests/regression/data/lookup/src/__init__.py b/tests/regression/data/lookup/src/__init__.py
new file mode 100644
index 00000000..ae186018
--- /dev/null
+++ b/tests/regression/data/lookup/src/__init__.py
@@ -0,0 +1,17 @@
+all_lists = [
+ "institutions/lst_healthcare_institution",
+ "institutions/lst_hospital",
+ "institutions/lst_hospital_abbr",
+ "locations/lst_placename",
+ "locations/lst_street",
+ "names/lst_first_name",
+ "names/lst_initial",
+ "names/lst_interfix",
+ "names/lst_interfix_surname",
+ "names/lst_prefix",
+ "names/lst_surname",
+ "whitelist/lst_common_word",
+ "whitelist/lst_eponymous_disease",
+ "whitelist/lst_medical_term",
+ "whitelist/lst_stop_word",
+]
diff --git a/tests/regression/data/lookup/src/institutions/lst_healthcare_institution/exceptions.txt b/tests/regression/data/lookup/src/institutions/lst_healthcare_institution/exceptions.txt
new file mode 100644
index 00000000..8441a19d
--- /dev/null
+++ b/tests/regression/data/lookup/src/institutions/lst_healthcare_institution/exceptions.txt
@@ -0,0 +1,3 @@
+Oktober
+PSOAS
+Prisma
diff --git a/tests/regression/data/lookup/src/institutions/lst_healthcare_institution/items.txt b/tests/regression/data/lookup/src/institutions/lst_healthcare_institution/items.txt
new file mode 100644
index 00000000..124a1778
--- /dev/null
+++ b/tests/regression/data/lookup/src/institutions/lst_healthcare_institution/items.txt
@@ -0,0 +1,241 @@
+Alphega apotheek Wilhelmina
+Alrijne Apotheek Leiden
+Alrijne Apotheek Leiderdorp
+Alrijne Zorggroep Fysiotherapie
+Alrijne Zorggroep, Verpleeghuis Leythenrode
+Alrijne Zorggroep, Verpleeghuis Oudshoorn
+Altrecht
+Altrecht Acute Psychiatrie
+Altrecht Angststoornissen
+Altrecht Bipolair
+Altrecht Eetstoornissen Rintveld
+Altrecht Kinder- en jeugdpsychiatrie
+Altrecht Neuropsychiatrie Vesalius
+Altrecht Ouderenpsychiatrie
+Altrecht Persoonlijkheidsstoornissen
+Altrecht Psychiatrie en verslaving
+Altrecht Psychosomatiek Eikenboom
+Altrecht Voortgezette Klinische Behandeling
+Altrecht Vroege psychose ABC
+Altrecht senior
+Altrecht, Expertise Centrum Diagnostiek
+Annelieke Janssen Fysiotherapie, Manuele Therapie en Training
+Antonius Apotheek
+Antonius Hypercare
+Antonius Kindzorg Thuis
+Antonius Zorg Thuis
+Antonius Zorggroep
+Apotheek Antoniushove
+Apotheek Centraal Militair Hospitaal
+Apotheek Erasmusplein
+Apotheek Wilhelmina
+BENU Apotheek Antoniusveld
+BENU Apotheek Erasmus
+BENU Apotheek Wilhelminapark
+BrabantZorg, Sint Antonius
+Buurtzorg Utrecht Wilhelminapark
+Canté praktijk voor Pedagogiek en Psychologie
+Careaz Antoniushove
+Careyn
+Careyn 1e lijns ergotherapie Utrecht Stad
+Careyn 1e lijns ergotherapie Zuid Hollandse Eilanden
+Careyn 1e lijns ergotherapie regio Utrecht West
+Careyn 1e-lijns Fysiotherapie Utrecht-Stad
+Careyn 1e-lijns fysiotherapie Zuid-Hollandse Eilanden
+Careyn Bernissesteyn
+Careyn Blankenburg
+Careyn Buitenhof
+Careyn De Ark
+Careyn De Geuzenveste
+Careyn De Plantage
+Careyn De Prinses
+Careyn De Rozenhoek
+Careyn De Vier Ambachten
+Careyn De Vloot
+Careyn Dierenriem
+Careyn Fysiotherapie
+Careyn Geriatrische Revalidatie
+Careyn Grootenhoek
+Careyn Hart van Groenewoud
+Careyn Hart van Rozenburg
+Careyn Hart van Zuidland
+Careyn Herman Gorterhof
+Careyn Het Waterschapshuis
+Careyn Maria-Oord
+Careyn Maria-Oord, Geriatrische revalidatie
+Careyn Nieuw Chartreuse
+Careyn Parkwijk
+Careyn Rosendael
+Careyn Rozenhof
+Careyn Snavelenburg
+Careyn Swellengrebel
+Careyn Tamarinde
+Careyn Tamarinde, Geriatrische revalidatie
+Careyn Thuiszorg en Wijkverpleging
+Careyn Thuiszorg en Wijkverpleging Breda
+Careyn Thuiszorg en Wijkverpleging De Lier, Maasdijk, Maasland, Schipluiden, Den Hoorn en Maassluis
+Careyn Thuiszorg en Wijkverpleging Goeree-Overflakkee
+Careyn Thuiszorg en Wijkverpleging Hellevoetsluis
+Careyn Thuiszorg en Wijkverpleging Hoeksche Waard
+Careyn Thuiszorg en Wijkverpleging Montfoort
+Careyn Thuiszorg en Wijkverpleging Naaldwijk, Honselersdijk, 's-Gravenzande, Monster, Poeldijk, Kwintsheul en Wateringen
+Careyn Thuiszorg en Wijkverpleging Nissewaard
+Careyn Thuiszorg en Wijkverpleging Oudewater, Woerden, Montfoort
+Careyn Thuiszorg en Wijkverpleging Pijnacker-Nootdorp en Delft
+Careyn Thuiszorg en Wijkverpleging Rozenburg
+Careyn Thuiszorg en Wijkverpleging Schiedam
+Careyn Thuiszorg en Wijkverpleging Stichtse Vecht, Maarssen, Breukelen, Loenen, Nigtevegt
+Careyn Thuiszorg en Wijkverpleging Utrecht Overvecht
+Careyn Thuiszorg en Wijkverpleging Vlaardingen
+Careyn Thuiszorg en Wijkverpleging Voorne-Bernisse
+Careyn Thuiszorg en Wijkverpleging Zeist
+Careyn Thuiszorg en Wijkverpleging, CVA-nazorg team Zuid-Hollandse Eilanden
+Careyn Thuiszorg en Wijkverpleging, Wondexpertiseteam Zuid-Hollandse Eilanden
+Careyn Torenhoeve
+Careyn Verpleging en Verzorging thuis Harmelen
+Careyn Voeding en Dieet
+Careyn Warande
+Careyn Weddesteyn
+Careyn Weddesteyn, Geriatrische revalidatie
+Careyn Woerdblok
+Careyn Zes Rozen
+Careyn Zorgcentrum
+Careyn, Thuiszorg en Wijkverpleging, Palliatief team Zuid-Hollandse Eilanden
+Careyn, Thuiszorg en Wijkverpleging, Specialistisch team Zuid-Hollandse Eilanden
+CareynThuiszorg en Wijkverpleging Ronde Venen, Mijdrecht, Vinkeveen, Wilnis, Abcoude
+Compas Huisartsenpraktijk
+Daan & Van Ardenne Huisartsen
+De Clavers Goes-Noord Erasmuspark
+De Hoogstraat
+De Hoogstraat Revalidatie
+De Kind- en Jeugdspecialist
+De Koperhorst
+De Rijnhoven, Antoniushof zorg thuis, Wijkverpleging en verzorging
+De SeT Residentie
+De Zorggroep, Woonzorgcentrum Bosscherhof
+Dialyseafdeling Alrijne Ziekenhuis Alphen aan den Rijn
+Dialyseafdeling Alrijne Ziekenhuis Leiderdorp
+Dialyseafdeling Canisius-Wilhelmina Ziekenhuis
+Dialyseafdeling Erasmus MC
+Dialyseafdeling HMC Antoniushove
+Dialysecentrum Canisius-Wilhelmina Ziekenhuis Druten
+Diëtheek Woerden, St. Antonius Ziekenhuis
+Erasmus Care - Tandartsen Mariastraat
+Erasmus MC - Kanker Instituut
+Erasmus MC - Poliklinische apotheek
+Erasmus Psy
+Evean Erasmushuis
+Fokusproject Utrecht Antonius
+Fysio Steins Hoogstraat
+Fysio-Oedeem-Manuele therapie Janssen Vaals
+Fysiohuis, vestiging Wilhelminastraat
+Fysiotherapie Antoniusveld
+Fysiotherapie De Clavers Wilhelminadorp
+Fysiotherapie De Jong, Wilhelminakade
+Fysiotherapie Janneke Janssen
+Fysiotherapie Joke Janssen
+Fysiotherapie Wilhelminapark
+Fysiotherapiepraktijk M.J.C.C. Janssen
+Fysiotherapiepraktijk Van den Berg, Alphen aan den Rijn, Alrijne Ziekenhuis
+GGz Centraal, Zon en Schild
+GOED Ridderkerk, Huisartsenpraktijk M.A. Janssen
+Gezondheidscentrum Wilhelminapier
+Hallux podotherapie Den Bosch Antonius
+Hoogstraat
+Huid- en Oedeemtherapie Gerdy Janssen
+Huisartsen WateringseVeld - Schuring, Janssens, Van Beek & Veldhoven
+Huisartsen Wolters en Janssen
+Huisartsengroep De Poort, Praktijk K.J. Janssen-van Hemmen
+Huisartsenpraktijk A.G.M. Janssen
+Huisartsenpraktijk Antoniusveld
+Huisartsenpraktijk De Hoogstraat
+Huisartsenpraktijk Janssen Breda
+Huisartsenpraktijk Janssen Eindhoven
+IJsselheem, Zorghuis Wilhelmina van Sonsbeeck
+Innofeet Kampen, Isala poli
+Innofeet Zwolle, Isala
+Isala Fertiliteitscentrum
+Isala Klinieken
+Isala Meppel - HD thuishemodialyse
+Isala Zwolle - hemodialyse, peritoneale dialyse en thuisdialyse
+Janssen & Partners
+Janssen Huidtherapie
+Janssen Manuele Therapie E.S.
+Janssen Podologie
+Janssen Psychiatrie
+Janssen Psychologie & Cognitieve Gedragstherapie
+Janssen van Dijke Fysiotherapeuten
+Laurens Antonius Binnenweg
+Livit Orthopedie Rotterdam, Servicepunt Erasmus MC Zorgwinkel
+Logopediepraktijk Canisius
+Logopediepraktijk M.C. Heidema en H.L. Janssen
+Lunet zorg, woonlocatie Hoogstraat
+Marjon Janssen Wervelendespieren
+Mea Fysio Leeuwarden - Erasmus
+Meander
+Meander Medisch Centrum, afdeling Trombosedienst
+Medisch Centrum Aalst, huisarts Janssen
+Medisch Centrum Antoniusveld, Diëtisten
+Medisch Centrum Nieuwenhagen, Huisartsenpraktijk Janssen
+Medisch Centrum Sint Anna, Huisartsenpraktijk Janssen en Van Loon
+Mijzo - Sint Antonius
+Mondzorg Erasmus
+Noorderbreedte, Woonzorgcentrum Erasmus
+O.M.G. Janssen, tandarts-implantoloog
+ORO - Wilhelminastraat
+Oktober
+Orthodontiepraktijk Wilhelminapark
+P.P.C. Janssen, zelfstandig bedrijfsarts
+Pameijer, werkplek Atelier Juffrouw Janssen
+Paul Janssen, arts acupunctuur
+Podologiepraktijk Janssen Buitensport
+Praktijk Orthomanuele geneeskunde R. Janssen
+Praktijk voor Logopedie Janssen
+Praktijk voor Psychiatrie en Psychotherapie A.M. Janssen
+Praktijk voor psychotherapie Caroline Janssen
+Psychiatrische afdeling van het Canisius-Wilhelmina Ziekenhuis
+Psychologen Wilhelmina
+Pysychologiepraktijk G.A.J. Janssen
+Radar Woonvorm Wilhelminasingel
+Reinaerde, Serviceteam Koningin Wilhelminalaan
+SVRZ ZorgThuis Goes - Erasmuspark
+Sint Annaklooster, Antonius
+St Antonius Apotheek Nieuwegein
+St Antonius Apotheek Utrecht
+St Antonius Dialysecentrum
+St Antonius Spatadercentrum
+St Antonius SportsClinic
+St. Antonius Cardicare
+Surplus, woonzorgcentrum Antonius Abt
+Tandartsen- en verwijspraktijk Wilhelminapark
+Tandartsenpraktijk Janssen
+Tandartsenpraktijk S. Lodder en A. Lodder-Janssen
+Tandartsenpraktijk Wilhelminapark
+Tandartsenpraktijk Wilhelminaweg, Praktijk voor Orale Implantologie Veluwezoom
+Tandartspraktijk Janssen en Linssen
+Tandartspraktijk Janssen&Janssen
+Tandheelkundig Centrum Wilhelminapier
+Thuiszorgwinkel Medipoint | Careyn | Spijkenisse
+Thuiszorgwinkel Medipoint | Careyn | Vlaardingen
+Trombosedienst Isala
+Trombosedienst Isala Meppel
+UMC Utrecht, Dialyseafdeling
+UMC Utrecht, Wilhelmina Kinderziekenhuis, afdeling Kinderperitoneale dialyse
+Verzorgingshuis De Koperhorst
+Voetencentrum Wender, Beuningen Wilhelminalaan
+Voetencentrum Wender, Rotterdam Wilhelminapier
+Wijk en Janssen Psychologisch Advies
+Wilhelmina Apotheek
+Wilhelmina Ziekenhuis Assen (WZA)
+WilhelminaOord
+Zon en Schild
+ZorgSaam Antonius
+Zorgboerderij Wilhelminahoeve
+Zorggroep Elde Maasduinen, zorglocatie Antoniushof
+Zorggroep Solis Paramedisch Centrum PW Janssen, diëtetiek
+Zorggroep Solis Paramedisch Centrum PW Janssen, ergotherapie
+Zorggroep Solis Paramedisch Centrum PW Janssen, fysiotherapie
+Zorggroep Solis Paramedisch Centrum PW Janssen, logopedie
+Zorggroep Solis, Geriatrische Revalidatie Zorg PW Janssen
+Zorggroep Solis, Verpleeghuis PW Janssen
+Zorgwinkel Erasmus MC
diff --git a/tests/regression/data/lookup/src/institutions/lst_healthcare_institution/transform.json b/tests/regression/data/lookup/src/institutions/lst_healthcare_institution/transform.json
new file mode 100644
index 00000000..a56ed996
--- /dev/null
+++ b/tests/regression/data/lookup/src/institutions/lst_healthcare_institution/transform.json
@@ -0,0 +1,51 @@
+{
+ "transforms": {
+ "instelling": {
+ "Huisartsenpraktijk": [
+ "Huisartsenpraktijk",
+ "huisartsenpraktijk",
+ "Huisartspraktijk",
+ "huisartspraktijk"
+ ]
+ },
+ "prefix": {
+ "\\bDe\\b": [
+ "De",
+ "de"
+ ]
+ },
+ "punct": {
+ "\\.": [
+ ".",
+ ""
+ ],
+ "-": [
+ "-",
+ "",
+ " "
+ ],
+ " & ": [
+ " & ",
+ " en "
+ ]
+ },
+ "spell": {
+ "y": [
+ "y",
+ "ij"
+ ],
+ "Y": [
+ "Y",
+ "IJ"
+ ],
+ "ij": [
+ "ij",
+ "y"
+ ],
+ "IJ": [
+ "IJ",
+ "Y"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/regression/data/lookup/src/institutions/lst_hospital/items.txt b/tests/regression/data/lookup/src/institutions/lst_hospital/items.txt
new file mode 100644
index 00000000..829115f9
--- /dev/null
+++ b/tests/regression/data/lookup/src/institutions/lst_hospital/items.txt
@@ -0,0 +1,39 @@
+Academisch Ziekenhuis Amsterdam
+Academisch Ziekenhuis Groningen
+Academisch Ziekenhuis Leiden
+Academisch Ziekenhuis Maastricht
+Academisch Ziekenhuis Nijmegen
+Academisch Ziekenhuis Rotterdam
+Academisch Ziekenhuis Utrecht
+Alrijne
+Alrijne Ziekenhuis
+Antonius
+Antonius Ziekenhuis
+Canisius-Wilhelmina Ziekenhuis
+Centraal Militair Hospitaal
+Centraal Militair Hospitaal Utrecht
+De Stadsmaten
+Erasmus
+Erasmus Medisch Centrum
+Isala
+Isala Ziekenhuis
+Meander
+Meander Medisch Centrum
+Militair Hospitaal
+Máxima
+P.W. Janssen Ziekenhuis
+Prinses Máxima Centrum
+Sint Antonius Stichting
+Sint Antonius Ziekenhuis
+Sint Vincentius Ziekenhuis
+Stads en Academisch Ziekenhuis
+Universitair Medisch Centrum
+Universitair Medisch Centrum Utrecht
+Weesperplein Ziekenhuis
+Wilhelmina Gasthuis
+Wilhelmina Kinder
+Wilhelmina Kinder Ziekenhuis
+Wilhelmina Ziekenhuis
+Zaans Medisch Centrum
+het Lange Land
+het Lange Land Ziekenhuis
diff --git a/tests/regression/data/lookup/src/institutions/lst_hospital/transform.json b/tests/regression/data/lookup/src/institutions/lst_hospital/transform.json
new file mode 100644
index 00000000..dca99a2b
--- /dev/null
+++ b/tests/regression/data/lookup/src/institutions/lst_hospital/transform.json
@@ -0,0 +1,101 @@
+{
+ "transforms": {
+ "zkh": {
+ " (Ziekenhuis|Gasthuis|Kliniek)": [
+ " Ziekenhuis",
+ " Ziekenhuizen",
+ " Zkh",
+ " Zkh.",
+ " Gasthuis",
+ " Kliniek",
+ " Klinieken",
+ " ziekenhuis",
+ " ziekenhuizen",
+ " zkh",
+ " zkh.",
+ " gasthuis",
+ " kliniek",
+ " klinieken",
+ "ziekenhuis",
+ "ziekenhuizen",
+ "zkh",
+ "zkh.",
+ "gasthuis",
+ "kliniek",
+ "klinieken"
+ ],
+ "^(Ziekenhuis|Gasthuis|Kliniek)": [
+ "Ziekenhuis",
+ "Zkh",
+ "Zkh.",
+ "Gasthuis",
+ "Kliniek",
+ "ziekenhuis",
+ "zkh",
+ "zkh.",
+ "gasthuis",
+ "kliniek"
+ ],
+ "Medisch Centrum": [
+ "Medisch Centrum",
+ "MC"
+ ]
+ },
+ "zkh_2": {
+ "Universitair Medisch Centrum": [
+ "Universitair Medisch Centrum",
+ "UMC"
+ ]
+ },
+ "prefix": {
+ "\\bhet\\b": [
+ "Het",
+ "het",
+ "'T",
+ "'t",
+ "`T",
+ "`t",
+ "T",
+ "t",
+ ""
+ ],
+ "\\bSint\\b": [
+ "Sint",
+ "sint",
+ "St.",
+ "st.",
+ "st",
+ ""
+ ]
+ },
+ "punct": {
+ "\\.": [
+ ".",
+ ""
+ ],
+ "-": [
+ "-",
+ "",
+ " "
+ ]
+ },
+ "spelling": {
+ "y": [
+ "y",
+ "ij"
+ ],
+ "Y": [
+ "Y",
+ "IJ"
+ ],
+ "ij": [
+ "ij",
+ "y"
+ ],
+ "IJ": [
+ "IJ",
+ "Y"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/regression/data/lookup/src/institutions/lst_hospital_abbr/items.txt b/tests/regression/data/lookup/src/institutions/lst_hospital_abbr/items.txt
new file mode 100644
index 00000000..2c06e0be
--- /dev/null
+++ b/tests/regression/data/lookup/src/institutions/lst_hospital_abbr/items.txt
@@ -0,0 +1,21 @@
+AMC
+AZU
+CMH
+Diak
+EKZ
+EMC
+ETZ
+JBZ
+LUMC
+MCL
+MMC
+MUMC
+PMC
+UMC
+UMCG
+UMCN
+UMCU
+VMC
+VUMC
+WKZ
+ZMC
diff --git a/tests/regression/data/lookup/src/locations/lst_placename/lst_municipality/items.txt b/tests/regression/data/lookup/src/locations/lst_placename/lst_municipality/items.txt
new file mode 100644
index 00000000..dc5fa599
--- /dev/null
+++ b/tests/regression/data/lookup/src/locations/lst_placename/lst_municipality/items.txt
@@ -0,0 +1,8 @@
+Alphen-Chaam
+Goeree-Overflakkee
+Hengelo
+Molenlanden
+Nederweert
+Sluis
+Smallingerland
+Utrecht
diff --git a/tests/regression/data/lookup/src/locations/lst_placename/lst_province/items.txt b/tests/regression/data/lookup/src/locations/lst_placename/lst_province/items.txt
new file mode 100644
index 00000000..deebd57e
--- /dev/null
+++ b/tests/regression/data/lookup/src/locations/lst_placename/lst_province/items.txt
@@ -0,0 +1,3 @@
+Friesland
+Noord-Brabant
+Utrecht
diff --git a/tests/regression/data/lookup/src/locations/lst_placename/lst_region/items.txt b/tests/regression/data/lookup/src/locations/lst_placename/lst_region/items.txt
new file mode 100644
index 00000000..df64de53
--- /dev/null
+++ b/tests/regression/data/lookup/src/locations/lst_placename/lst_region/items.txt
@@ -0,0 +1,9 @@
+Eemvallei
+Friese Wouden
+Goeree-Overflakkee
+Noordenveld
+Oostergo
+Overflakkee
+Prins Alexanderpolder
+Rijk Van Nijmegen
+Rottum
diff --git a/tests/regression/data/lookup/src/locations/lst_placename/lst_residence/exceptions.txt b/tests/regression/data/lookup/src/locations/lst_placename/lst_residence/exceptions.txt
new file mode 100644
index 00000000..b8d3363e
--- /dev/null
+++ b/tests/regression/data/lookup/src/locations/lst_placename/lst_residence/exceptions.txt
@@ -0,0 +1,7 @@
+Bosch
+Brand
+Ie
+Leeuwen
+Raar
+Smal
+Zittend
diff --git a/tests/regression/data/lookup/src/locations/lst_placename/lst_residence/items.txt b/tests/regression/data/lookup/src/locations/lst_placename/lst_residence/items.txt
new file mode 100644
index 00000000..a5766a3e
--- /dev/null
+++ b/tests/regression/data/lookup/src/locations/lst_placename/lst_residence/items.txt
@@ -0,0 +1,29 @@
+Alphen
+Bolsward
+Bontekoe
+Boornbergum
+Borkel En Schaft
+Bosch
+Bosch En Duin
+Boven-Leeuwen
+Bovenkerk
+Bovenkerk (Nh)
+Bovenkerk (Zh)
+Chaam
+Drieënhuizen
+Felland
+Friesland
+Heer
+Hoogstraat
+Hoogstraat (Li)
+Hoogstraat (Nb)
+Janssenstichting
+Leeuwen
+Nederweert
+Noord
+Sint Antoniusbank
+Sluis
+Utrecht
+Wilhelminadorp
+Wilhelminaoord
+Zuidgeest
diff --git a/tests/regression/data/lookup/src/locations/lst_placename/transform.json b/tests/regression/data/lookup/src/locations/lst_placename/transform.json
new file mode 100644
index 00000000..70239c9d
--- /dev/null
+++ b/tests/regression/data/lookup/src/locations/lst_placename/transform.json
@@ -0,0 +1,189 @@
+{
+ "transforms": {
+ "prefix": {
+ "\\bhet\\b": [
+ "Het",
+ "het",
+ "'T",
+ "'t",
+ "`T",
+ "`t",
+ "T",
+ "t"
+ ],
+ "\\bSint\\b": [
+ "Sint",
+ "sint",
+ "St.",
+ "st."
+ ],
+ "\\bit\\b": [
+ "It",
+ "it",
+ "Het",
+ "het",
+ "'T",
+ "'t",
+ "`T",
+ "`t",
+ "T",
+ "t"
+ ]
+ },
+ "prop": {
+ "(\\b|^)Aan\\b": [
+ "Aan",
+ "aan"
+ ],
+ "(\\b|^)Bij\\b": [
+ "Bij",
+ "bij"
+ ],
+ "(\\b|^)De\\b": [
+ "De",
+ "de"
+ ],
+ "(\\b|^)Den\\b": [
+ "Den",
+ "den"
+ ],
+ "(\\b|^)En\\b": [
+ "En",
+ "en"
+ ],
+ "(\\b|^)Het\\b": [
+ "Het",
+ "het",
+ "'T",
+ "'t",
+ "`T",
+ "`t",
+ "T",
+ "t"
+ ],
+ "(\\b|^)In\\b": [
+ "In",
+ "in"
+ ],
+ "(\\b|^)Oan\\b": [
+ "Oan",
+ "oan"
+ ],
+ "(\\b|^)Of\\b": [
+ "Of",
+ "of"
+ ],
+ "(\\b|^)Op\\b": [
+ "Op",
+ "op"
+ ],
+ "(\\b|^)Over\\b": [
+ "Over",
+ "over"
+ ],
+ "(\\b|^)'S\\b": [
+ "'S",
+ "'s"
+ ],
+ "(\\b|^)Ter\\b": [
+ "Ter",
+ "ter"
+ ],
+ "(\\b|^)Van\\b": [
+ "Van",
+ "van",
+ "v.",
+ "V."
+ ]
+ },
+ "province": {
+ "(?<=\\()Fr(?=\\))": [
+ "Fr",
+ "FR",
+ "Frl",
+ "FRL",
+ "F"
+ ],
+ "(?<=\\()Gr(?=\\))": [
+ "Gr",
+ "GR",
+ "Gn",
+ "GN",
+ "G"
+ ],
+ "(?<=\\()Dr(?=\\))": [
+ "Dr",
+ "DR",
+ "Dn",
+ "DN",
+ "D"
+ ],
+ "(?<=\\()Ov(?=\\))": [
+ "Ov",
+ "OV",
+ "O"
+ ],
+ "(?<=\\()Nh(?=\\))": [
+ "Nh",
+ "NH"
+ ],
+ "(?<=\\()Ut(?=\\))": [
+ "Ut",
+ "UT",
+ "U"
+ ],
+ "(?<=\\()Gld(?=\\))": [
+ "Gld",
+ "GLD",
+ "G"
+ ],
+ "(?<=\\()Li(?=\\))": [
+ "Li",
+ "LI",
+ "L"
+ ],
+ "(?<=\\()Nb(?=\\))": [
+ "Nb",
+ "NB"
+ ],
+ "(?<=\\()Zh(?=\\))": [
+ "Zh",
+ "ZH"
+ ],
+ "(?<=\\()Ze(?=\\))": [
+ "Ze",
+ "ZE",
+ "Z"
+ ]
+ },
+ "punct": {
+ "\\.": [
+ ".",
+ ""
+ ],
+ "-": [
+ "-",
+ "",
+ " "
+ ]
+ },
+ "spell": {
+ "y": [
+ "y",
+ "ij"
+ ],
+ "Y": [
+ "Y",
+ "IJ"
+ ],
+ "ij": [
+ "ij",
+ "y"
+ ],
+ "IJ": [
+ "IJ",
+ "Y"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/regression/data/lookup/src/locations/lst_street/exceptions.txt b/tests/regression/data/lookup/src/locations/lst_street/exceptions.txt
new file mode 100644
index 00000000..8cfa7df6
--- /dev/null
+++ b/tests/regression/data/lookup/src/locations/lst_street/exceptions.txt
@@ -0,0 +1,7 @@
+Berg
+Bosch
+Generaal
+Noord
+Oost
+Segment
+Vrij
diff --git a/tests/regression/data/lookup/src/locations/lst_street/items.txt b/tests/regression/data/lookup/src/locations/lst_street/items.txt
new file mode 100644
index 00000000..053f192b
--- /dev/null
+++ b/tests/regression/data/lookup/src/locations/lst_street/items.txt
@@ -0,0 +1,236 @@
+Abraham Martinus Sorgstraat
+Aertjanssenstraat
+Antonius Bieleveltstraat
+Antonius Deusinglaan
+Antonius Heggelaan
+Antonius Matthaeuslaan
+Antonius O.H. Tellegenlaan
+Antonius Struijckenstraat
+Antonius van Gilsweg
+Antonius van Lieropstraat
+Antoniushof
+Antoniuslaan
+Antoniusmeule
+Antoniuspark
+Antoniusplein
+Antoniusschutlaan
+Antoniusstede
+Antoniusstraat
+Antoniusweg
+Arnold Janssenlaan
+Arnoldus Janssenstraat
+August Janssenweg
+Bisschop Janssensstraat
+Blauw Druifje
+Bosch
+Brabant
+Burgemeester Canisiusstraat
+Burgemeester Erasmusstraat
+Burgemeester Janssen van Sonlaan
+Burgemeester Janssenring
+Burgemeester Janssensstraat
+Burgemeester Janssenstraat
+Canisiushof
+Canisiusstraat
+Christina Weidner-Slorsstraat
+Cor Janssenstraat
+Desiderius Erasmusstraat
+Dick de Hoogstraat
+Directeur Janssenstraat
+Doctor Janssenslaan
+Doctor Janssenstraat
+Doctor L.J.F. Janssenstraat
+Doctor Paul Janssenweg
+Dokter Janssenplein
+Duin
+Erasmusdomein
+Erasmusflat
+Erasmusgracht
+Erasmushage
+Erasmushof
+Erasmuslaan
+Erasmuspad
+Erasmuspark
+Erasmusplaats
+Erasmusplein
+Erasmussingel
+Erasmusstate
+Erasmusstraat
+Erasmustuin
+Erasmusweg
+Florastraat
+Gerrit Hoogstraatenlaan
+Goeree
+Goossen Janssenstraat
+Groot Hoogstraat
+Grote Hoogstraat
+Harrie Janssenstraat
+Hoogstraat
+Hoogstraat Zijpad
+Hoogstraatje
+IJsweg
+Jan Janssenpad
+Janssen & Fritsenplein
+Janssen van Raaystraat
+Janssen-Dingsweg
+Janssen-Stichting
+Janssenlaan
+Janssensstraat
+Janssenstichting
+Janssenstraat
+Janssenweg
+Joachim Kleinsorgstraat
+Juliana Wilhelmina van der Noordaatuin
+Juliana Wilhelmina van der Noordatuin
+Kapelaan Janssenstraat
+Kleine Hoogstraat
+Koningin Wilhelmina Boulevard
+Koningin Wilhelminahaven NZ
+Koningin Wilhelminahaven ZOZ
+Koningin Wilhelminahaven ZZ
+Koningin Wilhelminahof
+Koningin Wilhelminakade
+Koningin Wilhelminalaan
+Koningin Wilhelminapark
+Koningin Wilhelminaplein
+Koningin Wilhelminasingel
+Koningin Wilhelminastraa
+Koningin Wilhelminastraat
+Koningin Wilhelminaweg
+Koningin-Wilhelminalaan
+Korte Hoogstraat
+Laan
+Laantje achter Wilhelmina
+Lange Landen
+Lange Landweg
+Laurent Janssensstraat
+Loristraat
+Lorkstraat
+Louisalaan
+Magda Janssenslaan
+Magda Janssenspad
+Magda Janssensstraat
+Magda Janssenstraat
+Maria Montessoristraat
+Meander
+Meester Janssenpad
+Meester Janssenweg
+Monseigneur F. Janssenstraat
+Montessoristraat
+Nieuwe Hoogstraat
+Noord
+Oude
+Oude Hoogstraat
+Overflakkee
+P. W. Janssenweg
+P.W. Janssenlaan
+Park Hoogstraaten
+Pastoor Antoniusstraat
+Pastoor Janssenhof
+Pastoor Janssenlaan
+Pastoor Janssens van Calmthoutweg
+Pastoor Janssensplantsoen
+Pastoor Janssensstraat
+Pastoor Janssenstraat
+Pastoor Janssensweg
+Pastoor Leonardus Canisiusstraat
+Pastoor-Janssenstraat
+Pater Arnold Janssenpad
+Pater Janssenstraat
+Peter Janssenweg
+Petrus Canisiusstraat
+Pierre Janssenstraat
+Pieter
+Pieter Verhoogstraat
+Pieter de Hoogstraat
+Pr Wilhelminastraat
+Prinses Ariane Wilhelminapad
+Prinses Wilhelminalaan
+Prinses Wilhelminasingel
+Prinses Wilhelminastraat
+Prinses Wilhelminaweg
+Professor Doctor J.C. Schoutelaan
+Professor Loréstraat
+Protterstrjitte
+Secretaris Janssenstraat
+Sint Antonius Abt Hof
+Sint Antoniusbank
+Sint Antoniusdreef
+Sint Antoniusgilde
+Sint Antoniushof
+Sint Antoniuslaan
+Sint Antoniuspad
+Sint Antoniuspark
+Sint Antoniusplein
+Sint Antoniusstraat
+Sint Antoniusveldweg
+Sint Antoniusweg
+Sint Canisiussingel
+Sint Petrus Canisiuslaan
+Sint-Antoniusstraat
+Slot
+Sluis
+Smallingerland
+To Janssenstraat
+Verlengde Hoogstraat
+Verlengde Wilhelminalaan
+Verlengde Wilhelminastraat
+Visser
+Vlietsorgstraat
+Wethouder Janssenpad
+Wilhelmina Bladergroenstraat
+Wilhelmina Bladergroenweg
+Wilhelmina Blombergplein
+Wilhelmina Druckererf
+Wilhelmina Druckerhoeve
+Wilhelmina Druckerhof
+Wilhelmina Druckerlaan
+Wilhelmina Druckerpad
+Wilhelmina Druckerstraat
+Wilhelmina Druckertuin
+Wilhelmina Druckerweg
+Wilhelmina Geevestraat
+Wilhelmina Hofman-Pootstraat
+Wilhelmina Nijhoffstraat
+Wilhelmina Sangersstraat
+Wilhelmina Schweickhardtplein
+Wilhelmina Smitstraat
+Wilhelmina Voorwindenkade
+Wilhelmina van Essenlaan
+Wilhelmina van Haeftendreef
+Wilhelmina van Pruisenlaan
+Wilhelmina van Pruisenweg
+Wilhelminadijk
+Wilhelminadreef
+Wilhelminadwarsweg
+Wilhelminahoeve
+Wilhelminahof
+Wilhelminahofweg
+Wilhelminakade
+Wilhelminakanaal Noord
+Wilhelminakanaal Oost
+Wilhelminakanaal Zuid
+Wilhelminakanaalstraat
+Wilhelminalaan
+Wilhelminapark
+Wilhelminaparkflat
+Wilhelminapassage
+Wilhelminaplantsoen
+Wilhelminaplein
+Wilhelminapolderweg
+Wilhelminasingel
+Wilhelminasluis
+Wilhelminastichting
+Wilhelminastraat
+Wilhelminastrjitte
+Wilhelminatorenvoetpad
+Wilhelminaveld
+Wilhelminaweg
+Wilhelminawijk
+Wilhelminawyk
+Wilhelminazijstraat
+Willem Loréstraat
+Zuidgeest
+de Hoogstraat
+de Lormstraat
+het Lange Land
diff --git a/tests/regression/data/lookup/src/locations/lst_street/streets_bag.txt b/tests/regression/data/lookup/src/locations/lst_street/streets_bag.txt
new file mode 100644
index 00000000..db02c7b3
--- /dev/null
+++ b/tests/regression/data/lookup/src/locations/lst_street/streets_bag.txt
@@ -0,0 +1,255 @@
+Abraham Martinus Sorgstraat
+Aertjanssenstraat
+Antonius Bieleveltstraat
+Antonius Deusinglaan
+Antonius Heggelaan
+Antonius Matthaeuslaan
+Antonius O.H. Tellegenlaan
+Antonius Struijckenstraat
+Antonius van Gilsweg
+Antonius van Lieropstraat
+Antoniushof
+Antoniuslaan
+Antoniusmeule
+Antoniuspark
+Antoniusplein
+Antoniusschutlaan
+Antoniusstede
+Antoniusstraat
+Antoniusweg
+Arnold Janssenlaan
+Arnoldus Janssenstraat
+August Janssenweg
+Bisschop Janssensstraat
+Blauw druifje
+Bosch
+Brabant
+Burg Canisiusstraat
+Burg Janssen v Sonln
+Burg Janssenstraat
+Burg. Janssenring
+Burg. Janssensstraat
+Burg.Janssensstraat
+Burg.Janssenstraat
+Burgemeester Erasmusstraat
+Burgemeester Janssenstraat
+Canisiushof
+Canisiusstraat
+Christina Weidner-Slorsstraat
+Cor Janssenstraat
+De Hoogstraat
+De Lormstraat
+Desiderius Erasmusstraat
+Dick de Hoogstraat
+Directeur Janssenstraat
+Dokter Janssenplein
+Dr Janssenstraat
+Dr. Janssenslaan
+Dr. L.J.F. Janssenstraat
+Dr. Paul Janssenweg
+Duin
+Erasmusdomein
+Erasmusflat
+Erasmusgracht
+Erasmushage
+Erasmushof
+Erasmuslaan
+Erasmuspad
+Erasmuspark
+Erasmusplaats
+Erasmusplein
+Erasmussingel
+Erasmusstate
+Erasmusstraat
+Erasmustuin
+Erasmusweg
+Florastraat
+Gerrit Hoogstraatenlaan
+Goeree
+Goossen Janssenstraat
+Groot Hoogstraat
+Grote Hoogstraat
+Harrie Janssenstraat
+Het Lange Land
+Hoogstraat
+Hoogstraat Zijpad
+Hoogstraatje
+IJsweg
+Jan Janssenpad
+Janssen & Fritsenplein
+Janssen van Raaystraat
+Janssen-Dingsweg
+Janssen-Stichting
+Janssenlaan
+Janssensstraat
+Janssenstichting
+Janssenstraat
+Janssenweg
+Joachim Kleinsorgstraat
+Juliana Wilhelmina van der Noordaatuin
+Juliana Wilhelmina van der Noordatuin
+Kapelaan Janssenstraat
+Kleine Hoogstraat
+Kon Wilhelminastraat
+Kon Wilhelminaweg
+Kon. Wilhelminastraat
+Kon.Wilhelminalaan
+Koningin Wilhelmina Boulevard
+Koningin Wilhelminahaven NZ
+Koningin Wilhelminahaven ZOZ
+Koningin Wilhelminahaven ZZ
+Koningin Wilhelminahof
+Koningin Wilhelminakade
+Koningin Wilhelminalaan
+Koningin Wilhelminapark
+Koningin Wilhelminaplein
+Koningin Wilhelminasingel
+Koningin Wilhelminastr
+Koningin Wilhelminastraa
+Koningin Wilhelminastraat
+Koningin Wilhelminaweg
+Koningin-Wilhelminalaan
+Korte Hoogstraat
+Laan
+Laantje achter Wilhelmina
+Lange Landen
+Lange Landweg
+Laurent Janssensstraat
+Loristraat
+Lorkstraat
+Louisalaan
+Magda Janssenslaan
+Magda Janssenspad
+Magda Janssensstraat
+Magda Janssenstraat
+Maria Montessoristraat
+Meander
+Meester Janssenpad
+Meester Janssenweg
+Mgr. F. Janssenstraat
+Montessoristraat
+Nieuwe Hoogstraat
+Noord
+Oude
+Oude Hoogstraat
+Overflakkee
+P W Janssenweg
+P.W. Janssenlaan
+Park Hoogstraaten
+Pastoor Antoniusstraat
+Pastoor Janssenhof
+Pastoor Janssenlaan
+Pastoor Janssens van Calmthoutweg
+Pastoor Janssensplantsoen
+Pastoor Janssensstraat
+Pastoor Janssenstraat
+Pastoor Janssensweg
+Pastoor Leonardus Canisiusstraat
+Pastoor-Janssenstraat
+Pater Arnold Janssenpad
+Pater Janssenstraat
+Peter Janssenweg
+Petrus Canisiusstraat
+Pierre Janssenstraat
+Pieter
+Pieter Verhoogstraat
+Pieter de Hoogstraat
+Pr Wilhelminastraat
+Prinses Ariane Wilhelminapad
+Prinses Wilhelminalaan
+Prinses Wilhelminasingel
+Prinses Wilhelminastraat
+Prinses Wilhelminaweg
+Prof. Dr. J.C. Schoutelaan
+Professor Loréstraat
+Protterstrjitte
+Secr. Janssenstraat
+Secretaris Janssenstraat
+Sint Antonius Abt Hof
+Sint Antoniusbank
+Sint Antoniusdreef
+Sint Antoniusgilde
+Sint Antoniushof
+Sint Antoniuslaan
+Sint Antoniusplein
+Sint Antoniusstraat
+Sint Antoniusweg
+Sint-Antoniusstraat
+Slot
+Sluis
+Smallingerland
+St Antoniusstraat
+St Antoniusweg
+St Petrus Canisiuslaan
+St. Antoniuslaan
+St. Antoniuspad
+St. Antoniuspark
+St. Antoniusplein
+St. Antoniusstraat
+St. Antoniusveldweg
+St. Antoniusweg
+St. Canisiussingel
+St.-Antoniusstraat
+St.Antoniusstraat
+St.Antoniusweg
+To Janssenstraat
+Verlengde Hoogstraat
+Verlengde Wilhelminalaan
+Verlengde Wilhelminastraat
+Visser
+Vlietsorgstraat
+Wethouder Janssenpad
+Wilhelmina Bladergroenstraat
+Wilhelmina Bladergroenweg
+Wilhelmina Blombergplein
+Wilhelmina Druckererf
+Wilhelmina Druckerhoeve
+Wilhelmina Druckerhof
+Wilhelmina Druckerlaan
+Wilhelmina Druckerpad
+Wilhelmina Druckerstraat
+Wilhelmina Druckertuin
+Wilhelmina Druckerweg
+Wilhelmina Geevestraat
+Wilhelmina Hofman-Pootstraat
+Wilhelmina Nijhoffstraat
+Wilhelmina Sangersstraat
+Wilhelmina Schweickhardtplein
+Wilhelmina Smitstraat
+Wilhelmina Voorwindenkade
+Wilhelmina van Essenlaan
+Wilhelmina van Haeftendreef
+Wilhelmina van Pruisenlaan
+Wilhelmina van Pruisenweg
+Wilhelminadijk
+Wilhelminadreef
+Wilhelminadwarsweg
+Wilhelminahoeve
+Wilhelminahof
+Wilhelminahofweg
+Wilhelminakade
+Wilhelminakanaal Noord
+Wilhelminakanaal Oost
+Wilhelminakanaal Zuid
+Wilhelminakanaalstraat
+Wilhelminalaan
+Wilhelminapark
+Wilhelminaparkflat
+Wilhelminapassage
+Wilhelminaplantsoen
+Wilhelminaplein
+Wilhelminapolderweg
+Wilhelminasingel
+Wilhelminasluis
+Wilhelminastichting
+Wilhelminastraat
+Wilhelminastrjitte
+Wilhelminatorenvoetpad
+Wilhelminaveld
+Wilhelminaweg
+Wilhelminawijk
+Wilhelminawyk
+Wilhelminazijstraat
+Willem Loréstraat
+Zuidgeest
+het Lange Land
diff --git a/tests/regression/data/lookup/src/locations/lst_street/transform.json b/tests/regression/data/lookup/src/locations/lst_street/transform.json
new file mode 100644
index 00000000..44cca14c
--- /dev/null
+++ b/tests/regression/data/lookup/src/locations/lst_street/transform.json
@@ -0,0 +1,712 @@
+{
+ "transforms": {
+ "prefix": {
+ "\\bAbraham\\b": [
+ "Abraham",
+ "Abr.",
+ "abr."
+ ],
+ "\\bAdmiraal\\b": [
+ "Admiraal",
+ "Adm.",
+ "adm."
+ ],
+ "\\bAlbert\\b": [
+ "Albert",
+ "Alb.",
+ "alb."
+ ],
+ "\\bBurgemeester\\b": [
+ "Burgemeester",
+ "Burg.",
+ "burg."
+ ],
+ "\\bChris\\b": [
+ "Chris",
+ "Chr.",
+ "chr."
+ ],
+ "\\bCommissaris\\b": [
+ "Commissaris",
+ "Comm.",
+ "comm."
+ ],
+ "\\bDominee\\b": [
+ "Dominee",
+ "Ds.",
+ "ds."
+ ],
+ "\\bDoctor\\b": [
+ "Doctor",
+ "Dr.",
+ "dr."
+ ],
+ "\\bDokter\\b": [
+ "Dokter",
+ "Dr.",
+ "dr."
+ ],
+ "\\bDoctorandus\\b": [
+ "Doctorandus",
+ "Drs.",
+ "drs."
+ ],
+ "\\bFamilie\\b": [
+ "Familie",
+ "Fam.",
+ "fam."
+ ],
+ "\\bGebroeders\\b": [
+ "Gebroeders",
+ "Gebr.",
+ "gebr.",
+ "Gebrs.",
+ "gebrs."
+ ],
+ "\\bGeneraal\\b": [
+ "Generaal",
+ "Gen.",
+ "gen."
+ ],
+ "\\bHertog\\b": [
+ "Hertog",
+ "Hert.",
+ "hert."
+ ],
+ "\\bIngenieur\\b": [
+ "Ingenieur",
+ "Ir.",
+ "ir.",
+ "Ing.",
+ "ing."
+ ],
+ "\\bJacobus\\b": [
+ "Jacobus",
+ "Jac.",
+ "jac."
+ ],
+ "\\bJacob\\b": [
+ "Jacobus",
+ "Jac.",
+ "jac."
+ ],
+ "\\bJacqueline\\b": [
+ "Jacqueline",
+ "Jacq.",
+ "jacq."
+ ],
+ "\\bJonkhkeer\\b": [
+ "Jonkhkeer",
+ "Jhr.",
+ "jhr."
+ ],
+ "\\bJonkvrouw\\b": [
+ "Jonkvrouw",
+ "Jkvr.",
+ "jkvr."
+ ],
+ "\\bJohan\\b": [
+ "Johan",
+ "Joh.",
+ "joh."
+ ],
+ "\\bKardinaal\\b": [
+ "Kardinaal",
+ "Kard.",
+ "kard."
+ ],
+ "\\bKolonel\\b": [
+ "Kolonel",
+ "Kol.",
+ "kol."
+ ],
+ "\\bKoningin\\b": [
+ "Koningin",
+ "Kon.",
+ "kon."
+ ],
+ "\\bKoning\\b": [
+ "Koning",
+ "Kon.",
+ "kon."
+ ],
+ "\\bMajoor\\b": [
+ "Majoor",
+ "Maj.",
+ "maj."
+ ],
+ "\\bMevrouw\\b": [
+ "Mevrouw",
+ "Mevr.",
+ "mevr."
+ ],
+ "\\bMinister\\b": [
+ "Minister",
+ "Min.",
+ "min."
+ ],
+ "\\bMeester\\b": [
+ "Meester",
+ "Mr.",
+ "mr."
+ ],
+ "\\bMonseigneur\\b": [
+ "Monseigneur",
+ "Mgr.",
+ "mgr."
+ ],
+ "\\bPrinses\\b": [
+ "Prinses",
+ "Pr.",
+ "pr."
+ ],
+ "\\bProfessor\\b": [
+ "Professor",
+ "Prof.",
+ "prof."
+ ],
+ "\\bRector\\b": [
+ "Rector",
+ "Rect.",
+ "rect."
+ ],
+ "\\bSecretaris\\b": [
+ "Secretaris",
+ "Secr.",
+ "secr."
+ ],
+ "\\bSenior\\b": [
+ "Senior",
+ "Sr.",
+ "sr."
+ ],
+ "\\bSint\\b": [
+ "Sint",
+ "sint",
+ "St.",
+ "st."
+ ],
+ "\\bTheo\\b": [
+ "Theo",
+ "Th.",
+ "th."
+ ],
+ "\\bVeldmaarschalk\\b": [
+ "Veldmaarschalk",
+ "Veldm.",
+ "Veldm"
+ ],
+ "\\bVicaris\\b": [
+ "Vicaris",
+ "Vic.",
+ "vic."
+ ],
+ "\\bZuster\\b": [
+ "Zuster",
+ "Zr.",
+ "zr."
+ ]
+ },
+ "prop": {
+ "\\baan\\b": [
+ "Aan",
+ "aan"
+ ],
+ "\\bachter\\b": [
+ "Achter",
+ "achter"
+ ],
+ "\\band\\b": [
+ "And",
+ "and"
+ ],
+ "\\bbie\\b": [
+ "Bie",
+ "bie"
+ ],
+ "\\bbij\\b": [
+ "Bij",
+ "bij"
+ ],
+ "\\bbinnenzijde\\b": [
+ "Binnenzijde",
+ "binnenzijde",
+ "BZ",
+ "Bz",
+ "bz"
+ ],
+ "\\bbuitenzijde\\b": [
+ "Buitenzijde",
+ "buitenzijde",
+ "BZ",
+ "Bz",
+ "bz"
+ ],
+ "\\bda\\b": [
+ "Da",
+ "da"
+ ],
+ "\\bde\\b": [
+ "De",
+ "de"
+ ],
+ "\\bdel\\b": [
+ "Del",
+ "del"
+ ],
+ "\\bden\\b": [
+ "Den",
+ "den"
+ ],
+ "\\bder\\b": [
+ "Der",
+ "der"
+ ],
+ "\\bdes\\b": [
+ "Des",
+ "des"
+ ],
+ "\\bdi\\b": [
+ "Di",
+ "di"
+ ],
+ "\\bdie\\b": [
+ "Die",
+ "die"
+ ],
+ "\\bdoor\\b": [
+ "Door",
+ "door"
+ ],
+ "\\bdu\\b": [
+ "Du",
+ "du"
+ ],
+ "\\bein\\b": [
+ "Ein",
+ "ein"
+ ],
+ "\\ben\\b": [
+ "En",
+ "en"
+ ],
+ "\\bfan\\b": [
+ "Fan",
+ "fan"
+ ],
+ "\\bge\\b": [
+ "Ge",
+ "ge"
+ ],
+ "\\bgen\\b": [
+ "Gen",
+ "gen"
+ ],
+ "\\bhet\\b": [
+ "Het",
+ "het",
+ "'T",
+ "'t",
+ "`T",
+ "`t",
+ "T",
+ "t"
+ ],
+ "\\bin\\b": [
+ "In",
+ "in"
+ ],
+ "\\bis\\b": [
+ "Is",
+ "is"
+ ],
+ "\\bit\\b": [
+ "It",
+ "it",
+ "Het",
+ "het",
+ "'T",
+ "'t",
+ "`T",
+ "`t",
+ "T",
+ "t"
+ ],
+ "\\bla\\b": [
+ "La",
+ "la"
+ ],
+ "\\blangs\\b": [
+ "Langs",
+ "langs"
+ ],
+ "\\ble\\b": [
+ "Le",
+ "le"
+ ],
+ "\\bnaar\\b": [
+ "Naar",
+ "naar"
+ ],
+ "\\bnabij\\b": [
+ "Nabij",
+ "nabij"
+ ],
+ "\\boan\\b": [
+ "Oan",
+ "oan"
+ ],
+ "\\bof\\b": [
+ "Of",
+ "of"
+ ],
+ "\\bom\\b": [
+ "Om",
+ "om"
+ ],
+ "\\bonder\\b": [
+ "Onder",
+ "onder"
+ ],
+ "\\bop\\b": [
+ "Op",
+ "op"
+ ],
+ "\\bover\\b": [
+ "Over",
+ "over"
+ ],
+ "\\bsur\\b": [
+ "Sur",
+ "sur"
+ ],
+ "\\bte\\b": [
+ "Te",
+ "te"
+ ],
+ "\\bten\\b": [
+ "Ten",
+ "ten"
+ ],
+ "\\bter\\b": [
+ "Ter",
+ "ter"
+ ],
+ "\\btot\\b": [
+ "Tot",
+ "tot"
+ ],
+ "\\btusschen\\b": [
+ "Tusschen",
+ "tusschen"
+ ],
+ "\\btussen\\b": [
+ "Tussen",
+ "tussen"
+ ],
+ "\\but\\b": [
+ "Ut",
+ "ut"
+ ],
+ "\\buten\\b": [
+ "Uten",
+ "uten"
+ ],
+ "\\bvan\\b": [
+ "Van",
+ "van",
+ "v.",
+ "V."
+ ],
+ "\\bvon\\b": [
+ "Von",
+ "von"
+ ],
+ "\\bvoor\\b": [
+ "Voor",
+ "voor"
+ ]
+ },
+ "windrichting": {
+ "\\bNoord$": [
+ "Noord",
+ "noord",
+ "N"
+ ],
+ "\\bOost$": [
+ "Oost",
+ "oost",
+ "O"
+ ],
+ "\\bZuid$": [
+ "Zuid",
+ "zuid",
+ "Z"
+ ],
+ "\\bWest$": [
+ "West",
+ "west",
+ "W"
+ ],
+ "NZ$": [
+ "N.Z.",
+ "N.z.",
+ "n.z.",
+ "Noordzijde",
+ "noordzijde",
+ ""
+ ],
+ "OZ$": [
+ "O.Z.",
+ "O.z.",
+ "o.z.",
+ "Oostzijde",
+ "oostzijde",
+ ""
+ ],
+ "ZZ$": [
+ "Z.Z.",
+ "Z.z.",
+ "z.z.",
+ "Zuidzijde",
+ "zuidzijde",
+ ""
+ ],
+ "WZ$": [
+ "W.Z.",
+ "W.z.",
+ "w.z.",
+ "Westzijde",
+ "westzijde",
+ ""
+ ],
+ "NO$": [
+ "N.O.",
+ "N.o.",
+ "n.o.",
+ ""
+ ],
+ "NW$": [
+ "N.W.",
+ "N.w.",
+ "n.w.",
+ ""
+ ],
+ "ZO$": [
+ "Z.O.",
+ "Z.o.",
+ "z.o.",
+ ""
+ ],
+ "ZW$": [
+ "Z.W.",
+ "Z.w.",
+ "z.w.",
+ ""
+ ]
+ },
+ "suffix": {
+ "dreef$": [
+ "dreef",
+ "drf"
+ ],
+ "gracht$": [
+ "gracht",
+ "gr"
+ ],
+ "hof$": [
+ "hof",
+ "hf"
+ ],
+ "laan$": [
+ "laan",
+ "ln"
+ ],
+ "markt$": [
+ "markt",
+ "mrkt"
+ ],
+ "pad$": [
+ "pad",
+ "pd"
+ ],
+ "park$": [
+ "park",
+ "prk"
+ ],
+ "plantsoen$": [
+ "plantsoen",
+ "plnts",
+ "pltsn"
+ ],
+ "plein$": [
+ "plein",
+ "pln"
+ ],
+ "singel$": [
+ "singel",
+ "sngl"
+ ],
+ "steeg$": [
+ "steeg",
+ "stg",
+ "st"
+ ],
+ "straat$": [
+ "straat",
+ "str"
+ ],
+ "weg$": [
+ "weg",
+ "wg"
+ ]
+ },
+ "loc": {
+ "\\bAcker\\b": [
+ "Acker",
+ "acker"
+ ],
+ "\\bAkker\\b": [
+ "Akker",
+ "akker"
+ ],
+ "\\bBoulevard\\b": [
+ "Boulevard",
+ "boulevard"
+ ],
+ "\\bDijk\\b": [
+ "Dijk",
+ "dijk"
+ ],
+ "\\bDreef\\b": [
+ "Dreef",
+ "dreef"
+ ],
+ "\\bDwarsweg\\b": [
+ "Dwarsweg",
+ "dwarsweg"
+ ],
+ "\\bDyk\\b": [
+ "Dyk",
+ "dyk"
+ ],
+ "\\bErf\\b": [
+ "Erf",
+ "erf"
+ ],
+ "\\bHeide\\b": [
+ "Heide",
+ "heide"
+ ],
+ "\\bHof\\b": [
+ "Hof",
+ "hof"
+ ],
+ "\\bKade\\b": [
+ "Kade",
+ "kade"
+ ],
+ "\\bKanaal\\b": [
+ "Kanaal",
+ "kanaal"
+ ],
+ "\\bLaan\\b": [
+ "Laan",
+ "laan"
+ ],
+ "\\bPad\\b": [
+ "Pad",
+ "pad"
+ ],
+ "\\bPark\\b": [
+ "Park",
+ "park"
+ ],
+ "\\bPlantsoen\\b": [
+ "Plantsoen",
+ "plantsoen"
+ ],
+ "\\bPlein\\b": [
+ "Plein",
+ "plein"
+ ],
+ "\\bReed\\b": [
+ "Reed",
+ "reed"
+ ],
+ "\\bRotonde\\b": [
+ "Rotonde",
+ "rotonde"
+ ],
+ "\\bSloot\\b": [
+ "Sloot",
+ "sloot"
+ ],
+ "\\bSluis\\b": [
+ "Sluis",
+ "sluis"
+ ],
+ "\\bSteeg\\b": [
+ "Steeg",
+ "steeg"
+ ],
+ "\\bStraat\\b": [
+ "Straat",
+ "straat"
+ ],
+ "\\bTunnel\\b": [
+ "Tunnel",
+ "tunnel"
+ ],
+ "\\bWal\\b": [
+ "Wal",
+ "wal"
+ ],
+ "\\bWeg\\b": [
+ "Weg",
+ "weg"
+ ],
+ "\\bWei\\b": [
+ "Wei",
+ "wei"
+ ],
+ "\\bWijk\\b": [
+ "Wijk",
+ "wijk"
+ ],
+ "\\bVen\\b": [
+ "Ven",
+ "ven"
+ ]
+ },
+ "punct": {
+ "\\.": [
+ ".",
+ ""
+ ],
+ "-": [
+ "-",
+ "",
+ " "
+ ]
+ },
+ "spelling": {
+ "y": [
+ "y",
+ "ij"
+ ],
+ "Y": [
+ "Y",
+ "IJ"
+ ],
+ "ij": [
+ "ij",
+ "y"
+ ],
+ "IJ": [
+ "IJ",
+ "Y"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/regression/data/lookup/src/names/lst_first_name/exceptions.txt b/tests/regression/data/lookup/src/names/lst_first_name/exceptions.txt
new file mode 100644
index 00000000..c10fbe0f
--- /dev/null
+++ b/tests/regression/data/lookup/src/names/lst_first_name/exceptions.txt
@@ -0,0 +1,10 @@
+Ace
+Ad
+Antonius
+Canisius
+Erasmus
+Heino
+Lung
+Man
+Meander
+Wilhelmina
diff --git a/tests/regression/data/lookup/src/names/lst_first_name/items.txt b/tests/regression/data/lookup/src/names/lst_first_name/items.txt
new file mode 100644
index 00000000..bc7e7431
--- /dev/null
+++ b/tests/regression/data/lookup/src/names/lst_first_name/items.txt
@@ -0,0 +1,18 @@
+Ahmed
+Annes
+Antonius
+Canisius
+Daan
+Erasmus
+Jan
+Jan-Willem
+Jansen
+Kees
+Marie
+Maxima
+Meander
+Peter
+Piet
+Pieter
+Wilhelmina
+Willem
diff --git a/tests/regression/data/lookup/src/names/lst_initial/items.txt b/tests/regression/data/lookup/src/names/lst_initial/items.txt
new file mode 100644
index 00000000..cdfdac38
--- /dev/null
+++ b/tests/regression/data/lookup/src/names/lst_initial/items.txt
@@ -0,0 +1,54 @@
+A
+B
+C
+Ch
+Chr
+D
+E
+F
+G
+H
+I
+J
+K
+L
+M
+N
+O
+P
+Ph
+Q
+R
+S
+T
+Th
+U
+V
+W
+X
+Y
+Z
+À
+Á
+Â
+Ã
+Ä
+Å
+Ç
+È
+É
+Ê
+Ë
+Ì
+Í
+Î
+Ï
+Ñ
+Ó
+Ô
+Õ
+Ö
+Ø
+Ù
+Ü
+Š
diff --git a/tests/regression/data/lookup/src/names/lst_interfix/items.txt b/tests/regression/data/lookup/src/names/lst_interfix/items.txt
new file mode 100644
index 00000000..96d23a2e
--- /dev/null
+++ b/tests/regression/data/lookup/src/names/lst_interfix/items.txt
@@ -0,0 +1,44 @@
+'t
+aan de
+bij de
+d'
+da
+de
+de la
+del
+den
+der
+di
+dos
+du
+el
+in 't
+in den
+in het
+l'
+la
+le
+lo
+op 't
+op de
+op den
+op het
+te
+ten
+ter
+uit de
+v
+v.
+v.d.
+v/d
+van
+van 't
+van de
+van den
+van der
+van het
+van t
+vd
+vd.
+von
+zur
diff --git a/tests/regression/data/lookup/src/names/lst_interfix_surname/exceptions.txt b/tests/regression/data/lookup/src/names/lst_interfix_surname/exceptions.txt
new file mode 100644
index 00000000..87b8f311
--- /dev/null
+++ b/tests/regression/data/lookup/src/names/lst_interfix_surname/exceptions.txt
@@ -0,0 +1,3 @@
+Amersfoort
+Utrecht
+Veenendaal
diff --git a/tests/regression/data/lookup/src/names/lst_interfix_surname/items.txt b/tests/regression/data/lookup/src/names/lst_interfix_surname/items.txt
new file mode 100644
index 00000000..55e81785
--- /dev/null
+++ b/tests/regression/data/lookup/src/names/lst_interfix_surname/items.txt
@@ -0,0 +1,18 @@
+Alphen
+Bakker
+Bosch
+Boven
+Duin
+Groot
+Heer
+Laan
+Leeuwen
+Noord
+Olst
+Oude
+Schaft
+Slot
+Sluis
+Utrecht
+Visser
+Wouden
diff --git a/tests/regression/data/lookup/src/names/lst_prefix/items.txt b/tests/regression/data/lookup/src/names/lst_prefix/items.txt
new file mode 100644
index 00000000..6111ce14
--- /dev/null
+++ b/tests/regression/data/lookup/src/names/lst_prefix/items.txt
@@ -0,0 +1,45 @@
+bacc
+bacc.
+bc
+bc.
+collega
+de Hooggeleerde
+de Weledelgeleerde
+de Weledelzeergeleerde
+de heer
+de hooggeleerde
+de weledelgeleerde
+de weledelzeergeleerde
+dhr
+dhr.
+dr
+dr.
+dr.h.c
+dr.h.c.
+dra
+dra.
+drs
+drs.
+ds
+ds.
+ing
+ing.
+ir
+ir.
+kand
+kand.
+lec
+lec.
+mej
+mej.
+meneer
+mevr
+mevr.
+mevrouw
+mijnheer
+mr
+mr.
+mw
+mw.
+prof
+prof.de Weledelgeleerde
diff --git a/tests/regression/data/lookup/src/names/lst_surname/exceptions.txt b/tests/regression/data/lookup/src/names/lst_surname/exceptions.txt
new file mode 100644
index 00000000..67ad9f99
--- /dev/null
+++ b/tests/regression/data/lookup/src/names/lst_surname/exceptions.txt
@@ -0,0 +1,7 @@
+Bosch
+Broer
+Groot
+Klein
+Lang
+Oost
+Tel
diff --git a/tests/regression/data/lookup/src/names/lst_surname/items.txt b/tests/regression/data/lookup/src/names/lst_surname/items.txt
new file mode 100644
index 00000000..03d38368
--- /dev/null
+++ b/tests/regression/data/lookup/src/names/lst_surname/items.txt
@@ -0,0 +1,32 @@
+Ahmadi
+Ahmed
+Bakker
+Bontekoe
+Bosch
+Boven
+Bruins
+Bruins Slot
+Damhuis
+Duin
+Goeree
+Groot
+Groot Wassink
+Jansen
+Janssen
+Janssens
+Kees
+Killaars
+Laan
+Nijhuis
+Oude Nijhuis
+Peter
+Piet
+Pieter
+Schaft
+Slot
+Sluis
+Visser
+Wassink
+Wiegmans
+Zoutenbier
+Zuidgeest
diff --git a/tests/regression/data/lookup/src/whitelist/lst_common_word/exceptions.txt b/tests/regression/data/lookup/src/whitelist/lst_common_word/exceptions.txt
new file mode 100644
index 00000000..0eb5e047
--- /dev/null
+++ b/tests/regression/data/lookup/src/whitelist/lst_common_word/exceptions.txt
@@ -0,0 +1,6 @@
+bel
+boos
+groot
+helder
+hemel
+zondag
diff --git a/tests/regression/data/lookup/src/whitelist/lst_common_word/items.txt b/tests/regression/data/lookup/src/whitelist/lst_common_word/items.txt
new file mode 100644
index 00000000..7bd329c7
--- /dev/null
+++ b/tests/regression/data/lookup/src/whitelist/lst_common_word/items.txt
@@ -0,0 +1,12 @@
+boven
+en
+groot
+heer
+noord
+oktober
+oog
+raam
+slot
+soms
+we
+wijs
diff --git a/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/items.txt b/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/items.txt
new file mode 100644
index 00000000..f6c5cb15
--- /dev/null
+++ b/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/items.txt
@@ -0,0 +1,5 @@
+Alpers-Huttenlocher
+Baller-Gerold
+Diamond-Blackfan
+Gerbec-Morgagni-Adams-Stokes
+Non-Hodgkin
diff --git a/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/lst_eponymous_single/items.txt b/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/lst_eponymous_single/items.txt
new file mode 100644
index 00000000..33d5c540
--- /dev/null
+++ b/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/lst_eponymous_single/items.txt
@@ -0,0 +1,5 @@
+Dupuytren ziekte
+Krabbe ziekte
+ziekte van Eales
+ziekte van Glanzmann
+ziekte van Laron
diff --git a/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/lst_eponymous_single/transform.json b/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/lst_eponymous_single/transform.json
new file mode 100644
index 00000000..8f0e933a
--- /dev/null
+++ b/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/lst_eponymous_single/transform.json
@@ -0,0 +1,22 @@
+{
+ "transforms": {
+ "ziekte_1": {
+ " ziekte$": [
+ " ziekte",
+ "' ziekte",
+ "'s ziekte"
+ ]
+ },
+ "ziekte_2": {
+ "ziekte": [
+ "ziekte",
+ "syndroom",
+ "afwijking",
+ "tumor",
+ "reactie",
+ "complex",
+ "aandoening"
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/transform.json b/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/transform.json
new file mode 100644
index 00000000..1975e8bf
--- /dev/null
+++ b/tests/regression/data/lookup/src/whitelist/lst_eponymous_disease/transform.json
@@ -0,0 +1,39 @@
+{
+ "transforms": {
+ "ziekte_1": {
+ " ziekte$": [
+ " ziekte",
+ "' ziekte",
+ "'s ziekte"
+ ]
+ },
+ "ziekte_2": {
+ "ziekte": [
+ "ziekte",
+ "syndroom",
+ "afwijking",
+ "tumor",
+ "reactie",
+ "complex",
+ "aandoening"
+ ]
+ },
+ "prop": {
+ "\\bVon": [
+ "Von",
+ "von"
+ ]
+ },
+ "punct": {
+ "\\.": [
+ ".",
+ ""
+ ],
+ "-": [
+ "-",
+ "",
+ " "
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/regression/data/lookup/src/whitelist/lst_medical_term/items.txt b/tests/regression/data/lookup/src/whitelist/lst_medical_term/items.txt
new file mode 100644
index 00000000..5d5e74c2
--- /dev/null
+++ b/tests/regression/data/lookup/src/whitelist/lst_medical_term/items.txt
@@ -0,0 +1,18 @@
+alzheimer
+auricularis
+brachialis
+canisius
+cava
+coli
+functies
+inferior
+kinetic
+mdl
+multiple
+neuroloog
+olanzapine
+schaambeen
+suralis
+ulna
+weledelgeleerde
+zetpil
diff --git a/tests/regression/data/lookup/src/whitelist/lst_stop_word/items.txt b/tests/regression/data/lookup/src/whitelist/lst_stop_word/items.txt
new file mode 100644
index 00000000..d1a981ec
--- /dev/null
+++ b/tests/regression/data/lookup/src/whitelist/lst_stop_word/items.txt
@@ -0,0 +1,6 @@
+doen
+een
+en
+kan
+nu
+zonder
diff --git a/tests/data/regression_cases/names.json b/tests/regression/data/names.json
similarity index 100%
rename from tests/data/regression_cases/names.json
rename to tests/regression/data/names.json
diff --git a/tests/data/regression_cases/phone_numbers.json b/tests/regression/data/phone_numbers.json
similarity index 100%
rename from tests/data/regression_cases/phone_numbers.json
rename to tests/regression/data/phone_numbers.json
diff --git a/tests/data/regression_cases/urls.json b/tests/regression/data/urls.json
similarity index 100%
rename from tests/data/regression_cases/urls.json
rename to tests/regression/data/urls.json
diff --git a/tests/regression/test_regression.py b/tests/regression/test_regression.py
index 3bdb442e..75b0b18b 100644
--- a/tests/regression/test_regression.py
+++ b/tests/regression/test_regression.py
@@ -1,11 +1,24 @@
import json
from typing import Optional
+import pytest
from docdeid import Annotation, AnnotationSet
from deduce import Deduce
+@pytest.fixture
+def model(shared_datadir):
+ # FIXME Sorry, due to the design decision of pytest-datadir to create a new copy
+ # of `shared_datadir` for every test, we cannot reuse this fixture
+ # for all tests in this module or package.
+ return Deduce(
+ build_lookup_structs=True,
+ save_lookup_structs=False,
+ lookup_data_path=shared_datadir / "lookup",
+ )
+
+
def regression_test(
model: Deduce,
examples_file: str,
@@ -39,65 +52,65 @@ def annotators_from_group(model: Deduce, group: str) -> set[str]:
class TestRegression:
- def test_regression_name(self, model):
+ def test_regression_name(self, model, shared_datadir):
regression_test(
model=model,
- examples_file="tests/data/regression_cases/names.json",
+ examples_file=shared_datadir / "names.json",
enabled=annotators_from_group(model, "names"),
)
- def test_regression_location(self, model):
+ def test_regression_location(self, model, shared_datadir):
regression_test(
model=model,
- examples_file="tests/data/regression_cases/locations.json",
+ examples_file=shared_datadir / "locations.json",
enabled=annotators_from_group(model, "locations"),
)
- def test_regression_institution(self, model):
+ def test_regression_institution(self, model, shared_datadir):
regression_test(
model=model,
- examples_file="tests/data/regression_cases/institutions.json",
+ examples_file=shared_datadir / "institutions.json",
enabled=annotators_from_group(model, "institutions"),
)
- def test_regression_date(self, model):
+ def test_regression_date(self, model, shared_datadir):
regression_test(
model=model,
- examples_file="tests/data/regression_cases/dates.json",
+ examples_file=shared_datadir / "dates.json",
enabled=annotators_from_group(model, "dates"),
)
- def test_regression_age(self, model):
+ def test_regression_age(self, model, shared_datadir):
regression_test(
model=model,
- examples_file="tests/data/regression_cases/ages.json",
+ examples_file=shared_datadir / "ages.json",
enabled=annotators_from_group(model, "ages"),
)
- def test_regression_identifier(self, model):
+ def test_regression_identifier(self, model, shared_datadir):
regression_test(
model=model,
- examples_file="tests/data/regression_cases/identifiers.json",
+ examples_file=shared_datadir / "identifiers.json",
enabled=annotators_from_group(model, "identifiers"),
)
- def test_regression_phone(self, model):
+ def test_regression_phone(self, model, shared_datadir):
regression_test(
model=model,
- examples_file="tests/data/regression_cases/phone_numbers.json",
+ examples_file=shared_datadir / "phone_numbers.json",
enabled=annotators_from_group(model, "phone_numbers"),
)
- def test_regression_email(self, model):
+ def test_regression_email(self, model, shared_datadir):
regression_test(
model=model,
- examples_file="tests/data/regression_cases/emails.json",
+ examples_file=shared_datadir / "emails.json",
enabled=annotators_from_group(model, "email_addresses"),
)
- def test_regression_url(self, model):
+ def test_regression_url(self, model, shared_datadir):
regression_test(
model=model,
- examples_file="tests/data/regression_cases/urls.json",
+ examples_file=shared_datadir / "urls.json",
enabled=annotators_from_group(model, "urls"),
)
diff --git a/tests/data/lookup/cache/lookup_structs.pickle b/tests/unit/data/lookup/cache/lookup_structs.pickle
similarity index 100%
rename from tests/data/lookup/cache/lookup_structs.pickle
rename to tests/unit/data/lookup/cache/lookup_structs.pickle
diff --git a/tests/data/lookup/src/lst_test/exceptions.txt b/tests/unit/data/lookup/src/lst_test/exceptions.txt
similarity index 100%
rename from tests/data/lookup/src/lst_test/exceptions.txt
rename to tests/unit/data/lookup/src/lst_test/exceptions.txt
diff --git a/tests/data/lookup/src/lst_test/items.txt b/tests/unit/data/lookup/src/lst_test/items.txt
similarity index 100%
rename from tests/data/lookup/src/lst_test/items.txt
rename to tests/unit/data/lookup/src/lst_test/items.txt
diff --git a/tests/data/lookup/src/lst_test/transform.json b/tests/unit/data/lookup/src/lst_test/transform.json
similarity index 100%
rename from tests/data/lookup/src/lst_test/transform.json
rename to tests/unit/data/lookup/src/lst_test/transform.json
diff --git a/tests/data/lookup/src/lst_test_nested/items.txt b/tests/unit/data/lookup/src/lst_test_nested/items.txt
similarity index 100%
rename from tests/data/lookup/src/lst_test_nested/items.txt
rename to tests/unit/data/lookup/src/lst_test_nested/items.txt
diff --git a/tests/data/lookup/src/lst_test_nested/lst_sublist/items.txt b/tests/unit/data/lookup/src/lst_test_nested/lst_sublist/items.txt
similarity index 100%
rename from tests/data/lookup/src/lst_test_nested/lst_sublist/items.txt
rename to tests/unit/data/lookup/src/lst_test_nested/lst_sublist/items.txt
diff --git a/tests/data/small.json b/tests/unit/data/small.json
similarity index 100%
rename from tests/data/small.json
rename to tests/unit/data/small.json
diff --git a/tests/unit/test_annotation_processor.py b/tests/unit/test_annotation_processor.py
index 8e3ce8f5..4fdb00e9 100644
--- a/tests/unit/test_annotation_processor.py
+++ b/tests/unit/test_annotation_processor.py
@@ -14,16 +14,23 @@ def test_tags_match(self):
assert proc._tags_match("a", "a")
assert proc._tags_match("huisnummer", "huisnummer")
+
+ # XXX Dubious behaviour:
assert proc._tags_match("patient", "patient")
+
assert proc._tags_match("persoon", "persoon")
- assert proc._tags_match("patient", "persoon")
- assert proc._tags_match("persoon", "patient")
+ assert proc._tags_match("initiaal_patient", "persoon")
+ assert proc._tags_match("initiaal_patient", "name_patient")
assert not proc._tags_match("a", "b")
assert not proc._tags_match("patient", "huisnummer")
assert not proc._tags_match("huisnummer", "patient")
assert not proc._tags_match("persoon", "huisnummer")
assert not proc._tags_match("huisnummer", "persoon")
+ assert not proc._tags_match("patient", "persoon")
+ assert not proc._tags_match("persoon", "patient")
+ assert not proc._tags_match("name_patient", "patient")
+ assert not proc._tags_match("persoon", "initiaal_patient")
def test_annotation_replacement_equal_tags(self):
proc = DeduceMergeAdjacentAnnotations()
@@ -84,8 +91,12 @@ def test_patient_no_overlap(self):
expected_annotations = dd.AnnotationSet(
[
- dd.Annotation(text="Jan", start_char=0, end_char=3, tag="patient"),
- dd.Annotation(text="Jansen", start_char=4, end_char=10, tag="patient"),
+ dd.Annotation(
+ text="Jan", start_char=0, end_char=3, tag="voornaam_patient"
+ ),
+ dd.Annotation(
+ text="Jansen", start_char=4, end_char=10, tag="achternaam_patient"
+ ),
]
)
@@ -107,7 +118,11 @@ def test_patient_with_overlap(self):
)
expected_annotations = dd.AnnotationSet(
- [dd.Annotation(text="Jan Jansen", start_char=0, end_char=10, tag="patient")]
+ [
+ dd.Annotation(
+ text="Jan Jansen", start_char=0, end_char=10, tag="naam_patient"
+ )
+ ]
)
assert proc.process_annotations(annotations, text) == expected_annotations
@@ -129,7 +144,9 @@ def test_mixed_no_overlap(self):
expected_annotations = dd.AnnotationSet(
[
- dd.Annotation(text="Jan", start_char=0, end_char=3, tag="patient"),
+ dd.Annotation(
+ text="Jan", start_char=0, end_char=3, tag="voornaam_patient"
+ ),
dd.Annotation(text="Jansen", start_char=4, end_char=10, tag="persoon"),
]
)
@@ -153,7 +170,9 @@ def test_mixed_with_overlap(self):
expected_annotations = dd.AnnotationSet(
[
- dd.Annotation(text="Jan", start_char=0, end_char=3, tag="patient"),
+ dd.Annotation(
+ text="Jan", start_char=0, end_char=3, tag="voornaam_patient"
+ ),
dd.Annotation(text=" Jansen", start_char=3, end_char=10, tag="persoon"),
]
)
diff --git a/tests/unit/test_annotator.py b/tests/unit/test_annotator.py
index 2f7daec3..63c11472 100644
--- a/tests/unit/test_annotator.py
+++ b/tests/unit/test_annotator.py
@@ -1,4 +1,5 @@
import re
+from collections import defaultdict
from unittest.mock import patch
import docdeid as dd
@@ -7,11 +8,17 @@
from deduce.annotator import (
BsnAnnotator,
ContextAnnotator,
+ ContextPattern,
PatientNameAnnotator,
PhoneNumberAnnotator,
- RegexpPseudoAnnotator,
- TokenPatternAnnotator,
+ RegexpPseudoAnnotator
+)
+from docdeid.direction import Direction
+from docdeid.process.annotator import (
+ as_token_pattern,
_PatternPositionMatcher,
+ SequencePattern,
+ SimpleTokenPattern,
)
from deduce.person import Person
from deduce.tokenizer import DeduceTokenizer
@@ -91,6 +98,10 @@ def test_equal(self):
assert _PatternPositionMatcher.match({"equal": "test"}, token=token("test"))
assert not _PatternPositionMatcher.match({"equal": "_"}, token=token("test"))
+ def test_equal_with_dataclass(self):
+ assert _PatternPositionMatcher.match(SimpleTokenPattern("equal", "test"),
+ token=token("test"))
+
def test_re_match(self):
assert _PatternPositionMatcher.match({"re_match": "[a-z]"}, token=token("abc"))
assert _PatternPositionMatcher.match(
@@ -206,77 +217,6 @@ def test_match_or(self):
)
-class TestTokenPatternAnnotator:
- def test_match_sequence(self, pattern_doc, ds):
- pattern = [{"lookup": "first_names"}, {"like_name": True}]
-
- tpa = TokenPatternAnnotator(pattern=[{}], ds=ds, tag="_")
-
- assert tpa._match_sequence(
- pattern_doc.text, start_token=pattern_doc.get_tokens()[3], pattern=pattern
- ) == dd.Annotation(text="Andries Meijer", start_char=12, end_char=26, tag="_")
- assert (
- tpa._match_sequence(
- pattern_doc.text,
- start_token=pattern_doc.get_tokens()[7],
- pattern=pattern,
- )
- is None
- )
-
- def test_match_sequence_left(self, pattern_doc, ds):
- pattern = [{"lookup": "first_names"}, {"like_name": True}]
-
- tpa = TokenPatternAnnotator(pattern=[{}], ds=ds, tag="_")
-
- assert tpa._match_sequence(
- pattern_doc.text,
- start_token=pattern_doc.get_tokens()[4],
- pattern=pattern,
- direction="left",
- ) == dd.Annotation(text="Andries Meijer", start_char=12, end_char=26, tag="_")
-
- assert (
- tpa._match_sequence(
- pattern_doc.text,
- start_token=pattern_doc.get_tokens()[8],
- direction="left",
- pattern=pattern,
- )
- is None
- )
-
- def test_match_sequence_skip(self, pattern_doc, ds):
- pattern = [{"lookup": "surnames"}, {"like_name": True}]
-
- tpa = TokenPatternAnnotator(pattern=[{}], ds=ds, tag="_")
-
- assert tpa._match_sequence(
- pattern_doc.text,
- start_token=pattern_doc.get_tokens()[4],
- pattern=pattern,
- skip={"-"},
- ) == dd.Annotation(text="Meijer-Heerma", start_char=20, end_char=33, tag="_")
- assert (
- tpa._match_sequence(
- pattern_doc.text,
- start_token=pattern_doc.get_tokens()[4],
- pattern=pattern,
- skip=set(),
- )
- is None
- )
-
- def test_annotate(self, pattern_doc, ds):
- pattern = [{"lookup": "first_names"}, {"like_name": True}]
-
- tpa = TokenPatternAnnotator(pattern=pattern, ds=ds, tag="_")
-
- assert tpa.annotate(pattern_doc) == [
- dd.Annotation(text="Andries Meijer", start_char=12, end_char=26, tag="_")
- ]
-
-
class TestContextAnnotator:
def test_apply_context_pattern(self, pattern_doc):
annotator = ContextAnnotator(pattern=[])
@@ -295,14 +235,13 @@ def test_apply_context_pattern(self, pattern_doc):
)
assert annotator._apply_context_pattern(
- pattern_doc.text,
+ pattern_doc,
+ ContextPattern("voornaam",
+ "{tag}+naam",
+ SequencePattern(Direction.RIGHT,
+ set(),
+ [as_token_pattern({"like_name": True})])),
annotations,
- {
- "pattern": [{"like_name": True}],
- "direction": "right",
- "pre_tag": "voornaam",
- "tag": "{tag}+naam",
- },
) == dd.AnnotationSet(
[
dd.Annotation(
@@ -331,14 +270,13 @@ def test_apply_context_pattern_left(self, pattern_doc):
)
assert annotator._apply_context_pattern(
- pattern_doc.text,
+ pattern_doc,
+ ContextPattern("achternaam",
+ "naam+{tag}",
+ SequencePattern(Direction.LEFT,
+ set(),
+ [as_token_pattern({"like_name": True})])),
annotations,
- {
- "pattern": [{"like_name": True}],
- "direction": "left",
- "pre_tag": "achternaam",
- "tag": "naam+{tag}",
- },
) == dd.AnnotationSet(
[
dd.Annotation(
@@ -367,15 +305,13 @@ def test_apply_context_pattern_skip(self, pattern_doc):
)
assert annotator._apply_context_pattern(
- pattern_doc.text,
+ pattern_doc,
+ ContextPattern("achternaam",
+ "{tag}+naam",
+ SequencePattern(Direction.RIGHT,
+ {"-"},
+ [as_token_pattern({"like_name": True})])),
annotations,
- {
- "pattern": [{"like_name": True}],
- "direction": "right",
- "skip": ["-"],
- "pre_tag": "achternaam",
- "tag": "{tag}+naam",
- },
) == dd.AnnotationSet(
[
dd.Annotation(
@@ -406,7 +342,7 @@ def test_annotate_multiple(self, pattern_doc):
annotator = ContextAnnotator(pattern=pattern, iterative=False)
- annotations = dd.AnnotationSet(
+ pattern_doc.annotations = dd.AnnotationSet(
[
dd.Annotation(
text="Andries",
@@ -419,7 +355,7 @@ def test_annotate_multiple(self, pattern_doc):
]
)
- assert annotator._annotate(pattern_doc.text, annotations) == dd.AnnotationSet(
+ assert annotator._get_annotations(pattern_doc) == dd.AnnotationSet(
{
dd.Annotation(
text="Andries Meijer-Heerma",
@@ -443,7 +379,7 @@ def test_annotate_iterative(self, pattern_doc):
annotator = ContextAnnotator(pattern=pattern, iterative=True)
- annotations = dd.AnnotationSet(
+ pattern_doc.annotations = dd.AnnotationSet(
[
dd.Annotation(
text="Andries",
@@ -456,7 +392,7 @@ def test_annotate_iterative(self, pattern_doc):
]
)
- assert annotator._annotate(pattern_doc.text, annotations) == dd.AnnotationSet(
+ assert annotator._get_annotations(pattern_doc) == dd.AnnotationSet(
{
dd.Annotation(
text="Andries Meijer-Heerma",
diff --git a/tests/unit/test_lookup_struct.py b/tests/unit/test_lookup_struct.py
index dcc0f3ac..b2daa1b5 100644
--- a/tests/unit/test_lookup_struct.py
+++ b/tests/unit/test_lookup_struct.py
@@ -1,5 +1,4 @@
import io
-from pathlib import Path
from unittest.mock import patch
import docdeid as dd
@@ -12,12 +11,11 @@
validate_lookup_struct_cache,
)
-DATA_PATH = Path(".").cwd() / "tests" / "data" / "lookup"
-
class TestLookupStruct:
- def test_load_raw_itemset(self):
- raw_itemset = load_raw_itemset(DATA_PATH / "src" / "lst_test")
+ def test_load_raw_itemset(self, shared_datadir):
+
+ raw_itemset = load_raw_itemset(shared_datadir / "lookup" / "src" / "lst_test")
assert len(raw_itemset) == 5
assert "de Vries" in raw_itemset
@@ -27,14 +25,18 @@ def test_load_raw_itemset(self):
assert "Pieters" in raw_itemset
assert "Wolter" not in raw_itemset
- def test_load_raw_itemset_nested(self):
- raw_itemset = load_raw_itemset(DATA_PATH / "src" / "lst_test_nested")
+ def test_load_raw_itemset_nested(self, shared_datadir):
+
+ raw_itemset = load_raw_itemset(
+ shared_datadir / "lookup" / "src" / "lst_test_nested"
+ )
assert raw_itemset == {"a", "b", "c", "d"}
- def test_load_raw_itemsets(self):
+ def test_load_raw_itemsets(self, shared_datadir):
+
raw_itemsets = load_raw_itemsets(
- base_path=DATA_PATH, subdirs=["lst_test", "lst_test_nested"]
+ base_path=shared_datadir / "lookup", subdirs=["lst_test", "lst_test_nested"]
)
assert "test" in raw_itemsets
@@ -42,7 +44,8 @@ def test_load_raw_itemsets(self):
assert "test_nested" in raw_itemsets
assert len(raw_itemsets["test_nested"]) == 4
- def test_validate_lookup_struct_cache_valid(self):
+ def test_validate_lookup_struct_cache_valid(self, shared_datadir):
+
cache = {
"deduce_version": "2.5.0",
"saved_datetime": "2023-12-06 10:19:39.198133",
@@ -55,10 +58,13 @@ class MockStats:
with patch("pathlib.Path.glob", return_value=[1, 2, 3]):
with patch("os.stat", return_value=MockStats()):
assert validate_lookup_struct_cache(
- cache=cache, base_path=DATA_PATH, deduce_version="2.5.0"
+ cache=cache,
+ base_path=shared_datadir / "lookup",
+ deduce_version="2.5.0",
)
- def test_validate_lookup_struct_cache_file_changes(self):
+ def test_validate_lookup_struct_cache_file_changes(self, shared_datadir):
+
cache = {
"deduce_version": "2.5.0",
"saved_datetime": "2023-12-06 10:19:39.198133",
@@ -71,13 +77,16 @@ class MockStats:
with patch("pathlib.Path.glob", return_value=[1, 2, 3]):
with patch("os.stat", return_value=MockStats()):
assert not validate_lookup_struct_cache(
- cache=cache, base_path=DATA_PATH, deduce_version="2.5.0"
+ cache=cache,
+ base_path=shared_datadir / "lookup",
+ deduce_version="2.5.0",
)
@patch("deduce.lookup_structs.validate_lookup_struct_cache", return_value=True)
- def test_load_lookup_structs_from_cache(self, _):
+ def test_load_lookup_structs_from_cache(self, _, shared_datadir):
+
ds_collection = load_lookup_structs_from_cache(
- cache_path=DATA_PATH, deduce_version="_"
+ cache_path=shared_datadir / "lookup", deduce_version="_"
)
assert len(ds_collection) == 2
@@ -85,27 +94,30 @@ def test_load_lookup_structs_from_cache(self, _):
assert "test_nested" in ds_collection
@patch("deduce.lookup_structs.validate_lookup_struct_cache", return_value=True)
- def test_load_lookup_structs_from_cache_nofile(self, _):
+ def test_load_lookup_structs_from_cache_nofile(self, _, shared_datadir):
+
ds_collection = load_lookup_structs_from_cache(
- cache_path=DATA_PATH / "non_existing_dir", deduce_version="_"
+ cache_path=shared_datadir / "non_existing_dir", deduce_version="_"
)
assert ds_collection is None
@patch("deduce.lookup_structs.validate_lookup_struct_cache", return_value=False)
- def test_load_lookup_structs_from_cache_invalid(self, _):
+ def test_load_lookup_structs_from_cache_invalid(self, _, shared_datadir):
+
ds_collection = load_lookup_structs_from_cache(
- cache_path=DATA_PATH, deduce_version="_"
+ cache_path=shared_datadir / "lookup", deduce_version="_"
)
assert ds_collection is None
@patch("builtins.open", return_value=io.BytesIO())
@patch("pickle.dump")
- def test_cache_lookup_structs(self, _, mock_pickle_dump):
+ def test_cache_lookup_structs(self, _, mock_pickle_dump, shared_datadir):
+
cache_lookup_structs(
lookup_structs=dd.ds.DsCollection(),
- cache_path=DATA_PATH,
+ cache_path=shared_datadir / "lookup",
deduce_version="2.5.0",
)
diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py
index 0f0dff1a..e901fd7a 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/test_utils.py
@@ -1,10 +1,9 @@
-from pathlib import Path
-
import docdeid as dd
import pytest
from deduce import utils
-from deduce.annotator import TokenPatternAnnotator
+from docdeid.process.annotator import SequenceAnnotator
+from deduce.annotator import BsnAnnotator
class TestStrMatch:
@@ -30,17 +29,17 @@ def test_str_match_fuzzy(self):
class TestClassForName:
def test_class_for_name(self):
assert (
- utils.class_for_name(
- module_name="deduce.annotator", class_name="TokenPatternAnnotator"
+ utils.class_for_name(
+ module_name="deduce.annotator", class_name="BsnAnnotator"
)
- == TokenPatternAnnotator
+ == BsnAnnotator
)
class TestInitializeClass:
def test_initialize_class(self):
- cls = TokenPatternAnnotator
+ cls = SequenceAnnotator
tag = "_"
pattern = [{"key": "value"}]
@@ -54,7 +53,7 @@ def test_initialize_class(self):
def test_initialize_class_with_extras(self):
- cls = TokenPatternAnnotator
+ cls = SequenceAnnotator
tag = "_"
pattern = [{"key": "value"}]
@@ -229,26 +228,26 @@ def test_apply_transform_no_strip_lines(self):
class TestOptionalLoad:
- def test_optional_load_items(self):
+ def test_optional_load_items(self, shared_datadir):
- path = Path("tests/data/lookup/src/lst_test_nested/items.txt")
+ path = shared_datadir / "lookup" / "src" / "lst_test_nested" / "items.txt"
assert utils.optional_load_items(path) == {"a", "b"}
- def test_optional_load_items_nonexisting(self):
+ def test_optional_load_items_nonexisting(self, shared_datadir):
- path = Path("tests/data/non/existing/file.txt")
+ path = shared_datadir / "non" / "existing" / "file.txt"
assert utils.optional_load_items(path) is None
- def test_optional_load_json(self):
+ def test_optional_load_json(self, shared_datadir):
- path = Path("tests/data/small.json")
+ path = shared_datadir / "small.json"
assert utils.optional_load_json(path) == {"test": True}
- def test_optional_load_json_nonexisting(self):
+ def test_optional_load_json_nonexisting(self, shared_datadir):
- path = Path("tests/data/non/existing/file.json")
+ path = shared_datadir / "non" / "existing" / "file.json"
assert utils.optional_load_json(path) is None