Browse Source

Updated Zapette API and script

keplyx 1 year ago
parent
commit
2154c439df
5 changed files with 301 additions and 60 deletions
  1. 0
    35
      ajax/scan_article.php
  2. 10
    0
      ajax/zapette.php
  3. 8
    0
      classes/dao.php
  4. 121
    0
      classes/zapetteHandler.php
  5. 162
    25
      zapette.py

+ 0
- 35
ajax/scan_article.php View File

@@ -1,35 +0,0 @@
1
-<?php
2
-$rest_json = file_get_contents("php://input");
3
-$_POST = json_decode($rest_json, true);
4
-
5
-// Must have a code and password field
6
-$fp = fopen('.htpassajax', 'r');
7
-$password = trim(fread($fp, filesize('.htpassajax')));
8
-fclose($fp);
9
-
10
-if ($_POST["password"] != $password)
11
-    die("Wrong Password");
12
-
13
-// open the file and get the stock
14
-$file = '../data/stock-v2.json';
15
-$fp = fopen($file, 'r');
16
-$result = json_decode(fread($fp, filesize($file)));
17
-fclose($fp);
18
-
19
-$returnVal = 'N/A';
20
-
21
-// Get the price from the given code and remove 1 in quantity. Do not get the price if quantity is at 0
22
-foreach ($result->articles as $articleObject) {
23
-    if ($articleObject->code === $_POST["code"] && intval($articleObject->quantity) > 0) {
24
-        $returnVal = $articleObject->name . "\n". $articleObject->price . "€";
25
-        $articleObject->quantity = strval(intval($articleObject->quantity) - 1);
26
-    }
27
-}
28
-
29
-// open the file and write the updated stock
30
-$fp = fopen('../data/stock.json', 'w');
31
-fwrite($fp, json_encode($result));
32
-fclose($fp);
33
-
34
-echo $returnVal;
35
-

+ 10
- 0
ajax/zapette.php View File

@@ -0,0 +1,10 @@
1
+<?php
2
+$relativePath = "../";
3
+require_once $relativePath.'classes/zapetteHandler.php';
4
+
5
+$rest_json = file_get_contents("php://input");
6
+$_POST = json_decode($rest_json, true);
7
+
8
+$handler = new ZapetteHandler($_POST);
9
+
10
+echo json_encode($handler->do_action());

+ 8
- 0
classes/dao.php View File

@@ -35,6 +35,14 @@ class Dao
35 35
         return $cursor->fetchAll(PDO::FETCH_ASSOC);
36 36
     }
37 37
 
38
+    public function get_article_of_code($code)
39
+    {
40
+        $sql = 'SELECT * FROM articles WHERE code=?';
41
+        $cursor = $this->conn->prepare($sql);
42
+        $cursor->execute([$code]);
43
+        return $cursor->fetchAll(PDO::FETCH_ASSOC)[0];
44
+    }
45
+
38 46
     public function get_categories_of_article($articleid)
39 47
     {
40 48
         $sql = 'SELECT category_id FROM article_categories WHERE article_id=?';

+ 121
- 0
classes/zapetteHandler.php View File

@@ -0,0 +1,121 @@
1
+<?php
2
+require_once 'dao.php';
3
+
4
+class ZapetteHandler
5
+{
6
+    private $valid_actions = ["scan", "validate",];
7
+
8
+    private $action;
9
+    private $data;
10
+    private $given_password;
11
+    private $dao;
12
+    private $password;
13
+
14
+    private $responseArray = array(
15
+        "status" => 0,
16
+        "message" => "Success",
17
+        "data" => "",
18
+    );
19
+
20
+    public function __construct($post)
21
+    {
22
+        $this->read_password();
23
+        $this->given_password = $this->get_password($post);
24
+        $this->action = $this->get_action($post);
25
+        $this->data = $this->get_data($post);
26
+        $this->dao = new Dao();
27
+    }
28
+
29
+    public function do_action()
30
+    {
31
+        $result = "";
32
+        if ($this->is_password_valid()) {
33
+            if ($this->action == "scan") {
34
+                $result = $this->get_scanned_article();
35
+                if (sizeof($result) == 0)
36
+                    $this->setUnknownCodeErrorResponse();
37
+            } else if ($this->action == "validate") {
38
+                $result = $this->update_stock();
39
+                if (!$result)
40
+                    $this->setUpdateStockErrorResponse();
41
+            }
42
+        } else {
43
+            $this->setWrongPasswordErrorResponse();
44
+        }
45
+
46
+
47
+        $this->responseArray["data"] = $result;
48
+        return $this->responseArray;
49
+    }
50
+
51
+    private function read_password() {
52
+        $real_path = __DIR__ . "/.htpasszapette";
53
+        $fp = fopen($real_path, 'r');
54
+        $this->password = trim(fread($fp, filesize($real_path)));
55
+        fclose($fp);
56
+    }
57
+
58
+    private function is_password_valid() {
59
+        return $this->given_password == $this->password;
60
+    }
61
+
62
+    private function get_scanned_article() {
63
+        $article = [];
64
+        if ($this->data != "") {
65
+            $article = $this->dao->get_article_of_code($this->data);
66
+        }
67
+        return $article;
68
+    }
69
+
70
+    private function update_stock() {
71
+        $result = false;
72
+        if ($this->data != "") {
73
+            foreach ($this->data as $row) {
74
+                $result = $this->dao->update_article_stock($row["id"], $row["quantity"]);
75
+            }
76
+        }
77
+        return $result;
78
+    }
79
+
80
+    function setWrongPasswordErrorResponse()
81
+    {
82
+        $this->responseArray["status"] = 1;
83
+        $this->responseArray["message"] = "Error: Wrong password";
84
+    }
85
+
86
+    function setUnknownCodeErrorResponse()
87
+    {
88
+        $this->responseArray["status"] = 2;
89
+        $this->responseArray["message"] = "Error: Unknown code scanned";
90
+    }
91
+
92
+    function setUpdateStockErrorResponse()
93
+    {
94
+        $this->responseArray["status"] = 3;
95
+        $this->responseArray["message"] = "Error: Impossible to update stock";
96
+    }
97
+
98
+    private function get_action($inputData)
99
+    {
100
+        if (!in_array($inputData["action"], $this->valid_actions))
101
+            return "";
102
+        else
103
+            return $inputData["action"];
104
+    }
105
+
106
+    private function get_data($inputData)
107
+    {
108
+        if ($inputData["data"] == null)
109
+            return "";
110
+        else
111
+            return $inputData["data"];
112
+    }
113
+
114
+    private function get_password($inputData)
115
+    {
116
+        if ($inputData["password"] == null)
117
+            return "";
118
+        else
119
+            return $inputData["password"];
120
+    }
121
+}

+ 162
- 25
zapette.py View File

@@ -1,37 +1,174 @@
1 1
 # run pip3 install requests
2 2
 import requests
3 3
 import json
4
+import os
5
+from signal import signal, SIGINT
6
+from sys import exit
4 7
 
5
-API_ENDPOINT = "https://srv-falcon.etud.insa-toulouse.fr/~proximo/ajax/scan_article.php"
8
+# API_ENDPOINT = "https://etud.insa-toulouse.fr/~proximo/ajax/zapette.php"
9
+API_ENDPOINT = "http://localhost/proximo/ajax/zapette.php"
6 10
 
7
-def get_password():
8
-    with open('pass') as f:
9
-        password = f.readline()
10
-    return password.strip()
11
+class bcolors:
12
+    HEADER = '\033[95m'
13
+    OKBLUE = '\033[94m'
14
+    OKGREEN = '\033[92m'
15
+    WARNING = '\033[93m'
16
+    FAIL = '\033[91m'
17
+    ENDC = '\033[0m'
18
+    BOLD = '\033[1m'
19
+    UNDERLINE = '\033[4m'
11 20
 
21
+class scan_types:
22
+    SELL = 'v',
23
+    BUY = 'a',
12 24
 
13
-def search_product(code):
14
-    # data to be sent to api
15
-    data = {
16
-        'password': get_password(),
17
-        'code': str(code)
18
-    }
19
-    # sending post request and saving response as response object
20
-    r = requests.post(url=API_ENDPOINT, data=json.dumps(data))
21
-    return r.text
25
+class error_types:
26
+    NONE = 0,
27
+    NETWORK = 1,
28
+    URL = 2,
29
+    INPUT = 3,
30
+    NO_EXIST = 4,
22 31
 
32
+class Scanner:
33
+
34
+    def __init__(self):
35
+        self.ask_type()
36
+        self.password = self.get_password()
37
+        self.scannedArticles = []
38
+
39
+    def ask_type(self):
40
+        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 + "] ")
41
+        if (typeInput.lower() == 'a'):
42
+            self.type = scan_types.BUY
43
+        else:
44
+            self.type = scan_types.SELL
45
+
46
+    def get_password(self):
47
+        with open('pass') as f:
48
+            password = f.readline()
49
+        return password.strip()
50
+
51
+    def display_type(self):
52
+        if (self.type == scan_types.SELL):
53
+            print("    ==> Mode " + bcolors.FAIL + bcolors.BOLD + "VENTE")
54
+        else:
55
+            print("    ==> Mode " + bcolors.OKGREEN + bcolors.BOLD + "ACHAT")
56
+        print(bcolors.ENDC)
57
+
58
+    def scan_product(self, code):
59
+        data = {
60
+            'password': self.password,
61
+            'action': 'scan',
62
+            'data': str(code)
63
+        }
64
+        r = requests.post(url=API_ENDPOINT, data=json.dumps(data))
65
+        if (r.json()['status'] == 0):
66
+            article = r.json()["data"]
67
+            self.scannedArticles.append(article)
68
+        return r.json()['status'] == 0
69
+
70
+    def display_cart(self):
71
+        total = 0.0
72
+        for article in self.scannedArticles:
73
+            print(article["name"] + ' : ' + bcolors.BOLD + article["price"] + '€' + bcolors.ENDC)
74
+            total += float(article["price"])
75
+        # Print only to only 2 decimals
76
+        total_display = "{:.2f}".format(total)
77
+        print(bcolors.OKGREEN + "Total: " + bcolors.BOLD + total_display + '€' + bcolors.ENDC)
78
+
79
+    def send_cart(self):
80
+        scanned_list = []
81
+        modifier = -1 if self.type == scan_types.SELL else 1
82
+        for article in self.scannedArticles:
83
+            scanned_list.append({"id": article["id"], "quantity": modifier})
84
+        data = {
85
+            'password': self.password,
86
+            'action': 'validate',
87
+            'data': scanned_list
88
+        }
89
+        r = requests.post(url=API_ENDPOINT, data=json.dumps(data))
90
+        return r.json()['status'] == 0
91
+
92
+def ask_confirmation(message):
93
+    confirm_input = input(message)
94
+    return confirm_input.lower() == 'o'
95
+
96
+def clear_screen():
97
+    os.system('clear')
98
+    print("Appuyez sur " + bcolors.BOLD + "[CTRL + C]" + bcolors.ENDC + " à tout moment pour quitter.\n")
99
+
100
+def printStartScreen():
101
+    clear_screen()
102
+    print(bcolors.BOLD)
103
+    print(bcolors.WARNING + "#########################################")
104
+    print("#/                                     \#")
105
+    print("#            " + bcolors.FAIL + "-=|" + bcolors.OKGREEN + " ZAPETTE " + bcolors.FAIL + "|=-" + bcolors.WARNING + "            #")
106
+    print("#\                                     /#")
107
+    print("#########################################")
108
+    print(bcolors.ENDC)
109
+    print("Bienvenue dans le programme de la Zapette !")
110
+
111
+def display_scan_header(scanner, last_error):
112
+    clear_screen()
113
+    scanner.display_type()
114
+    print("Scannez le codes puis appuyez sur [ENTRÉE] pour valider.")
115
+    print("Appuyez sur [ENTRÉE] sans code pour valider la commande.\n")
116
+    scanner.display_cart()
117
+    if (last_error == error_types.URL):
118
+        print(bcolors.FAIL + "Format URL invalide !" + bcolors.ENDC)
119
+    elif (last_error == error_types.NETWORK):
120
+        print(bcolors.FAIL + "URL invalide !" + bcolors.ENDC)
121
+    elif (last_error == error_types.INPUT):
122
+       print(bcolors.FAIL + "Code invalide !" + bcolors.ENDC)
123
+    elif (last_error == error_types.NO_EXIST):
124
+       print(bcolors.FAIL + "L'article n'existe pas." + bcolors.ENDC)
125
+    print()
126
+
127
+def confirm_end_scan(scanner):
128
+    display_scan_header(scanner, error_types.NONE)
129
+    return ask_confirmation("Voulez vous vraiment terminer et envoyer les modifications ? [o/n] ")
130
+
131
+def handler(signal_received, frame):
132
+    os.system('clear')
133
+    print('Programme de la zapette terminé.')
134
+    exit(0)
135
+
136
+def validate_cart(scanner):
137
+    clear_screen()
138
+    print("Envoi des modifications au serveur...")
139
+    if (scanner.send_cart()):
140
+        print(bcolors.OKGREEN + bcolors.BOLD + "Succès !" + bcolors.ENDC)
141
+    else:
142
+        print(bcolors.FAIL + bcolors.BOLD + "Échec !" + bcolors.ENDC)
143
+    input("\nAppuyez sur [ENTRÉE] pour continuer...")
23 144
 
24 145
 def main():
25
-    code_input = input('Scannez le code\n')
26
-    try:
27
-        code = int(code_input)
28
-        result = search_product(code)
29
-        print(result)
30
-    except requests.exceptions.MissingSchema:
31
-        print("Format URL invalide !")
32
-    except requests.exceptions.ConnectionError:
33
-        print("URL invalide !")
34
-    except ValueError:
35
-        print("Code invalide !")
146
+    signal(SIGINT, handler)
147
+    printStartScreen()
148
+    while True:
149
+        scanner = Scanner()
150
+        last_error = error_types.NONE
151
+        while True:
152
+            display_scan_header(scanner, last_error)
153
+            code_input = input('=> ')
154
+            if (code_input == ""):
155
+                if (confirm_end_scan(scanner)):
156
+                    validate_cart(scanner)
157
+                    break
158
+                else:
159
+                    continue
160
+            try:
161
+                code = int(code_input)
162
+                if (scanner.scan_product(code)):
163
+                    last_error = error_types.NONE
164
+                else:
165
+                    last_error = error_types.NO_EXIST
166
+            except requests.exceptions.MissingSchema:
167
+                last_error = error_types.URL
168
+            except requests.exceptions.ConnectionError:
169
+                last_error = error_types.NETWORK
170
+            except ValueError:
171
+                last_error = error_types.INPUT
172
+        clear_screen()
36 173
 
37 174
 main()

Loading…
Cancel
Save