import os import re def process_file(filepath): with open(filepath, 'r', encoding='utf-8') as f: content = f.read() # 1. Update logger statements # We want to match: _logger.LogInformation("something", args) or _logger.LogError(ex, "something", args) # This regex is a bit tricky, let's use a simpler approach or careful regex. # Pattern to match logger.Log...( optionally (ex, ) then string then args ) # Let's match: (logger\.Log[A-Za-z]+)\((.*?)"(.*?)"(.*?)\) # Wait, multi-line strings or strings with escaped quotes might break it. # The regex approach: # Match: (logger\.Log(?:Information|Warning|Error))\(([^"]*)"([^"]*)"(.*)\) # If the string already starts with "[{Channel}] ", skip it. def replacer(m): func_part = m.group(1) # e.g. _logger.LogInformation pre_str = m.group(2) # e.g. (ex, or empty if it's the first arg) msg_str = m.group(3) post_str = m.group(4) if "[{Channel}]" in msg_str: return m.group(0) new_msg = f"[{{Channel}}] {msg_str}" # append "FundamentalsChannel" as the first argument after the string, or right after if there are no args if post_str.strip().startswith(','): # args exist, we need to insert our channel arg before the existing ones, but wait, the channel arg corresponds to {Channel} which is FIRST in the string, so we must pass "FundamentalsChannel" as the FIRST format argument. new_post = f', "FundamentalsChannel"{post_str}' elif post_str.strip() == '': # no extra args, just the closing paren new_post = f', "FundamentalsChannel")' # Note: m.group(4) didn't include the closing paren if we don't match it. Let's adjust the regex to match up to closing paren. else: new_post = f', "FundamentalsChannel"{post_str}' return f'{func_part}({pre_str}"{new_msg}"{new_post}' # Regex to capture the parts. # group 1: logger.Log... # group 2: anything before the first quote (like exception) # group 3: the string itself # group 4: the rest of the arguments up to the closing parenthesis # We need to find all instances. Let's do a line-by-line or simple regex. lines = content.split('\n') new_lines = [] # 2. Add /// to public methods # Method pattern: public (async )?(Task|void|[A-Za-z0-9_<>]+) [A-Za-z0-9_]+\(.*\) method_pattern = re.compile(r'^\s*public\s+(?:async\s+)?[A-Za-z0-9_<>\[\]]+\s+[A-Za-z0-9_]+\(.*') for i, line in enumerate(lines): # Apply logger transformation # We look for _logger.LogInformation, _logger.LogWarning, _logger.LogError, logger.LogError etc. if '.LogInformation(' in line or '.LogWarning(' in line or '.LogError(' in line: # simple replacement logic match = re.search(r'([_a-zA-Z0-9]+\.Log(?:Information|Warning|Error))\(([^"]*)"(.*?)"(.*)\)', line) if match: func_part = match.group(1) pre_str = match.group(2) msg_str = match.group(3) post_str = match.group(4) if "[{Channel}]" not in msg_str: new_msg = f"[{{Channel}}] {msg_str}" if post_str.strip() == ')': new_post = ', "FundamentalsChannel")' elif post_str.endswith(');'): new_post = ', "FundamentalsChannel");' # strip the ); from post_str for clean insertion post_str = post_str[:-2] new_post = f', "FundamentalsChannel"{post_str});' else: new_post = f', "FundamentalsChannel"{post_str}' line = f'{line[:match.start()]}{func_part}({pre_str}"{new_msg}"{new_post}{line[match.end():]}' # Check for public method to add /// # We need to make sure we don't add it if it already has one. # Also interface methods: Task Something(); if method_pattern.match(line): # Check previous line if i > 0 and '///' not in lines[i-1] and '[' not in lines[i-1]: indent = len(line) - len(line.lstrip()) summary = ' ' * indent + '/// \n' + ' ' * indent + '/// \n' + ' ' * indent + '/// ' new_lines.append(summary) # Interface methods inside public interface if re.match(r'^\s*(?:Task|void|[A-Za-z0-9_<>\[\]]+)\s+[A-Za-z0-9_]+\(.*', line): if i > 0 and '///' not in lines[i-1] and '[' not in lines[i-1]: # check if we are in an interface # kinda hard with just line by line, but let's try. pass new_lines.append(line) with open(filepath, 'w', encoding='utf-8') as f: f.write('\n'.join(new_lines)) def main(): dirs = ['Services', 'Util', '.'] base = r'e:\Projects\Finlytic\FinlyticFundamentals' for d in dirs: p = os.path.join(base, d) if os.path.isdir(p): for file in os.listdir(p): if file.endswith('.cs'): filepath = os.path.join(p, file) process_file(filepath) if __name__ == "__main__": main()