From 973d60008d0dca3754470288c9ba4ccefae3d6a4 Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Wed, 2 Jun 2021 18:08:56 -0700 Subject: [PATCH] Add index backend --- amanuensis/backend/index.py | 74 +++++++++++++++++++++++++++++++++++++ tests/test_index.py | 50 +++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 amanuensis/backend/index.py create mode 100644 tests/test_index.py diff --git a/amanuensis/backend/index.py b/amanuensis/backend/index.py new file mode 100644 index 0000000..7f108ec --- /dev/null +++ b/amanuensis/backend/index.py @@ -0,0 +1,74 @@ +""" +Index query interface +""" + +import re +from typing import Optional + +from amanuensis.db import DbContext, ArticleIndex, IndexType +from amanuensis.errors import ArgumentError + + +def create( + db: DbContext, + lexicon_id: int, + index_type: IndexType, + pattern: str, + logical_order: int, + display_order: int, + capacity: Optional[int], +) -> ArticleIndex: + """ + Create a new index in a lexicon. + """ + # Verify argument types are correct + if not isinstance(lexicon_id, int): + raise ArgumentError("lexicon_id") + if not isinstance(index_type, IndexType): + raise ArgumentError("index_type") + if not isinstance(pattern, str): + raise ArgumentError("pattern") + if not isinstance(logical_order, int): + raise ArgumentError("logical_order") + if not isinstance(display_order, int): + raise ArgumentError("display_order") + if capacity is not None and not isinstance(capacity, int): + raise ArgumentError("capacity") + + # Verify the pattern is valid for the index type: + if index_type == IndexType.CHAR: + if len(pattern) < 1: + raise ArgumentError( + f"Pattern '{pattern}' too short for index type {index_type}" + ) + elif index_type == IndexType.RANGE: + range_def = re.match(r"^(.)-(.)$", pattern) + if not range_def: + raise ArgumentError(f"Pattern '{pattern}' is not a valid range format") + start_char, end_char = range_def.group(1), range_def.group(2) + if start_char >= end_char: + raise ArgumentError( + f"Range start '{start_char}' is not before range end '{end_char}'" + ) + elif index_type == IndexType.PREFIX: + if len(pattern) < 1: + raise ArgumentError( + f"Pattern '{pattern}' too short for index type {index_type}" + ) + elif index_type == IndexType.ETC: + if len(pattern) < 1: + raise ArgumentError( + f"Pattern '{pattern}' too short for index type {index_type}" + ) + + new_index = ArticleIndex( + lexicon_id=lexicon_id, + index_type=index_type, + pattern=pattern, + logical_order=logical_order, + display_order=display_order, + capacity=capacity, + ) + db.session.add(new_index) + db.session.commit() + return new_index diff --git a/tests/test_index.py b/tests/test_index.py new file mode 100644 index 0000000..c5a2ac7 --- /dev/null +++ b/tests/test_index.py @@ -0,0 +1,50 @@ +from amanuensis.db.models import IndexType +import pytest + +import amanuensis.backend.index as indq +from amanuensis.db import DbContext, Lexicon, User + +from amanuensis.errors import ArgumentError + + +def test_create_index(db: DbContext, make): + """Test new index creation""" + lexicon: Lexicon = make.lexicon() + defaults: dict = { + "db": db, + "lexicon_id": lexicon.id, + "index_type": IndexType.ETC, + "pattern": "&c.", + "logical_order": 0, + "display_order": 0, + "capacity": 0, + } + kwargs: dict + + # Character indexes require nonempty patterns + with pytest.raises(ArgumentError): + kwargs = {**defaults, "index_type": IndexType.CHAR, "pattern": ""} + indq.create(**kwargs) + kwargs = {**defaults, "index_type": IndexType.CHAR, "pattern": "ABC"} + assert indq.create(**kwargs) + + # Range indexes must follow the 1-2 format + with pytest.raises(ArgumentError): + kwargs = {**defaults, "index_type": IndexType.RANGE, "pattern": "ABC"} + indq.create(**kwargs) + kwargs = {**defaults, "index_type": IndexType.RANGE, "pattern": "A-F"} + assert indq.create(**kwargs) + + # Prefix indexes require nonempty patterns + with pytest.raises(ArgumentError): + kwargs = {**defaults, "index_type": IndexType.CHAR, "pattern": ""} + indq.create(**kwargs) + kwargs = {**defaults, "index_type": IndexType.CHAR, "pattern": "Prefix:"} + assert indq.create(**kwargs) + + # Etc indexes require nonempty patterns + with pytest.raises(ArgumentError): + kwargs = {**defaults, "index_type": IndexType.CHAR, "pattern": ""} + indq.create(**kwargs) + kwargs = {**defaults, "index_type": IndexType.CHAR, "pattern": "&c."} + assert indq.create(**kwargs)