diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml index f3d04d0..447d35f 100644 --- a/.github/workflows/unit-tests.yaml +++ b/.github/workflows/unit-tests.yaml @@ -11,29 +11,30 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version}} + - name: Install Poetry with pipx + run: pipx install poetry - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install -r dev-requirements.txt + run: poetry install --no-root - name: Run tests - run: python -m pytest --cov=record_validator/ + run: poetry run python -m pytest --cov=record_validator/ --cov-report=xml - name: Send report to Coveralls - uses: AndreMiras/coveralls-python-action@develop + uses: coverallsapp/github-action@v2 with: parallel: true github-token: ${{ secrets.GITHUB_TOKEN}} + flag-name: ${{ matrix.python-version}} finish: needs: test runs-on: ubuntu-latest steps: - - name: Coveralls Finished - uses: AndreMiras/coveralls-python-action@develop + - name: Coveralls finished + uses: coverallsapp/github-action@v2 with: parallel-finished: true \ No newline at end of file diff --git a/dev-requirements.txt b/dev-requirements.txt index 2f07902..c249cf0 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,10 +1,10 @@ -annotated-types==0.7.0 ; python_version >= "3.10" and python_version < "4.0" \ +annotated-types==0.7.0 ; python_version >= "3.10" \ --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 -colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" and sys_platform == "win32" \ +colorama==0.4.6 ; python_version >= "3.10" and sys_platform == "win32" \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 -coverage[toml]==7.6.1 ; python_version >= "3.10" and python_version < "4.0" \ +coverage==7.6.1 ; python_version >= "3.10" \ --hash=sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca \ --hash=sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d \ --hash=sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6 \ @@ -80,16 +80,16 @@ coverage[toml]==7.6.1 ; python_version >= "3.10" and python_version < "4.0" \ exceptiongroup==1.2.2 ; python_version >= "3.10" and python_version < "3.11" \ --hash=sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b \ --hash=sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc -iniconfig==2.0.0 ; python_version >= "3.10" and python_version < "4.0" \ +iniconfig==2.0.0 ; python_version >= "3.10" \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 -packaging==24.1 ; python_version >= "3.10" and python_version < "4.0" \ +packaging==24.1 ; python_version >= "3.10" \ --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 -pluggy==1.5.0 ; python_version >= "3.10" and python_version < "4.0" \ +pluggy==1.5.0 ; python_version >= "3.10" \ --hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \ --hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 -pydantic-core==2.20.1 ; python_version >= "3.10" and python_version < "4.0" \ +pydantic-core==2.20.1 ; python_version >= "3.10" \ --hash=sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d \ --hash=sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f \ --hash=sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686 \ @@ -179,21 +179,21 @@ pydantic-core==2.20.1 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f \ --hash=sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a \ --hash=sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27 -pydantic==2.8.2 ; python_version >= "3.10" and python_version < "4.0" \ +pydantic==2.8.2 ; python_version >= "3.10" \ --hash=sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a \ --hash=sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8 -pymarc==5.2.2 ; python_version >= "3.10" and python_version < "4.0" \ +pymarc==5.2.2 ; python_version >= "3.10" \ --hash=sha256:0913b4167a4337190598914c3642c5ae4f0f64a26bc4316f380cc54f65d8a741 \ --hash=sha256:d654ee1faae5d80845b7a44b27deb6ea9b5377994d16aeba5d3e19e7856d7d43 -pytest-cov==5.0.0 ; python_version >= "3.10" and python_version < "4.0" \ +pytest-cov==5.0.0 ; python_version >= "3.10" \ --hash=sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652 \ --hash=sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857 -pytest==8.3.2 ; python_version >= "3.10" and python_version < "4.0" \ +pytest==8.3.2 ; python_version >= "3.10" \ --hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \ --hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce tomli==2.0.1 ; python_version >= "3.10" and python_full_version <= "3.11.0a6" \ --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f -typing-extensions==4.12.2 ; python_version >= "3.10" and python_version < "4.0" \ +typing-extensions==4.12.2 ; python_version >= "3.10" \ --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 diff --git a/poetry.lock b/poetry.lock index 6788e34..7698520 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -6,6 +6,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -17,6 +18,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -28,6 +31,7 @@ version = "7.6.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, @@ -115,6 +119,8 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -129,6 +135,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -140,6 +147,7 @@ version = "24.1" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, @@ -151,6 +159,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -166,6 +175,7 @@ version = "2.8.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, @@ -188,6 +198,7 @@ version = "2.20.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, @@ -289,6 +300,7 @@ version = "5.2.2" description = "Read, write and modify MARC bibliographic data" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "pymarc-5.2.2-py3-none-any.whl", hash = "sha256:d654ee1faae5d80845b7a44b27deb6ea9b5377994d16aeba5d3e19e7856d7d43"}, {file = "pymarc-5.2.2.tar.gz", hash = "sha256:0913b4167a4337190598914c3642c5ae4f0f64a26bc4316f380cc54f65d8a741"}, @@ -300,6 +312,7 @@ version = "8.3.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, @@ -322,6 +335,7 @@ version = "5.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, @@ -340,6 +354,8 @@ version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_full_version <= \"3.11.0a6\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -351,12 +367,13 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [metadata] -lock-version = "2.0" -python-versions = "^3.10" -content-hash = "9d16f0157e6edafba05c023a91f2f9b565ddd8bbdaf6ea67766a0effa82224a1" +lock-version = "2.1" +python-versions = ">=3.10" +content-hash = "08a339f654d26d3af21c665b8c91cc6e9062386abfcd44ad5ce0fb76cd015d3d" diff --git a/pyproject.toml b/pyproject.toml index 0299b18..66562b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,15 +1,14 @@ -[tool.poetry] +[project] name = "record-validator" -version = "0.1.0" -description = "" -authors = ["Charlotte Kostelic "] +version = "0.1.1" +description = "MARC record validator for shelf-ready materials" +authors = [{name = "Charlotte Kostelic", email = "charlottekostelic@bookops.org"}] readme = "README.md" - -[tool.poetry.dependencies] -python = "^3.10" -pydantic = "^2.8.2" -pymarc = "^5.2.2" - +requires-python = ">=3.10" +dependencies = [ + "pydantic (>=2.8.2)", + "pymarc (>=5.2.2)" +] [tool.poetry.group.dev.dependencies] pytest = "^8.3.2" @@ -17,11 +16,20 @@ pytest-cov = "^5.0.0" [tool.pytest.ini_options] testpaths = ["tests"] +addopts = '--cov=overload_web/' [tool.coverage.run] relative_files = true source = ["."] +omit = ["tests/*"] [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["E501"] + +[[tool.mypy.overrides]] +module = ["tests.*"] +disallow_untyped_defs = false \ No newline at end of file diff --git a/record_validator/adapters.py b/record_validator/adapters.py index 53145d3..7f81654 100644 --- a/record_validator/adapters.py +++ b/record_validator/adapters.py @@ -27,8 +27,10 @@ """ from typing import Annotated, Union -from pydantic import Tag, Discriminator, TypeAdapter + +from pydantic import Discriminator, Tag, TypeAdapter from pymarc import Field as MarcField + from record_validator.constants import AllFields from record_validator.field_models import ( AuxBibCallNo, diff --git a/record_validator/base_fields.py b/record_validator/base_fields.py index fc707b5..ead38c3 100644 --- a/record_validator/base_fields.py +++ b/record_validator/base_fields.py @@ -23,8 +23,10 @@ class for all control field models. """ from typing import Annotated, Any, Dict, List, Literal, Union -from pymarc import Field as MarcField + from pydantic import BaseModel, ConfigDict, Field, model_serializer, model_validator +from pymarc import Field as MarcField + from record_validator.constants import AllFields, AllSubfields diff --git a/record_validator/constants.py b/record_validator/constants.py index e04af52..7091d70 100644 --- a/record_validator/constants.py +++ b/record_validator/constants.py @@ -114,7 +114,6 @@ class ValidOrderItems(Enum): """ MAB = {"order_location": "MAB", "item_location": "rcmb2", "item_type": "2"} - MAS = {"order_location": "MAS", "item_location": "rcmb2", "item_type": "2"} MAF = {"order_location": "MAF", "item_location": "rcmf2", "item_type": "55"} MAF_NO_ITEM_TYPE = { "order_location": "MAF", @@ -144,6 +143,8 @@ class ValidOrderItems(Enum): "item_type": None, } MAP = {"order_location": "MAP", "item_location": "rcmp2", "item_type": "2"} + MAS = {"order_location": "MAS", "item_location": "rcmb2", "item_type": "2"} + PAD = {"order_location": "PAD", "item_location": "rcpd2", "item_type": "55"} PAH = {"order_location": "PAH", "item_location": "rcph2", "item_type": "55"} PAH_NO_ITEM_TYPE = { "order_location": "PAH", diff --git a/record_validator/field_models.py b/record_validator/field_models.py index 09924be..042f6a7 100644 --- a/record_validator/field_models.py +++ b/record_validator/field_models.py @@ -1,8 +1,10 @@ """This module contains classes that define MARC fields provided in vendor records""" from typing import Annotated, Any, Dict, List, Literal, Optional, Union + from pydantic import Field, model_validator -from record_validator.base_fields import BaseDataField, BaseControlField + +from record_validator.base_fields import BaseControlField, BaseDataField class AuxBibCallNo(BaseDataField): @@ -341,6 +343,7 @@ class ItemField(BaseDataField): "rc2ma", "rcmp2", "rcmb2", + "rcpd2", "rcph2", "rcpm2", "rcpt2", diff --git a/record_validator/marc_errors.py b/record_validator/marc_errors.py index d4a38a5..d4da31d 100644 --- a/record_validator/marc_errors.py +++ b/record_validator/marc_errors.py @@ -1,7 +1,9 @@ """A module to translate errors from the validator to a more readable format""" from typing import Any, Dict, List, Tuple, Union + from pydantic_core import ErrorDetails + from record_validator.adapters import get_adapter from record_validator.constants import AllFields, AllSubfields diff --git a/record_validator/marc_models.py b/record_validator/marc_models.py index 67d639c..9041cf4 100644 --- a/record_validator/marc_models.py +++ b/record_validator/marc_models.py @@ -1,13 +1,12 @@ """This module contains pydantic models for validating vendor-provided MARC records.""" from typing import Annotated, Dict, List, Union + from pydantic import BaseModel, ConfigDict, Field from pydantic.functional_validators import AfterValidator, BeforeValidator from pymarc import Field as MarcField -from record_validator.validators import ( - validate_leader, - validate_all, -) + +from record_validator.validators import validate_all, validate_leader class RecordModel(BaseModel): diff --git a/record_validator/utils.py b/record_validator/utils.py index 1a2fa80..7e1db92 100644 --- a/record_validator/utils.py +++ b/record_validator/utils.py @@ -1,8 +1,9 @@ """This module contains utility functions for working with MARC records.""" -from itertools import chain import re +from itertools import chain from typing import Any, Dict, List, Union + from pymarc import Field as MarcField @@ -59,7 +60,6 @@ def get_record_type(fields: List[Union[MarcField, Dict[str, Any]]]) -> str: subject_fields = [i for i in field_list if next(iter(i)).startswith("6")] aux_call_nos = list(chain(*[dict2subfield(i, "h") for i in aux_call_no_fields])) - order_locs = list(chain(*[dict2subfield(i, "t") for i in field_list if "960" in i])) phys_desc = list(chain(*[dict2subfield(i, "a") for i in field_list if "300" in i])) subjects = list(chain(*[dict2subfield(i, "v") for i in subject_fields])) @@ -71,8 +71,6 @@ def get_record_type(fields: List[Union[MarcField, Dict[str, Any]]]) -> str: material_type = "other" elif any(j is not None for j in [catalogue.match(str(i)) for i in subjects]): material_type = "other" - elif any(i == "PAD" for i in order_locs) is True: - material_type = "other" elif any(j is not None for j in [multivol.match(str(i)) for i in phys_desc]): material_type = "other" elif any(j is not None for j in [pamphlet.match(str(i)) for i in phys_desc]): diff --git a/record_validator/validators.py b/record_validator/validators.py index ca29af2..4c02b6f 100644 --- a/record_validator/validators.py +++ b/record_validator/validators.py @@ -3,21 +3,19 @@ from itertools import chain from typing import Any, Dict, List, Union -from pymarc import Leader -from pymarc import Field as MarcField + from pydantic import ValidationError -from pydantic_core import PydanticCustomError, InitErrorDetails +from pydantic_core import InitErrorDetails, PydanticCustomError +from pymarc import Field as MarcField +from pymarc import Leader + from record_validator.adapters import get_adapter -from record_validator.utils import ( - get_record_type, - field2dict, - dict2subfield, -) from record_validator.constants import AllFields, ValidOrderItems +from record_validator.utils import dict2subfield, field2dict, get_record_type def validate_all( - fields: List[Union[MarcField, Dict[str, Any]]] + fields: List[Union[MarcField, Dict[str, Any]]], ) -> List[Union[MarcField, Dict[str, Any]]]: """ Validate MARC record fields. This function validates validates the fields of a @@ -69,7 +67,7 @@ def validate_fields( extra_tags.append("949") elif "other" in record_type: extra_tags.extend(AllFields.monograph_fields()) - elif "monograph" in record_type: + else: required_tags.extend(AllFields.monograph_fields()) extra_fields = [i for i in extra_tags if i in tag_list] missing_fields = [i for i in required_tags if i not in tag_list] diff --git a/requirements.txt b/requirements.txt index cd1ff79..b6f8cb5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ -annotated-types==0.7.0 ; python_version >= "3.10" and python_version < "4.0" \ +annotated-types==0.7.0 ; python_version >= "3.10" \ --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 -pydantic-core==2.20.1 ; python_version >= "3.10" and python_version < "4.0" \ +pydantic-core==2.20.1 ; python_version >= "3.10" \ --hash=sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d \ --hash=sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f \ --hash=sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686 \ @@ -91,12 +91,12 @@ pydantic-core==2.20.1 ; python_version >= "3.10" and python_version < "4.0" \ --hash=sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f \ --hash=sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a \ --hash=sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27 -pydantic==2.8.2 ; python_version >= "3.10" and python_version < "4.0" \ +pydantic==2.8.2 ; python_version >= "3.10" \ --hash=sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a \ --hash=sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8 -pymarc==5.2.2 ; python_version >= "3.10" and python_version < "4.0" \ +pymarc==5.2.2 ; python_version >= "3.10" \ --hash=sha256:0913b4167a4337190598914c3642c5ae4f0f64a26bc4316f380cc54f65d8a741 \ --hash=sha256:d654ee1faae5d80845b7a44b27deb6ea9b5377994d16aeba5d3e19e7856d7d43 -typing-extensions==4.12.2 ; python_version >= "3.10" and python_version < "4.0" \ +typing-extensions==4.12.2 ; python_version >= "3.10" \ --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 diff --git a/tests/conftest.py b/tests/conftest.py index cc2ff67..6956c2e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,6 @@ import pytest -from pymarc import Record, Subfield from pymarc import Field as MarcField +from pymarc import Record, Subfield @pytest.fixture @@ -210,13 +210,35 @@ def stub_record_multiple_items(stub_record): @pytest.fixture def stub_dance_record(stub_record): - stub_record.remove_fields("949", "852") - stub_record.remove_fields("960") + stub_record.remove_fields("949", "960") stub_record.add_field( MarcField( - tag="960", + tag="949", indicators=[" ", "1"], - subfields=[Subfield(code="t", value="PAD")], + subfields=[ + Subfield(code="z", value="8528"), + Subfield(code="a", value="ReCAP 24-100000"), + Subfield(code="c", value="1"), + Subfield(code="h", value="43"), + Subfield(code="i", value="33433123456789"), + Subfield(code="l", value="rcpd2"), + Subfield(code="m", value="bar"), + Subfield(code="p", value="1.00"), + Subfield(code="t", value="55"), + Subfield(code="u", value="foo"), + Subfield(code="v", value="EVP"), + ], + ) + ) + stub_record.add_field( + MarcField( + tag="960", + indicators=[" ", " "], + subfields=[ + Subfield(code="s", value="100"), + Subfield(code="t", value="PAD"), + Subfield(code="u", value="123456apprv"), + ], ) ) return stub_record diff --git a/tests/test_adapters.py b/tests/test_adapters.py index ba5d0bc..a628c58 100644 --- a/tests/test_adapters.py +++ b/tests/test_adapters.py @@ -1,14 +1,16 @@ from typing import get_args + +import pytest from pydantic import Tag, TypeAdapter from pymarc import Field as MarcField -import pytest + from record_validator.adapters import ( - get_adapter, - tag_discriminator, AuxOtherFields, FieldList, MonographFields, OtherFields, + get_adapter, + tag_discriminator, ) from record_validator.field_models import ( AuxBibCallNo, diff --git a/tests/test_base_fields.py b/tests/test_base_fields.py index bc772f0..557b34d 100644 --- a/tests/test_base_fields.py +++ b/tests/test_base_fields.py @@ -1,6 +1,7 @@ import pytest from pydantic import ValidationError from pymarc import Field as MarcField + from record_validator.base_fields import ( BaseControlField, BaseDataField, diff --git a/tests/test_constants.py b/tests/test_constants.py index 430d246..cffa76e 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -1,4 +1,5 @@ import pytest + from record_validator.constants import AllFields, AllSubfields, ValidOrderItems @@ -115,7 +116,6 @@ def test_AllSubfields_get_alias(field_name, mapping): def test_ValidOrderItems_to_list(): assert ValidOrderItems.to_list() == [ {"order_location": "MAB", "item_location": "rcmb2", "item_type": "2"}, - {"order_location": "MAS", "item_location": "rcmb2", "item_type": "2"}, {"order_location": "MAF", "item_location": "rcmf2", "item_type": "55"}, {"order_location": "MAF", "item_location": "rcmf2", "item_type": None}, {"order_location": "MAG", "item_location": "rcmg2", "item_type": "55"}, @@ -125,6 +125,8 @@ def test_ValidOrderItems_to_list(): {"order_location": "MAL", "item_location": "rc2ma", "item_type": None}, {"order_location": "MAL", "item_location": None, "item_type": None}, {"order_location": "MAP", "item_location": "rcmp2", "item_type": "2"}, + {"order_location": "MAS", "item_location": "rcmb2", "item_type": "2"}, + {"order_location": "PAD", "item_location": "rcpd2", "item_type": "55"}, {"order_location": "PAH", "item_location": "rcph2", "item_type": "55"}, {"order_location": "PAH", "item_location": "rcph2", "item_type": None}, {"order_location": "PAM", "item_location": "rcpm2", "item_type": "55"}, diff --git a/tests/test_field_models.py b/tests/test_field_models.py index 5172ce8..004de76 100644 --- a/tests/test_field_models.py +++ b/tests/test_field_models.py @@ -1,6 +1,7 @@ import pytest from pydantic import ValidationError from pymarc import Field as MarcField + from record_validator.field_models import ( AuxBibCallNo, BibCallNo, diff --git a/tests/test_marc_errors.py b/tests/test_marc_errors.py index b81a29f..9528518 100644 --- a/tests/test_marc_errors.py +++ b/tests/test_marc_errors.py @@ -1,9 +1,10 @@ import pytest from pydantic import ValidationError + from record_validator.marc_errors import ( - get_field_examples, MarcError, MarcValidationError, + get_field_examples, ) from record_validator.marc_models import RecordModel @@ -652,9 +653,10 @@ def test_RecordModel_aux_other_call_no_length_error(self, stub_auxam_monograph): errors = [MarcError(i) for i in e.value.errors()] assert len(errors) == 1 assert [i.loc for i in errors][0] == ("fields", "852", "call_no") - assert [i.msg for i in errors][ - 0 - ] == "String should have at most 9 characters. Examples: ['ReCAP 23-', 'ReCAP 24-', 'ReCAP 25-']" + assert ( + [i.msg for i in errors][0] + == "String should have at most 9 characters. Examples: ['ReCAP 23-', 'ReCAP 24-', 'ReCAP 25-']" + ) assert [i.type for i in errors][0] == "string_too_long" assert [i.loc_marc for i in errors][0] == "852$h" diff --git a/tests/test_marc_models.py b/tests/test_marc_models.py index e479124..5fcb689 100644 --- a/tests/test_marc_models.py +++ b/tests/test_marc_models.py @@ -1,7 +1,9 @@ +from contextlib import nullcontext as does_not_raise + import pytest from pydantic import ValidationError -from contextlib import nullcontext as does_not_raise from pymarc import Leader, MARCReader + from record_validator.marc_models import RecordModel @@ -269,6 +271,7 @@ def test_RecordModel_invalid_fields( ("MAF", "rcmf2", None), ("MAG", "rcmg2", "55"), ("MAG", "rcmg2", None), + ("PAD", "rcpd2", "55"), ], ) def test_RecordModel_valid_location_combos( @@ -304,6 +307,7 @@ def test_RecordModel_order_item_data_not_checked(self, stub_record): ("MAG", "rcmf2", "55"), ("MAF", "rc2ma", None), ("MAL", "rcmg2", "55"), + ("PAD", "rcpd2", "2"), ], ) def test_RecordModel_invalid_order_item_data( diff --git a/tests/test_utils.py b/tests/test_utils.py index e31189d..bf3c536 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,5 @@ import pytest + from record_validator.utils import ( dict2subfield, field2dict, @@ -57,7 +58,7 @@ def test_get_record_type_monograph(stub_leila_monograph): def test_get_record_type_dance(stub_dance_record): - assert get_record_type(fields=stub_dance_record.fields) == "evp_other" + assert get_record_type(fields=stub_dance_record.fields) == "evp_monograph" def test_get_record_type_pamphlet(stub_pamphlet_record): diff --git a/tests/test_validators.py b/tests/test_validators.py index 199f4aa..10d3706 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -1,10 +1,12 @@ from contextlib import nullcontext as does_not_raise -from pydantic import ValidationError + import pytest +from pydantic import ValidationError + from record_validator.validators import ( - validate_leader, validate_all, validate_fields, + validate_leader, validate_order_items, )