Coverage for src/inheritance_calculator_core/database/repositories.py: 0%
194 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-17 05:31 +0900
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-17 05:31 +0900
1"""リポジトリパターンの実装
3Neo4jデータベースへのデータアクセスを抽象化するリポジトリクラス。
4"""
5from typing import List, Optional, Dict, Any
6from datetime import date
7import logging
9from .neo4j_client import Neo4jClient
10from .queries import PersonQueries, RelationshipQueries, InheritanceQueries, build_person_params
11from ..models.person import Person, Gender
12from ..models.relationship import BloodType
13from ..utils.exceptions import DatabaseException
16class PersonRepository:
17 """
18 人物(Person)のリポジトリ
20 Neo4jとPydanticモデル間の変換を行う。
21 """
23 def __init__(self, client: Neo4jClient) -> None:
24 """
25 初期化
27 Args:
28 client: Neo4jクライアント
29 """
30 self.client = client
31 self.logger = logging.getLogger(__name__)
33 def create(self, person: Person) -> Person:
34 """
35 人物を作成
37 Args:
38 person: 作成する人物
40 Returns:
41 作成された人物
43 Raises:
44 DatabaseException: 作成に失敗した場合
45 """
46 params = build_person_params(
47 name=person.name,
48 is_alive=person.is_alive,
49 is_decedent=person.is_decedent,
50 birth_date=person.birth_date.isoformat() if person.birth_date else None,
51 death_date=person.death_date.isoformat() if person.death_date else None,
52 gender=person.gender.value if person.gender else None,
53 address=person.address,
54 phone=person.phone,
55 email=person.email
56 )
58 try:
59 result = self.client.execute(PersonQueries.CREATE, params)
60 self.logger.info(f"Created person: {person.name}")
61 return person
63 except Exception as e:
64 self.logger.error(f"Failed to create person {person.name}: {e}")
65 raise DatabaseException(f"人物作成エラー: {str(e)}")
67 def find_by_name(self, name: str) -> Optional[Person]:
68 """
69 名前で人物を検索
71 Args:
72 name: 氏名
74 Returns:
75 見つかった人物、存在しない場合はNone
76 """
77 try:
78 result = self.client.execute(PersonQueries.FIND_BY_NAME, {"name": name})
80 if not result:
81 return None
83 return self._to_person(result[0]["p"])
85 except Exception as e:
86 self.logger.error(f"Failed to find person {name}: {e}")
87 raise DatabaseException(f"人物検索エラー: {str(e)}")
89 def find_decedent(self) -> Optional[Person]:
90 """
91 被相続人を検索
93 Returns:
94 被相続人、存在しない場合はNone
95 """
96 try:
97 result = self.client.execute(PersonQueries.FIND_DECEDENT)
99 if not result:
100 return None
102 return self._to_person(result[0]["p"])
104 except Exception as e:
105 self.logger.error(f"Failed to find decedent: {e}")
106 raise DatabaseException(f"被相続人検索エラー: {str(e)}")
108 def find_all(self) -> List[Person]:
109 """
110 全ての人物を取得
112 Returns:
113 人物のリスト
114 """
115 try:
116 result = self.client.execute(PersonQueries.FIND_ALL)
117 return [self._to_person(record["p"]) for record in result]
119 except Exception as e:
120 self.logger.error(f"Failed to find all persons: {e}")
121 raise DatabaseException(f"人物一覧取得エラー: {str(e)}")
123 def update(self, person: Person) -> Person:
124 """
125 人物を更新
127 Args:
128 person: 更新する人物
130 Returns:
131 更新された人物
133 Raises:
134 DatabaseException: 更新に失敗した場合
135 """
136 params = {
137 "name": person.name,
138 "is_alive": person.is_alive,
139 "death_date": person.death_date.isoformat() if person.death_date else None,
140 "gender": person.gender.value if person.gender else None,
141 "address": person.address,
142 "phone": person.phone,
143 "email": person.email
144 }
146 try:
147 result = self.client.execute(PersonQueries.UPDATE, params)
149 if not result:
150 raise DatabaseException(f"人物が見つかりません: {person.name}")
152 self.logger.info(f"Updated person: {person.name}")
153 return person
155 except Exception as e:
156 self.logger.error(f"Failed to update person {person.name}: {e}")
157 raise DatabaseException(f"人物更新エラー: {str(e)}")
159 def delete(self, name: str) -> None:
160 """
161 人物を削除
163 Args:
164 name: 削除する人物の氏名
166 Raises:
167 DatabaseException: 削除に失敗した場合
168 """
169 try:
170 self.client.execute(PersonQueries.DELETE, {"name": name})
171 self.logger.info(f"Deleted person: {name}")
173 except Exception as e:
174 self.logger.error(f"Failed to delete person {name}: {e}")
175 raise DatabaseException(f"人物削除エラー: {str(e)}")
177 def delete_all(self) -> None:
178 """
179 全ての人物を削除(テスト用)
181 Warning:
182 本番環境では使用しないこと
183 """
184 try:
185 self.client.execute(PersonQueries.DELETE_ALL)
186 self.logger.warning("Deleted all persons")
188 except Exception as e:
189 self.logger.error(f"Failed to delete all persons: {e}")
190 raise DatabaseException(f"全人物削除エラー: {str(e)}")
192 def _to_person(self, node: Dict[str, Any]) -> Person:
193 """
194 Neo4jノードをPersonモデルに変換
196 Args:
197 node: Neo4jノード
199 Returns:
200 Personモデル
201 """
202 # Neo4jのdateオブジェクトをPythonのdateに変換
203 birth_date = None
204 if node.get("birth_date"):
205 bd = node["birth_date"]
206 birth_date = date(bd.year, bd.month, bd.day) if hasattr(bd, "year") else None
208 death_date = None
209 if node.get("death_date"):
210 dd = node["death_date"]
211 death_date = date(dd.year, dd.month, dd.day) if hasattr(dd, "year") else None
213 gender = Gender(node["gender"]) if node.get("gender") else Gender.UNKNOWN
215 return Person(
216 name=node["name"],
217 is_alive=node["is_alive"],
218 is_decedent=node["is_decedent"],
219 birth_date=birth_date,
220 death_date=death_date,
221 gender=gender,
222 address=node.get("address"),
223 phone=node.get("phone"),
224 email=node.get("email")
225 )
228class RelationshipRepository:
229 """
230 リレーションシップのリポジトリ
232 親子関係、配偶者関係、兄弟姉妹関係などを管理する。
233 """
235 def __init__(self, client: Neo4jClient) -> None:
236 """
237 初期化
239 Args:
240 client: Neo4jクライアント
241 """
242 self.client = client
243 self.person_repo = PersonRepository(client)
244 self.logger = logging.getLogger(__name__)
246 def create_child_of(
247 self,
248 child_name: str,
249 parent_name: str,
250 adoption: bool = False,
251 is_biological: bool = True
252 ) -> None:
253 """
254 親子関係を作成
256 Args:
257 child_name: 子の氏名
258 parent_name: 親の氏名
259 adoption: 養子縁組フラグ
260 is_biological: 実子フラグ
261 """
262 params = {
263 "child_name": child_name,
264 "parent_name": parent_name,
265 "adoption": adoption,
266 "is_biological": is_biological
267 }
269 try:
270 self.client.execute(RelationshipQueries.CREATE_CHILD_OF, params)
271 self.logger.info(f"Created CHILD_OF: {child_name} -> {parent_name}")
273 except Exception as e:
274 self.logger.error(f"Failed to create CHILD_OF relationship: {e}")
275 raise DatabaseException(f"親子関係作成エラー: {str(e)}")
277 def create_spouse_of(
278 self,
279 person1_name: str,
280 person2_name: str,
281 marriage_date: Optional[date] = None,
282 divorce_date: Optional[date] = None,
283 is_current: bool = True
284 ) -> None:
285 """
286 配偶者関係を作成
288 Args:
289 person1_name: 配偶者1の氏名
290 person2_name: 配偶者2の氏名
291 marriage_date: 婚姻日
292 divorce_date: 離婚日
293 is_current: 現在の配偶者フラグ
294 """
295 params = {
296 "person1_name": person1_name,
297 "person2_name": person2_name,
298 "marriage_date": marriage_date.isoformat() if marriage_date else None,
299 "divorce_date": divorce_date.isoformat() if divorce_date else None,
300 "is_current": is_current
301 }
303 try:
304 self.client.execute(RelationshipQueries.CREATE_SPOUSE_OF, params)
305 self.logger.info(f"Created SPOUSE_OF: {person1_name} <-> {person2_name}")
307 except Exception as e:
308 self.logger.error(f"Failed to create SPOUSE_OF relationship: {e}")
309 raise DatabaseException(f"配偶者関係作成エラー: {str(e)}")
311 def create_sibling_of(
312 self,
313 person1_name: str,
314 person2_name: str,
315 blood_type: BloodType = BloodType.FULL,
316 shared_parent: str = "both"
317 ) -> None:
318 """
319 兄弟姉妹関係を作成
321 Args:
322 person1_name: 兄弟姉妹1の氏名
323 person2_name: 兄弟姉妹2の氏名
324 blood_type: 血縁タイプ
325 shared_parent: 共通の親
326 """
327 params = {
328 "person1_name": person1_name,
329 "person2_name": person2_name,
330 "blood_type": blood_type.value,
331 "shared_parent": shared_parent
332 }
334 try:
335 self.client.execute(RelationshipQueries.CREATE_SIBLING_OF, params)
336 self.logger.info(f"Created SIBLING_OF: {person1_name} <-> {person2_name}")
338 except Exception as e:
339 self.logger.error(f"Failed to create SIBLING_OF relationship: {e}")
340 raise DatabaseException(f"兄弟姉妹関係作成エラー: {str(e)}")
342 def create_renounced(
343 self,
344 person_name: str,
345 decedent_name: str,
346 renounce_date: date,
347 reason: Optional[str] = None
348 ) -> None:
349 """
350 相続放棄関係を作成
352 Args:
353 person_name: 放棄者の氏名
354 decedent_name: 被相続人の氏名
355 renounce_date: 放棄日
356 reason: 理由
357 """
358 params = {
359 "person_name": person_name,
360 "decedent_name": decedent_name,
361 "renounce_date": renounce_date.isoformat(),
362 "reason": reason
363 }
365 try:
366 self.client.execute(RelationshipQueries.CREATE_RENOUNCED, params)
367 self.logger.info(f"Created RENOUNCED: {person_name} -> {decedent_name}")
369 except Exception as e:
370 self.logger.error(f"Failed to create RENOUNCED relationship: {e}")
371 raise DatabaseException(f"相続放棄関係作成エラー: {str(e)}")
373 def find_children(self, parent_name: str) -> List[Person]:
374 """
375 親の子を検索
377 Args:
378 parent_name: 親の氏名
380 Returns:
381 子のリスト
382 """
383 try:
384 result = self.client.execute(
385 RelationshipQueries.FIND_CHILDREN,
386 {"parent_name": parent_name}
387 )
388 return [self.person_repo._to_person(record["child"]) for record in result]
390 except Exception as e:
391 self.logger.error(f"Failed to find children of {parent_name}: {e}")
392 raise DatabaseException(f"子の検索エラー: {str(e)}")
394 def find_parents(self, child_name: str) -> List[Person]:
395 """
396 子の親を検索
398 Args:
399 child_name: 子の氏名
401 Returns:
402 親のリスト
403 """
404 try:
405 result = self.client.execute(
406 RelationshipQueries.FIND_PARENTS,
407 {"child_name": child_name}
408 )
409 return [self.person_repo._to_person(record["parent"]) for record in result]
411 except Exception as e:
412 self.logger.error(f"Failed to find parents of {child_name}: {e}")
413 raise DatabaseException(f"親の検索エラー: {str(e)}")
415 def find_spouse(self, person_name: str) -> Optional[Person]:
416 """
417 配偶者を検索
419 Args:
420 person_name: 氏名
422 Returns:
423 配偶者、存在しない場合はNone
424 """
425 try:
426 result = self.client.execute(
427 RelationshipQueries.FIND_SPOUSE,
428 {"person_name": person_name}
429 )
431 if not result:
432 return None
434 return self.person_repo._to_person(result[0]["spouse"])
436 except Exception as e:
437 self.logger.error(f"Failed to find spouse of {person_name}: {e}")
438 raise DatabaseException(f"配偶者の検索エラー: {str(e)}")
440 def find_siblings(self, person_name: str) -> List[tuple[Person, BloodType]]:
441 """
442 兄弟姉妹を検索
444 Args:
445 person_name: 氏名
447 Returns:
448 (兄弟姉妹, 血縁タイプ)のリスト
449 """
450 try:
451 result = self.client.execute(
452 RelationshipQueries.FIND_SIBLINGS,
453 {"person_name": person_name}
454 )
456 siblings = []
457 for record in result:
458 sibling = self.person_repo._to_person(record["sibling"])
459 blood_type = BloodType(record["r"]["blood_type"]) if "r" in record else BloodType.FULL
460 siblings.append((sibling, blood_type))
462 return siblings
464 except Exception as e:
465 self.logger.error(f"Failed to find siblings of {person_name}: {e}")
466 raise DatabaseException(f"兄弟姉妹の検索エラー: {str(e)}")
469class InheritanceRepository:
470 """
471 相続計算用のリポジトリ
473 相続人の取得など、相続計算に特化したクエリを実行する。
474 """
476 def __init__(self, client: Neo4jClient) -> None:
477 """
478 初期化
480 Args:
481 client: Neo4jクライアント
482 """
483 self.client = client
484 self.person_repo = PersonRepository(client)
485 self.logger = logging.getLogger(__name__)
487 def get_spouse(self) -> Optional[Person]:
488 """
489 配偶者を取得
491 Returns:
492 配偶者、存在しない場合はNone
493 """
494 try:
495 result = self.client.execute(InheritanceQueries.GET_SPOUSE)
497 if not result:
498 return None
500 return self.person_repo._to_person(result[0]["spouse"])
502 except Exception as e:
503 self.logger.error(f"Failed to get spouse: {e}")
504 raise DatabaseException(f"配偶者取得エラー: {str(e)}")
506 def get_first_rank_heirs(self) -> List[Person]:
507 """
508 第1順位相続人(子)を取得
510 Returns:
511 子のリスト
512 """
513 try:
514 result = self.client.execute(InheritanceQueries.GET_FIRST_RANK_HEIRS)
515 return [self.person_repo._to_person(record["child"]) for record in result]
517 except Exception as e:
518 self.logger.error(f"Failed to get first rank heirs: {e}")
519 raise DatabaseException(f"第1順位相続人取得エラー: {str(e)}")
521 def get_second_rank_heirs(self) -> List[Person]:
522 """
523 第2順位相続人(直系尊属)を取得
525 Returns:
526 直系尊属のリスト
527 """
528 try:
529 result = self.client.execute(InheritanceQueries.GET_SECOND_RANK_HEIRS)
530 return [self.person_repo._to_person(record["ancestor"]) for record in result]
532 except Exception as e:
533 self.logger.error(f"Failed to get second rank heirs: {e}")
534 raise DatabaseException(f"第2順位相続人取得エラー: {str(e)}")
536 def get_third_rank_heirs(self) -> List[tuple[Person, BloodType]]:
537 """
538 第3順位相続人(兄弟姉妹)を取得
540 Returns:
541 (兄弟姉妹, 血縁タイプ)のリスト
542 """
543 try:
544 result = self.client.execute(InheritanceQueries.GET_THIRD_RANK_HEIRS)
546 heirs = []
547 for record in result:
548 sibling = self.person_repo._to_person(record["sibling"])
549 blood_type = BloodType(record["blood_type"])
550 heirs.append((sibling, blood_type))
552 return heirs
554 except Exception as e:
555 self.logger.error(f"Failed to get third rank heirs: {e}")
556 raise DatabaseException(f"第3順位相続人取得エラー: {str(e)}")