def __init__(self):
     self.db_registered_product = RegisteredProductDatabase()
     self.db_product = ProductDatabase()
     self.db_category = CategoryDatabase()
     self.db_update = LogDatabase()
     self.db_injection = InjectData()
     self.interface = ConsoleCreateView()
class CreateDatabase:
    """ This class manages the creation of the database. """
    def __init__(self):
        self.db_registered_product = RegisteredProductDatabase()
        self.db_product = ProductDatabase()
        self.db_category = CategoryDatabase()
        self.db_update = LogDatabase()
        self.db_injection = InjectData()
        self.interface = ConsoleCreateView()

    def prepare(self):
        """ This method creates all the necessary databases for the application """

        # Databases are created
        self.interface.start_db_creation()
        self.db_registered_product.create_db()
        self.db_product.create_db()
        self.db_category.create_db()
        self.db_update.create_db()

        # Foreign keys are created
        self.db_product.create_keys()
        self.db_registered_product.create_keys()

        # Databases are feed
        self.interface.start_get_categories()
        self.db_injection.feed_categories()
        self.db_injection.feed_products()

        # Update date is registered
        self.db_update.inject_update_date()
        self.interface.end_creation()
    def __init__(self):
        self.daily_date = datetime.date.today()
        self.db_registered_product = RegisteredProductDatabase()
        self.db_update = LogDatabase()
        self.db_category = CategoryDatabase()
        self.db_product = ProductDatabase()
        self.db_injection = InjectData()
        self.interface = ConsoleUpdateView()

        self.update = self.__update_decision()
class UpdateDatabase:
    """ This class manages all the actions linked to the update of the database """

    def __init__(self):
        self.daily_date = datetime.date.today()
        self.db_registered_product = RegisteredProductDatabase()
        self.db_update = LogDatabase()
        self.db_category = CategoryDatabase()
        self.db_product = ProductDatabase()
        self.db_injection = InjectData()
        self.interface = ConsoleUpdateView()

        self.update = self.__update_decision()

    def update_database(self, force=False):
        """ This method updates the database with modification identified in API response """
        if self.update or force:
            self.interface.start_update()
            saved_products_ref_list = self.db_registered_product.get_products_ref()

            #We delete registered_product and product tables and recreate them empty
            self.db_registered_product.create_db()
            self.db_product.create_db()
            self.db_product.create_keys()
            self.db_registered_product.create_keys()

            #We feed the products for each get_categories
            self.interface.start_feed_product()
            self.db_injection.feed_products()

            #for each ref from saved_products_ref_tuple:
            for product_ref in saved_products_ref_list:
                product_id = self.db_product.get_product_from_ref(product_ref)
                print(product_ref)
                if product_id:
                    print('injection produit disponible')
                    self.db_registered_product.inject_product(product_ref, 'disponible')
                else:
                    print('injection produit indisponible')
                    self.db_registered_product.inject_product(product_ref, 'indisponible')

            #Don't forget to update the update date
            self.db_update.inject_update_date()
            self.interface.end_update()

    def __update_decision(self):
        """ This method decides if an update of the database has to be done """
        last_update_date = self.db_update.get_last_update_date() #type: datetime.date
        duration = self.daily_date - last_update_date #type: datetime.timedelta

        if duration.days >= 7:
            return True
    def __init__(self):
        self.interface = ConsoleApplicationView()
        self.db_category = CategoryDatabase()
        self.db_product = ProductDatabase()
        self.db_registered_product = RegisteredProductDatabase()
        self.update = UpdateDatabase()

        self.cat_information = ()
        self.cat_id_selected = 0
        self.prd_tuple = ()
        self.subst_prd_tuple = ()

        self.play = True
class Application:
    """ This class is the main class of the program. It manages the main loop
    and all the necessary features. The start() method is the brain of the
    application and triggers all the features when it is necessary. """
    def __init__(self):
        self.interface = ConsoleApplicationView()
        self.db_category = CategoryDatabase()
        self.db_product = ProductDatabase()
        self.db_registered_product = RegisteredProductDatabase()
        self.update = UpdateDatabase()

        self.cat_information = ()
        self.cat_id_selected = 0
        self.prd_tuple = ()
        self.subst_prd_tuple = ()

        self.play = True

    def start(self):
        """ This method manages the different steps of the application"""
        while self.play:
            self.update.update_database()
            action = self.action_choice()
            if action == 'research':
                self.category_selection()
                self.product_selection()
                self.selected_substitute_products()
                self.save_substitute_product()
            else:
                self.consult_product_saved()
            #Quit or new action?
            self.new_action()

    def category_selection(self):
        """ This method manages the category selection """
        self.cat_id_selected = 0  #to be sure to ask category each time
        self.cat_information = self.db_category.get_categories_with_id()
        self.interface.print_category_selection(self.cat_information)
        while self.cat_id_selected < 1 or self.cat_id_selected > len(
                self.cat_information):
            try:
                self.cat_id_selected = int(input("numéro de la catégorie: "))
            except:
                self.interface.print_error_input_not_int()
        self.interface.print_category_selected(self.cat_information,
                                               self.cat_id_selected)

    def product_selection(self):
        """ This method manages the product selection """

        self.prd_tuple = self.db_product.get_dirty_product_from_category(
            self.cat_id_selected)
        self.interface.print_product_selection(self.prd_tuple)
        prd_number = self.__prd_input(self.prd_tuple)
        #prd_number - 1 because index starts from zero
        self.interface.print_product_selected(self.prd_tuple, prd_number - 1)

    def selected_substitute_products(self):
        """ This method manages the selection of substitute products """

        self.subst_prd_tuple = self.db_product.get_subsitute_products(
            self.cat_id_selected)
        self.interface.print_subsitute_products(self.subst_prd_tuple)

    def save_substitute_product(self):
        """ This method manages the substitute product registered feature """

        save = True
        while save:
            self.interface.print_save_product_question()
            answer = 'W'
            while answer != 'Y' and answer != 'N':
                answer = input("Votre réponse: ").upper()

            if answer == 'Y':
                self.interface.print_product_to_save_question()
                prd_number = self.__prd_input(self.subst_prd_tuple)
                self.__product_save_process(self.subst_prd_tuple[prd_number -
                                                                 1][5])
            else:
                save = False
                self.interface.print_end_save_process_message()

    def new_action(self):
        """ This method manages the process of reloading a research or stopping
        the application """
        self.interface.print_new_action()
        answer = 'W'
        while answer != 'Y' and answer != 'N':
            answer = input("Votre réponse: ").upper()

        if answer == 'Y':
            self.play = True
        else:
            self.play = False
            self.interface.print_good_bye_message()

    def action_choice(self):
        """This method manages the choice of the user between doing a research
        or seeing his saved products"""
        self.interface.print_action_choice()
        answer = 0
        while answer != 1 and answer != 2:
            try:
                answer = int(input("Votre choix:"))
            except:
                self.interface.print_error_input_not_int()

        if answer == 1:
            return 'research'

    def consult_product_saved(self):
        """ This method prints all the products saved by the user"""
        products = self.db_registered_product.get_all_products_saved()
        self.interface.print_products_saved(products)

    def __prd_input(self, product_tuple):
        """ This method manages the product selection """
        prd_number = 0
        while prd_number < 1 or prd_number > len(product_tuple):
            try:
                prd_number = int(input("numéro de produit: "))
            except:
                self.interface.print_error_input_not_int()

        return prd_number

    def __product_save_process(self, product_ref):
        """ This private method manages the process to save a product """
        product_to_save = self.db_registered_product.get_product_from_ref(
            product_ref)
        #Si le produit a déjà été enregistré
        if product_to_save:
            self.interface.print_product_already_saved()
        else:
            self.db_registered_product.inject_product(product_ref,
                                                      'disponible')
            self.interface.print_selected_product_to_save()
 def __init__(self):
     self.api = OpenFoodFactsInteractions()
     self.db_category = CategoryDatabase()
     self.db_product = ProductDatabase()
     self.db_update = LogDatabase()
     self.interface = ConsoleInjectionView()
class InjectData:
    """ This class manages all the actions linked to data injection into
    the database """
    def __init__(self):
        self.api = OpenFoodFactsInteractions()
        self.db_category = CategoryDatabase()
        self.db_product = ProductDatabase()
        self.db_update = LogDatabase()
        self.interface = ConsoleInjectionView()

    def feed_categories(self):
        """ This method injects the categories from OpenFoodFacts into the database """
        #self.api.get_categories()
        self.db_category.inject_categories(self.api.category_list)

    def feed_products(self):
        """ This method injects the products linked to the categories selected
        into the database """

        category_list = self.db_category.get_categories_name()

        page_size = 1000
        for category in category_list:
            self.interface.product_injection_per_category(category)
            page_number = self.api.get_product_pages_number(
                category, str(page_size))
            for page in range(1, page_number + 1, 1):
                data = self.api.get_product_page(category, str(page_size),
                                                 str(page))
                self.__manage_products_injection(data['products'], category)

    def __manage_products_injection(self, data, category):
        for product in data:
            product_info = self.__get_product_information(product)
            if product_info['injection']:
                self.db_product.inject_product(product_info, category)

    def __get_product_information(self, product):
        """ This method cleans and get the needed product informations """

        product_info = {
            'injection': True,
            'url': '',
            'nutrition_grade_fr': '',
            'product_name_fr': '',
            'code': '',
            'stores': '',
            'generic_name_fr': ''
        }

        for key in product_info:
            try:
                if str(product[key]).split():
                    product_info[key] = str(product[key])
                else:
                    raise KeyError
            except KeyError:
                product_info[key] = 'NULL'
                if key == 'nutrition_grade_fr' or key == 'product_name' or key == 'code':
                    product_info['injection'] = False

        return product_info