todonotifier.todo_notifier

This module contains the core logic of the application

 1"""This module contains the core logic of the application
 2"""
 3
 4import logging
 5import os
 6import re
 7from typing import Dict, List
 8
 9from todonotifier.constants import UNKNOWN_USER_NAME
10from todonotifier.models import POSITION, TODO, USER
11from todonotifier.utils import (
12    compute_file_line_no_to_chars_map,
13    compute_line_and_pos_given_span,
14)
15
16# logging configuration
17logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(process)d - %(name)s - %(levelname)s - %(message)s")
18logger = logging.getLogger(__name__)
19
20
21def parse_files_for_todo_items(project_parent_dir: str, files: List[str], ignore_todo_case: bool) -> Dict[str, List[TODO]]:
22    """Parses the list of `files` one by one to collect all todo items
23
24    Args:
25        project_parent_dir (str): Parent directory of the project folder (required to get relative path of files and avoid exposing temporary paths)
26        files (List[str]): List of all files that need to be parsed
27        ignore_todo_case (bool): Boolean whether to look for case insensitive todo items like todo, Todo etc.
28
29    Returns:
30        Dict[str, List[TODO]]: Returns a key-value pair where key is relative path of file parsed and value is list of todo objects in that file
31    """
32    all_todos_objs = {}
33    for file in files:
34        try:
35            rel_file_path = os.path.relpath(file, project_parent_dir)
36            all_todos_objs[rel_file_path] = []
37            line_no_to_chars_map = compute_file_line_no_to_chars_map(file)
38            with open(file, "r") as f:
39                file_content = f.read()
40
41                flags = re.MULTILINE
42                if ignore_todo_case:
43                    flags |= re.IGNORECASE
44
45                todo_items = re.finditer(r"TODO.*", file_content, flags=flags)
46                for todo_item_idx, todo_item in enumerate(todo_items):
47                    try:
48                        todo_item_group = todo_item.group()
49                        todo_date_username = re.findall(r"TODO\s*(\{.*\})?\s*(@[^\s]*)?\s*(.*)?", todo_item_group, flags=flags)
50
51                        if todo_date_username:
52                            todo_date_username = todo_date_username[0]
53
54                        msg = ""
55                        if len(todo_date_username) > 2:
56                            msg = todo_date_username[2]
57
58                        user = USER(UNKNOWN_USER_NAME)  # By default we assume an unknown user
59                        if len(todo_date_username) > 1:
60                            user = USER(todo_date_username[1][1:] or UNKNOWN_USER_NAME)  # handle empty string
61
62                        completion_date_str = ""
63                        if len(todo_date_username) > 0:
64                            completion_date_str = todo_date_username[0]
65                            if completion_date_str:
66                                completion_date_str = completion_date_str[1:-1]
67
68                        module = rel_file_path
69                        todo_position = todo_item.span()
70
71                        line = compute_line_and_pos_given_span(line_no_to_chars_map, todo_position)
72                        position = POSITION(line)
73
74                        todo = TODO(msg, user, completion_date_str, module, position)
75
76                        all_todos_objs[rel_file_path].append(todo)
77                    except Exception:
78                        logger.exception(f"Error in parsing todo item: {todo_item}, idx: {todo_item_idx}, all_todos: {all_todos_objs}")
79        except Exception:
80            logger.exception(f"Error in parsing todo items in file: {file}")
81
82    return all_todos_objs
logger = <Logger todonotifier.todo_notifier (INFO)>
def parse_files_for_todo_items( project_parent_dir: str, files: List[str], ignore_todo_case: bool) -> Dict[str, List[todonotifier.models.TODO]]:
22def parse_files_for_todo_items(project_parent_dir: str, files: List[str], ignore_todo_case: bool) -> Dict[str, List[TODO]]:
23    """Parses the list of `files` one by one to collect all todo items
24
25    Args:
26        project_parent_dir (str): Parent directory of the project folder (required to get relative path of files and avoid exposing temporary paths)
27        files (List[str]): List of all files that need to be parsed
28        ignore_todo_case (bool): Boolean whether to look for case insensitive todo items like todo, Todo etc.
29
30    Returns:
31        Dict[str, List[TODO]]: Returns a key-value pair where key is relative path of file parsed and value is list of todo objects in that file
32    """
33    all_todos_objs = {}
34    for file in files:
35        try:
36            rel_file_path = os.path.relpath(file, project_parent_dir)
37            all_todos_objs[rel_file_path] = []
38            line_no_to_chars_map = compute_file_line_no_to_chars_map(file)
39            with open(file, "r") as f:
40                file_content = f.read()
41
42                flags = re.MULTILINE
43                if ignore_todo_case:
44                    flags |= re.IGNORECASE
45
46                todo_items = re.finditer(r"TODO.*", file_content, flags=flags)
47                for todo_item_idx, todo_item in enumerate(todo_items):
48                    try:
49                        todo_item_group = todo_item.group()
50                        todo_date_username = re.findall(r"TODO\s*(\{.*\})?\s*(@[^\s]*)?\s*(.*)?", todo_item_group, flags=flags)
51
52                        if todo_date_username:
53                            todo_date_username = todo_date_username[0]
54
55                        msg = ""
56                        if len(todo_date_username) > 2:
57                            msg = todo_date_username[2]
58
59                        user = USER(UNKNOWN_USER_NAME)  # By default we assume an unknown user
60                        if len(todo_date_username) > 1:
61                            user = USER(todo_date_username[1][1:] or UNKNOWN_USER_NAME)  # handle empty string
62
63                        completion_date_str = ""
64                        if len(todo_date_username) > 0:
65                            completion_date_str = todo_date_username[0]
66                            if completion_date_str:
67                                completion_date_str = completion_date_str[1:-1]
68
69                        module = rel_file_path
70                        todo_position = todo_item.span()
71
72                        line = compute_line_and_pos_given_span(line_no_to_chars_map, todo_position)
73                        position = POSITION(line)
74
75                        todo = TODO(msg, user, completion_date_str, module, position)
76
77                        all_todos_objs[rel_file_path].append(todo)
78                    except Exception:
79                        logger.exception(f"Error in parsing todo item: {todo_item}, idx: {todo_item_idx}, all_todos: {all_todos_objs}")
80        except Exception:
81            logger.exception(f"Error in parsing todo items in file: {file}")
82
83    return all_todos_objs

Parses the list of files one by one to collect all todo items

Arguments:
  • project_parent_dir (str): Parent directory of the project folder (required to get relative path of files and avoid exposing temporary paths)
  • files (List[str]): List of all files that need to be parsed
  • ignore_todo_case (bool): Boolean whether to look for case insensitive todo items like todo, Todo etc.
Returns:

Dict[str, List[TODO]]: Returns a key-value pair where key is relative path of file parsed and value is list of todo objects in that file