Coverage for src/secchi/workflows/report.py: 42%

33 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-08-04 22:15 +0000

1"""Package and project report workflow.""" 

2 

3from dataclasses import dataclass 

4from pathlib import Path 

5 

6from secchi.config import find_config, load_project 

7from secchi.errors import ConfigError, ReportError 

8from secchi.renderers.reports import ( 

9 build_project_report, 

10 default_report_path, 

11 render_project_report, 

12 render_report, 

13) 

14from secchi.services.intelligence import PackageIntelligenceService 

15from secchi.services.resolver import parse_package_spec 

16from secchi.workflows.common import require_package 

17 

18 

19@dataclass(frozen=True) 

20class ReportOutput: 

21 content: str 

22 format_name: str 

23 target: Path 

24 

25 

26async def run( 

27 *, 

28 package: str | None = None, 

29 project_name: str | None = None, 

30 config: str | None = None, 

31 registry: str | None = None, 

32 format_name: str = "json", 

33 output: str | None = None, 

34 refresh: bool = False, 

35 service: PackageIntelligenceService | None = None, 

36) -> ReportOutput: 

37 normalized_format = "md" if format_name == "markdown" else format_name 

38 if project_name: 

39 config_path = find_config(config) 

40 if not config_path: 

41 raise ConfigError("No config found for project report.") 

42 project = load_project(config_path, project_name) 

43 pipeline = service or PackageIntelligenceService() 

44 intelligence = await pipeline.fetch_project( 

45 project.packages, force_refresh=refresh 

46 ) 

47 project_report = build_project_report(project, intelligence.results) 

48 content = render_project_report(normalized_format, project_report) 

49 subject = project.title or project.name 

50 target = ( 

51 Path(output) 

52 if output and output != "-" 

53 else default_report_path(subject, normalized_format, project=True) 

54 ) 

55 else: 

56 if not package: 

57 raise ReportError( 

58 "Provide a package name or --project PROJECT for a report." 

59 ) 

60 ref = parse_package_spec(package, registry) 

61 result = ( 

62 await require_package(ref, refresh, service=service) 

63 if service is not None 

64 else await require_package(ref, refresh) 

65 ) 

66 content = render_report( 

67 normalized_format, 

68 result.info, 

69 result.derived, 

70 ref, 

71 ref.name, 

72 result.warnings, 

73 ) 

74 target = ( 

75 Path(output) 

76 if output and output != "-" 

77 else default_report_path(ref.name, normalized_format) 

78 ) 

79 return ReportOutput(content, normalized_format, target)