Source code for gitlab_overviewer.models.mapper

  1"""
  2Model mapping implementation.
  3
  4Implements :any:`/specs/spec_model_mapping` §1-5, covering:
  5
  6* General mapping rules (§1)
  7* Entity-specific details (§2)
  8* Content extraction rules (§3)
  9* Derived metadata extraction (§4)
 10* Error handling (§5)
 11
 12"""
 13
 14from __future__ import annotations
 15
 16from datetime import datetime, timezone
 17from typing import Any, Mapping
 18
 19from ..config.settings import Settings
 20
 21from ..rendering.renderer_base import Renderer
 22
 23from .group import Group
 24from .project import Project
 25from .issue import Issue
 26from .readme import Readme
 27
 28ISO_KEYS = {"last_activity_at"}
 29
 30
[docs] 31def _parse_dates(data: Mapping[str, Any]) -> dict[str, Any]: # noqa: D401 32 """Return *data* copy with ISO date strings converted to datetime.""" 33 converted: dict[str, Any] = {} 34 for k, v in data.items(): 35 if k in ISO_KEYS and isinstance(v, str): 36 parsed_date = Renderer.parse_iso_date(v) 37 converted[k] = parsed_date if parsed_date is not None else v 38 else: 39 converted[k] = v 40 return converted
41 42
[docs] 43def _robust_type_conversion(data: Mapping[str, Any]) -> dict[str, Any]: 44 """Convert types and handle nulls/str/int mismatches for all fields.""" 45 result = dict(data) 46 # Convert known int fields 47 int_fields = ["id"] 48 for key in int_fields: 49 if key in result and result[key] is not None: 50 result[key] = Renderer.safe_int(result[key], result[key]) 51 # Convert date fields 52 for key in ISO_KEYS: 53 if key in result and result[key] is not None: 54 v = result[key] 55 if isinstance(v, str): 56 parsed_date = Renderer.parse_iso_date(v) 57 if parsed_date is not None: 58 result[key] = parsed_date 59 return result
60 61 62# ---------- Public mapper helpers ------------------------------------------ 63 64
[docs] 65def group_from_json(data: Mapping[str, Any]) -> Group: # noqa: D401 66 return Group.from_api_json(_robust_type_conversion(_parse_dates(data)))
67 68
[docs] 69def project_from_json(data: Mapping[str, Any]) -> Project: # noqa: D401 70 return Project.from_api_json(_robust_type_conversion(_parse_dates(data)))
71 72
[docs] 73def issue_from_json(data: Mapping[str, Any]) -> Issue: # noqa: D401 74 return Issue.from_api_json(_robust_type_conversion(_parse_dates(data)))
75 76
[docs] 77def readme_from_str( 78 project_id: int, content: str, ref: str = "main", path: str = "README.md" 79) -> Readme: # noqa: D401 80 """Create Readme from content string with extracted metadata.""" 81 from ..services.readme_extraction import ( 82 extract_readme_data, 83 extract_first_paragraph, 84 extract_todo_sections, 85 ) 86 87 extra = extract_readme_data(content) 88 first_paragraph = extract_first_paragraph(content) or "" 89 todo_md = extract_todo_sections(content, Settings.current().todo_keywords) 90 return Readme( 91 project_id=project_id, 92 content=first_paragraph, 93 full_content=content, 94 extra=extra, 95 todo=todo_md, 96 ref=ref, 97 path=path, 98 )
99 100 101__all__ = [ 102 "group_from_json", 103 "project_from_json", 104 "issue_from_json", 105 "readme_from_str", 106]