Parcourir la source

Merge branch 'feature/expe-same-extract' into develop

rigwild il y a 4 ans
Parent
commit
8865c01ad7

+ 1 - 1
cleanExtracts.js

@@ -61,4 +61,4 @@ if (argv.includes('--execute')) {
   setup(process.env.IMAGES_PATH, false, true)
 }
 
-module.exports = setup
+module.exports = { setup, extractsRemoverServiceLogger: fileLogger }

+ 17 - 12
experimentConfig.default.js

@@ -1,13 +1,7 @@
 export const mixins = {
   ExperimentBase: {
-    defaultConfig: {
-      lockConfig: true
-    },
-    scenesConfig: {
-      // bathroom: {
-      //   lockConfig: true
-      // }
-    }
+    defaultConfig: {},
+    scenesConfig: {}
   },
 
   ExperimentBaseAreSameImages: {
@@ -23,7 +17,8 @@ export const mixins = {
 
   ExperimentBaseExtracts: {
     defaultConfig: {
-      showHoverBorder: false,
+      lockConfig: false,
+      showHoverBorder: true,
       extractConfig: {
         x: 4,
         y: 4
@@ -31,6 +26,7 @@ export const mixins = {
     },
     scenesConfig: {
       // bathroom: {
+      //   lockConfig: false,
       //   showHoverBorder: false,
       //   extractConfig: {
       //     x: 4,
@@ -44,7 +40,7 @@ export const mixins = {
 
 export const experiments = {
   MatchExtractsWithReference: {
-    mixin: mixins.ExperimentBaseExtracts,
+    mixins: [mixins.ExperimentBaseExtracts],
     defaultConfig: {},
     scenesConfig: {},
     availableScenes: {
@@ -58,7 +54,7 @@ export const experiments = {
     }
   },
   AreSameImagesRandom: {
-    mixin: mixins.ExperimentBaseAreSameImages,
+    mixins: [mixins.ExperimentBaseAreSameImages],
     defaultConfig: {},
     scenesConfig: {},
     availableScenes: {
@@ -67,7 +63,16 @@ export const experiments = {
     }
   },
   AreSameImagesReference: {
-    mixin: mixins.ExperimentBaseAreSameImages,
+    mixins: [mixins.ExperimentBaseAreSameImages],
+    defaultConfig: {},
+    scenesConfig: {},
+    availableScenes: {
+      whitelist: null,
+      blacklist: null
+    }
+  },
+  AreSameImagesReferenceOneExtract: {
+    mixins: [mixins.ExperimentBaseAreSameImages, mixins.ExperimentBaseExtracts],
     defaultConfig: {},
     scenesConfig: {},
     availableScenes: {

+ 2 - 2
index.js

@@ -3,7 +3,7 @@
 import { CronJob } from 'cron'
 
 import server from './server'
-import cleanExtracts from './cleanExtracts'
+import { setup as cleanExtracts, extractsRemoverServiceLogger } from './cleanExtracts'
 import { imagesPath, deleteExtractsCronTime } from './config'
 
 const argv = process.argv.slice(2)
@@ -11,7 +11,7 @@ const argv = process.argv.slice(2)
 // Start the extracts remover service
 if (!argv.includes('--no-delete')) { /* eslint no-new: 0 */
   new CronJob(deleteExtractsCronTime, () => cleanExtracts(imagesPath, false, true), null, true, null, null, false)
-  console.log('Started the extracts remover service.')
+  extractsRemoverServiceLogger.info('Started the extracts remover service.')
 }
 
 // Start the server

+ 38 - 0
src/components/ExperimentsComponents/ExtractsToImage.vue

@@ -0,0 +1,38 @@
+<template>
+  <div>
+    <template v-for="i in extractConfig.y">
+      <v-layout row wrap :key="`row-${i}`">
+        <v-flex
+          v-for="(anExtract, index) in extractsSliced(i)"
+          :key="`extract-${i}-${extractConfig.x}-${extractConfig.y}-${index}-${anExtract.quality}`"
+          class="pa-0"
+        >
+          <v-card flat tile class="d-flex height100">
+            <v-img :src="anExtract" />
+          </v-card>
+        </v-flex>
+      </v-layout>
+    </template>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ExtractsToImage',
+  props: {
+    extractConfig: {
+      type: Object,
+      required: true
+    },
+    extracts: {
+      type: Array,
+      required: true
+    }
+  },
+  computed: {
+    extractsSliced() {
+      return vForIndex => this.extracts.slice(this.extractConfig.x * (vForIndex - 1), (this.extractConfig.x * vForIndex))
+    }
+  }
+}
+</script>

+ 4 - 1
src/config.utils.js

@@ -6,6 +6,9 @@ import { experiments } from '@/../experimentConfig'
 const buildConfig = ({ defaultConfig = {}, scenesConfig = {} }, sceneName) =>
   deepmerge(defaultConfig, scenesConfig[sceneName] || {})
 
+const buildMultiConfig = (confArr, sceneName) =>
+  deepmerge.all(confArr.map(aConfig => buildConfig(aConfig, sceneName)))
+
 /**
 * Build a configuration file by merging the default config with the asked scene.
 * The asked scene config will overwrite the default config.
@@ -21,7 +24,7 @@ export const getExperimentConfig = (experimentName, sceneName) => {
     throw new Error(`Could not find the experiment "${experimentName}" in the config file.`)
 
   // Build parent mixin config
-  const mixinConfig = buildConfig(experiments[experimentName].mixin, sceneName)
+  const mixinConfig = buildMultiConfig(experiments[experimentName].mixins, sceneName)
   // Build global config
   const globalConfig = buildConfig(experiments[experimentName], sceneName)
   // Merge configs

+ 1 - 3
src/mixins/ExperimentBase.vue

@@ -24,9 +24,7 @@ export default {
 
       loadingMessage: null,
       loadingErrorMessage: null,
-      qualities: null,
-
-      lockConfig: null
+      qualities: null
     }
   },
   computed: {

+ 16 - 13
src/mixins/ExperimentBaseAreSameImages.vue

@@ -19,8 +19,8 @@ export default {
       maxTestCount: null,
       testCount: 1,
 
-      leftImage: { link: null, quality: null },
-      rightImage: { link: null, quality: null }
+      image1: null,
+      image2: null
     }
   },
   computed: {
@@ -35,11 +35,11 @@ export default {
     // Get images links for a test
     async getTest(leftQuality, rightQuality) {
       const res = await Promise.all([this.getImage(leftQuality), this.getImage(rightQuality)])
-      const [leftImage, rightImage] = res.map(x => {
+      const [image1, image2] = res.map(x => {
         x.link = `${this.getHostURI}${x.link}`
         return x
       })
-      return { leftImage, rightImage }
+      return { image1, image2 }
     },
 
     // Get a test with random qualities
@@ -58,6 +58,7 @@ export default {
       const randomQuality = this.qualities[rand(0, this.qualities.length - 1)]
 
       const res = [this.qualities[this.qualities.length - 1], randomQuality]
+      this.referenceImagePosition = isReferenceLeft ? 'left' : 'right'
       const table = isReferenceLeft ? res : res.reverse()
       return this.getTest(table[0], table[1])
     },
@@ -65,26 +66,28 @@ export default {
     /** An action was triggered, load a new test and save progression
      * @param {Boolean} areTheSame Are the images the same
      * @param {Function} getTestFn Function to be called to get the next tests
+     * @param {Function} additionalData Object to concat to log
      * @returns {void}
      */
-    async areTheSameAction(areTheSame, getTestFn) {
+    async areTheSameAction(areTheSame, getTestFn, additionalData) {
       this.loadingMessage = 'Loading new test...'
       this.loadingErrorMessage = null
       try {
         this.testCount++
 
-        const obj = {
-          leftImage: this.leftImage,
-          rightImage: this.rightImage,
+        const obj = Object.assign({
+          image1: this.image1,
+          image2: this.image2,
           areTheSame,
           experimentName: this.experimentName,
-          sceneName: this.sceneName
-        }
+          sceneName: this.sceneName,
+          referenceImagePosition: this.referenceImagePosition || undefined
+        }, additionalData || {})
         this.sendMessage({ msgId: experimentMsgId.DATA, msg: obj })
 
-        const { leftImage, rightImage } = await getTestFn()
-        this.leftImage = leftImage
-        this.rightImage = rightImage
+        const { image1, image2 } = await getTestFn()
+        this.image1 = image1
+        this.image2 = image2
 
         // Experiment end
         if (this.testCount > this.maxTestCount) return this.finishExperiment()

+ 18 - 12
src/mixins/ExperimentBaseExtracts.vue

@@ -24,7 +24,8 @@ export default {
       extracts: [],
       extractsInfos: null,
 
-      showHoverBorder: null
+      showHoverBorder: null,
+      lockConfig: null
     }
   },
   computed: {
@@ -46,6 +47,19 @@ export default {
       return data
     },
 
+    // Convert a simple API extracts object to get more informations
+    getExtractFullObject(extractsApiObj) {
+      return extractsApiObj.extracts.map((url, i) => ({
+        link: this.getHostURI + url,
+        quality: extractsApiObj.info.image.quality,
+        zone: i + 1,
+        index: i,
+        nextQuality: findNearestUpper(extractsApiObj.info.image.quality, this.qualities),
+        precQuality: findNearestLower(extractsApiObj.info.image.quality, this.qualities),
+        loading: false
+      }))
+    },
+
     // Config was updated, load extracts and save progression
     async setExtractConfig(config, configuratorRef) {
       if (!config) return
@@ -55,18 +69,10 @@ export default {
       try {
         this.extractConfig.x = config.x
         this.extractConfig.y = config.y
-        const data = await this.getExtracts()
-        const hostURI = this.getHostURI
+        this.extractConfig.quality = config.quality
+        const data = await this.getExtracts(config.quality || undefined)
         this.extractsInfos = data.info
-        this.extracts = data.extracts.map((url, i) => ({
-          link: hostURI + url,
-          quality: data.info.image.quality,
-          zone: i + 1,
-          index: i,
-          nextQuality: findNearestUpper(data.info.image.quality, this.qualities),
-          precQuality: findNearestLower(data.info.image.quality, this.qualities),
-          loading: false
-        }))
+        this.extracts = this.getExtractFullObject(data)
 
         // If there is a configurator, retract it
         if (configuratorRef) configuratorRef.setVisibility(false)

+ 9 - 0
src/router/experiments.js

@@ -25,5 +25,14 @@ export default [
     meta: {
       fullName: 'Are images the same ? (One is reference image, the other is random quality)'
     }
+  },
+  {
+    path: '/experiments/AreSameImagesReferenceOneExtract/:sceneName',
+    name: 'AreSameImagesReferenceOneExtract',
+    component: () => import('@/views/Experiments/AreSameImagesReferenceOneExtract'),
+    props: true,
+    meta: {
+      fullName: 'Are images the same ? (Both are reference images but one contains a random quality extract)'
+    }
   }
 ]

+ 3 - 1
src/store/actions.js

@@ -14,7 +14,9 @@ export default {
     if (!state.uuid) commit('setAppUniqueId')
   },
 
-  resetApp({ commit }, { gdprConsent = false, hostConfig = false, progression = false }) {
+  resetApp({ commit, state }, { gdprConsent = false, hostConfig = false, progression = false }) {
+    if (hostConfig && state.socket.isConnected)
+      this._vm.$disconnect()
     commit('resetApp', { gdprConsent, hostConfig, progression })
   },
 

+ 0 - 2
src/store/mutations.js

@@ -45,8 +45,6 @@ export default {
     }
 
     if (hostConfig) {
-      if (state.socket.isConnected)
-        this._vm.$disconnect()
       state.hostConfig = defaultStateObj.hostConfig
       state.userId = defaultStateObj.userId
       state.experimentId = defaultStateObj.experimentId

+ 1 - 1
src/views/ExperimentValidated.vue

@@ -20,7 +20,7 @@
           Go back to scene selection
         </v-btn>
 
-        <v-btn v-if="hasScenesLeft" flat exact :to="`/experiments/${experimentName}/${getRandomScene()}`">
+        <v-btn v-if="hasScenesLeft" flat exact :to="`/experiments/${experimentName}/${getRandomScene}`">
           <v-icon left>shuffle</v-icon>
           Continue with a random scene
         </v-btn>

+ 6 - 6
src/views/Experiments/AreSameImagesRandom.vue

@@ -23,7 +23,7 @@
             <v-card dark color="primary">
               <v-card-text class="px-0">Image 1</v-card-text>
 
-              <v-img v-if="leftImage && leftImage.link" :src="leftImage.link">
+              <v-img v-if="image1 && image1.link" :src="image1.link">
                 <template v-slot:placeholder>
                   <v-layout fill-height align-center justify-center ma-0>
                     <v-progress-circular indeterminate color="grey lighten-5" />
@@ -36,7 +36,7 @@
             <v-card dark color="primary">
               <v-card-text>Image 2</v-card-text>
 
-              <v-img v-if="rightImage && rightImage.link" :src="rightImage.link" @load="scrollToChoiceButtons">
+              <v-img v-if="image2 && image2.link" :src="image2.link" @load="scrollToChoiceButtons">
                 <template v-slot:placeholder>
                   <v-layout fill-height align-center justify-center ma-0>
                     <v-progress-circular indeterminate color="grey lighten-5" />
@@ -99,10 +99,10 @@ export default {
     await this.getQualitiesList()
 
     // Load a test if not already one loaded
-    if (!this.leftImage || !this.leftImage.link || !this.rightImage || !this.rightImage.link) {
-      const { leftImage, rightImage } = await this.getRandomTest()
-      this.leftImage = leftImage
-      this.rightImage = rightImage
+    if (!this.image1 || !this.image1.link || !this.image2 || !this.image2.link) {
+      const { image1, image2 } = await this.getRandomTest()
+      this.image1 = image1
+      this.image2 = image2
     }
 
     this.saveProgress()

+ 8 - 7
src/views/Experiments/AreSameImagesReference.vue

@@ -23,7 +23,7 @@
             <v-card dark color="primary">
               <v-card-text class="px-0">Image 1</v-card-text>
 
-              <v-img v-if="leftImage && leftImage.link" :src="leftImage.link">
+              <v-img v-if="image1 && image1.link" :src="image1.link">
                 <template v-slot:placeholder>
                   <v-layout fill-height align-center justify-center ma-0>
                     <v-progress-circular indeterminate color="grey lighten-5" />
@@ -36,7 +36,7 @@
             <v-card dark color="primary">
               <v-card-text>Image 2</v-card-text>
 
-              <v-img v-if="rightImage && rightImage.link" :src="rightImage.link" @load="scrollToChoiceButtons">
+              <v-img v-if="image2 && image2.link" :src="image2.link" @load="scrollToChoiceButtons">
                 <template v-slot:placeholder>
                   <v-layout fill-height align-center justify-center ma-0>
                     <v-progress-circular indeterminate color="grey lighten-5" />
@@ -84,7 +84,8 @@ export default {
 
   data() {
     return {
-      experimentName: 'AreSameImagesReference'
+      experimentName: 'AreSameImagesReference',
+      referenceImagePosition: null
     }
   },
 
@@ -99,10 +100,10 @@ export default {
     await this.getQualitiesList()
 
     // Load a test if not already one loaded
-    if (!this.leftImage || !this.leftImage.link || !this.rightImage || !this.rightImage.link) {
-      const { leftImage, rightImage } = await this.getReferenceTest()
-      this.leftImage = leftImage
-      this.rightImage = rightImage
+    if (!this.image1 || !this.image1.link || !this.image2 || !this.image2.link) {
+      const { image1, image2 } = await this.getReferenceTest()
+      this.image1 = image1
+      this.image2 = image2
     }
 
     this.saveProgress()

+ 173 - 0
src/views/Experiments/AreSameImagesReferenceOneExtract.vue

@@ -0,0 +1,173 @@
+<template>
+  <div>
+    <v-container grid-list-md text-xs-center fluid>
+      <v-layout row wrap>
+        <v-flex xs12>
+          <v-layout justify-start>
+            <v-btn flat exact :to="`/experiments/${experimentName}`">
+              <v-icon left>arrow_back</v-icon>
+              Back to scene selection
+            </v-btn>
+          </v-layout>
+
+          <h2>Experiment "{{ $route.meta.fullName }}"</h2>
+          <h3>{{ sceneName }}</h3>
+        </v-flex>
+        <!-- Loading screen -->
+        <loader v-if="loadingMessage" :message="loadingMessage" />
+        <!--/ Loading screen -->
+
+        <!-- Experiment -->
+        <template v-else-if="!loadingErrorMessage && image1 && image2">
+          <v-flex xs12 sm6>
+            <v-card dark color="primary">
+              <v-card-text class="px-0">Image 1</v-card-text>
+
+              <v-container v-if="imageOneExtractPosition === 'left'" class="pa-1">
+                <ExtractsToImage :extracts="image1" :extract-config="extractConfig" />
+              </v-container>
+              <v-img v-else :src="image2.link" @load="scrollToChoiceButtons">
+                <template v-slot:placeholder>
+                  <v-layout fill-height align-center justify-center ma-0>
+                    <v-progress-circular indeterminate color="grey lighten-5" />
+                  </v-layout>
+                </template>
+              </v-img>
+            </v-card>
+          </v-flex>
+          <v-flex xs12 sm6>
+            <v-card dark color="primary">
+              <v-card-text>Image 2</v-card-text>
+              <v-container v-if="imageOneExtractPosition === 'right'" class="pa-1">
+                <ExtractsToImage :extracts="image1" :extract-config="extractConfig" />
+              </v-container>
+              <v-img v-else :src="image2.link" @load="scrollToChoiceButtons">
+                <template v-slot:placeholder>
+                  <v-layout fill-height align-center justify-center ma-0>
+                    <v-progress-circular indeterminate color="grey lighten-5" />
+                  </v-layout>
+                </template>
+              </v-img>
+            </v-card>
+          </v-flex>
+
+
+          <!-- Experiment validation button -->
+          <v-layout justify-center align-content-center>
+            <div id="choice">
+              <v-container grid-list-md text-xs-center fluid>
+                <h2>Test {{ testCount }} / {{ maxTestCount }}</h2>
+                <v-layout row wrap>
+                  <v-flex sm6 xs12>
+                    <v-btn @click="areTheSameActionLocal(false)" color="error" large>Images are NOT the same</v-btn>
+                  </v-flex>
+                  <v-flex sm6 xs12>
+                    <v-btn @click="areTheSameActionLocal(true)" color="success" large>Images are the same</v-btn>
+                  </v-flex>
+                </v-layout>
+              </v-container>
+            </div>
+          </v-layout>
+          <!--/ Experiment validation button -->
+        </template>
+        <!--/ Experiment -->
+      </v-layout>
+    </v-container>
+  </div>
+</template>
+
+<script>
+import ExperimentBaseExtracts from '@/mixins/ExperimentBaseExtracts'
+import ExperimentBaseAreSameImages from '@/mixins/ExperimentBaseAreSameImages'
+import Loader from '@/components/Loader'
+import ExtractsToImage from '@/components/ExperimentsComponents/ExtractsToImage'
+import { rand } from '@/functions'
+
+export default {
+  name: 'AreSameImagesReferenceOneExtract',
+  components: {
+    Loader,
+    ExtractsToImage
+  },
+  mixins: [
+    ExperimentBaseExtracts,
+    ExperimentBaseAreSameImages
+  ],
+
+  data() {
+    return {
+      experimentName: 'AreSameImagesReferenceOneExtract',
+
+      imageOneExtractPosition: null,
+      randomZoneIndex: null,
+      randomZoneQuality: null
+    }
+  },
+
+  async mounted() {
+    // Load config for this scene to local state
+    this.loadConfig()
+
+    // Load progress from store into local state
+    this.loadProgress()
+
+    // Load scene data from the API
+    await this.getQualitiesList()
+
+    // Load a test if not already one loaded
+    if (!this.image1 || !this.image2) {
+      const { image1, image2 } = await this.getReferenceOneExtractTest()
+      this.image1 = image1
+      this.image2 = image2
+    }
+
+    this.saveProgress()
+  },
+
+  methods: {
+    // Get a test with one random quality and a reference
+    async getReferenceOneExtractTest() {
+      // Randomly choose a quality for the extract
+      const randomQuality = this.qualities[rand(0, this.qualities.length - 1)]
+
+      const maxQuality = this.qualities[this.qualities.length - 1]
+
+      // Get the reference image, extracts of reference image and random quality extracts
+      const [maxExtracts, randomExtracts, maxImage] = await Promise.all([
+        this.getExtracts('max'),
+        this.getExtracts(randomQuality),
+        this.getImage(maxQuality)
+      ])
+
+      // Select which zone is the random extract (-1 to get array index)
+      const randomZoneIndex = rand(0, maxExtracts.extracts.length - 1)
+      // Apply the random quality extract
+      maxExtracts.extracts[randomZoneIndex] = randomExtracts.extracts[randomZoneIndex]
+
+      // Fix uris
+      const referenceWithOneExtract = maxExtracts.extracts.map(url => this.getHostURI + url)
+      maxImage.link = this.getHostURI + maxImage.link
+
+      // Backup test data
+      this.randomZoneIndex = randomZoneIndex
+      this.randomZoneQuality = randomQuality
+      this.imageOneExtractPosition = rand(0, 1) === 0 ? 'left' : 'right'
+
+      return {
+        image1: referenceWithOneExtract,
+        image2: maxImage
+      }
+    },
+
+    areTheSameActionLocal(areTheSame) {
+      const additionalData = {
+        imageOneExtractPosition: this.imageOneExtractPosition,
+        randomZoneIndex: this.randomZoneIndex,
+        randomZone: this.randomZoneIndex + 1,
+        randomZoneQuality: this.randomZoneQuality
+      }
+      this.areTheSameAction(areTheSame, this.getReferenceOneExtractTest, additionalData)
+    }
+  }
+}
+</script>

+ 1 - 1
src/views/Experiments/MatchExtractsWithReference.vue

@@ -129,7 +129,7 @@ export default {
     // Load the cached configuration in the configurator component
     if (this.lockConfig === false) this.$refs.configurator.setDefaultConfig(this.extractConfig)
 
-    // Load extracts of none were cached
+    // Load extracts if none were cached
     if (this.extracts.length === 0) await this.setExtractConfig(this.extractConfig, this.$refs.configurator)
 
     this.saveProgress()

+ 2 - 2
src/views/ExperimentsList.vue

@@ -66,11 +66,11 @@ export default {
         link: `/experiments/${expe.name}`
       }
       // Check cache has an entry for each scenes in this experiment
-      if (this.progression[expe.name]) {
+      if (this.progression && this.progression[expe.name]) {
         // Set experiment completion percentage
         const numberOfDoneScenes = Object.keys(this.progression[expe.name]).filter(y => this.progression[expe.name][y].done).length
         const percentage = Math.round(numberOfDoneScenes / scenesList.length * 100)
-        res.completion = `${percentage}%`
+        res.completion = `${numberOfDoneScenes}/${scenesList.length} - ${percentage}%`
       }
       else res.completion = '0%'
 

+ 13 - 2
src/views/SelectExperimentScene.vue

@@ -7,7 +7,8 @@
       </v-btn>
     </v-layout>
 
-    Select a scene for the experiment "{{ experimentFullName }}"
+    <h4>Select a scene for the experiment "{{ experimentFullName }}"</h4>
+    <span>Completion: {{ numberOfValidatedScenes }}/{{ numberOfScenes }} - {{ completionPercent }}%</span>
 
     <v-card>
       <v-container
@@ -83,7 +84,17 @@ export default {
   },
   computed: {
     ...mapState(['progression']),
-    ...mapGetters(['getHostURI'])
+    ...mapGetters(['getHostURI']),
+
+    numberOfScenes() {
+      return this.scenes.length
+    },
+    numberOfValidatedScenes() {
+      return this.scenes.filter(x => x.progression === 'done').length
+    },
+    completionPercent() {
+      return Math.round(this.numberOfValidatedScenes / this.numberOfScenes * 100)
+    }
   },
   async mounted() {
     const scenesList = getExperimentSceneList(this.experimentName)