Пример #1
0
    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)
Пример #2
0
def login():
    """
    Landing page.
    Launches and handles login form if needed.
    """
    # Database access + loads basic info
    Database.on()
    params = models.Params()
    lang = utils.Lang(params.lang)
    Database.off()

    # Prepare template data
    data = {'lang': lang.__dict__, 'body_class': 'login', 'error': False}

    # If the user is already logged : go to "grocery_list"
    if 'is_logged' in session and session['is_logged']:
        return redirect(url_for('groceries'))

    # Handle post data
    if request.form and 'password' in request.form:
        password = request.form['password']
        password = bytearray(password, encoding='utf-8')
        password = hashlib.sha1(password).hexdigest()

        # Right password
        if password == params.user_password:
            session['is_logged'] = True
            return redirect(url_for('groceries'))
        # Wrong password
        else:
            data['error'] = True

    return render_template('login.html', **data)
Пример #3
0
def api_products_list_key(api):
    """
    API method : Gets all items from the products table.
    Outputs JSON.
    Returns the latest version of the grocery list.
    JSON format :
    - {"status": ..., "items" ...}
    Possible return status :
    - OK (200)
    """
    # AJAX Auth check
    Database.on()
    # Load parameters
    params = models.Params()
    # Database : end connection
    # (open it only when needed because this program is meant to be shutdown harshly)
    Database.off()
    # Database access
    api_key = params.api_key
    if api_key != api:
        return render_template('json.html', json="{}"), 401
    # Output
    data = {"status": "OK", "items": []}

    # Get info
    Database.on()
    data['items'] = models.Products().get_list()
    Database.off()
    # Render
    return render_template('json.html', json=json.dumps(data))
Пример #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)
Пример #5
0
def api_lang():
    """
    Returns JSON of the lang file
    """
    # Database access + loads basic info
    Database.on()
    lang = utils.Lang(models.Params().lang)
    lang = lang.__dict__
    Database.off()

    # Return template
    return render_template('json.html', json=json.dumps(lang))
Пример #6
0
    def test_execute_invalid(self, monkeypatch):
        """
        Tests the configuration assistant with invalid parameters.
        Uses a dummy database.
        Success conditions :
        - The database has been created and contains fallback data
        """
        # Language
        lang = Lang()

        # Monkeypatch : inputs
        def input_monkeypatched_invalid(phrase):
            """
            Returns what input() should have returned : invalid inputs
            """
            if phrase == lang.config_buzzer_on:
                return "Y"

            if phrase == lang.config_buzzer_port:
                return "NaN"

            if phrase == lang.config_camera_res_x:
                return "Foo"

            if phrase == lang.config_camera_res_y:
                return "Bar"

            if phrase == lang.config_password:
                return ""

            if phrase == lang.config_language_set:
                return 'xxx'

        monkeypatch.setitem(__builtins__, 'input', input_monkeypatched_invalid)
        monkeypatch.setattr(getpass, 'getpass', input_monkeypatched_invalid)

        # Launch
        controllers.Config.execute()

        # Test
        params = models.Params()
        assert params.buzzer_on == 0
        assert params.buzzer_port == 0
        assert params.camera_res_x == 500
        assert params.camera_res_y == 500
        assert params.user_password
        assert params.lang == 'en'
Пример #7
0
def products():
    """
    Shows PRODUCTS template if the user is logged.
    """
    # Auth check
    if 'is_logged' not in session or not session['is_logged']:
        return redirect(url_for('login'))

    # Database access + loads basic info
    Database.on()
    lang = utils.Lang(models.Params().lang)
    Database.off()

    # Prepare template data
    data = {'lang': lang.__dict__, 'body_class': 'products'}

    # Return template
    return render_template('products.html', **data)
Пример #8
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()
Пример #9
0
def groceries():
    """
    Shows GROCERIES template if the user is logged.
    """
    # Auth check
    if 'is_logged' not in session or not session['is_logged']:
        return redirect(url_for('login'))

    # Database access + loads basic info
    Database.on()
    lang = utils.Lang(models.Params().lang)
    items = models.Products().get_list()
    Database.off()

    # Prepare template data
    data = {
        'lang': lang.__dict__,
        'body_class': 'groceries',
        'products_list': items
    }

    # Return template
    return render_template('groceries.html', **data)
Пример #10
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)
Пример #11
0
    def execute(cls):
        """
        This method constantly tries to catch barcodes from the camera :
        if it does, search for info and add it to the user's grocery list.
        """
        # Intro
        print("-" * 80)
        print("[:{ Jean-Pierre's Barcode Scanner : Running")
        print("-" * 80)

        # Database connection
        Database.on()

        # Load parameters
        params = models.Params()

        # Database : end connection
        # (open it only when needed because this program is meant to be shutdown harshly)
        Database.off()

        # Camera setup
        camera = picamera.PiCamera()
        camera.sharpness = 100
        camera.brightness = 50
        camera.contrast = 25
        camera.resolution = (params.camera_res_x, params.camera_res_y)
        camera.start_preview()

        # Double beep to inform the user that Jean-Pierre is ready
        if params.buzzer_on:
            buzzer = utils.Buzzer(params.buzzer_port)
            buzzer.beep(2)

        # Capture loop
        last_scan = ''
        empty_scans = 0
        while True:
            # Get an image from the camera
            stream = BytesIO()
            camera.capture(stream, format='jpeg')
            stream.seek(0)

            # Scan image for barcodes
            barcodes = pyzbar_decode(pil_image.open(stream))[::-1]

            # Count every empty scan
            if not barcodes:
                empty_scans += 1
                # clean last_scan every 3 empty scans in a row
                # Note : this allows to add an item multiple times
                # by just leaving it in front of the camera.
                if empty_scans == 3:
                    last_scan = ''
                    empty_scans = 0

            # If something has been scanned
            if barcodes:

                # Get the first readable EAN-13 barcode from the scan
                barcode = False
                for seek in barcodes:
                    if seek.type == 'EAN13':
                        barcode = seek.data.decode()
                        break

                if not barcode:
                    continue

                # Break count of empty scans in a row
                empty_scans = 0

                # If the item has just been scanned : ignore this round
                if barcode == last_scan:
                    continue

                # Add found item to history
                last_scan = barcode
                print("Scanned and considered : {}".format(barcode))

                # Beep !
                if params.buzzer_on:
                    buzzer.beep()

                # Analyze it in a separate thread
                utils.FindProduct(barcode).start()
Пример #12
0
def api_products_editt_key(api, barcode, name):
    """
    API method : Add/Edit/Delete items from the products database.
    If an item doesn't exist, it will be created.
    Outputs JSON.
    :param barcode: An unknown product barcode
    :param name: The new product's name
    JSON format :
    - {"status": ..., "barcode" ..., "name": ...}
    Possible return status :
    - ADDED (200) / ADD ERROR (400)
    - EDITED (200) / EDIT ERROR (400)
    """
    # AJAX Auth check
    Database.on()
    # Load parameters
    params = models.Params()
    # Database : end connection
    # (open it only when needed because this program is meant to be shutdown harshly)
    Database.off()
    # Database access
    api_key = params.api_key
    if api_key == api:
        session['is_logged'] = True
    #   return redirect(url_for('groceries'))
    # Remove unwanted chars
    to_escape = ['"', "<", ">", "&", "'"]
    for char in to_escape:
        barcode = barcode.replace(char, '')
        name = name.replace(char, '')

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

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

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

    # If it doesn't exist : add it
    if not exists:
        try:
            products_db.add_item(barcode, name, False)
            data['status'] = 'ADDED'
        except Exception as trace:
            data['status'] = 'ADD ERROR'
            status_code = 400
    # If it exists : edit it
    else:
        try:
            products_db.edit_item(barcode, name, exists['pic'])
            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
Пример #13
0
def api_groceries_edit_key(api, 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
    Database.on()
    # Load parameters
    params = models.Params()
    # Database : end connection
    # (open it only when needed because this program is meant to be shutdown harshly)
    Database.off()
    # Database access
    api_key = params.api_key
    if api_key != api:
        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
Пример #14
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)