repository.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. from __future__ import annotations
  2. from pathlib import Path
  3. from threading import RLock
  4. from rdflib import Graph
  5. from rdflib.query import ResultRow
  6. from rdflib.term import Node
  7. class RDFRepository:
  8. """Loads the ontology and data files into one local, queryable RDF graph."""
  9. def __init__(self, root: Path):
  10. self.root = root
  11. self.graph = Graph()
  12. self._lock = RLock()
  13. self.loaded_files: list[Path] = []
  14. self.reload()
  15. def reload(self) -> None:
  16. paths = [
  17. self.root / "ontology" / "global-shipment.ttl",
  18. *sorted((self.root / "ontology").glob("*-extension.ttl")),
  19. *sorted((self.root / "data").glob("*.ttl")),
  20. ]
  21. graph = Graph()
  22. loaded_files: list[Path] = []
  23. for path in paths:
  24. if path.exists() and path not in loaded_files:
  25. graph.parse(path, format="turtle")
  26. loaded_files.append(path)
  27. with self._lock:
  28. self.graph = graph
  29. self.loaded_files = loaded_files
  30. self.base_triple_count = len(graph)
  31. def add_triples(self, triples: list[tuple[Node, Node, Node]]) -> int:
  32. with self._lock:
  33. before = len(self.graph)
  34. for triple in triples:
  35. self.graph.add(triple)
  36. return len(self.graph) - before
  37. def query_file(self, path: Path) -> dict[str, list[dict[str, str]]]:
  38. if not path.exists():
  39. raise FileNotFoundError(path)
  40. return self.query(path.read_text(encoding="utf-8"))
  41. def query(self, sparql: str) -> dict[str, list[dict[str, str]]]:
  42. with self._lock:
  43. result = self.graph.query(sparql)
  44. variables = [str(variable) for variable in result.vars or []]
  45. rows = [self._row_to_dict(row, variables) for row in result]
  46. return {"rows": rows}
  47. @staticmethod
  48. def _row_to_dict(row: ResultRow, variables: list[str]) -> dict[str, str]:
  49. values: dict[str, str] = {}
  50. for variable in variables:
  51. value = row.get(variable)
  52. values[variable] = "" if value is None else str(value)
  53. return values
  54. @property
  55. def triple_count(self) -> int:
  56. return len(self.graph)
  57. @property
  58. def in_memory_triple_count(self) -> int:
  59. return self.triple_count - self.base_triple_count
  60. @property
  61. def loaded_file_names(self) -> list[str]:
  62. return [str(path.relative_to(self.root)) for path in self.loaded_files]