From fdb6e739100983dfd937ec523a00c502bc259c09 Mon Sep 17 00:00:00 2001
From: antoinekia <antoinekia@gmail.com>
Date: Sun, 13 Jun 2021 17:49:04 +0200
Subject: [PATCH] Dist V1

---
 client/src/App.vue                       | 31 +++++----
 client/src/components/Login.vue          |  3 +-
 client/src/components/PageFooter.vue     |  2 +-
 client/src/components/Register.vue       |  3 +-
 client/src/components/ResultsDisplay.vue | 69 ++++++++++++++++++--
 client/src/components/SchoolManager.vue  | 77 ++++++++++++++++++----
 client/src/components/TeamAdder.vue      |  3 +-
 client/src/components/TeamElement.vue    | 58 ++++++++++-------
 client/src/puzzles.json                  | 15 +++++
 client/src/usbNfcReader.js               | 81 +++++++++++++++++++++++-
 server/db/puzzle.model.js                |  3 +-
 server/db/team.route.js                  |  8 +++
 12 files changed, 292 insertions(+), 61 deletions(-)
 create mode 100644 client/src/puzzles.json

diff --git a/client/src/App.vue b/client/src/App.vue
index c51a8c8..e7bc862 100644
--- a/client/src/App.vue
+++ b/client/src/App.vue
@@ -18,22 +18,17 @@
         </div>
         <div id="navMenu" class="navbar-menu" :class="{ 'is-active': showNav }">
           <div class="navbar-start">
-            <a v-if="$store.state.isLoggedIn" class="navbar-item"
+            <a class="navbar-item"
               ><router-link to="/results"
                 ><i class="fas fa-flag-checkered" /> Enregistrer des résultats</router-link
               ></a
             >
-            <a class="navbar-item"
+            <!--a class="navbar-item"
               ><router-link to="/encoder"
                 ><i class="fas fa-code" /> Encodeur NFC</router-link
               ></a
-            >
-            <a class="navbar-item"
-              ><router-link to="/legal"
-                ><i class="fas fa-balance-scale" /> Mentions
-                légales</router-link
-              ></a
-            >
+            -->
+            
             <a v-if="$store.state.isLoggedIn" class="navbar-item"
               ><router-link to="/school"
                 ><i class="fas fa-school" /> Mon établissement ({{
@@ -41,6 +36,12 @@
                 }})</router-link
               ></a
             >
+            <a class="navbar-item"
+              ><router-link to="/legal"
+                ><i class="fas fa-balance-scale" /> Mentions
+                légales</router-link
+              ></a
+            >
           </div>
           <div class="navbar-end">
             
@@ -99,7 +100,8 @@ export default {
     this.usbNfcReader = new UsbNfcReader();
   },
   mounted() {
-    let uri = "//localhost:3000/loginCheck";
+    var domainName = '//'+location.hostname+(location.port ? ':'+location.port: '');
+    let uri = domainName + "/loginCheck";
     this.axios.post(uri, {}, { withCredentials: true }).then(response => {
         console.log(response);
         if (response.data != "ok")
@@ -108,12 +110,13 @@ export default {
     setTimeout(() => this.checkNfcModule(), 1000)
     setInterval(() => {
       this.checkNfcModule()
-    }, 50000)
+    }, 2500)
       
   },
   methods: {
     logout() {
-      let uri = "//localhost:3000/logout";
+      var domainName = '//'+location.hostname+(location.port ? ':'+location.port: '');
+      let uri = domainName + "/logout";
       this.axios.post(uri, {}, { withCredentials: true }).then(response => {
         console.log(response);
         this.$store.commit("logout");
@@ -121,11 +124,11 @@ export default {
     },
     connectUsb() {
       this.usbNfcReader.selectAndConnect()
-      this.setTimeout(() => this.checkNfcModule(), 1000);
+      this.setTimeout(() => this.checkNfcModule(), 500);
     },
     disconnectUsb() {
       this.usbNfcReader.disconnect()
-      this.setTimeout(() => this.checkNfcModule(), 1000);
+      this.setTimeout(() => this.checkNfcModule(), 500);
     },
     checkNfcModule() {
       this.usbNfcReader.ping().then(() => {
diff --git a/client/src/components/Login.vue b/client/src/components/Login.vue
index 31f49fe..dd49578 100644
--- a/client/src/components/Login.vue
+++ b/client/src/components/Login.vue
@@ -47,7 +47,8 @@ export default {
   },
   methods: {
     login() {
-      let uri = "//localhost:3000/login";
+      var domainName = '//'+location.hostname+(location.port ? ':'+location.port: '');
+      let uri = domainName + "/login";
       this.axios
         .post(
           uri,
diff --git a/client/src/components/PageFooter.vue b/client/src/components/PageFooter.vue
index cf1150e..75263bf 100644
--- a/client/src/components/PageFooter.vue
+++ b/client/src/components/PageFooter.vue
@@ -14,7 +14,7 @@
     <div class="content">
       <p>
         Conçu et développé par les associations Clubelek et Objectif21, de
-        l'INSA Lyon - Saison 2020/2021
+        l'INSA Lyon - Saison 2020/2021 - Made with 💚 by Antoine Rochebois
       </p>
     </div>
   </footer>
diff --git a/client/src/components/Register.vue b/client/src/components/Register.vue
index c8248f3..36da9f1 100644
--- a/client/src/components/Register.vue
+++ b/client/src/components/Register.vue
@@ -62,7 +62,8 @@ export default {
   },
   methods: {
     register() {
-      let uri = "//localhost:3000/register";
+      var domainName = '//'+location.hostname+(location.port ? ':'+location.port: '');
+      let uri = domainName + "/register";
       this.axios
         .post(
           uri,
diff --git a/client/src/components/ResultsDisplay.vue b/client/src/components/ResultsDisplay.vue
index 85dbb58..667d03e 100644
--- a/client/src/components/ResultsDisplay.vue
+++ b/client/src/components/ResultsDisplay.vue
@@ -1,8 +1,14 @@
 <template>
   <div class="content">
-    <article v-if="this.team.name.length > 0" class="message is-info">
+    <div v-if="globalScore >= 0" class="has-text-centered">
+      <p class = "is-size-4 m-0">Score total :</p>
+      <p class = "is-size-1 m-0" style="color:green;">🔥  {{globalScore}}  🔥</p>
+      <p class = "is-size-3 mb-6">🥳  Félicitations ! 🥳</p>
+    </div>
+
+    <article v-if="this.team.name.length > 0" class="message is-success">
       <div class="message-header">
-        Résultats de l'équipe {{ team.name }} ({{ memberCount }} membres)
+        🏁 Résultats de l'équipe "{{ team.name }}" ({{ memberCount }} membres) 🏁
       </div>
       <div class="message-body">
         <div>
@@ -52,9 +58,9 @@
         </div>
       </div>
     </article>
-    <article v-else>
-      <p>Merci de scanner une carte d'équipe pour afficher et enregistrer des résultats</p>
-      <button class="button is-primary" @click="showPairingModal = !showPairingModal"><i class="fas fa-download"></i> Scanner la carte de l'équipe !</button>
+    <article class="container has-text-centered">
+      <p class="is-size-4">🏁  Merci de scanner une carte d'équipe pour afficher et enregistrer des résultats  🏁</p>
+      <button class="button is-primary is-large is-fullwidth" @click="manageCardReading()"><i class="fas fa-download"></i> Scanner la carte de l'équipe !</button>
     </article>
 
     <div class="modal" :class="{ 'is-active': showPairingModal }">
@@ -80,13 +86,16 @@
 </template>
 
 <script>
+import puzzleJson from '../puzzles.json'
 export default {
   name: "ResultDisplay",
 
   data() {
     return {
       showPairingModal: false,
-      team : { name: "", members: [], donePuzzles : [] }
+      puzzles: puzzleJson,
+      team : { name: "", members: [], donePuzzles : [] },
+      globalScore: -1
     };
   },
 
@@ -97,6 +106,54 @@ export default {
   },
 
   methods: {
+    manageCardReading() {
+      this.showPairingModal = true;
+      this.$parent.usbNfcReader.readCard().then(cardContent=> {
+        this.$parent.usbNfcReader.readTagUid().then(tagUid => {
+          this.team = { name: "", members: [], donePuzzles : [] }
+          let resultsStr = cardContent.split(';')
+          resultsStr.shift()
+          resultsStr.pop()
+          if (resultsStr.length == 0) {
+            console.log("Carte vierge de toute épreuve !")
+            return;
+          }
+          resultsStr.forEach(resultStr => {
+            let resShort = resultStr.split('_')[0]
+            let score = resultStr.split('_')[1]
+            let puzzle = this.puzzles[resShort]
+            var donePuzzle = {name: puzzle.name, room:puzzle.room, coef:puzzle.coef}
+            if (puzzle.type == "boolean") {
+              if (score >= 1) donePuzzle.validated = true;
+              else donePuzzle.validated = false;
+            } else if (puzzle.type == "number") {
+              donePuzzle.maxScore = puzzle.maxScore;
+              donePuzzle.score = score
+            }
+            this.team.donePuzzles.push(donePuzzle)
+          });
+          console.log(this.team.donePuzzles)
+          var domainName = '//'+location.hostname+(location.port ? ':'+location.port: '');
+          let uri = domainName + "/school/updateTeamPuzzles/" + tagUid;
+          let uriGet = domainName + "/school/getTeamByCardID/" + tagUid;
+          this.axios.post(uri, this.team.donePuzzles, { withCredentials: true }).then(() => {
+            console.log("Score ajouté à la BD !");
+            this.axios.get(uriGet, { withCredentials: true }).then((response) => {
+              this.team = response.data[0]
+              this.globalScore = 0
+              this.team.donePuzzles.forEach(puzzle => {
+                if (puzzle.score)
+                  this.globalScore += parseInt(puzzle.coef)*parseInt(puzzle.score)
+                else if (puzzle.validated)
+                this.globalScore += parseInt(puzzle.coef)
+                
+              });
+              this.showPairingModal = false
+            });
+          });
+        });
+      });
+    }
   }
 };
 </script>
diff --git a/client/src/components/SchoolManager.vue b/client/src/components/SchoolManager.vue
index 643f521..6f3b6fe 100644
--- a/client/src/components/SchoolManager.vue
+++ b/client/src/components/SchoolManager.vue
@@ -1,16 +1,48 @@
 <template>
   <div id="app" class="content">
-    <div class="message is-medium">
-      <div class="message-header">Mes équipes</div>
-      <div class="message-body">
-        <div @addedTeam="updateTeams" v-for="team in teams" :key="team._id">
+
+        <div v-if="teams[0].donePuzzles.length > 0" class="container mb-6 has-text-centered">
+          <p class="is-size-3">🏅  Classement des équipes  🏅</p>
+
+          <div v-for="(team, index) in teams" :key="team._id" >
+            <div v-if="team.donePuzzles">
+              <div v-if="team.donePuzzles.length > 0" class="box mb-2">
+                <nav class="level">
+                  <div class="level-item has-text-centered">
+                    <div>
+                      <p class="heading">Rang</p>
+                      <p v-if="index==0" class="title">🥇</p>
+                      <p v-else-if="index==1" class="title">🥈</p>
+                      <p v-else-if="index==2" class="title">🥉</p>
+                      <p v-else class="title">{{index+1}}</p>
+                    </div>
+                  </div>
+                  <div class="level-item has-text-centered">
+                    <div>
+                      <p class="heading">équipe</p>
+                      <p class="title">{{team.name}}</p>
+                    </div>
+                  </div>
+                  <div class="level-item has-text-centered">
+                    <div>
+                      <p class="heading">Score</p>
+                      <p class="title">{{getGlobalScore(team)}}</p>
+                    </div>
+                  </div>
+                </nav>
+              </div>
+            </div>
+          </div>
+        </div>
+
+
+        <div class="mb-2" @addedTeam="updateTeams" v-for="team in teams" :key="team._id">
           <TeamElement :team="team" :teamArray="teams" />
         </div>
-        <TeamAdder :teamArray="teams"></TeamAdder>
+
+
+        <TeamAdder class="mt-6" :teamArray="teams"></TeamAdder>
       </div>
-    </div>
-    
-  </div>
 </template>
 
 <script>
@@ -29,19 +61,42 @@ export default {
     };
   },
   created() {
-    let uri = "//localhost:3000/school";
+    
+  },
+
+  mounted() {
+    var domainName = '//'+location.hostname+(location.port ? ':'+location.port: '');
+    let uri = domainName + "/school";
     this.axios.get(uri, { withCredentials: true }).then(response => {
       this.teams = response.data;
+      this.sortTeams()
       console.log(response);
     });
-  },
 
-  mounted() {},
+  },
   methods: {
     updateTeams(teams) {
       this.teams = teams;
+      this.sortTeams()
+    },
+    getGlobalScore(team) {
+      var globalScore = 0
+      team.donePuzzles.forEach(puzzle => {
+        if (puzzle.score)
+          globalScore += parseInt(puzzle.coef)*parseInt(puzzle.score)
+        else if (puzzle.validated)
+          globalScore += parseInt(puzzle.coef)
+        
+      });
+      return globalScore;
+    },
+    sortTeams() {
+      this.teams.sort((a, b) => {
+        return this.getGlobalScore(b) - this.getGlobalScore(a)
+      })
     }
   }
+  
 };
 </script>
 
diff --git a/client/src/components/TeamAdder.vue b/client/src/components/TeamAdder.vue
index 22997ee..9c95987 100644
--- a/client/src/components/TeamAdder.vue
+++ b/client/src/components/TeamAdder.vue
@@ -53,7 +53,8 @@ export default {
   },
   methods: {
     addTeam() {
-      let uri = "//localhost:3000/school/addTeam";
+      var domainName = '//'+location.hostname+(location.port ? ':'+location.port: '');
+      let uri = domainName + "/school/addTeam";
       this.axios
         .post(uri, this.team, { withCredentials: true })
         .then(response => {
diff --git a/client/src/components/TeamElement.vue b/client/src/components/TeamElement.vue
index b4c5bce..09fca68 100644
--- a/client/src/components/TeamElement.vue
+++ b/client/src/components/TeamElement.vue
@@ -85,7 +85,11 @@
               Veuillez scanner une carte sur le lecteur pour l'associer à cette
               équipe. <br>
               <br>
-              ATTENTION : Ceci effacera le contenu actuel de cette carte !
+              ATTENTION : Ceci effacera le contenu actuel de cette carte ! Vous ne pourrez pas attribuer à cette équipe une carte déjà attribuée à une autre équipe.
+              Merci donc de dissocier toutes les cartes de votre équipe une fois le jeu fini.
+              <br>
+              <br>
+              Vous devez avoir connecté le lecteur USB au site. Déposez la carte d'équipe sur le lecteur NFC (face en plastique). Vous devez entendre deux bip consécutifs. Si cette fenêtre se ferme, c'est bon ! Si cela prend du temps ou qu'il n'y a qu'un seul bip, fermez et recommencez. Si le problème persiste, débranchez le lecteur, rechargez la page puis rebranchez le lecteur.
             </div>
           </div>
         </div>
@@ -138,7 +142,8 @@ export default {
 
   methods: {
     removeTeam: function() {
-      let uri = `//localhost:3000/school/deleteTeam/${this.team._id}`;
+      var domainName = '//'+location.hostname+(location.port ? ':'+location.port: '');
+      let uri = domainName + `/school/deleteTeam/${this.team._id}`;
       this.axios.delete(uri, { withCredentials: true }).then(response => {
         console.log("bien supprimé !", response);
         this.teamArray.splice(this.teamArray.indexOf(this.team), 1);
@@ -148,33 +153,40 @@ export default {
     manageCardPairing: function() {
       this.showPairingModal = !this.showPairingModal
       console.log("Trying to reset card")
-      this.$parent.$parent.usbNfcReader.writeCard("team").then(response=> {
-        console.log("Reset successful, trying to read uid", response)
-        this.$parent.$parent.usbNfcReader.readTagUid()
-        .then(response => {   
-          let uri = `//localhost:3000/school/updateTeamCard/${this.team._id}`;
-          this.axios
-          .post(uri, {cardId: response}, { withCredentials: true })
-          .then(() => {
-            console.log("Carte ajoutée !");
-            this.team.cardId = response   
-            this.showPairingModal = false;
-            this.$forceUpdate()
-          }).catch(response => {
-            this.showPairingModal = false;
-            this.showAlreadyPairedError = true;
-            this.team.cardId = "";
-            this.$forceUpdate()
-            console.log("Carte déjà utilisée !", response)
+      this.$parent.$parent.usbNfcReader.cleanCard().then(()=> {
+      this.$parent.$parent.usbNfcReader.formatCard().then(()=> {
+      this.$parent.$parent.usbNfcReader.eraseCard().then(()=> {
+        this.$parent.$parent.usbNfcReader.writeCard("team").then(response=> {
+          console.log("Reset successful, trying to read uid", response)
+          this.$parent.$parent.usbNfcReader.readTagUid()
+          .then(response => {   
+            var domainName = '//'+location.hostname+(location.port ? ':'+location.port: '');
+            let uri = domainName + `/school/updateTeamCard/${this.team._id}`;
+            this.axios
+            .post(uri, {cardId: response}, { withCredentials: true })
+            .then(() => {
+              console.log("Carte ajoutée !");
+              this.team.cardId = response   
+              this.showPairingModal = false;
+              this.$forceUpdate()
+            }).catch(response => {
+              this.showPairingModal = false;
+              this.showAlreadyPairedError = true;
+              this.team.cardId = "";
+              this.$forceUpdate()
+              console.log("Carte déjà utilisée !", response)
+            })
+        
           })
-      
         })
       })
-      
+      })
+      })
     },
 
     removeCardPairing: function() {
-      let uri = `//localhost:3000/school/removeTeamCard/${this.team._id}`;
+      var domainName = '//'+location.hostname+(location.port ? ':'+location.port: '');
+      let uri = domainName `/school/removeTeamCard/${this.team._id}`;
         this.axios
         .post(uri, this.team, { withCredentials: true })
         .then(response => {
diff --git a/client/src/puzzles.json b/client/src/puzzles.json
new file mode 100644
index 0000000..d65d9e7
--- /dev/null
+++ b/client/src/puzzles.json
@@ -0,0 +1,15 @@
+{
+"C": {   
+    "name": "Faire une recette de saison",
+    "room": "Cuisine",
+    "type":"number",
+    "maxScore": 100,
+    "coef":5
+    },
+"A": {  
+    "name": "Manger des pommes",
+    "room": "Cuisine",
+    "type":"boolean",
+    "coef":20
+    }
+}
\ No newline at end of file
diff --git a/client/src/usbNfcReader.js b/client/src/usbNfcReader.js
index 343ce93..c8a185d 100644
--- a/client/src/usbNfcReader.js
+++ b/client/src/usbNfcReader.js
@@ -3,6 +3,7 @@ import Serial from "./serial.js"
 export default class UsbNfcReader {
     constructor() {
         this.textEncoder = new TextEncoder();
+        this.locked = false;
         //Tentative de connexion/reconnexion automatique
         Serial.getPorts().then(ports => {
             if (ports.length == 0) {
@@ -53,6 +54,7 @@ export default class UsbNfcReader {
 
     readTagUid() {
       return new Promise((resolve, reject) => {
+        this.locked = true;
         this.sendString("GETID_")
         this.port.onReceive = data => {
           let textDecoder = new TextDecoder();
@@ -63,7 +65,8 @@ export default class UsbNfcReader {
           if (msg == "RECEIVED_GETID;") {
             console.log("GETID request ACK by Arduino")
           } else if (cmdIdentifier == "UID" && msg.slice(-1) == ';') {
-            resolve(msg.split('_')[1].slice(0, -1))
+            this.locked = false;
+            resolve(msg.split('_')[1].slice(0, -1))        
           } else {
             reject("Bad message structure received")
           }
@@ -72,8 +75,35 @@ export default class UsbNfcReader {
       })
     }
 
+    readCard() {
+      return new Promise((resolve, reject) => {
+        this.locked = true;
+        this.sendString("READ_")
+        var failCount = 0
+        var msg = ""
+        this.port.onReceive = data => {
+          let textDecoder = new TextDecoder();
+          let msgBuff = textDecoder.decode(data);
+          msgBuff = msgBuff.replace(/(\r\n|\n|\r)/gm, "").replace(/[^\w;]+/g, '');
+          msg += msgBuff
+          if (msg.includes("CONTENT_BEGIN_en") && msg.includes("_END")) {
+            msg = msg.split("CONTENT_BEGIN_en")
+            msg = msg[msg.length-1].split("_END")[0]
+            this.locked = false;
+            resolve(msg)
+          } else if (!msg.includes("RECEIVED_READ")) {
+            if (failCount > 100) {
+              reject("Too many reading fail")
+            }
+            failCount++
+          }
+        }
+      })
+    }
+
     writeCard(content) {
       return new Promise((resolve, reject) => {
+        this.locked = true;
         this.sendString("WRITE_" + content)
         this.port.onReceive = data => {
           let textDecoder = new TextDecoder();
@@ -82,6 +112,7 @@ export default class UsbNfcReader {
           console.log("recu ", msg)
           if (msg == "WRITTEN;") {
             console.log("wrote team message")
+            this.locked = false;
             resolve("")
           } else if (!msg.includes("RECEIVED_WRITE")) {
             reject("Write : Bad message structure received")
@@ -92,6 +123,7 @@ export default class UsbNfcReader {
 
     cleanCard() {
       return new Promise((resolve, reject) => {
+        this.locked = true;
         this.sendString("CLEAN_")
         this.port.onReceive = data => {
           let textDecoder = new TextDecoder();
@@ -100,16 +132,61 @@ export default class UsbNfcReader {
           console.log("recu ", msg)
           if (msg == "CLEANED;") {
             console.log("Cleaned card")
+            this.locked = false;
             resolve("")
-          } else if (msg != "RECEIVED_CLEAN;") {
+          } else if (!msg.includes("RECEIVED_CLEAN")) {
             reject("Clean : Bad message structure received")
           }
         }
       })
     }
 
+    formatCard() {
+      return new Promise((resolve, reject) => {
+        this.locked = true;
+        this.sendString("FORMAT_")
+        this.port.onReceive = data => {
+          let textDecoder = new TextDecoder();
+          let msg = textDecoder.decode(data);
+          msg = msg.replace(/(\r\n|\n|\r)/gm, "");
+          console.log("recu ", msg)
+          if (msg == "FORMATED;") {
+            console.log("Formated card")
+            this.locked = false;
+            resolve("")
+          } else if (!msg.includes("RECEIVED_FORMAT")) {
+            reject("Format : Bad message structure received")
+          }
+        }
+      })
+    }
+
+    eraseCard() {
+      return new Promise((resolve, reject) => {
+        this.locked = true;
+        this.sendString("ERASE_")
+        this.port.onReceive = data => {
+          let textDecoder = new TextDecoder();
+          let msg = textDecoder.decode(data);
+          msg = msg.replace(/(\r\n|\n|\r)/gm, "");
+          console.log("recu ", msg)
+          if (msg == "ERASED;") {
+            console.log("Erased card")
+            this.locked = false;
+            resolve("")
+          } else if (!msg.includes("RECEIVED_ERASE")) {
+            reject("Erase : Bad message structure received")
+          }
+        }
+      })
+    }
+
     ping() {
       return new Promise((resolve, reject) => {
+        if (this.locked) {
+          console.log("Trying to ping while performing another task")
+          return resolve("")
+        }
         this.sendString("PING_")
         var timeout = setTimeout(() => {
           reject("ping timeout")}
diff --git a/server/db/puzzle.model.js b/server/db/puzzle.model.js
index 27b9a12..6cbbc4b 100644
--- a/server/db/puzzle.model.js
+++ b/server/db/puzzle.model.js
@@ -6,7 +6,8 @@ var Puzzle = Schema({
     room: String,
     validated: Boolean,
     score: Number,
-    maxScore: Number
+    maxScore: Number,
+    coef: Number
 })
 
 module.exports = mongoose.model('Puzzle', Puzzle)
\ No newline at end of file
diff --git a/server/db/team.route.js b/server/db/team.route.js
index 060ec51..9f71e25 100644
--- a/server/db/team.route.js
+++ b/server/db/team.route.js
@@ -66,6 +66,14 @@ router.route('/updateTeamCard/:id').post(function (req, res) {
   }
 });
 
+router.route('/getTeamByCardID/:cardId').get(function (req, res) {
+    console.log("GETing team for :", req.params.cardId)
+    Team.find({cardId: req.params.cardId}, function (err, team) {
+      if (err) {res.json(err)}
+      else {res.json(team)}
+    });
+});
+
 router.route('/removeTeamCard/:id').post(function (req, res) {
   if (req.user) {
     console.log("Request for removing card ID")
-- 
GitLab