bluecore-models 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.2
2
+ Name: bluecore-models
3
+ Version: 0.2.0
4
+ Summary: Blue Core BIBFRAME Data Models
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: alembic>=1.14.1
8
+ Requires-Dist: psycopg2-binary>=2.9.10
9
+ Requires-Dist: rdflib>=7.1.3
10
+
11
+ # Blue Core Data Models
12
+ The Blue Core Data Models are used in [Blue Core API](https://github.com/blue-core-lod/bluecore_api)
13
+ and in the [Blue Core Workflows](https://github.com/blue-core-lod/bluecore-workflows) services.
14
+
15
+ ## Run Postgres with Docker
16
+ To run the Postgres with the Blue Core Database, run the following command from this directory:
17
+
18
+ `docker run --name bluecore_db -e POSTGRES_USER=bluecore_admin -e POSTGRES_PASSWORD=bluecore_admin -v ./create-db.sql:/docker-entrypoint-initdb.d/create_database.sql -p 5432:5432 postgres:17`
19
+
20
+ ## Installing
21
+ - Install via pip: `pip install blue-core-data-models`
22
+ - Install via uv: `uv add blue-core-data-models`
23
+
24
+ ## Database Management
25
+ The [SQLAlchemy](https://www.sqlalchemy.org/) Object Relational Mapper (ORM) is used to create
26
+ the Bluecore database models.
27
+
28
+ ```mermaid
29
+ erDiagram
30
+ ResourceBase ||--o{ Instance : "has"
31
+ ResourceBase ||--o{ Work : "has"
32
+ ResourceBase ||--o{ OtherResource : "has"
33
+ ResourceBase ||--o{ ResourceBibframeClass : "has classes"
34
+ ResourceBase ||--o{ Version : "has versions"
35
+ ResourceBase ||--o{ BibframeOtherResources : "has other resources"
36
+
37
+ Work ||--o{ Instance : "has"
38
+
39
+ BibframeClass ||--o{ ResourceBibframeClass : "classifies"
40
+
41
+ OtherResource ||--o{ BibframeOtherResources : "links to"
42
+ ```
43
+
44
+ ### Database Migrations with Alembic
45
+ The [Alembic](https://alembic.sqlalchemy.org/en/latest/) database migration package is used
46
+ to manage database changes with the Bluecore Data models.
47
+
48
+ To create a new migration, ensure that the Postgres database is available and then run:
49
+ - `uv run alembic revision --autogenerate -m "{short message describing change}`
50
+
51
+ A new migration script will be created in the `bluecore_store_migration` directory. Be sure
52
+ to add the new script to the repository with `git`.
53
+
54
+ #### Applying Migrations
55
+ To apply all of the migrations, run the following command:
56
+ - `uv run alembic upgrade head`
@@ -0,0 +1,46 @@
1
+ # Blue Core Data Models
2
+ The Blue Core Data Models are used in [Blue Core API](https://github.com/blue-core-lod/bluecore_api)
3
+ and in the [Blue Core Workflows](https://github.com/blue-core-lod/bluecore-workflows) services.
4
+
5
+ ## Run Postgres with Docker
6
+ To run the Postgres with the Blue Core Database, run the following command from this directory:
7
+
8
+ `docker run --name bluecore_db -e POSTGRES_USER=bluecore_admin -e POSTGRES_PASSWORD=bluecore_admin -v ./create-db.sql:/docker-entrypoint-initdb.d/create_database.sql -p 5432:5432 postgres:17`
9
+
10
+ ## Installing
11
+ - Install via pip: `pip install blue-core-data-models`
12
+ - Install via uv: `uv add blue-core-data-models`
13
+
14
+ ## Database Management
15
+ The [SQLAlchemy](https://www.sqlalchemy.org/) Object Relational Mapper (ORM) is used to create
16
+ the Bluecore database models.
17
+
18
+ ```mermaid
19
+ erDiagram
20
+ ResourceBase ||--o{ Instance : "has"
21
+ ResourceBase ||--o{ Work : "has"
22
+ ResourceBase ||--o{ OtherResource : "has"
23
+ ResourceBase ||--o{ ResourceBibframeClass : "has classes"
24
+ ResourceBase ||--o{ Version : "has versions"
25
+ ResourceBase ||--o{ BibframeOtherResources : "has other resources"
26
+
27
+ Work ||--o{ Instance : "has"
28
+
29
+ BibframeClass ||--o{ ResourceBibframeClass : "classifies"
30
+
31
+ OtherResource ||--o{ BibframeOtherResources : "links to"
32
+ ```
33
+
34
+ ### Database Migrations with Alembic
35
+ The [Alembic](https://alembic.sqlalchemy.org/en/latest/) database migration package is used
36
+ to manage database changes with the Bluecore Data models.
37
+
38
+ To create a new migration, ensure that the Postgres database is available and then run:
39
+ - `uv run alembic revision --autogenerate -m "{short message describing change}`
40
+
41
+ A new migration script will be created in the `bluecore_store_migration` directory. Be sure
42
+ to add the new script to the repository with `git`.
43
+
44
+ #### Applying Migrations
45
+ To apply all of the migrations, run the following command:
46
+ - `uv run alembic upgrade head`
@@ -0,0 +1,21 @@
1
+ [project]
2
+ name = "bluecore-models"
3
+ version = "0.2.0"
4
+ description = "Blue Core BIBFRAME Data Models"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "alembic>=1.14.1",
9
+ "psycopg2-binary>=2.9.10",
10
+ "rdflib>=7.1.3",
11
+ ]
12
+
13
+ [dependency-groups]
14
+ dev = [
15
+ "pytest>=8.3.4",
16
+ "pytest-mock-resources>=2.12.1",
17
+ "ruff>=0.9.6",
18
+ ]
19
+
20
+ [tool.setuptools]
21
+ license-files = []
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,13 @@
1
+
2
+ from bluecore.models.base import Base
3
+ from bluecore.models.resource import ResourceBase
4
+ from bluecore.models.bf_classes import BibframeClass, ResourceBibframeClass
5
+ from bluecore.models.instance import Instance
6
+ from bluecore.models.version import Version
7
+ from bluecore.models.work import Work
8
+ from bluecore.models.other_resource import OtherResource, BibframeOtherResources
9
+
10
+
11
+
12
+
13
+
@@ -0,0 +1,3 @@
1
+ from sqlalchemy.orm import declarative_base
2
+
3
+ Base = declarative_base()
@@ -0,0 +1,49 @@
1
+ from datetime import datetime
2
+
3
+
4
+ from sqlalchemy import (
5
+ DateTime,
6
+ ForeignKey,
7
+ Integer,
8
+ String,
9
+ )
10
+
11
+ from sqlalchemy.orm import (
12
+ mapped_column,
13
+ Mapped,
14
+ relationship,
15
+ )
16
+ from bluecore.models.base import Base
17
+ from bluecore.models.resource import ResourceBase
18
+
19
+
20
+ class BibframeClass(Base):
21
+ __tablename__ = "bibframe_classes"
22
+
23
+ id: Mapped[int] = mapped_column(primary_key=True)
24
+ name: Mapped[str] = mapped_column(String, nullable=False)
25
+ uri: Mapped[str] = mapped_column(String, nullable=False, unique=True)
26
+ created_at = mapped_column(DateTime, default=datetime.utcnow)
27
+ updated_at = mapped_column(
28
+ DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
29
+ )
30
+
31
+ def __repr__(self):
32
+ return f"<BibframeClass {self.name}>"
33
+
34
+
35
+ class ResourceBibframeClass(Base):
36
+ __tablename__ = "resource_bibframe_classes"
37
+
38
+ id: Mapped[int] = mapped_column(primary_key=True)
39
+ bf_class_id: Mapped[int] = mapped_column(
40
+ Integer, ForeignKey("bibframe_classes.id"), nullable=False
41
+ )
42
+ bf_class: Mapped[BibframeClass] = relationship("BibframeClass")
43
+ resource_id: Mapped[int] = mapped_column(
44
+ Integer, ForeignKey("resource_base.id"), nullable=False
45
+ )
46
+ resource: Mapped[ResourceBase] = relationship("ResourceBase", backref="classes")
47
+
48
+ def __repr__(self):
49
+ return f"<ResourceBibframeClass {self.bf_class.name} for {self.resource.uri}>"
@@ -0,0 +1,30 @@
1
+ from sqlalchemy import (
2
+ ForeignKey,
3
+ Integer,
4
+ )
5
+
6
+ from sqlalchemy.orm import (
7
+ mapped_column,
8
+ Mapped,
9
+ relationship,
10
+ )
11
+
12
+ from bluecore.models.resource import ResourceBase
13
+
14
+ class Instance(ResourceBase):
15
+ __tablename__ = "instances"
16
+
17
+ id: Mapped[int] = mapped_column(
18
+ Integer, ForeignKey("resource_base.id"), primary_key=True
19
+ )
20
+ work_id: Mapped[int] = mapped_column(Integer, ForeignKey("works.id"), nullable=True)
21
+ work: Mapped["Work"] = relationship(
22
+ "Work", foreign_keys=work_id, backref="instances"
23
+ )
24
+
25
+ __mapper_args__ = {
26
+ "polymorphic_identity": "instances",
27
+ }
28
+
29
+ def __repr__(self):
30
+ return f"<Instance {self.uri}>"
@@ -0,0 +1,66 @@
1
+ from datetime import datetime
2
+
3
+ from sqlalchemy import (
4
+ Boolean,
5
+ DateTime,
6
+ Integer,
7
+ ForeignKey
8
+ )
9
+
10
+ from sqlalchemy.orm import (
11
+ mapped_column,
12
+ Mapped,
13
+ relationship,
14
+ )
15
+
16
+ from bluecore.models.base import Base
17
+ from bluecore.models.resource import ResourceBase
18
+
19
+
20
+ class OtherResource(ResourceBase):
21
+ """
22
+ Stores JSON or JSON-LD resources used to support Work and Instances.
23
+ """
24
+
25
+ __tablename__ = "other_resources"
26
+ id: Mapped[int] = mapped_column(
27
+ Integer, ForeignKey("resource_base.id"), primary_key=True
28
+ )
29
+ is_profile: Mapped[bool] = mapped_column(Boolean, default=False)
30
+
31
+ __mapper_args__ = {
32
+ "polymorphic_identity": "other_resources",
33
+ }
34
+
35
+ def __repr__(self):
36
+ return f"<OtherResource {self.uri or self.id}>"
37
+
38
+
39
+ class BibframeOtherResources(Base):
40
+ """
41
+ Creates relationships between Work or Instance and supporting resources
42
+ """
43
+
44
+ __tablename__ = "bibframe_other_resources"
45
+
46
+ id: Mapped[int] = mapped_column(primary_key=True)
47
+ other_resource_id: Mapped[int] = mapped_column(
48
+ Integer, ForeignKey("other_resources.id"), nullable=False
49
+ )
50
+ other_resource: Mapped[OtherResource] = relationship(
51
+ "OtherResource", foreign_keys=other_resource_id
52
+ )
53
+ bibframe_resource_id: Mapped[int] = mapped_column(
54
+ Integer, ForeignKey("resource_base.id"), nullable=False
55
+ )
56
+ bibframe_resource: Mapped[ResourceBase] = relationship(
57
+ "ResourceBase", backref="other_resources"
58
+ )
59
+
60
+ created_at = mapped_column(DateTime, default=datetime.utcnow)
61
+ updated_at = mapped_column(
62
+ DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
63
+ )
64
+
65
+ def __repr__(self):
66
+ return f"<BibframeOtherResources {self.other_resource.uri or self.other_resource.id} for {self.bibframe_resource.uri}>"
@@ -0,0 +1,32 @@
1
+ from datetime import datetime
2
+
3
+ from sqlalchemy import (
4
+ DateTime,
5
+ String,
6
+ )
7
+
8
+ from sqlalchemy.orm import (
9
+ mapped_column,
10
+ Mapped,
11
+ )
12
+
13
+ from sqlalchemy.dialects.postgresql import JSONB
14
+
15
+ from bluecore.models.base import Base
16
+
17
+ class ResourceBase(Base):
18
+ __tablename__ = "resource_base"
19
+
20
+ id: Mapped[int] = mapped_column(primary_key=True)
21
+ type: Mapped[str] = mapped_column(String, nullable=False)
22
+ data: Mapped[bytes] = mapped_column(JSONB, nullable=False)
23
+ uri: Mapped[str] = mapped_column(String, nullable=True, unique=True)
24
+ created_at = mapped_column(DateTime, default=datetime.utcnow)
25
+ updated_at = mapped_column(
26
+ DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
27
+ )
28
+
29
+ __mapper_args__ = {
30
+ "polymorphic_on": type,
31
+ "polymorphic_identity": "resource_base",
32
+ }
@@ -0,0 +1,35 @@
1
+ from datetime import datetime
2
+
3
+ from sqlalchemy import (
4
+ DateTime,
5
+ Integer,
6
+ ForeignKey
7
+ )
8
+
9
+ from sqlalchemy.orm import (
10
+ mapped_column,
11
+ Mapped,
12
+ relationship,
13
+ )
14
+
15
+ from sqlalchemy.dialects.postgresql import JSONB
16
+
17
+ from bluecore.models.base import Base
18
+ from bluecore.models.resource import ResourceBase
19
+
20
+ class Version(Base):
21
+ __tablename__ = "versions"
22
+
23
+ id: Mapped[int] = mapped_column(primary_key=True)
24
+ resource_id: Mapped[int] = mapped_column(
25
+ Integer, ForeignKey("resource_base.id"), nullable=False
26
+ )
27
+ resource: Mapped[ResourceBase] = relationship("ResourceBase", backref="versions")
28
+ data: Mapped[bytes] = mapped_column(JSONB, nullable=False)
29
+ created_at = mapped_column(DateTime, default=datetime.utcnow)
30
+ updated_at = mapped_column(
31
+ DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
32
+ )
33
+
34
+ def __repr__(self):
35
+ return f"<Version at {self.created_at} for {self.resource.uri}>"
@@ -0,0 +1,25 @@
1
+ from sqlalchemy import (
2
+ ForeignKey,
3
+ Integer,
4
+ )
5
+
6
+ from sqlalchemy.orm import (
7
+ mapped_column,
8
+ Mapped,
9
+ )
10
+
11
+ from bluecore.models.resource import ResourceBase
12
+
13
+
14
+ class Work(ResourceBase):
15
+ __tablename__ = "works"
16
+ id: Mapped[int] = mapped_column(
17
+ Integer, ForeignKey("resource_base.id"), primary_key=True
18
+ )
19
+
20
+ __mapper_args__ = {
21
+ "polymorphic_identity": "works",
22
+ }
23
+
24
+ def __repr__(self):
25
+ return f"<Work {self.uri}>"
@@ -0,0 +1,88 @@
1
+ """Utility functions for working with RDF graphs."""
2
+ import rdflib
3
+
4
+ BF = rdflib.Namespace("http://id.loc.gov/ontologies/bibframe/")
5
+ BFLC = rdflib.Namespace("http://id.loc.gov/ontologies/bflc/")
6
+ LCLOCAL = rdflib.Namespace("http://id.loc.gov/ontologies/lclocal/")
7
+ MADS = rdflib.Namespace("http://www.loc.gov/mads/rdf/v1#")
8
+
9
+ def init_graph() -> rdflib.Graph:
10
+ """Initialize a new RDF graph with the necessary namespaces."""
11
+ new_graph = rdflib.Graph()
12
+ new_graph.namespace_manager.bind("bf", BF)
13
+ new_graph.namespace_manager.bind("bflc", BFLC)
14
+ new_graph.namespace_manager.bind("mads", MADS)
15
+ new_graph.namespace_manager.bind("lclocal", LCLOCAL)
16
+ return new_graph
17
+
18
+
19
+ def _check_for_namespace(node: rdflib.URIRef) -> bool:
20
+ """Check if a node is in the LCLOCAL or DCTERMS namespace."""
21
+ return node in LCLOCAL or node in rdflib.DCTERMS
22
+
23
+
24
+ def _exclude_uri_from_other_resources(uri: rdflib.URIRef) -> bool:
25
+ """Checks if uri is in the BF, MADS, or RDF namespaces"""
26
+ return (
27
+ uri in BF or uri in MADS or uri in rdflib.RDF
28
+ )
29
+
30
+
31
+ def _expand_bnode(graph: rdflib.Graph, entity_graph: rdflib.Graph, bnode: rdflib.BNode):
32
+ """Expand a blank node in the entity graph."""
33
+ for pred, obj in graph.predicate_objects(subject=bnode):
34
+ if _check_for_namespace(pred) or _check_for_namespace(obj):
35
+ continue
36
+ entity_graph.add((bnode, pred, obj))
37
+ if isinstance(obj, rdflib.BNode):
38
+ _expand_bnode(graph, entity_graph, obj)
39
+
40
+
41
+ def _is_work_or_instance(uri: rdflib.URIRef, graph: rdflib.Graph) -> bool:
42
+ """Checks if uri is a BIBFRAME Work or Instance"""
43
+ for class_ in graph.objects(subject=uri, predicate=rdflib.RDF.type):
44
+ # In the future we may want to include Work and Instances subclasses
45
+ # maybe through inference
46
+ if class_ == BF.Work or class_ == BF.Instance:
47
+ return True
48
+ return False
49
+
50
+
51
+ def generate_entity_graph(graph: rdflib.Graph, entity: rdflib.URIRef) -> rdflib.Graph:
52
+ """Generate an entity graph from a larger RDF graph."""
53
+ entity_graph = init_graph()
54
+ for pred, obj in graph.predicate_objects(subject=entity):
55
+ if _check_for_namespace(pred) or _check_for_namespace(obj):
56
+ continue
57
+ entity_graph.add((entity, pred, obj))
58
+ if isinstance(obj, rdflib.BNode):
59
+ _expand_bnode(graph, entity_graph, obj)
60
+ return entity_graph
61
+
62
+
63
+ def generate_other_resources(record_graph: rdflib.Graph, entity_graph: rdflib.Graph) -> list:
64
+ """
65
+ Takes a Record Graph and Entity Graph and returns a list of dictionaries
66
+ where each dict contains the sub-graphs and URIs that referenced in the
67
+ entity graph and present in the record graph.
68
+ """
69
+ other_resources = []
70
+ for row in entity_graph.query("""
71
+ SELECT DISTINCT ?object
72
+ WHERE {
73
+ ?subject ?predicate ?object .
74
+ FILTER(isIRI(?object))
75
+ }
76
+ """):
77
+ uri = row[0]
78
+ if _exclude_uri_from_other_resources(uri) or _is_work_or_instance(uri, record_graph):
79
+ continue
80
+ other_resource_graph = generate_entity_graph(record_graph, uri)
81
+ if len(other_resource_graph) > 0:
82
+ other_resources.append(
83
+ {
84
+ "uri": str(uri),
85
+ "graph": other_resource_graph
86
+ }
87
+ )
88
+ return other_resources
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.2
2
+ Name: bluecore-models
3
+ Version: 0.2.0
4
+ Summary: Blue Core BIBFRAME Data Models
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: alembic>=1.14.1
8
+ Requires-Dist: psycopg2-binary>=2.9.10
9
+ Requires-Dist: rdflib>=7.1.3
10
+
11
+ # Blue Core Data Models
12
+ The Blue Core Data Models are used in [Blue Core API](https://github.com/blue-core-lod/bluecore_api)
13
+ and in the [Blue Core Workflows](https://github.com/blue-core-lod/bluecore-workflows) services.
14
+
15
+ ## Run Postgres with Docker
16
+ To run the Postgres with the Blue Core Database, run the following command from this directory:
17
+
18
+ `docker run --name bluecore_db -e POSTGRES_USER=bluecore_admin -e POSTGRES_PASSWORD=bluecore_admin -v ./create-db.sql:/docker-entrypoint-initdb.d/create_database.sql -p 5432:5432 postgres:17`
19
+
20
+ ## Installing
21
+ - Install via pip: `pip install blue-core-data-models`
22
+ - Install via uv: `uv add blue-core-data-models`
23
+
24
+ ## Database Management
25
+ The [SQLAlchemy](https://www.sqlalchemy.org/) Object Relational Mapper (ORM) is used to create
26
+ the Bluecore database models.
27
+
28
+ ```mermaid
29
+ erDiagram
30
+ ResourceBase ||--o{ Instance : "has"
31
+ ResourceBase ||--o{ Work : "has"
32
+ ResourceBase ||--o{ OtherResource : "has"
33
+ ResourceBase ||--o{ ResourceBibframeClass : "has classes"
34
+ ResourceBase ||--o{ Version : "has versions"
35
+ ResourceBase ||--o{ BibframeOtherResources : "has other resources"
36
+
37
+ Work ||--o{ Instance : "has"
38
+
39
+ BibframeClass ||--o{ ResourceBibframeClass : "classifies"
40
+
41
+ OtherResource ||--o{ BibframeOtherResources : "links to"
42
+ ```
43
+
44
+ ### Database Migrations with Alembic
45
+ The [Alembic](https://alembic.sqlalchemy.org/en/latest/) database migration package is used
46
+ to manage database changes with the Bluecore Data models.
47
+
48
+ To create a new migration, ensure that the Postgres database is available and then run:
49
+ - `uv run alembic revision --autogenerate -m "{short message describing change}`
50
+
51
+ A new migration script will be created in the `bluecore_store_migration` directory. Be sure
52
+ to add the new script to the repository with `git`.
53
+
54
+ #### Applying Migrations
55
+ To apply all of the migrations, run the following command:
56
+ - `uv run alembic upgrade head`
@@ -0,0 +1,18 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/bluecore/models/__init__.py
4
+ src/bluecore/models/base.py
5
+ src/bluecore/models/bf_classes.py
6
+ src/bluecore/models/instance.py
7
+ src/bluecore/models/other_resource.py
8
+ src/bluecore/models/resource.py
9
+ src/bluecore/models/version.py
10
+ src/bluecore/models/work.py
11
+ src/bluecore/utils/graph.py
12
+ src/bluecore_models.egg-info/PKG-INFO
13
+ src/bluecore_models.egg-info/SOURCES.txt
14
+ src/bluecore_models.egg-info/dependency_links.txt
15
+ src/bluecore_models.egg-info/requires.txt
16
+ src/bluecore_models.egg-info/top_level.txt
17
+ tests/test_models.py
18
+ tests/test_utils.py
@@ -0,0 +1,3 @@
1
+ alembic>=1.14.1
2
+ psycopg2-binary>=2.9.10
3
+ rdflib>=7.1.3
@@ -0,0 +1,198 @@
1
+ import json
2
+ import pathlib
3
+ from datetime import datetime, UTC
4
+
5
+ import pytest
6
+ from pytest_mock_resources import create_sqlite_fixture, Rows
7
+
8
+ from sqlalchemy.orm import sessionmaker
9
+
10
+ from bluecore.models import (
11
+ Base,
12
+ BibframeClass,
13
+ ResourceBibframeClass,
14
+ Instance,
15
+ OtherResource,
16
+ Version,
17
+ Work,
18
+ BibframeOtherResources,
19
+ )
20
+
21
+
22
+ def create_test_rows():
23
+ return Rows(
24
+ # BibframeClass
25
+ BibframeClass(
26
+ id=1,
27
+ name="Instance",
28
+ uri="http://id.loc.gov/ontologies/bibframe/Instance",
29
+ created_at=datetime.now(UTC),
30
+ updated_at=datetime.now(UTC),
31
+ ),
32
+ BibframeClass(
33
+ id=2,
34
+ name="Work",
35
+ uri="http://id.loc.gov/ontologies/bibframe/Work",
36
+ created_at=datetime.now(UTC),
37
+ updated_at=datetime.now(UTC),
38
+ ),
39
+ # Work
40
+ Work(
41
+ id=1,
42
+ uri="https://bluecore.info/work/e0d6-40f0-abb3-e9130622eb8a",
43
+ created_at=datetime.now(UTC),
44
+ updated_at=datetime.now(UTC),
45
+ data=pathlib.Path("tests/blue-core-work.jsonld").read_text(),
46
+ type="works",
47
+ ),
48
+ # Instance
49
+ Instance(
50
+ id=2,
51
+ uri="https://bluecore.info/instance/75d831b9-e0d6-40f0-abb3-e9130622eb8a",
52
+ created_at=datetime.now(UTC),
53
+ updated_at=datetime.now(UTC),
54
+ data=pathlib.Path("tests/blue-core-instance.jsonld").read_text(),
55
+ type="instances",
56
+ work_id=1,
57
+ ),
58
+ # OtherResource
59
+ OtherResource(
60
+ id=3,
61
+ uri="https://bluecore.info/other-resource/sample",
62
+ created_at=datetime.now(UTC),
63
+ updated_at=datetime.now(UTC),
64
+ data='{"description": "Sample Other Resource"}',
65
+ type="other_resources",
66
+ is_profile=False,
67
+ ),
68
+ # ResourceBibframeClass
69
+ ResourceBibframeClass(
70
+ id=1,
71
+ bf_class_id=1,
72
+ resource_id=2,
73
+ ),
74
+ ResourceBibframeClass(
75
+ id=2,
76
+ bf_class_id=2,
77
+ resource_id=1,
78
+ ),
79
+ # Version
80
+ Version(
81
+ id=1,
82
+ resource_id=1,
83
+ data=pathlib.Path("tests/blue-core-work.jsonld").read_text(),
84
+ created_at=datetime.now(UTC),
85
+ updated_at=datetime.now(UTC),
86
+ ),
87
+ Version(
88
+ id=2,
89
+ resource_id=2,
90
+ data=pathlib.Path("tests/blue-core-instance.jsonld").read_text(),
91
+ created_at=datetime.now(UTC),
92
+ updated_at=datetime.now(UTC),
93
+ ),
94
+ # BibframeOtherResources
95
+ BibframeOtherResources(
96
+ id=1,
97
+ other_resource_id=3,
98
+ bibframe_resource_id=1,
99
+ created_at=datetime.now(UTC),
100
+ updated_at=datetime.now(UTC),
101
+ ),
102
+ )
103
+
104
+
105
+ engine = create_sqlite_fixture(create_test_rows())
106
+
107
+
108
+ @pytest.fixture()
109
+ def pg_session(engine):
110
+ Base.metadata.create_all(engine)
111
+ return sessionmaker(bind=engine)
112
+
113
+
114
+ def test_bibframe_class(pg_session):
115
+ with pg_session() as session:
116
+ bf_instance = (
117
+ session.query(BibframeClass).where(BibframeClass.name == "Instance").first()
118
+ )
119
+ assert bf_instance is not None
120
+ assert bf_instance.uri.startswith(
121
+ "http://id.loc.gov/ontologies/bibframe/Instance"
122
+ )
123
+ assert bf_instance.created_at
124
+ assert bf_instance.updated_at
125
+
126
+
127
+ def test_resource_bibframe_class(pg_session):
128
+ with pg_session() as session:
129
+ resource_bf_class = (
130
+ session.query(ResourceBibframeClass)
131
+ .where(ResourceBibframeClass.id == 1)
132
+ .first()
133
+ )
134
+ assert resource_bf_class.resource.uri.startswith(
135
+ "https://bluecore.info/instance"
136
+ )
137
+ assert resource_bf_class.bf_class.name == "Instance"
138
+
139
+
140
+ def test_instance(pg_session):
141
+ with pg_session() as session:
142
+ instance = session.query(Instance).where(Instance.id == 2).first()
143
+ assert instance.uri.startswith("https://bluecore.info/instance")
144
+ assert instance.data
145
+ assert instance.created_at
146
+ assert instance.updated_at
147
+ assert instance.work is not None
148
+ assert instance.work.uri.startswith("https://bluecore.info/work")
149
+
150
+
151
+ def test_work(pg_session):
152
+ with pg_session() as session:
153
+ work = session.query(Work).where(Work.id == 1).first()
154
+ assert work.uri.startswith("https://bluecore.info/work")
155
+ assert work.data
156
+ assert work.created_at
157
+ assert work.updated_at
158
+ assert len(work.instances) > 0
159
+
160
+
161
+ def test_other_resource(pg_session):
162
+ with pg_session() as session:
163
+ other_resource = (
164
+ session.query(OtherResource).where(OtherResource.id == 3).first()
165
+ )
166
+ assert other_resource.uri.startswith("https://bluecore.info/other-resource")
167
+ assert other_resource.data
168
+ assert other_resource.created_at
169
+ assert other_resource.updated_at
170
+ assert other_resource.is_profile is False
171
+
172
+
173
+ def test_versions(pg_session):
174
+ with pg_session() as session:
175
+ version = session.query(Version).where(Version.id == 1).first()
176
+ work = session.query(Work).where(Work.id == 1).first()
177
+ assert version.resource is not None
178
+ assert version.resource == work
179
+ assert version.data
180
+ assert version.created_at
181
+ assert version.updated_at
182
+ version2 = session.query(Version).where(Version.id == 2).first()
183
+ instance = session.query(Instance).where(Instance.id == 2).first()
184
+ assert version2.resource == instance
185
+
186
+
187
+ def test_bibframe_other_resources(pg_session):
188
+ with pg_session() as session:
189
+ bibframe_other_resource = (
190
+ session.query(BibframeOtherResources)
191
+ .where(BibframeOtherResources.id == 1)
192
+ .first()
193
+ )
194
+ assert bibframe_other_resource.other_resource is not None
195
+ assert bibframe_other_resource.bibframe_resource is not None
196
+ assert bibframe_other_resource.created_at
197
+ assert bibframe_other_resource.updated_at
198
+
@@ -0,0 +1,36 @@
1
+ import pathlib
2
+
3
+ import rdflib
4
+
5
+ from bluecore.utils.graph import (
6
+ BF,
7
+ BFLC,
8
+ MADS,
9
+ generate_entity_graph,
10
+ init_graph,
11
+ )
12
+
13
+ def test_init_graph():
14
+ graph = init_graph()
15
+ assert graph.namespace_manager.store.namespace("bf") == rdflib.URIRef(BF)
16
+ assert graph.namespace_manager.store.namespace("bflc") == rdflib.URIRef(BFLC)
17
+ assert graph.namespace_manager.store.namespace("mads") == rdflib.URIRef(MADS)
18
+
19
+
20
+ def test_generate_entity_graph():
21
+ loc_graph = init_graph()
22
+ loc_graph.parse(data=pathlib.Path("tests/23807141.jsonld").read_text(), format="json-ld")
23
+ work_uri = rdflib.URIRef("http://id.loc.gov/resources/works/23807141")
24
+ dcterm_part_of = loc_graph.value(subject=work_uri, predicate=rdflib.DCTERMS.isPartOf)
25
+ assert dcterm_part_of == rdflib.URIRef("http://id.loc.gov/resources/works")
26
+ work_graph = generate_entity_graph(loc_graph, work_uri)
27
+ assert len(work_graph) == 118
28
+
29
+ work_title = work_graph.value(subject=work_uri, predicate=BF.title)
30
+ main_title = work_graph.value(subject=work_title, predicate=BF.mainTitle)
31
+ assert str(main_title).startswith("HBR guide to generative AI for managers")
32
+
33
+ # Tests if DCTERMs triples are filtered out of entity graph
34
+ work_dcterm_part_of = work_graph.value(subject=work_uri, predicate=rdflib.DCTERMS.isPartOf)
35
+ assert work_dcterm_part_of is None
36
+