""" This module has no real meaning. Simply implemented for evaluating students. """ from pathlib import Path from itertools import chain import re import string def list_filenames_of_log_files(*foldernames) -> list: """ List all the `*.log` files stored in the folders specified by foldernames. This method returns a list containing the corresponding strings. Ordered in alphabetical order. This method does not print anything. :param foldernames: :return: list of filenames :raises FileNotFoundError: If one of the speficied folders does not exist """ def walk(folder): if not folder.exists(): raise FileNotFoundError(f"{folder} does not exist") for subfold, childfolds, files in folder.walk(): for f in files: if ".log" in f: yield subfold / f # final cast as list would generally be omited but due to the test expecting # an indexable object, this cast is need. It would be more idiomatic to return # a generator and iterate over it tho. return list(chain(*[walk(Path(foldername)) for foldername in foldernames])) def list_ipv4_listed_in_logfiles(*foldernames) -> list: """ Parse all the `*.log` files stored in the folders specified by foldernames. This method returns a list of ip adresses stored in those files. This method does not print anything. Only IPv4 format are listed No duplicates are returned. :param foldernames: :return: list of ips :raises FileNotFoundError: If one of the speficied folders does not exist """ def get_ip_from_file(file): matcher = re.compile(r"(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})") with open(file) as f: for line in f: if match := matcher.search(line): yield match.group() # cast to a set to delete duplicates # cast to a list 'caus the test is hardcoded to get a list return list(set(chain(*[get_ip_from_file(file) for file in list_filenames_of_log_files(*foldernames)]))) def cryptarithme(message: str = "SEND + MORE = MONEY") -> list: """ The `message` is a formatted mathematical operation. Each letter represents a number between 0 and 9. Each letter represents a different value. Find the value for each letter so that the full sentence is true. Knowing that if the word is longer than 1 letter, the first letter of the word cannot be a 0. It can occur that several results are possible. If so, all solutions must be returned in differents dictionnairies. The order is not relevant. Example : A + A = C returns [ {"A" : 1, "C" : 2}, {"A" : 2, "C" : 4}, {"A" : 3, "C" : 6}, {"A" : 4, "C" : 8} ] :param message: The string to analyse :return: List of dictionnaries with the following format : { 'letter1': value1, 'letter2': value2 } :raises ValueError: If the string cannot be parsed properly. :raises CryptarithmeError : If the string is not a possible Cryptarithme """ # check for syntax for i, character in enumerate(message): if character not in [*string.ascii_uppercase, "+", "=", " "]: raise ValueError(f"illegal character {character} at {i}") if len([i for i in message if i == "="]) >= 2 or len([i for i in message if i == "+"]) >= 2: raise ValueError("too much =") return message class CryptarithmeError(Exception): pass if __name__ == "__main__": # list_filenames_of_log_files('data') # list_ipv4_listed_in_logfiles('data') # cryptarithme('A + A +A= B') pass