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)
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)
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))
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 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))
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'
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)
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()
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)
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)
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()
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
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
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)