Parcourir la source

Merge branch 'release/v0.1.8'

rigwild il y a 5 ans
Parent
commit
e9329415b6

+ 1 - 1
package.json

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

+ 6 - 8
server/functions.js

@@ -28,12 +28,9 @@ export const errorHandler = (err, req, res, next) => {
   const { output: { payload } } = err
 
   // Pass the error to the logging handler
-  let logMsg = `Error ${payload.statusCode} - ${payload.error}` +
-    ` - Message :\n${payload.message}`
-  if (err.data) logMsg += `\nData : \n${JSON.stringify(err.data, null, 2) || err.data}`
-  logMsg += `\nStack trace : \n${err.stack}`
-
-  logger.error(logMsg)
+  let errorLogged = new Error(`Error ${payload.statusCode} - ${payload.error} - Message :\n${payload.message}`)
+  errorLogged.stack = err.stack
+  logger.error(formatError(errorLogged, err.data))
 
   // Send the error to the client
   res.status(payload.statusCode).json({
@@ -179,7 +176,7 @@ export const getSceneFilesData = async sceneName => {
  * @param {('info'|'message'|'error'|any|undefined)} event the type of event
  * @returns {string} the log object stringified
  */
-export const formatLog = (data, event = undefined) => (JSON.stringify({
+export const formatLog = (data, event = undefined) => (({
   event,
   log: data,
   date: new Date()
@@ -189,6 +186,7 @@ export const formatLog = (data, event = undefined) => (JSON.stringify({
  * Format an error object
  *
  * @param {Error} errObj an Error object
+ * @param {any} data any data to pass to the formatter
  * @returns {string} formatted log object stringified
  */
-export const formatError = errObj => formatLog({ error: errObj.message, stack: errObj.stack })
+export const formatError = (errObj, data = undefined) => formatLog({ error: errObj.message, stack: errObj.stack, data })

+ 3 - 3
server/winston.config.js

@@ -12,7 +12,7 @@ export const logger = winston.createLogger({
     new winston.transports.Console({
       level: 'debug',
       handleExceptions: true,
-      format: winston.format.simple()
+      format: winston.format.json()
     })
   ],
   exitOnError: false
@@ -28,7 +28,7 @@ export const wsLogger = winston.createLogger({
     new winston.transports.Console({
       level: 'debug',
       handleExceptions: true,
-      format: winston.format.simple()
+      format: winston.format.json()
     })
   ],
   exitOnError: false
@@ -44,7 +44,7 @@ export const dbLogger = winston.createLogger({
     new winston.transports.Console({
       level: 'debug',
       handleExceptions: true,
-      format: winston.format.simple()
+      format: winston.format.json()
     })
   ],
   exitOnError: false

+ 79 - 0
src/components/ExperimentsComponents/ExtractConfiguration.vue

@@ -0,0 +1,79 @@
+<template>
+  <v-card dark>
+    <v-container grid-list-sm fluid>
+      <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-btn @click="setConfig" :disabled="!isConfigNew">Confirm</v-btn>
+
+          <v-alert v-if="loadingErrorMessage" :value="true" type="error" v-text="loadingErrorMessage" />
+        </v-flex>
+      </v-layout>
+    </v-container>
+  </v-card>
+</template>
+
+<script>
+export default {
+  name: 'ExtractConfiguration',
+  props: {
+    loadingErrorMessage: {
+      type: String,
+      required: false,
+      default: null
+    }
+  },
+  data() {
+    return {
+      // Experiment config sliders
+      experimentConfig: {
+        x: 4,
+        y: 4
+      },
+      // Updated when `setConfig` is called
+      extractConfig: {
+        x: 4,
+        y: 4
+      }
+    }
+  },
+  computed: {
+    isConfigNew() {
+      return this.extractConfig.x !== this.experimentConfig.x || this.extractConfig.y !== this.experimentConfig.y
+    }
+  },
+  methods: {
+    setDefaultConfig(config) {
+      this.experimentConfig.x = config.x
+      this.experimentConfig.y = config.y
+      this.extractConfig.x = this.experimentConfig.x
+      this.extractConfig.y = this.experimentConfig.y
+    },
+    setConfig() {
+      this.extractConfig.x = this.experimentConfig.x
+      this.extractConfig.y = this.experimentConfig.y
+      this.$emit('setConfig', this.experimentConfig)
+    }
+  }
+}
+</script>

+ 1 - 1
src/functions.js

@@ -23,7 +23,7 @@ export const delay = ms => new Promise(res => setTimeout(res, ms))
 export const buildURI = (ssl, host, port, route = '') => `${ssl ? 'https' : 'http'}://${host}:${port}${route}`
 export const buildWsURI = (ssl, host, port, uuid = '') => `${ssl ? 'wss' : 'ws'}://${host}:${port}?uuid=${uuid}`
 
-export const sortIntArray = intArray => intArray.sort((a, b) => a - b)
+export const sortIntArray = intArray => intArray ? intArray.sort((a, b) => a - b) : null
 
 export const findNearestUpper = (value, arrInt) => {
   const arr = sortIntArray(arrInt)

+ 56 - 0
src/mixins/ExperimentBase.vue

@@ -0,0 +1,56 @@
+<script>
+import { mapGetters, mapActions } from 'vuex'
+import { API_ROUTES } from '@/functions'
+
+export default {
+  props: {
+    sceneName: {
+      type: String,
+      required: true
+    }
+  },
+  data() {
+    return {
+      experimentName: null, // Must be redefined in parent component
+
+      loadingMessage: null,
+      loadingErrorMessage: null,
+      qualities: null
+    }
+  },
+  computed: {
+    ...mapGetters(['getHostURI', 'getExperimentProgress'])
+  },
+  methods: {
+    ...mapActions(['setExperimentProgress', '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')
+
+      const progress = this.getExperimentProgress({ experimentName: this.experimentName, sceneName: this.sceneName })
+      Object.assign(this.$data, progress)
+      // console.log('Loaded data from store to local state.', progress)
+    },
+
+    // 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')
+      this.setExperimentProgress({ experimentName: this.experimentName, sceneName: this.sceneName, data: this.$data })
+      // console.log('Saved data from local state to store.', this.$data)
+    },
+
+    // Load qualities list from the API
+    async getQualitiesList() {
+      if (this.qualities) return
+
+      const URI = `${this.getHostURI}${API_ROUTES.listSceneQualities(this.sceneName)}`
+      const { data } = await fetch(URI).then(res => res.json())
+      this.qualities = data
+      this.saveProgress()
+    }
+  }
+}
+</script>

+ 103 - 0
src/mixins/ExperimentBaseExtracts.vue

@@ -0,0 +1,103 @@
+<script>
+import ExperimentBase from '@/mixins/ExperimentBase.vue'
+
+import { mapGetters } from 'vuex'
+import { API_ROUTES, findNearestUpper, findNearestLower } from '@/functions'
+
+export default {
+  mixins: [ExperimentBase],
+  data() {
+    return {
+      // Updated when `setConfig` is called
+      extractConfig: {
+        x: 4,
+        y: 4
+      },
+      extracts: []
+    }
+  },
+  computed: {
+    ...mapGetters(['getHostURI'])
+  },
+  methods: {
+    // Load extracts from the API
+    async getExtracts(quality = 'min') {
+      const URI = `${this.getHostURI}${API_ROUTES.getImageExtracts(this.sceneName, quality, this.extractConfig.x, this.extractConfig.y)}`
+      const { data } = await fetch(URI)
+        .then(async res => {
+          res.json = await res.json()
+          return res
+        })
+        .then(res => {
+          if (!res.ok) throw new Error(res.json.message + res.json.data ? `\n${res.json.data}` : '')
+          return res.json
+        })
+      return data
+    },
+
+    // Config was updated, load extracts and save progression
+    async setConfig(config) {
+      if (!config) return
+
+      this.loadingMessage = 'Loading configuration extracts...'
+      this.loadingErrorMessage = null
+      try {
+        this.extractConfig.x = config.x
+        this.extractConfig.y = config.y
+        const data = await this.getExtracts()
+        const hostURI = this.getHostURI
+        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
+        }))
+      }
+      catch (err) {
+        console.error('Failed to load new configuration', err)
+        this.loadingErrorMessage = 'Failed to load new configuration. ' + err.message
+      }
+      finally {
+        this.loadingMessage = null
+        this.saveProgress()
+      }
+    },
+
+    // An action was triggered, load extracts and save progression
+    async extractAction(event, extractObj) {
+      console.log(event, extractObj)
+      const { index, nextQuality, precQuality, quality } = extractObj
+
+      let newQuality
+      if (event.button === 0) newQuality = precQuality // Left click
+      if (event.button === 2) newQuality = nextQuality // Right click
+
+      // Do not load a new extract if same quality
+      if (newQuality === quality) return
+
+      // Set loading state
+      this.extracts[index].loading = true
+      try {
+        // Loading new extract
+        const data = await this.getExtracts(newQuality)
+        this.extracts[index].link = this.getHostURI + data.extracts[index]
+        this.extracts[index].quality = data.info.image.quality
+        this.extracts[index].nextQuality = findNearestUpper(data.info.image.quality, this.qualities)
+        this.extracts[index].precQuality = findNearestLower(data.info.image.quality, this.qualities)
+        this.extracts[index].loading = false
+      }
+      catch (err) {
+        // TODO: toast message if fail
+        console.error('Failed to load extract', err)
+      }
+      finally {
+        this.extracts[index].loading = false
+        this.saveProgress()
+      }
+    }
+  }
+}
+</script>

+ 2 - 2
src/router/experiments.js

@@ -1,13 +1,13 @@
 export default [
   {
-    path: '/experiments/ExperimentNoReference/:sceneId',
+    path: '/experiments/ExperimentNoReference/:sceneName',
     name: 'ExperimentNoReference',
     fullName: 'No reference image',
     component: () => import('@/views/Experiments/NoReference.vue'),
     props: true
   },
   {
-    path: '/experiments/ExperimentWithReference/:sceneId',
+    path: '/experiments/ExperimentWithReference/:sceneName',
     name: 'ExperimentWithReference',
     fullName: 'With reference image',
     component: () => import('@/views/Experiments/WithReference.vue'),

+ 1 - 0
src/store/actions.js

@@ -9,6 +9,7 @@ export default {
   resetApp({ commit }, { hostConfig = false, progression = false }) {
     commit('resetApp', { hostConfig, progression })
   },
+
   async setHostConfig({ state, commit }, { ssl, host, port }) {
     // Timeout after 1s
     const controller = new AbortController()

+ 8 - 1
src/store/getters.js

@@ -20,10 +20,17 @@ export default {
   areScenesLoaded(state) {
     if (!state) return
     return state.scenesList !== null
-  }
+  },
 
   // TODO: Cache scene thumb URI
   // areScenesThumbsLoaded(state) {
   //   return state.scenesList !== null
   // }
+
+  getExperimentProgress: state => ({ experimentName, sceneName }) => {
+    if (!state) return
+    if (state.progression && state.progression[experimentName])
+      return state.progression[experimentName][sceneName].data
+  }
+
 }

+ 20 - 14
src/store/mutations.js

@@ -8,6 +8,20 @@ const checkProgression = (state, experimentName, sceneName) => {
   if (!state.progression[experimentName][sceneName])
     state.progression[experimentName][sceneName] = { done: false, data: {} }
 }
+const createProgressionObj = (state, scenes) => {
+  const progressionObj = Experiments.reduce((accExpe, expe) => {
+    const scenesProgressObj = scenes.reduce((accScene, scene) => {
+      // Do not overwrite current progression
+      if (state.progression[expe.name] && state.progression[expe.name][scene])
+        accScene[scene] = state.progression[expe.name][scene]
+      else accScene[scene] = { done: false, data: {} }
+      return accScene
+    }, {})
+    accExpe[expe.name] = scenesProgressObj
+    return accExpe
+  }, {})
+  state.progression = progressionObj
+}
 
 export default {
   setAppUniqueId(state) {
@@ -20,7 +34,11 @@ export default {
         this._vm.$disconnect()
       state.hostConfig = defaultState().hostConfig
     }
-    if (progression) state.progression = defaultState().progression
+    if (progression) {
+      // Reset progression and recreate the progression object
+      state.progression = defaultState().progression
+      createProgressionObj(state, state.scenesList)
+    }
   },
 
   setHostConfig(state, newConfig) {
@@ -29,19 +47,7 @@ export default {
 
   setListScenes(state, scenes) {
     state.scenesList = scenes
-    const progressionObj = Experiments.reduce((accExpe, expe) => {
-      const scenesProgressObj = scenes.reduce((accScene, scene) => {
-        // Do not overwrite current progression
-        if (state.progression[expe.name] && state.progression[expe.name][scene])
-          accScene[scene] = state.progression[expe.name][scene]
-        else accScene[scene] = { done: false, data: {} }
-        return accScene
-      }, {})
-      accExpe[expe.name] = scenesProgressObj
-      return accExpe
-    }, {})
-
-    state.progression = progressionObj
+    createProgressionObj(state, scenes)
   },
 
   setExperimentProgress(state, { experimentName, sceneName, data }) {

+ 28 - 149
src/views/Experiments/WithReference.vue

@@ -4,40 +4,9 @@
       <v-layout row wrap>
         <v-flex xs12>
           <h1>Experiment with reference</h1>
-          <v-card dark>
-            <v-container grid-list-sm fluid>
-              <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-btn @click="setConfig" :disabled="!isConfigNew">Confirm</v-btn>
-
-                  <v-alert v-if="loadingErrorMessage" :value="true" type="error" v-text="loadingErrorMessage" />
-                </v-flex>
-              </v-layout>
-            </v-container>
-          </v-card>
+          <!-- Extract configuration -->
+          <extract-configuration @setConfig="setConfig" :loading-error-message="loadingErrorMessage" ref="configurator" />
+          <!--/ Extract configuration -->
         </v-flex>
         <!-- Loading screen -->
         <loader v-if="loadingMessage" :message="loadingMessage" />
@@ -106,140 +75,50 @@
 </template>
 
 <script>
-import { mapGetters, mapActions } from 'vuex'
-import { API_ROUTES, findNearestUpper, findNearestLower } from '@/functions'
+import ExperimentBaseExtracts from '@/mixins/ExperimentBaseExtracts.vue'
+import { API_ROUTES } from '@/functions'
 import Loader from '@/components/Loader.vue'
+import ExtractConfiguration from '@/components/ExperimentsComponents/ExtractConfiguration.vue'
 
 export default {
   name: 'ExperimentWithReference',
   components: {
-    Loader
-  },
-  props: {
-    sceneId: {
-      type: String,
-      required: true
-    }
+    Loader,
+    ExtractConfiguration
   },
+  mixins: [ExperimentBaseExtracts],
+
   data() {
     return {
-      referenceImage: null,
-      qualities: null,
-
-      experimentConfig: { // Experiment config sliders
-        x: 8,
-        y: 4,
-        error: null
-      },
-      extractConfig: { // Updated when `setConfig` is called
-        x: 8,
-        y: 4
-      },
-      loadingMessage: null,
-      loadingErrorMessage: null,
-      extracts: []
-    }
-  },
-  computed: {
-    ...mapGetters(['getHostURI']),
-    isConfigNew() {
-      return this.extractConfig.x !== this.experimentConfig.x || this.extractConfig.y !== this.experimentConfig.y
+      experimentName: 'ExperimentWithReference',
+      referenceImage: null
     }
   },
 
   async mounted() {
-    await this.getReferenceImage()
-    await this.getQualitiesList()
+    // Load progress from store into local state
+    this.loadProgress()
+
+    // Load scene data from the API
+    await Promise.all([
+      this.getReferenceImage(),
+      this.getQualitiesList()
+    ])
 
     // Get default extracts : min quality, cut config : x = 4, y = 4
-    await this.setConfig()
+    await this.setConfig(this.extractConfig)
+    this.$refs.configurator.setDefaultConfig(this.extractConfig)
+    this.saveProgress()
   },
   methods: {
-    ...mapActions([]),
-
+    // Load the reference image from the API
     async getReferenceImage() {
-      const URI = `${this.getHostURI}${API_ROUTES.getImage(this.sceneId, 'max')}`
-      const { data } = await fetch(URI).then(res => res.json())
-      this.referenceImage = this.getHostURI + data.link
-    },
+      if (this.referenceImage) return
 
-    async getQualitiesList() {
-      const URI = `${this.getHostURI}${API_ROUTES.listSceneQualities(this.sceneId)}`
+      const URI = `${this.getHostURI}${API_ROUTES.getImage(this.sceneName, 'max')}`
       const { data } = await fetch(URI).then(res => res.json())
-      this.qualities = data
-    },
-
-    async getExtracts(quality = 'max') {
-      const URI = `${this.getHostURI}${API_ROUTES.getImageExtracts(this.sceneId, quality, this.extractConfig.x, this.extractConfig.y)}`
-      const { data } = await fetch(URI)
-        .then(async res => {
-          res.json = await res.json()
-          return res
-        })
-        .then(res => {
-          if (!res.ok) throw new Error(res.json.message + res.json.data ? `\n${res.json.data}` : '')
-          return res.json
-        })
-      return data
-    },
-
-    async setConfig() {
-      // Check if the config is the same
-      if (this.extracts.length > 0 && !this.isConfigNew) return
-
-      this.loadingMessage = 'Loading configuration extracts...'
-      this.loadingErrorMessage = null
-      try {
-        this.extractConfig.x = this.experimentConfig.x
-        this.extractConfig.y = this.experimentConfig.y
-        const data = await this.getExtracts()
-        const hostURI = this.getHostURI
-        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
-        }))
-      }
-      catch (err) {
-        console.error('Failed to load new configuration', err)
-        this.loadingErrorMessage = 'Failed to load new configuration. ' + err.message
-      }
-      this.loadingMessage = null
-    },
-
-    async extractAction(event, extractObj) {
-      console.log(event, extractObj)
-      const { index, nextQuality, precQuality, quality } = extractObj
-
-      let newQuality
-      if (event.button === 0) newQuality = precQuality // Left click
-      if (event.button === 2) newQuality = nextQuality // Right click
-
-      // Do not load a new extract if same quality
-      if (newQuality === quality) return
-
-      // Set loading state
-      this.extracts[index].loading = true
-      try {
-        // Loading new extract
-        const data = await this.getExtracts(newQuality)
-        this.extracts[index].link = this.getHostURI + data.extracts[index]
-        this.extracts[index].quality = data.info.image.quality
-        this.extracts[index].nextQuality = findNearestUpper(data.info.image.quality, this.qualities)
-        this.extracts[index].precQuality = findNearestLower(data.info.image.quality, this.qualities)
-        this.extracts[index].loading = false
-      }
-      catch (err) {
-        // TODO: toast message if fail
-        console.error('Failed to load extract', err)
-      }
-      finally {
-        this.extracts[index].loading = false
-      }
+      this.referenceImage = this.getHostURI + data.link
+      this.saveProgress()
     }
   }
 }

+ 1 - 2
src/views/SelectExperimentScene.vue

@@ -80,8 +80,7 @@ export default {
       const { data: thumb } = await fetch(`${this.getHostURI}${API_ROUTES.getImage(aScene, 'max')}`)
         .then(res => res.json())
 
-      let sceneObj = {}
-      sceneObj = {
+      let sceneObj = {
         name: thumb.sceneName,
         thumbLink: `${this.getHostURI}${thumb.link}`,
         experimentLink: `/experiments/${this.experimentName}/${thumb.sceneName}`