Parcourir la source

Merge branch 'feature/config-file' into develop

rigwild il y a 4 ans
Parent
commit
6bb4c8ca18

+ 1 - 0
.gitignore

@@ -26,3 +26,4 @@ yarn-error.log*
 /test/images
 /data/*
 /doc
+/experimentConfig/

+ 18 - 1
README.md

@@ -8,7 +8,7 @@ cd Antoine_Internship
 ```
 
 ## Run as a Docker instance
-### Configure
+### Configure application
 Use the following environment variables to configure the application.
 
 | Option      | Default value | Description | Server | Client |
@@ -35,6 +35,16 @@ Configure more deeply the way the app works by modifying *[config.js](config.js)
 | `wsLogger` | Logs : `logs/ws.log` Errors : `logs/ws.error.log` | WebSocket logger configuration |
 | `dbLogger` | Logs : `logs/db.log` Errors : `logs/db.error.log` | Database logger configuration |
 
+### Configure experiments
+If you want to use the default experiments configurations, you can skip this part, the Dockerfile does it for you.
+
+You can modify the configuration for each mixins, experiments or scene. To do so, you first need to initialize experiment configurations first.
+```sh
+cp -r experimentConfig.default experimentConfig
+```
+You can modify the configuration for each mixins, experiments or scene by modifying files in the newly created *`./experimentConfig`* directory.
+You configuration will be copied in the Docker instance.
+
 ### Run server + client
 Linux
 ```sh
@@ -109,6 +119,13 @@ yarn run server:lint
 
 
 ### Client
+To use the client, you need to initialize experiment configurations first.
+```sh
+cp -r experimentConfig.default experimentConfig
+```
+You can modify the configuration for each mixins, experiments or scene by modifying files in the newly created *`./experimentConfig`* directory.
+
+
 #### Compile and minify for production
 Files will be built to the `dist/` directory.
 ```sh

+ 10 - 1
back.Dockerfile

@@ -8,7 +8,16 @@ WORKDIR /usr/src/app
 # Server port
 EXPOSE 5000
 
+# Install dependencies and generate documentation
 RUN yarn install && yarn doc
 
 # Build front if SERVE_CLIENT=true
-CMD if [ "$SERVE_CLIENT" == "true" ] ; then NODE_ENV=test yarn test && yarn run app:build && yarn run server:start ; else NODE_ENV=test yarn test && yarn run server:start ; fi
+CMD if [ "$SERVE_CLIENT" == "true" ] ; \
+  then \
+    ([ -d ./experimentConfig ] && \
+      echo "Experiment configuration found" \
+      || echo "Experiment configuration not found, copying default" && cp -r experimentConfig.default experimentConfig) && \
+    NODE_ENV=test yarn test && \
+    yarn run app:build && \
+    yarn run server:start ; \
+  else NODE_ENV=test yarn test && yarn run server:start ; fi

+ 2 - 0
config.messagesId.js

@@ -1,3 +1,5 @@
+'use strict'
+
 // List of IDs for messages sent using WebSockets
 
 // Message IDs for experiments events

+ 9 - 0
experimentConfig.default/Experiments/WithReference.js

@@ -0,0 +1,9 @@
+import { buildConfig } from '@/functions'
+
+// This will apply to all the scenes
+export const defaultConfig = {}
+
+// This will overwrite the config for the given scene
+export const scenesConfig = {}
+
+export default buildConfig(defaultConfig, scenesConfig)

+ 9 - 0
experimentConfig.default/mixins/ExperimentBase.js

@@ -0,0 +1,9 @@
+import { buildConfig } from '@/functions'
+
+// This will apply to all the scenes
+export const defaultConfig = {}
+
+// This will overwrite the config for the given scene
+export const scenesConfig = {}
+
+export default buildConfig(defaultConfig, scenesConfig)

+ 25 - 0
experimentConfig.default/mixins/ExperimentBaseExtracts.js

@@ -0,0 +1,25 @@
+import { buildConfig } from '@/functions'
+
+// This will apply to all the scenes
+export const defaultConfig = {
+  // showHoverBorder: false,
+  // extractConfig: {
+  //   x: 4,
+  //   y: 6
+  // },
+  // lockConfig: true
+}
+
+// This will overwrite the config for the given scene
+export const scenesConfig = {
+  // bathroom: {
+  //   showHoverBorder: false,
+  //   extractConfig: {
+  //     x: 4,
+  //     y: 10
+  //   },
+  //   lockConfig: false
+  // }
+}
+
+export default buildConfig(defaultConfig, scenesConfig)

+ 8 - 7
package.json

@@ -23,12 +23,6 @@
     "morgan": "^1.9.1",
     "serve-static": "^1.13.2",
     "sharp": "^0.22.1",
-    "vue": "^2.6.10",
-    "vue-native-websocket": "^2.0.13",
-    "vue-router": "^3.0.6",
-    "vuetify": "^1.5.14",
-    "vuex": "^3.1.0",
-    "vuex-persist": "^2.0.0",
     "winston": "^3.2.1",
     "ws": "^7.0.0"
   },
@@ -40,6 +34,7 @@
     "apidoc": "^0.17.7",
     "ava": "^1.4.1",
     "babel-eslint": "^10.0.1",
+    "deepmerge": "^3.2.0",
     "eslint": "^5.16.0",
     "eslint-plugin-vue": "^5.2.2",
     "fs-extra": "^7.0.1",
@@ -47,9 +42,15 @@
     "stylus": "^0.54.5",
     "stylus-loader": "^3.0.2",
     "supertest": "^4.0.2",
+    "vue": "^2.6.10",
     "vue-cli-plugin-vuetify": "^0.5.0",
+    "vue-native-websocket": "^2.0.13",
+    "vue-router": "^3.0.6",
     "vue-template-compiler": "^2.6.10",
-    "vuetify-loader": "^1.2.2"
+    "vuetify": "^1.5.14",
+    "vuetify-loader": "^1.2.2",
+    "vuex": "^3.1.0",
+    "vuex-persist": "^2.0.0"
   },
   "postcss": {
     "plugins": {

+ 4 - 4
src/components/ExperimentsComponents/ExtractConfiguration.vue

@@ -27,7 +27,7 @@
                 max="15"
               />
 
-              <v-btn @click="setConfig" :disabled="!isConfigNew">Confirm</v-btn>
+              <v-btn @click="setExtractConfig" :disabled="!isConfigNew">Confirm</v-btn>
             </div>
           </v-slide-y-transition>
 
@@ -63,7 +63,7 @@ export default {
         x: 4,
         y: 4
       },
-      // Updated when `setConfig` is called
+      // Updated when `setExtractConfig` is called
       extractConfig: {
         x: 4,
         y: 4
@@ -86,10 +86,10 @@ export default {
       this.extractConfig.x = this.experimentConfig.x
       this.extractConfig.y = this.experimentConfig.y
     },
-    setConfig() {
+    setExtractConfig() {
       this.extractConfig.x = this.experimentConfig.x
       this.extractConfig.y = this.experimentConfig.y
-      this.$emit('setConfig', this.experimentConfig)
+      this.$emit('setExtractConfig', this.experimentConfig)
     }
   }
 }

+ 10 - 0
src/functions.js

@@ -57,3 +57,13 @@ export const shuffleArray = array => {
   }
   return array
 }
+
+/**
+ * Build a configuration file by merging the default config with the asked scene.
+ * The asked scene config will overwrite the default config.
+ * @param {Object} defaultConfig The default configuration object
+ * @param {Object} scenesConfig The scenes specific configuration
+ * @returns {Function} A function that will return the scene configuration
+ */
+export const buildConfig = (defaultConfig = {}, scenesConfig = {}) =>
+  sceneName => Object.assign(defaultConfig, scenesConfig[sceneName])

+ 27 - 0
src/mixins/ExperimentBase/config.js

@@ -0,0 +1,27 @@
+import deepmerge from 'deepmerge'
+import { buildConfig } from '@/functions'
+
+// const getMixinConfig = () => {}
+const getGlobalConfig = () => import('@/../experimentConfig/mixins/ExperimentBase')
+
+// This will apply to all the scenes
+export const defaultConfig = async () => {
+  const globalConfig = (await getGlobalConfig()).defaultConfig
+  return deepmerge.all([
+    {
+      lockConfig: true
+    },
+    globalConfig
+  ])
+}
+
+// This will overwrite the config for the given scene
+export const scenesConfig = async () => {
+  const globalConfig = (await getGlobalConfig()).scenesConfig
+  return deepmerge.all([
+    {},
+    globalConfig
+  ])
+}
+
+export default async () => buildConfig(await defaultConfig(), await scenesConfig())

+ 12 - 1
src/mixins/ExperimentBase/index.vue

@@ -23,12 +23,15 @@ export default {
 
       loadingMessage: null,
       loadingErrorMessage: null,
-      qualities: null
+      qualities: null,
+
+      lockConfig: null
     }
   },
   computed: {
     ...mapGetters(['getHostURI', 'getExperimentProgress', 'isExperimentDone'])
   },
+
   mounted() {
     if (!this.getExperimentProgress({ experimentName: this.experimentName, sceneName: this.sceneName }).experimentName)
       this.sendMessage({ msgId: experimentMsgId.STARTED })
@@ -39,6 +42,7 @@ export default {
       this.$router.push(`/experiments/${this.experimentName}`)
     }
   },
+
   methods: {
     ...mapActions(['setExperimentProgress', 'setExperimentDone', 'sendMessage']),
 
@@ -60,6 +64,13 @@ export default {
       // console.log('Saved data from local state to store.', this.$data)
     },
 
+    // Load a config object into the local state
+    async loadConfig(configFn) {
+      const config = (await configFn())(this.sceneName)
+      // console.log('Loaded configuration', config)
+      Object.assign(this.$data, config)
+    },
+
     // Finish an experiment, sending full data to the server
     // Don't forget to surcharge this function when using this mixin to add more data
     finishExperiment() {

+ 43 - 0
src/mixins/ExperimentBaseExtracts/config.js

@@ -0,0 +1,43 @@
+import deepmerge from 'deepmerge'
+import { buildConfig } from '@/functions'
+
+const getMixinConfig = () => import('@/mixins/ExperimentBase/config')
+const getGlobalConfig = () => import('@/../experimentConfig/mixins/ExperimentBaseExtracts')
+
+// This will apply to all the scenes
+export const defaultConfig = async () => {
+  // Import parent mixin config
+  const mixinConfig = await getMixinConfig().then(({ defaultConfig: fn }) => fn())
+
+  // Import global config
+  const globalConfig = (await getGlobalConfig()).defaultConfig
+
+  return deepmerge.all([
+    mixinConfig,
+    {
+      showHoverBorder: false,
+      extractConfig: {
+        x: 4,
+        y: 4
+      }
+    },
+    globalConfig
+  ])
+}
+
+// This will overwrite the config for the given scene
+export const scenesConfig = async () => {
+  // Import parent mixin config
+  const mixinConfig = await getMixinConfig().then(({ scenesConfig: fn }) => fn())
+
+  // Import global config
+  const globalConfig = (await getGlobalConfig()).scenesConfig
+
+  return deepmerge.all([
+    mixinConfig,
+    {},
+    globalConfig
+  ])
+}
+
+export default async () => buildConfig(await defaultConfig(), await scenesConfig())

+ 19 - 7
src/mixins/ExperimentBaseExtracts/index.vue

@@ -17,12 +17,14 @@ export default {
   mixins: [ExperimentBase],
   data() {
     return {
-      // Updated when `setConfig` is called
+      // Updated when `setExtractConfig` is called
       extractConfig: {
-        x: 4,
-        y: 4
+        x: null,
+        y: null
       },
-      extracts: []
+      extracts: [],
+
+      showHoverBorder: null
     }
   },
   computed: {
@@ -45,8 +47,8 @@ export default {
     },
 
     // Config was updated, load extracts and save progression
-    async setConfig(config, configuratorRef) {
-      if (!config || !configuratorRef) return
+    async setExtractConfig(config, configuratorRef) {
+      if (!config) return
 
       this.loadingMessage = 'Loading configuration extracts...'
       this.loadingErrorMessage = null
@@ -64,7 +66,9 @@ export default {
           precQuality: findNearestLower(data.info.image.quality, this.qualities),
           loading: false
         }))
-        configuratorRef.setVisibility(false)
+
+        // If there is a configurator, retract it
+        if (configuratorRef) configuratorRef.setVisibility(false)
       }
       catch (err) {
         console.error('Failed to load new configuration', err)
@@ -137,3 +141,11 @@ export default {
   }
 }
 </script>
+
+<style>
+/* White border when hovering on extracts */
+ .extract-hover-border:hover {
+  z-index: 1;
+  outline: 2px #f4f4f4 solid;
+}
+</style>

+ 2 - 2
src/router/experiments.js

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

src/views/Experiments/NoReference.vue → src/views/Experiments/NoReference/index.vue


+ 37 - 0
src/views/Experiments/WithReference/config.js

@@ -0,0 +1,37 @@
+import deepmerge from 'deepmerge'
+import { buildConfig } from '@/functions'
+
+const getMixinConfig = () => import('@/mixins/ExperimentBaseExtracts/config')
+const getGlobalConfig = () => import('@/../experimentConfig/Experiments/WithReference')
+
+// This will apply to all the scenes
+export const defaultConfig = async () => {
+  // Import parent mixin config
+  const mixinConfig = await getMixinConfig().then(({ defaultConfig: fn }) => fn())
+
+  // Import global config
+  const globalConfig = (await getGlobalConfig()).defaultConfig
+
+  return deepmerge.all([
+    mixinConfig,
+    {},
+    globalConfig
+  ])
+}
+
+// This will overwrite the config for the given scene
+export const scenesConfig = async () => {
+  // Import parent mixin config
+  const mixinConfig = await getMixinConfig().then(({ scenesConfig: fn }) => fn())
+
+  // Import global config
+  const globalConfig = (await getGlobalConfig()).scenesConfig
+
+  return deepmerge.all([
+    mixinConfig,
+    {},
+    globalConfig
+  ])
+}
+
+export default async () => buildConfig(await defaultConfig(), await scenesConfig())

+ 10 - 11
src/views/Experiments/WithReference.vue

@@ -12,7 +12,7 @@
 
           <h1>Experiment with reference - {{ sceneName }}</h1>
           <!-- Extract configuration -->
-          <extract-configuration @setConfig="setConfig($event, $refs.configurator)" :loading-error-message="loadingErrorMessage" ref="configurator" />
+          <extract-configuration v-if="lockConfig === false" @setExtractConfig="setExtractConfig($event, $refs.configurator)" :loading-error-message="loadingErrorMessage" ref="configurator" />
           <!--/ Extract configuration -->
         </v-flex>
         <!-- Loading screen -->
@@ -48,7 +48,8 @@
                           :src="anExtract.link"
                           @click.left.prevent="extractAction($event, anExtract)"
                           @click.right.prevent="extractAction($event, anExtract)"
-                          class="cursor extract"
+                          class="cursor"
+                          :class="{ 'extract-hover-border': showHoverBorder === true }"
                         >
                           <template v-slot:placeholder>
                             <v-layout
@@ -91,6 +92,7 @@ import ExperimentBaseExtracts from '@/mixins/ExperimentBaseExtracts'
 import { API_ROUTES } from '@/functions'
 import Loader from '@/components/Loader.vue'
 import ExtractConfiguration from '@/components/ExperimentsComponents/ExtractConfiguration.vue'
+import experimentConfig from './config'
 
 export default {
   name: 'ExperimentWithReference',
@@ -108,6 +110,9 @@ export default {
   },
 
   async mounted() {
+    // Load config for this scene to local state
+    await this.loadConfig(experimentConfig)
+
     // Load progress from store into local state
     this.loadProgress()
 
@@ -118,13 +123,14 @@ export default {
     ])
 
     // Load the cached configuration in the configurator component
-    this.$refs.configurator.setDefaultConfig(this.extractConfig)
+    if (this.lockConfig === false) 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)
+    if (this.extracts.length === 0) await this.setExtractConfig(this.extractConfig, this.$refs.configurator)
 
     this.saveProgress()
   },
+
   methods: {
     // Load the reference image from the API
     async getReferenceImage() {
@@ -138,10 +144,3 @@ export default {
   }
 }
 </script>
-
-<style scoped>
-/* White border when hovering on extracts
- .extract:hover {
-  outline: 2px #f4f4f4 solid;
-} */
-</style>

+ 5 - 0
yarn.lock

@@ -3122,6 +3122,11 @@ deepmerge@^1.5.2:
   resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
   integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==
 
+deepmerge@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.2.0.tgz#58ef463a57c08d376547f8869fdc5bcee957f44e"
+  integrity sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow==
+
 default-gateway@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b"