<jupyter_start><jupyter_text>Problem 1```Imagine that you have the following scenario: you have a file containing some text, in case the text contains the word "error", you want to log it as an error (in a certain way that you can decide), otherwise, if the text contains the word "file", you should log something in a new file (again, you can decide hot to structure the log), otherwise, if its not one of the 2 cases, you need to log something in the console (again, you can decide hot to structure the log). The actions of logging can be simple print statements, they just need to be different for error logs console logs and file logs. You need to implement this logic using chain of responsibility design pattern and classes of your choice. Use the diagram below as a guide.``` ![image.png](attachment:image.png)<jupyter_code>class AbstractLogger:    
    LEVELS = {
        0: 'info',
        1: 'debug',
        2: 'error',
    }
  
  
    def __init__(self, level = 0):  
        self.__next_logger = None
        self.level = level
        
    def set_next_logger(self, next_logger):
        self.__next_logger = next_logger
  
    def log_message(self, body):  
        written = self._write(body)
    
        if not written:
            self.__next_logger.log_message(body)
  
    def _write(self, body):  
        raise NotImplementedError('It is not implemented at this level yet.')
        
        
class ConsoleLogger(AbstractLogger):
    
    def _write(self, body):
        print(f'CONSOLE: Logging the incoming request body: {body}')
        
        return True
        

class ErrorLogger(AbstractLogger):
    
    def _write(self, body):
        if 'error' in body:
            print(f'ERROR: Logging the incoming request body: {body}')

            return True
        
        
class FileLogger(AbstractLogger):
    
    def _write(self, body):
        if 'file' in body:
            print(f'FILE: Logging the incoming request body: {body}')

            return True
        
        
class Client:
  
    def __init__(self):
        console_logger = ConsoleLogger()
        error_logger = ErrorLogger()
        file_logger = FileLogger()

        error_logger.set_next_logger(file_logger)
        file_logger.set_next_logger(console_logger)
    
        self.logger = error_logger

    def call(self, request):
        self.logger.log_message(request.body)
        

class Request:
    
    def __init__(self, body):
        self.body = body
        
        
requests = [
    Request('<<First request>>: lorem ipsum dolor file sit amet, consectetur error adipiscing elit.'),
    Request('<<Second request>>: lorem ipsum file dolor sit amet, consectetur adipiscing elit.'),
    Request('<<Third request>>: lorem ipsum dolor sit amet, error consectetur adipiscing elit.'),
    Request('<<Third request>>: lorem ipsum dolor sit amet, consectetur adipiscing elit.'),
]

client = Client()

for req in requests:
    client.call(req)<jupyter_output>ERROR: Logging the incoming request body: <<First request>>: lorem ipsum dolor file sit amet, consectetur error adipiscing elit.
FILE: Logging the incoming request body: <<Second request>>: lorem ipsum file dolor sit amet, consectetur adipiscing elit.
ERROR: Logging the incoming request body: <<Third request>>: lorem ipsum dolor sit amet, error consectetur adipiscing elit.
CONSOLE: Logging the incoming request body: <<Third request>>: lorem ipsum dolor sit amet, consectetur adipiscing elit.<jupyter_text>Problem 2```Imagine that you have customers with different orders, a waiter and a cook. Use command design pattern and classes of your choice to implenent this logic. You can use the diagram below as a guide. ``` ![image.png](attachment:image.png)<jupyter_code>from abc import ABC, abstractmethod
  
class Order(ABC):
    
    """command"""
      
    def __init__(self, cook):
        self.cook = cook
      
    @abstractmethod
    def place_order(self, dishes):
        pass

class Waiter(Order):
    
    """director"""
      
    def __init__(self, cook):
        self.cook = cook
  
    def place_order(self, dishes):
        self.cook.do_cooking(dishes)


class Cook:
    
    """receiver"""
      
    def do_cooking(self, dishes):
        print(f'Cooking {", ".join(dishes)} ...')


class Customer:
    
    """client"""

    def waiter(self, waiter):
        self.waiter = waiter
  
    def order(self, dishes):
        self.waiter.place_order(dishes)


cook = Cook()
waiter = Waiter(cook)
customer = Customer()
customer.waiter(waiter)
customer.order(['Checken salad', 'Pasta', 'Ice tea', 'Cheese cake'])<jupyter_output>Cooking Checken salad, Pasta, Ice tea, Cheese cake ...<jupyter_text>Problem 3```Imagine that you have different chat rooms and different users who can become members of one or multiple chat rooms. Create a system with such possibilities using a mediator design pattern. You can use the diagram below as a guide. ``` ![image.png](attachment:image.png)<jupyter_code>from abc import ABC, abstractmethod
  
class ApnaChatRoom(ABC):
      
    @abstractmethod
    def show_msg(self, msg, participant):
        pass
    

class ApnaChatRoomImpl(ApnaChatRoom):
    
    def show_msg(self, msg, participant):
        print(f'[{participant.get_name()}]: {msg}')
        
        
class Participant(ABC):
      
    @abstractmethod
    def set_name(self, name):
        pass
    
    @abstractmethod
    def get_name(self):
        pass
    
    @abstractmethod
    def send_msg(self, msg):
        pass
    
    
class User1(Participant):


    def __init__(self, chat):
        self.__chat = chat
        
    def set_name(self, name):
        self.__name = name
        
    def get_name(self):
        return self.__name
    
    def send_msg(self, msg):
        self.__chat.show_msg(msg, self)
        
        
class User2(Participant):


    def __init__(self, chat):
        self.__chat = chat
        
    def set_name(self, name):
        self.__name = name
        
    def get_name(self):
        return self.__name
    
    def send_msg(self, msg):
        self.__chat.show_msg(msg, self)
        
        
class MediatorPatternDemo:
    
    def main(self):
        chat = ApnaChatRoomImpl()
        
        john = User1(chat)
        john.set_name('<NAME>')
        
        jane = User1(chat)
        jane.set_name('<NAME>')
        
        michael = User2(chat)
        michael.set_name('<NAME>')
        
        jane.send_msg('Hey all!')
        michael.send_msg('Hi! How are you doing?')
        john.send_msg("Hello! What's up?")
        
mediator = MediatorPatternDemo()
mediator.main()<jupyter_output>[<NAME>]: Hey all!
[<NAME>]: Hi! How are you doing?
[<NAME>]: Hello! What's up?
{'fraction_non_alphanumeric': 0.09621121718377089, 'fraction_numerical': 0.0019391408114558472, 'mean_word_length': 2.6380900705371677, 'pattern_counts': {'":': 0, '<': 32, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 8, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '129183', 'license': 'MIT', 'n_tokens_mistral': 2038, 'n_tokens_neox': 1970, 'n_words': 640, 'path': 'homeworks/10/design_patterns.ipynb', 'repo_name': 'armgabrielyan/python-2', 'size': '6730'}