def setup_method(self):
        """
        Setup method :
        - Sets database to test mode and populates it
        - Launches Flask in test mode
        """
        # Database
        Database.TEST_MODE = True
        Database.on()

        # Create test database
        models.Params(autoload=False).create_table()
        models.Products().create_table()
        models.Groceries().create_table()

        # Params : user config
        self.password_raw = 'abcdefg'
        self.password_sha1 = bytearray(self.password_raw, encoding='utf-8')
        self.password_sha1 = hashlib.sha1(self.password_sha1).hexdigest()
        params = models.Params()
        params.add_item('user_password', self.password_sha1)
        params.add_item('lang', 'en')

        # Products : 1 sample item
        self.default_barcode = '1234567890123'
        self.default_name = 'Lorem Ipsum'
        models.Products().add_item(self.default_barcode, self.default_name,
                                   True)

        # Groceries : 1 sample item
        models.Groceries().add_item(self.default_barcode, 1)
Beispiel #2
0
    def test_run_valid(self):
        """
        Tests FindProduct process with both valid and invalid inputs
        Success conditions :
        - Valid input : an item has been added to the Products and Groceries databases
        - Invalid input : an "unknown" item has been added to the Products and Groceries databases
        """
        # Launch threads
        thread_valid = utils.FindProduct(self.valid_barcode)
        thread_valid.start()

        thread_invalid = utils.FindProduct(self.invalid_barcode)
        thread_invalid.start()

        thread_valid.join()
        thread_invalid.join()

        # Tests
        groceries = models.Groceries(
        )  # Re-opens the DB connexion too, closed by FindProduct
        valid = groceries.get_item(self.valid_barcode)
        invalid = groceries.get_item(self.invalid_barcode)
        assert valid
        assert valid['name'] != '???'
        assert invalid
        assert invalid['name'] == '???'
    def test_api_groceries_list(self):
        """
        Tests the "api_groceries_list" API method.
        Success conditions :
        - The API can't be accessed without being logged : HTTP 401
        - Returns HTTP 200 when logged with the expected content as JSON
        """
        with webapp.test_client() as app:
            # Tests access without being authenticated
            response = app.get('/api/groceries_list')
            assert response.status_code == 401

            # Authenticate
            data = {'password': self.password_raw}
            response = app.post('/', data=data, follow_redirects=True)

            # Does the API returns the expected data ?
            # JSON "item" entry must contain the same thing as Groceries > get_list
            response = app.get('/api/groceries_list')

            expected_data = models.Groceries().get_list()
            given_data = str(response.data, encoding='utf-8')
            given_data = json.loads(given_data)
            given_data = given_data['items'].keys()

            assert response.status_code == 200
            assert set(expected_data) == set(given_data)
Beispiel #4
0
    def setup_method(self):
        """
        Creates a dummy database for tests
        """
        # Creates database
        Database.TEST_MODE = True
        Database.on()
        self.cursor = Database.CURSOR

        # Create tables
        self.params = models.Params(autoload=False)
        self.products = models.Products()
        self.groceries = models.Groceries()
        self.params.create_table()
        self.products.create_table()
        self.groceries.create_table()

        # Insert dummy data
        self.default_barcode = '123456789ABCD'
        self.default_name = 'Lorem Ipsum'
        self.default_pic = 'PIC'

        self.params.add_item('foo', 'bar')
        self.products.add_item(self.default_barcode, self.default_name,
                               self.default_pic)
        self.groceries.add_item(self.default_barcode, 1)
    def test_api_groceries_edit(self):
        """
        Tests the "api_groceries_edit" API method.
        Success conditions :
        - The API can't be accessed without being logged : HTTP 401
        - Returns HTTP 200 when logged with the expected content as JSON
        - Perfoms correctly ADD / EDIT / DELETE operations
        """
        with webapp.test_client() as app:
            # Tests access without being authenticated
            response = app.get('/api/groceries_edit/' + self.default_barcode +
                               '/1')
            assert response.status_code == 401

            # Authenticate
            data = {'password': self.password_raw}
            response = app.post('/', data=data, follow_redirects=True)

            # Test : delete, valid input
            response = app.get('/api/groceries_edit/' + self.default_barcode +
                               '/0')
            assert response.status_code == 200
            assert not models.Groceries().get_item(self.default_barcode)

            # Test : add, valid input
            response = app.get('/api/groceries_edit/' + self.default_barcode +
                               '/2')
            entry = models.Groceries().get_item(self.default_barcode)
            assert response.status_code == 200
            assert entry
            assert entry['barcode'] == self.default_barcode
            assert entry['quantity'] == 2

            # Test : edit, valid input
            response = app.get('/api/groceries_edit/' + self.default_barcode +
                               '/4')
            entry = models.Groceries().get_item(self.default_barcode)
            assert response.status_code == 200
            assert entry
            assert entry['barcode'] == self.default_barcode
            assert entry['quantity'] == 4
Beispiel #6
0
    def delete_item(self, barcode):
        """
        Deletes a product from the database
        :param barcode: barcode
        :type barcode: str
        :rtype: bool
        """
        # Deletion
        query = """
                DELETE FROM Products WHERE `barcode` = ?;
                """
        params = (barcode, )
        Database.LINK.execute(query, params)
        Database.LINK.commit()

        # Side effect : delete it from the grocery list
        models.Groceries().delete_item(barcode)

        return True
Beispiel #7
0
    def setup_method(self):
        """
        Setup method, creates a dummy database
        """
        # Creates test database
        Database.TEST_MODE = True
        Database.on()
        self.cursor = Database.CURSOR

        # Create tables
        models.Params(autoload=False).create_table()
        models.Products().create_table()
        models.Groceries().create_table()

        # Defaults barcodes
        self.valid_barcode = '3017620424403'
        self.invalid_barcode = '123456789ABCD'

        # Deconnects database to let threads use them
        Database.off()
Beispiel #8
0
def api_groceries_list():
    """
    API method : Gets all items from the grocery list.
    Outputs JSON.
    Returns the latest version of the grocery list.
    JSON format :
    - {"status": ..., "items" ...}
    Possible return status :
    - OK
    """
    # AJAX Auth check
    if not ('is_logged' in session and session['is_logged']):
        return render_template('json.html', json="{}"), 401

    # Output
    data = {"status": "OK", "items": []}

    # Get info
    Database.on()
    data['items'] = models.Groceries().get_list()
    Database.off()

    # Render
    return render_template('json.html', json=json.dumps(data))
Beispiel #9
0
    def execute(cls, language='en'):
        """
        Creates database, tables and ask for user parameters.
        :param language: Set language for this menu
        :type language: str
        Defined parameters :
        - buzzer_on
        - buzzer_port
        - camera_res_x
        - camera_res_y
        - user_password
        - lang
        """
        # Language file
        lang = Lang(language)

        # Intro
        print("-" * 80)
        print(lang.config_intro)
        print("-" * 80)

        # Create / connects to the database
        Database.on()
        print(lang.config_database_created)

        # Create table : params
        params = models.Params(autoload=False)
        params.create_table()
        print(lang.config_table_params_set)

        # Create table : groceries
        groceries = models.Groceries()
        groceries.create_table()
        print(lang.config_table_groceries_set)

        # Create table : products
        products = models.Products()
        products.create_table()
        print(lang.config_table_products_set)

        # Set language
        params.delete_item('lang')
        language = input(lang.config_language_set)
        if language not in Lang.available():
            language = 'en'
        params.add_item('lang', language)

        # Ask for : use buzzer / on which port ?
        params.delete_item('buzzer_on')
        params.delete_item('buzzer_port')
        buzzer_on = input(lang.config_buzzer_on)

        # Yes
        if buzzer_on.upper() == "Y":
            buzzer_port = input(lang.config_buzzer_port)
            if re.findall('([0-9]+)', buzzer_port):
                params.add_item('buzzer_on', '1')
                params.add_item('buzzer_port', buzzer_port)
            else:
                print(lang.config_buzzer_invalid_port)
                params.add_item('buzzer_on', '0')
                params.add_item('buzzer_port', '0')
        # No
        else:
            params.add_item('buzzer_on', '0')
            params.add_item('buzzer_port', '0')

        # Ask for : camera resolution
        params.delete_item('camera_res_x')
        params.delete_item('camera_res_y')

        for axis in ['x', 'y']:
            if axis == 'x':
                question = lang.config_camera_res_x
            else:
                question = lang.config_camera_res_y

            resolution = input(question)
            if not re.findall('([0-9]+)', resolution):
                resolution = 500

            params.add_item('camera_res_{}'.format(axis), resolution)

        # Ask for : user password
        params.delete_item('user_password')
        user_password = getpass.getpass(lang.config_password)
        if not user_password:
            user_password = '******'
        user_password = bytearray(user_password, encoding='utf-8')
        user_password = hashlib.sha1(user_password).hexdigest()
        params.add_item('user_password', user_password)

        # Close connection to the database
        Database.off()

        # Bye !
        print(lang.config_done)
Beispiel #10
0
def api_groceries_edit(barcode, quantity):
    """
    API method : Add/Edit/Delete items from the grocery list.
    If the item doesn't exist, it will be created.
    Outputs JSON.
    :param barcode: A known product barcode
    :param quantity: The quantity defines the operation (quantity 0 = delete)
    JSON format :
    - {"status": ..., "barcode" ..., "quantity": ...}
    Possible return status :
    - PRODUCT NOT FOUND (404)
    - ADDED (200) / ADD ERROR (400)
    - EDITED (200) / EDIT ERROR (400)
    - DELETED (200) / DELETE ERROR (400)
    """
    # AJAX Auth check
    if not ('is_logged' in session and session['is_logged']):
        return render_template('json.html', json="{}"), 401

    # Remove unwanted chars
    to_escape = ['"', "<", ">", "&", "'"]
    for char in to_escape:
        barcode = barcode.replace(char, '')
    quantity = int(quantity)

    # Output
    data = {"status": "", "barcode": barcode, "quantity": quantity}

    # Database access
    Database.on()
    products_db = models.Products()
    groceries_db = models.Groceries()

    # Try to get the product associated with the barcode
    product = products_db.get_item(barcode)

    if not product:
        data['status'] = 'PRODUCT NOT FOUND'
        return render_template('json.html', json=json.dumps(data)), 404

    # Try to get the entry in the grocery list
    exists = groceries_db.get_item(barcode)
    status_code = 200

    # If it doesn't exist : add it
    if not exists:
        try:
            if not quantity:
                quantity = 1
            groceries_db.add_item(barcode, quantity)
            data['status'] = 'ADDED'
        except Exception as trace:
            data['status'] = 'ADD ERROR'
            status_code = 400
    # If it exists :
    else:
        # If quantity = 0 : Delete
        if quantity <= 0:
            try:
                groceries_db.delete_item(barcode)
                data['status'] = 'DELETED'
            except Exception as trace:
                data['status'] = 'DELETE ERROR'
                status_code = 400
        # If quantity > 0 : Edit quantity
        else:
            try:
                groceries_db.edit_item(barcode, abs(quantity))
                data['status'] = 'EDITED'
            except Exception as trace:
                data['status'] = 'EDIT ERROR'
                status_code = 400

    # Database : off and outputs data
    Database.off()
    return render_template('json.html', json=json.dumps(data)), status_code
Beispiel #11
0
    def run(self):
        """
        Fetch, gathers, insert product infos and adds it to the grocery list.
        Threaded
        :rtype: bool
        """
        with FindProduct.LOCK:
            # Database connection
            Database.on()

            # Marks
            found = False
            cache = False
            products = models.Products()
            groceries = models.Groceries()
            message = ""

            # Try to find the product in the local products database
            cache = products.get_item(self.barcode)
            if cache:
                found = True
                self.name = cache['name']
                self.barcode = cache['barcode']
                message += "{barcode} : Found {name} from local products database.\n"

            # If not found locally : Try to find the product in the OpenFoodFacts API
            if not found:
                # If found on OpenFoodFacts : add it to the local database
                if self.__fetch_openfoodfacts():
                    found = True
                    products.add_item(self.barcode, self.name, self.pic)
                    message += "{barcode} : Found {name} from OpenFoodFacts.\n"
                    message += "{barcode} : {name} added to cache.\n"
                else:
                    message += "{barcode} : Not found locally nor on OpenFoodFacts.\n"

            # If not found : add as unknown item (name = ???)
            if not found:
                self.name = '???'
                self.pic = False
                products.add_item(self.barcode, self.name, self.pic)
                message += "{barcode} : Unknown product added to the database.\n"

            # If the product's already present in the grocery list: increase its quantity by 1
            previous = groceries.get_item(self.barcode)
            if previous:
                self.quantity = previous['quantity'] + 1
                groceries.edit_item(self.barcode, self.quantity)
                message += "{barcode} : {quantity} {name} now in grocery list.\n"
            # Otherwise : add it
            else:
                groceries.add_item(self.barcode, 1)
                message += "{barcode} : 1 {name} added to the grocery list.\n"

            # Disconnect the database, allowing it to be used by another thread
            Database.off()

            # Print message
            message = message.format(barcode=self.barcode,
                                     name=self.name,
                                     quantity=self.quantity)
            print(message)
Beispiel #12
0
    def execute(cls, language='en'):
        """
        Creates database, tables and ask for user parameters.
        :param language: Set language for this menu
        :type language: str
        Defined parameters :
        - buzzer_on
        - buzzer_port
        - camera_res_x
        - camera_res_y
        - user_password
        - lang
        - api_key
        """
        # Language file
        lang = Lang(language)

        # Intro
        print("-" * 80)
        print(lang.config_intro)
        print("-" * 80)

        # Create / connects to the database
        Database.on()
        print(lang.config_database_created)

        # Create table : params
        params = models.Params(autoload=False)
        params.create_table()
        print(lang.config_table_params_set)

        # Create table : groceries
        groceries = models.Groceries()
        groceries.create_table()
        print(lang.config_table_groceries_set)

        # Create table : products
        products = models.Products()
        products.create_table()
        print(lang.config_table_products_set)

        # Set language
        params.delete_item('lang')
        language = input(lang.config_language_set)
        if language not in Lang.available():
            language = 'en'
        params.add_item('lang', language)

        # Ask for : use buzzer / on which port ?
        params.delete_item('buzzer_on')
        params.delete_item('buzzer_port')
        buzzer_on = input(lang.config_buzzer_on)

        # Yes
        if buzzer_on.upper() == "Y":
            buzzer_port = input(lang.config_buzzer_port)
            if re.findall('([0-9]+)', buzzer_port):
                params.add_item('buzzer_on', '1')
                params.add_item('buzzer_port', buzzer_port)
            else:
                print(lang.config_buzzer_invalid_port)
                params.add_item('buzzer_on', '0')
                params.add_item('buzzer_port', '0')
        # No
        else:
            params.add_item('buzzer_on', '0')
            params.add_item('buzzer_port', '0')

        # Ask for : camera resolution
        params.delete_item('camera_res_x')
        params.delete_item('camera_res_y')

        for axis in ['x', 'y']:
            if axis == 'x':
                question = lang.config_camera_res_x
            else:
                question = lang.config_camera_res_y

            resolution = input(question)
            if not re.findall('([0-9]+)', resolution):
                resolution = 500

            params.add_item('camera_res_{}'.format(axis), resolution)

        # Ask for : user password
        params.delete_item('user_password')
        user_password = getpass.getpass(lang.config_password)
        if not user_password:
            user_password = '******'
        user_password = bytearray(user_password, encoding='utf-8')
        user_password = hashlib.sha1(user_password).hexdigest()
        params.add_item('user_password', user_password)

        import random
        random = random.SystemRandom()

        def get_random_string(length=12,
                              allowed_chars='abcdefghijklmnopqrstuvwxyz'
                              'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):

            return ''.join(random.choice(allowed_chars) for i in range(length))

        def get_secret_key():
            """
            Create a random secret key.
 
            Taken from the Django project.
            """
            chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$^*(-_=+)'
            return get_random_string(24, chars)

        # Ask for : api key
        params.delete_item('api_key')
        api_key = getpass.getpass(lang.config_api)
        if not api_key:
            api_key = (get_secret_key())
        params.add_item('api_key', api_key)
        print('Api-Key:  ' + api_key)

        # Close connection to the database
        Database.off()

        # Bye !
        print(lang.config_done)