Parcourir la source

Merge branch 'release/v0.1.9'

rigwild il y a 5 ans
Parent
commit
30aeb6a6de

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "expe-web",
-  "version": "0.1.8",
+  "version": "0.1.9",
   "private": true,
   "scripts": {
     "server:start": "node -r esm server/index.js",

+ 1 - 0
src/App.vue

@@ -74,6 +74,7 @@
 </template>
 
 <script>
+import './style.css'
 import ResetAppButton from '@/components/ResetAppButton.vue'
 import Loader from '@/components/Loader.vue'
 import HostConfig from '@/components/HostConfig.vue'

+ 35 - 19
src/components/ExperimentsComponents/ExtractConfiguration.vue

@@ -4,27 +4,37 @@
       <v-layout row wrap>
         <v-flex xs12>
           <h1>Configuration</h1>
-          <v-card-text class="px-0">Extracts per line (horizontal)</v-card-text>
-          <v-slider
-            v-model="experimentConfig.x"
-            always-dirty
-            persistent-hint
-            thumb-label="always"
-            min="1"
-            max="25"
-          />
 
-          <v-card-text class="px-0">Extracts per row (vertical)</v-card-text>
-          <v-slider
-            v-model="experimentConfig.y"
-            always-dirty
-            persistent-hint
-            thumb-label="always"
-            min="1"
-            max="25"
-          />
+          <v-slide-y-transition mode="out-in">
+            <div v-if="isExpanded" key="configurator">
+              <v-card-text class="px-0">Extracts per line (horizontal)</v-card-text>
+              <v-slider
+                v-model="experimentConfig.x"
+                always-dirty
+                persistent-hint
+                thumb-label="always"
+                min="1"
+                max="15"
+              />
 
-          <v-btn @click="setConfig" :disabled="!isConfigNew">Confirm</v-btn>
+              <v-card-text class="px-0">Extracts per row (vertical)</v-card-text>
+              <v-slider
+                v-model="experimentConfig.y"
+                always-dirty
+                persistent-hint
+                thumb-label="always"
+                min="1"
+                max="15"
+              />
+
+              <v-btn @click="setConfig" :disabled="!isConfigNew">Confirm</v-btn>
+            </div>
+            <div v-else key="arrow">
+              <v-btn flat round @click="isExpanded = true">
+                <v-icon>keyboard_arrow_down</v-icon>
+              </v-btn>
+            </div>
+          </v-slide-y-transition>
 
           <v-alert v-if="loadingErrorMessage" :value="true" type="error" v-text="loadingErrorMessage" />
         </v-flex>
@@ -45,6 +55,8 @@ export default {
   },
   data() {
     return {
+      isExpanded: true,
+
       // Experiment config sliders
       experimentConfig: {
         x: 4,
@@ -63,6 +75,10 @@ export default {
     }
   },
   methods: {
+    setVisibility(bool) {
+      this.isExpanded = bool
+    },
+
     setDefaultConfig(config) {
       this.experimentConfig.x = config.x
       this.experimentConfig.y = config.y

+ 15 - 0
src/functions.js

@@ -42,3 +42,18 @@ export const findNearestLower = (value, arrInt) => {
       ? arr[index]
       : arr[index - 1]
 }
+
+/**
+ * Randomize array element order in-place.
+ * Using Durstenfeld shuffle algorithm.
+ * @param {any[]} array Array to randomize
+ * @returns {any[]} The randomized array
+ * @see https://stackoverflow.com/a/12646864
+ */
+export const shuffleArray = array => {
+  for (let i = array.length - 1; i > 0; i--) {
+    const j = Math.floor(Math.random() * (i + 1));
+    [array[i], array[j]] = [array[j], array[i]]
+  }
+  return array
+}

+ 24 - 4
src/mixins/ExperimentBase.vue

@@ -1,8 +1,15 @@
+<template>
+  <div>
+    <slot></slot>
+  </div>
+</template>
+
 <script>
 import { mapGetters, mapActions } from 'vuex'
 import { API_ROUTES } from '@/functions'
 
 export default {
+  name: 'ExperimentBase',
   props: {
     sceneName: {
       type: String,
@@ -19,15 +26,22 @@ export default {
     }
   },
   computed: {
-    ...mapGetters(['getHostURI', 'getExperimentProgress'])
+    ...mapGetters(['getHostURI', 'getExperimentProgress', 'isExperimentDone'])
+  },
+  mounted() {
+    // Check if the experiment is already finished
+    if (this.experimentName && this.sceneName && this.isExperimentDone({ experimentName: this.experimentName, sceneName: this.sceneName })) {
+      console.warn('Redirected from experiment. You can\'t go back in an experiment after finishing it.')
+      this.$router.push(`/experiments/${this.experimentName}`)
+    }
   },
   methods: {
-    ...mapActions(['setExperimentProgress', 'sendMessage']),
+    ...mapActions(['setExperimentProgress', 'setExperimentDone', 'sendMessage']),
 
     // Load progress from store into local state
     loadProgress() {
       if (!this.experimentName || !this.sceneName)
-        console.warn('Could not load progress : experimentName and sceneName must be defined')
+        return console.warn('Could not load progress : experimentName and sceneName must be defined')
 
       const progress = this.getExperimentProgress({ experimentName: this.experimentName, sceneName: this.sceneName })
       Object.assign(this.$data, progress)
@@ -37,11 +51,17 @@ export default {
     // Save progress from local state into store
     saveProgress() {
       if (!this.experimentName || !this.sceneName)
-        console.warn('Could not load progress : experimentName and sceneName must be defined')
+        return console.warn('Could not save progress : experimentName and sceneName must be defined')
       this.setExperimentProgress({ experimentName: this.experimentName, sceneName: this.sceneName, data: this.$data })
       // console.log('Saved data from local state to store.', this.$data)
     },
 
+    async finishExperiment() {
+      this.setExperimentDone({ experimentName: this.experimentName, sceneName: this.sceneName, done: true })
+      this.$router.push(`/experiments/${this.experimentName}`)
+    },
+
+
     // Load qualities list from the API
     async getQualitiesList() {
       if (this.qualities) return

+ 12 - 3
src/mixins/ExperimentBaseExtracts.vue

@@ -1,10 +1,18 @@
+<template>
+  <div>
+    <slot></slot>
+  </div>
+</template>
+
 <script>
-import ExperimentBase from '@/mixins/ExperimentBase.vue'
+import './style.css'
+import ExperimentBase from '@/mixins/ExperimentBase'
 
 import { mapGetters } from 'vuex'
 import { API_ROUTES, findNearestUpper, findNearestLower } from '@/functions'
 
 export default {
+  name: 'ExperimentBaseExtracts',
   mixins: [ExperimentBase],
   data() {
     return {
@@ -36,8 +44,8 @@ export default {
     },
 
     // Config was updated, load extracts and save progression
-    async setConfig(config) {
-      if (!config) return
+    async setConfig(config, configuratorRef) {
+      if (!config || !configuratorRef) return
 
       this.loadingMessage = 'Loading configuration extracts...'
       this.loadingErrorMessage = null
@@ -55,6 +63,7 @@ export default {
           precQuality: findNearestLower(data.info.image.quality, this.qualities),
           loading: false
         }))
+        configuratorRef.setVisibility(false)
       }
       catch (err) {
         console.error('Failed to load new configuration', err)

+ 7 - 0
src/mixins/ExperimentBaseExtracts/style.css

@@ -0,0 +1,7 @@
+.img-extract-loader {
+  height: 100%;
+  width: 0px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}

+ 1 - 1
src/router/index.js

@@ -17,7 +17,7 @@ export default new Router({
       component: ExperimentsList
     },
     {
-      path: '/experiments/selectScene/:experimentName',
+      path: '/experiments/:experimentName',
       name: 'SelectExperimentScene',
       component: () => import('@/views/SelectExperimentScene.vue'),
       props: true

+ 6 - 1
src/store/getters.js

@@ -31,6 +31,11 @@ export default {
     if (!state) return
     if (state.progression && state.progression[experimentName])
       return state.progression[experimentName][sceneName].data
-  }
+  },
 
+  isExperimentDone: state => ({ experimentName, sceneName }) => {
+    if (!state) return
+    if (state.progression && state.progression[experimentName])
+      return state.progression[experimentName][sceneName].done
+  }
 }

+ 7 - 0
src/style.css

@@ -0,0 +1,7 @@
+.height100 {
+  height: 100%;
+}
+
+.cursor {
+  cursor: pointer;
+}

+ 18 - 23
src/views/Experiments/WithReference.vue

@@ -5,7 +5,7 @@
         <v-flex xs12>
           <h1>Experiment with reference</h1>
           <!-- Extract configuration -->
-          <extract-configuration @setConfig="setConfig" :loading-error-message="loadingErrorMessage" ref="configurator" />
+          <extract-configuration @setConfig="setConfig($event, $refs.configurator)" :loading-error-message="loadingErrorMessage" ref="configurator" />
           <!--/ Extract configuration -->
         </v-flex>
         <!-- Loading screen -->
@@ -19,7 +19,7 @@
               <v-card-text class="px-0">Experiment image</v-card-text>
 
               <v-container class="pa-1">
-                <template v-for="i in extractConfig.x">
+                <template v-for="i in extractConfig.y">
                   <v-layout row wrap :key="`row-${i}`">
                     <v-flex
                       v-for="(anExtract, index) in extracts.slice(extractConfig.x * (i - 1), (extractConfig.x * i))"
@@ -29,7 +29,7 @@
                       <v-card flat tile class="d-flex height100">
                         <div
                           v-if="anExtract.loading"
-                          class="img-loader"
+                          class="img-extract-loader"
                           @click.right.prevent
                         >
                           <v-progress-circular
@@ -67,15 +67,20 @@
               <v-img v-if="referenceImage" :src="referenceImage" />
             </v-card>
           </v-flex>
+          <!-- Experiment validation button -->
+          <v-layout justify-end align-content-end>
+            <v-btn @click="finishExperiment" color="primary" large right>Finish experiment</v-btn>
+          </v-layout>
+          <!--/ Experiment validation button -->
         </template>
-      <!--/ Experiment -->
+        <!--/ Experiment -->
       </v-layout>
     </v-container>
   </div>
 </template>
 
 <script>
-import ExperimentBaseExtracts from '@/mixins/ExperimentBaseExtracts.vue'
+import ExperimentBaseExtracts from '@/mixins/ExperimentBaseExtracts'
 import { API_ROUTES } from '@/functions'
 import Loader from '@/components/Loader.vue'
 import ExtractConfiguration from '@/components/ExperimentsComponents/ExtractConfiguration.vue'
@@ -105,9 +110,12 @@ export default {
       this.getQualitiesList()
     ])
 
-    // Get default extracts : min quality, cut config : x = 4, y = 4
-    await this.setConfig(this.extractConfig)
+    // Load the cached configuration in the configurator component
     this.$refs.configurator.setDefaultConfig(this.extractConfig)
+
+    // Load extracts of none were cached
+    if (this.extracts.length === 0) await this.setConfig(this.extractConfig, this.$refs.configurator)
+
     this.saveProgress()
   },
   methods: {
@@ -125,21 +133,8 @@ export default {
 </script>
 
 <style scoped>
-.height100 {
-  height: 100%;
-}
-.img-loader {
-  height: 100%;
-  width: 0px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-}
-.cursor {
-  cursor: pointer;
-}
-.extract:hover {
-  z-index: 999999;
+/* White border when hovering on extracts
+ .extract:hover {
   outline: 2px #f4f4f4 solid;
-}
+} */
 </style>

+ 1 - 1
src/views/ExperimentsList.vue

@@ -61,7 +61,7 @@ export default {
     this.items = Experiments.map(expe => {
       const res = {
         name: expe.fullName,
-        link: `/experiments/selectScene/${expe.name}`
+        link: `/experiments/${expe.name}`
       }
       // Check cache has an entry for each scenes in this experiment
       if (this.progression[expe.name] && Object.keys(this.progression[expe.name]).every(y => this.scenesList.includes(y))) {

+ 22 - 6
src/views/SelectExperimentScene.vue

@@ -43,7 +43,7 @@
                 </v-card-actions>
                 <v-spacer />
                 <v-card-actions>
-                  <v-btn flat round :to="aScene.experimentLink">Start experiment</v-btn>
+                  <v-btn round :disabled="aScene.progression === 'done'" :to="aScene.experimentLink">Start experiment</v-btn>
                 </v-card-actions>
               </v-card-title>
             </v-card>
@@ -56,7 +56,7 @@
 
 <script>
 import { mapState, mapGetters } from 'vuex'
-import { API_ROUTES } from '@/functions'
+import { API_ROUTES, shuffleArray } from '@/functions'
 
 export default {
   name: 'SelectExperimentScene',
@@ -76,6 +76,10 @@ export default {
     ...mapGetters(['getHostURI'])
   },
   async mounted() {
+    let todo = []
+    let working = []
+    let done = []
+
     for (const aScene of this.scenesList) {
       const { data: thumb } = await fetch(`${this.getHostURI}${API_ROUTES.getImage(aScene, 'max')}`)
         .then(res => res.json())
@@ -87,15 +91,27 @@ export default {
       }
       if (this.progression[this.experimentName] && this.progression[this.experimentName][thumb.sceneName]) {
         const obj = this.progression[this.experimentName][thumb.sceneName]
-        if (obj.done)
+        if (obj.done) {
           sceneObj.progression = 'done'
-        else if (Object.entries(obj.data).length !== 0 && obj.constructor === Object)
+          done.push(sceneObj)
+        }
+        else if (Object.entries(obj.data).length !== 0 && obj.constructor === Object) {
           sceneObj.progression = 'working'
-        else
+          working.push(sceneObj)
+        }
+        else {
           sceneObj.progression = 'todo'
+          todo.push(sceneObj)
+        }
       }
-      this.scenes.push(sceneObj)
     }
+    // Randomize each group
+    todo = shuffleArray(todo)
+    working = shuffleArray(working)
+    done = shuffleArray(done)
+
+    // Render the scenes, in the following order : working, todo, done
+    this.scenes = this.scenes.concat(working, todo, done)
   }
 }
 </script>