Coverage for putki/discover.py: 45.35%
58 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-04 22:07:46 +00:00
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-04 22:07:46 +00:00
1import pathlib
2from typing import Union, no_type_check
4import yaml
5from git.exc import GitCommandError, InvalidGitRepositoryError, NoSuchPathError
6from git.repo import Repo
7from putki import ENCODING
10@no_type_check
11def root(within: Union[str, pathlib.Path] = '.') -> str:
12 """Detect the root folder for tasks."""
13 try:
14 repo = Repo(within, search_parent_directories=True)
15 try:
16 repo_root = repo.git.rev_parse(show_toplevel=True)
17 start_here = pathlib.Path(repo_root)
18 for path in start_here.rglob('*'): 18 ↛ 21line 18 didn't jump to line 21, because the loop on line 18 didn't complete
19 if path.name == 'tasks' and path.is_dir():
20 return str(path)
21 return ''
22 except GitCommandError:
23 return ''
24 except (InvalidGitRepositoryError, NoSuchPathError):
25 return ''
28@no_type_check
29def tasks(below: Union[str, pathlib.Path] = '.') -> dict[str, list[dict[str, str]]]:
30 """Collect the tasks below by mapping the paths to lists of key value string maps."""
31 jobs: dict[str, list[dict[str, str]]] = {}
32 start_here = pathlib.Path(below)
33 for path in start_here.rglob('**/*'):
34 if path.is_file() and path.stat().st_size and path.suffix.lower() in ('.yaml', '.yml'):
35 with open(path, 'rt', encoding=ENCODING) as handle:
36 data = yaml.safe_load(handle)
37 if data and data.get('tasks', None):
38 jobs[str(path.parent)] = data['tasks']
39 return jobs
42@no_type_check
43def combine(jobs: dict[str, list[dict[str, str]]]) -> list[dict[str, str]]:
44 """Combine the tasks by mapping the local ids to path prefixed ids."""
45 tasks_seq: list[dict[str, str]] = []
46 common = min(sorted(jobs.keys()))
47 for path, job_seq in jobs.items():
48 prefix = path.replace(common, '', 1)
49 for task in job_seq:
50 local_id = task['id']
51 task['id'] = f'{prefix}/{local_id}'
52 tasks_seq.append(task)
53 return tasks_seq
56@no_type_check
57def assemble_path(path_elements: dict[str, str]) -> str:
58 """Assemble a connection string from /source/path_elements."""
59 address_template = path_elements['address_template']
60 user = path_elements.get('user')
61 return (
62 address_template.replace('{{protocol}}', path_elements['protocol'])
63 .replace('{{user}}', user if user is not None else '')
64 .replace('{{host}}', path_elements['host'])
65 .replace('{{port}}', path_elements['port'])
66 .replace('{{service_root}}', path_elements['service_root'])
67 )
70@no_type_check
71def derive(
72 tasks_seq: list[dict[str, Union[str, dict[str, str]]]]
73) -> dict[str, dict[str, Union[str, int, dict[str, str]]]]:
74 """Derive map with actionable names by mapping the path prefixed ids and assembling path elements."""
75 actions: dict[str, dict[str, Union[str, int, dict[str, str]]]] = {}
76 for slot, task in enumerate(tasks_seq):
77 folder_slug = task['id'].replace('/', '_')
79 action: dict[str, Union[str, int]] = {**task, 'rank': slot}
80 source: dict[str, Union[str, dict[str, str]]] = task['source']
81 action['url'] = source['path'] if source.get('path') else assemble_path(source['path_elements'])
82 actions[folder_slug] = action
84 return actions