From 2154c439df93821228bd303ecfb4d84b4ceb4ec7 Mon Sep 17 00:00:00 2001 From: keplyx Date: Tue, 25 Feb 2020 16:20:56 +0100 Subject: [PATCH] Updated Zapette API and script --- ajax/scan_article.php | 35 ------- ajax/zapette.php | 10 ++ classes/dao.php | 8 ++ classes/zapetteHandler.php | 121 ++++++++++++++++++++++++ zapette.py | 187 ++++++++++++++++++++++++++++++++----- 5 files changed, 301 insertions(+), 60 deletions(-) delete mode 100644 ajax/scan_article.php create mode 100644 ajax/zapette.php create mode 100644 classes/zapetteHandler.php diff --git a/ajax/scan_article.php b/ajax/scan_article.php deleted file mode 100644 index 8e640e0..0000000 --- a/ajax/scan_article.php +++ /dev/null @@ -1,35 +0,0 @@ -articles as $articleObject) { - if ($articleObject->code === $_POST["code"] && intval($articleObject->quantity) > 0) { - $returnVal = $articleObject->name . "\n". $articleObject->price . "€"; - $articleObject->quantity = strval(intval($articleObject->quantity) - 1); - } -} - -// open the file and write the updated stock -$fp = fopen('../data/stock.json', 'w'); -fwrite($fp, json_encode($result)); -fclose($fp); - -echo $returnVal; - diff --git a/ajax/zapette.php b/ajax/zapette.php new file mode 100644 index 0000000..6aba367 --- /dev/null +++ b/ajax/zapette.php @@ -0,0 +1,10 @@ +do_action()); diff --git a/classes/dao.php b/classes/dao.php index 78f3405..36d6dd8 100644 --- a/classes/dao.php +++ b/classes/dao.php @@ -35,6 +35,14 @@ class Dao return $cursor->fetchAll(PDO::FETCH_ASSOC); } + public function get_article_of_code($code) + { + $sql = 'SELECT * FROM articles WHERE code=?'; + $cursor = $this->conn->prepare($sql); + $cursor->execute([$code]); + return $cursor->fetchAll(PDO::FETCH_ASSOC)[0]; + } + public function get_categories_of_article($articleid) { $sql = 'SELECT category_id FROM article_categories WHERE article_id=?'; diff --git a/classes/zapetteHandler.php b/classes/zapetteHandler.php new file mode 100644 index 0000000..eb48180 --- /dev/null +++ b/classes/zapetteHandler.php @@ -0,0 +1,121 @@ + 0, + "message" => "Success", + "data" => "", + ); + + public function __construct($post) + { + $this->read_password(); + $this->given_password = $this->get_password($post); + $this->action = $this->get_action($post); + $this->data = $this->get_data($post); + $this->dao = new Dao(); + } + + public function do_action() + { + $result = ""; + if ($this->is_password_valid()) { + if ($this->action == "scan") { + $result = $this->get_scanned_article(); + if (sizeof($result) == 0) + $this->setUnknownCodeErrorResponse(); + } else if ($this->action == "validate") { + $result = $this->update_stock(); + if (!$result) + $this->setUpdateStockErrorResponse(); + } + } else { + $this->setWrongPasswordErrorResponse(); + } + + + $this->responseArray["data"] = $result; + return $this->responseArray; + } + + private function read_password() { + $real_path = __DIR__ . "/.htpasszapette"; + $fp = fopen($real_path, 'r'); + $this->password = trim(fread($fp, filesize($real_path))); + fclose($fp); + } + + private function is_password_valid() { + return $this->given_password == $this->password; + } + + private function get_scanned_article() { + $article = []; + if ($this->data != "") { + $article = $this->dao->get_article_of_code($this->data); + } + return $article; + } + + private function update_stock() { + $result = false; + if ($this->data != "") { + foreach ($this->data as $row) { + $result = $this->dao->update_article_stock($row["id"], $row["quantity"]); + } + } + return $result; + } + + function setWrongPasswordErrorResponse() + { + $this->responseArray["status"] = 1; + $this->responseArray["message"] = "Error: Wrong password"; + } + + function setUnknownCodeErrorResponse() + { + $this->responseArray["status"] = 2; + $this->responseArray["message"] = "Error: Unknown code scanned"; + } + + function setUpdateStockErrorResponse() + { + $this->responseArray["status"] = 3; + $this->responseArray["message"] = "Error: Impossible to update stock"; + } + + private function get_action($inputData) + { + if (!in_array($inputData["action"], $this->valid_actions)) + return ""; + else + return $inputData["action"]; + } + + private function get_data($inputData) + { + if ($inputData["data"] == null) + return ""; + else + return $inputData["data"]; + } + + private function get_password($inputData) + { + if ($inputData["password"] == null) + return ""; + else + return $inputData["password"]; + } +} diff --git a/zapette.py b/zapette.py index ab7b028..d63cd2c 100644 --- a/zapette.py +++ b/zapette.py @@ -1,37 +1,174 @@ # run pip3 install requests import requests import json +import os +from signal import signal, SIGINT +from sys import exit -API_ENDPOINT = "https://srv-falcon.etud.insa-toulouse.fr/~proximo/ajax/scan_article.php" +# API_ENDPOINT = "https://etud.insa-toulouse.fr/~proximo/ajax/zapette.php" +API_ENDPOINT = "http://localhost/proximo/ajax/zapette.php" -def get_password(): - with open('pass') as f: - password = f.readline() - return password.strip() +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' +class scan_types: + SELL = 'v', + BUY = 'a', -def search_product(code): - # data to be sent to api - data = { - 'password': get_password(), - 'code': str(code) - } - # sending post request and saving response as response object - r = requests.post(url=API_ENDPOINT, data=json.dumps(data)) - return r.text +class error_types: + NONE = 0, + NETWORK = 1, + URL = 2, + INPUT = 3, + NO_EXIST = 4, +class Scanner: + + def __init__(self): + self.ask_type() + self.password = self.get_password() + self.scannedArticles = [] + + def ask_type(self): + typeInput = input("\nVoulez vous " + bcolors.OKGREEN + "acheter" + bcolors.ENDC + " ou " + bcolors.FAIL + "vendre" + bcolors.ENDC + " ? [" + bcolors.OKGREEN + "a" + bcolors.ENDC + "/" + bcolors.FAIL + "v" + bcolors.ENDC + "] ") + if (typeInput.lower() == 'a'): + self.type = scan_types.BUY + else: + self.type = scan_types.SELL + + def get_password(self): + with open('pass') as f: + password = f.readline() + return password.strip() + + def display_type(self): + if (self.type == scan_types.SELL): + print(" ==> Mode " + bcolors.FAIL + bcolors.BOLD + "VENTE") + else: + print(" ==> Mode " + bcolors.OKGREEN + bcolors.BOLD + "ACHAT") + print(bcolors.ENDC) + + def scan_product(self, code): + data = { + 'password': self.password, + 'action': 'scan', + 'data': str(code) + } + r = requests.post(url=API_ENDPOINT, data=json.dumps(data)) + if (r.json()['status'] == 0): + article = r.json()["data"] + self.scannedArticles.append(article) + return r.json()['status'] == 0 + + def display_cart(self): + total = 0.0 + for article in self.scannedArticles: + print(article["name"] + ' : ' + bcolors.BOLD + article["price"] + '€' + bcolors.ENDC) + total += float(article["price"]) + # Print only to only 2 decimals + total_display = "{:.2f}".format(total) + print(bcolors.OKGREEN + "Total: " + bcolors.BOLD + total_display + '€' + bcolors.ENDC) + + def send_cart(self): + scanned_list = [] + modifier = -1 if self.type == scan_types.SELL else 1 + for article in self.scannedArticles: + scanned_list.append({"id": article["id"], "quantity": modifier}) + data = { + 'password': self.password, + 'action': 'validate', + 'data': scanned_list + } + r = requests.post(url=API_ENDPOINT, data=json.dumps(data)) + return r.json()['status'] == 0 + +def ask_confirmation(message): + confirm_input = input(message) + return confirm_input.lower() == 'o' + +def clear_screen(): + os.system('clear') + print("Appuyez sur " + bcolors.BOLD + "[CTRL + C]" + bcolors.ENDC + " à tout moment pour quitter.\n") + +def printStartScreen(): + clear_screen() + print(bcolors.BOLD) + print(bcolors.WARNING + "#########################################") + print("#/ \#") + print("# " + bcolors.FAIL + "-=|" + bcolors.OKGREEN + " ZAPETTE " + bcolors.FAIL + "|=-" + bcolors.WARNING + " #") + print("#\ /#") + print("#########################################") + print(bcolors.ENDC) + print("Bienvenue dans le programme de la Zapette !") + +def display_scan_header(scanner, last_error): + clear_screen() + scanner.display_type() + print("Scannez le codes puis appuyez sur [ENTRÉE] pour valider.") + print("Appuyez sur [ENTRÉE] sans code pour valider la commande.\n") + scanner.display_cart() + if (last_error == error_types.URL): + print(bcolors.FAIL + "Format URL invalide !" + bcolors.ENDC) + elif (last_error == error_types.NETWORK): + print(bcolors.FAIL + "URL invalide !" + bcolors.ENDC) + elif (last_error == error_types.INPUT): + print(bcolors.FAIL + "Code invalide !" + bcolors.ENDC) + elif (last_error == error_types.NO_EXIST): + print(bcolors.FAIL + "L'article n'existe pas." + bcolors.ENDC) + print() + +def confirm_end_scan(scanner): + display_scan_header(scanner, error_types.NONE) + return ask_confirmation("Voulez vous vraiment terminer et envoyer les modifications ? [o/n] ") + +def handler(signal_received, frame): + os.system('clear') + print('Programme de la zapette terminé.') + exit(0) + +def validate_cart(scanner): + clear_screen() + print("Envoi des modifications au serveur...") + if (scanner.send_cart()): + print(bcolors.OKGREEN + bcolors.BOLD + "Succès !" + bcolors.ENDC) + else: + print(bcolors.FAIL + bcolors.BOLD + "Échec !" + bcolors.ENDC) + input("\nAppuyez sur [ENTRÉE] pour continuer...") def main(): - code_input = input('Scannez le code\n') - try: - code = int(code_input) - result = search_product(code) - print(result) - except requests.exceptions.MissingSchema: - print("Format URL invalide !") - except requests.exceptions.ConnectionError: - print("URL invalide !") - except ValueError: - print("Code invalide !") + signal(SIGINT, handler) + printStartScreen() + while True: + scanner = Scanner() + last_error = error_types.NONE + while True: + display_scan_header(scanner, last_error) + code_input = input('=> ') + if (code_input == ""): + if (confirm_end_scan(scanner)): + validate_cart(scanner) + break + else: + continue + try: + code = int(code_input) + if (scanner.scan_product(code)): + last_error = error_types.NONE + else: + last_error = error_types.NO_EXIST + except requests.exceptions.MissingSchema: + last_error = error_types.URL + except requests.exceptions.ConnectionError: + last_error = error_types.NETWORK + except ValueError: + last_error = error_types.INPUT + clear_screen() main()