Commit 1c9dc97a authored by Sascha Herzinger's avatar Sascha Herzinger
Browse files

Some tests and some documentation

parent 71b5d84b
......@@ -132,7 +132,7 @@
import DataBox from '../DataBox.vue'
import Icon from '../Icon.vue'
import store from '../../store/store'
import requestHandling from '../methods/request-handling'
import requestHandling from '../methods/run-analysis'
import * as d3 from 'd3'
import svgtooltip from '../directives/v-svgtooltip'
import { TweenLite } from 'gsap'
......
/**
* This directive can be used to add tooltips to your svg elements.
* HowTo use:
* 1. import it similar to this: `import svgtooltip from '../directives/v-svgtooltip'`
* 2. add it to the `mixins` key of your Vue component: `mixins: [svgtooltip]`
* 3. import the css to style the tooltip: `<style> @import './src/assets/svgtooltip.sass'` ...
* 4. use it on any svg element you like: `<rect v-svgtooltip='myTooltip'>` where myTooltip is an JS object.
*/
import * as d3 from 'd3'
import d3Tip from 'd3-tip'; d3.tip = d3Tip
......
......@@ -2,6 +2,18 @@ import store from '../../store/store'
export default {
methods: {
/**
* A helper method to submit an analysis.
* This method returns a promise that resolves into the statistic results of the task once it is finished.
* Please pay special attention to the `args` syntax.
* @param task_name The name to execute. For instance `compute-correlation`
* @param args An object containing all parameters for the task. Strings wrapped in '$' characters are treated as
* data ids. The backend will attempt to replace them with a Python Pandas DataFrame.
* Example args = {method: 'sum', x: '$1234-5678-12345678$'} will replace x with a DataFrame, if available. This
* value is usually retrieved from the data-box component, which will emit an `update` signal containing currently
* selected data ids. Note that Arrays of '$' wrapped strings are also valid.
* @returns {Promise.<void>} An ES6 promise. Resolves into the result of the analysis.
*/
async runAnalysis ({task_name, args}) {
function timeout (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
......
......@@ -17,7 +17,7 @@ export default class {
vm.$mount(selector)
}
removeChart ({elementId}) {
removeChart ({selector}) {
// TODO: Implement
}
}
import axios from 'axios'
import store from '../store/store'
/**
* The RequestManager class is responsible for the communication with the back end.
* All atomic API calls are present here and only here.
* This class uses axios for AJAX calls.
*
* Note: You should avoid calling the RequestManager methods directly.
* Instead use the provided helpers available as mixins.
*/
export default class {
constructor ({handler, thisBaseURL, fractalisBaseURL, getAuth}) {
this._handler = handler
......@@ -14,8 +22,15 @@ export default class {
})
}
/**
* Submit a POST request that will trigger matching ETLs in the back end.
* The request returns nothing, but sets a session cookie which grants the necessary permission
* to work with the submitted data.
* @param descriptors An array of one or more objects that describe the data to be downloaded.
* @returns {AxiosPromise} An ES6 promise.
*/
createData ({descriptors}) {
this._axios.post('/data', {
return this._axios.post('/data', {
descriptors,
auth: this._getAuth(),
handler: this._handler,
......@@ -23,33 +38,68 @@ export default class {
})
}
reloadData ({taskID}) {
/**
* A combination of deleteData and createData to simulate reload functionality.
* @param taskID The data taskID to be reloaded.
* @returns {AxiosPromise} An ES6 promise.
*/
async reloadData ({taskID}) {
const dataItem = store.getters.data.find(d => d.task_id === taskID)
const descriptors = [dataItem.descriptor]
this.deleteData({taskID})
this.createData({descriptors})
await this.deleteData({taskID})
return this.createData({descriptors})
}
/**
* Submits a GET request that will return meta information of all data available in the current session.
* @returns {AxiosPromise} An ES6 promise.
* */
getAllDataStates () {
return this._axios.get('/data')
}
/**
* Submits a DELETE request that will remove data from the back end if the current session has access to it.
* @param taskID The id of the data to be removed.
* @returns {AxiosPromise} An ES6 promise.
*/
deleteData ({taskID}) {
return this._axios.delete(`/data/${taskID}`)
}
/**
* Submits a DELETE requests that will wipe all data from the back end associated with the session.
* @returns {AxiosPromise}
*/
deleteAllData () {
return this._axios.delete('/data')
}
/**
* Submits a POST request that will launch an analysis for the given parameters.
* The returned promise will resolve into the taskID, which can be used to check the status of the task.
* @param task_name The name of the task to submit.
* @param args The arguments of the task.
* @returns {AxiosPromise} An ES6 promise.
*/
createAnalysis ({task_name, args}) {
return this._axios.post('/analytics', {task_name, args})
}
/**
* Submits a GET request that will return the status for the given id.
* @param taskID The id of the task.
* @returns {AxiosPromise} An ES6 promise.
*/
getAnalysisStatus ({taskID}) {
return this._axios.get(`/analytics/${taskID}`)
}
/**
* Submits a DELETE request that will attempt to cancel an already submitted task.
* @param taskID The id of the task.
* @returns {AxiosPromise} An ES6 promise.
*/
cancelAnalysis ({taskID}) {
store.dispatch('unsetTask', {taskID})
return this._axios.delete(`/analytics/${taskID}`)
......
import types from './mutation-types'
import RequestManager from '../services/request-manager'
export default {
/**
* Commits RequestManager mutation that will overwrite the old value.
* @param context The context of the action.
* @param manager Instance of RequestManager
*/
setRequestManager: (context, manager) => {
context.commit(types.SET_REQUEST_MANAGER, manager)
if (manager instanceof RequestManager) {
context.commit(types.SET_REQUEST_MANAGER, manager)
} else {
throw new Error('The dispatched value must be an instance of RequestManager.')
}
},
/**
* Commits subsets mutation that will overwrite the old value.
* @param context The context of the action.
* @param subsets An array of arrays of ids defining the subsets.
*/
setSubsets: (context, subsets) => {
context.commit(types.SET_SUBSETS, subsets)
if (subsets instanceof Array && ((subsets.length > 0 && subsets[0] instanceof Array) || !subsets.length)) {
context.commit(types.SET_SUBSETS, subsets)
} else {
throw new Error('The dispatched value must be an Array containing Arrays (unless empty).')
}
},
/**
* This method is triggered once during initialization. Ask in a certain time interval for all available
* data in the back end to update the views accordingly. Every interval will commit a data mutation to reflect
* the current back end state.
* @param context The action context.
*/
updateData: context => {
context.getters.requestManager.getAllDataStates().then(response => {
const data = response.data.data_states
......@@ -19,12 +44,31 @@ export default {
}, 2000)
})
},
/**
* Commits a filter mutation that will replace the specified filter with a new value.
* @param context The action context.
* @param filter The filter to apply (e.g. 'ids').
* @param value The value of the new filter (e.g. Array of ids).
*/
setFilter: (context, {filter, value}) => {
context.commit(types.SET_FILTER, {filter, value})
},
/**
* Commits a tasks mutation that will add a new task to the store.
* @param context The context of the action.
* @param taskID The id of the task.
* @param taskName The name of the task.
* @param taskState The current state of the task. (SUCCESS, PENDING, FAILURE)
* @param taskMessage A message in case the task failed.
*/
setTask: (context, {taskID, taskName, taskState, taskMessage}) => {
context.commit(types.SET_TASK, { taskID, taskName, taskState, taskMessage })
},
/**
* Commits a tasks mutation that will remove the task for the given taskID.
* @param context The context of the action.
* @param taskID The id of the task to remove.
*/
unsetTask: (context, {taskID}) => {
context.commit(types.UNSET_TASK, {taskID})
}
......
/**
* Created by sascha on 21/03/2017.
*/
import ChartManager from '../src/services/chart-manager'
describe('Chart manager', () => {
const cm = new ChartManager()
beforeEach(() => {
const el = document.createElement('div')
el.className = 'placeholder'
document.body.appendChild(el)
})
afterEach(() => {
const el = document.querySelector('.placeholder')
el.parentNode.removeChild(el)
})
it('fails to set non existing charts', () => {
const f = () => cm.setChart({chart: 'foo', selector: 'placeholder'})
expect(f).toThrow()
})
it('sets chart if it exists', () => {
cm.setChart({chart: 'correlation-analysis', selector: 'placeholder'})
expect(document.querySelector('.fjs-correlation-analysis')).toBeDefined()
})
})
import requestHandling from '../src/components/methods/request-handling'
import requestHandling from '../src/components/methods/run-analysis'
import RequestManager from '../src/services/request-manager'
import store from '../src/store/store'
......
......@@ -18,4 +18,11 @@ describe('store', () => {
store.dispatch('setRequestManager', requestManager)
expect(store.getters.requestManager).not.toBeNull()
})
it('should have working setTask', () => {
expect(Object.keys(store.getters.tasks).length).toBe(0)
const task = {taskID: 'A', taskName: 'foo', taskState: 'PENDING'}
store.dispatch('setTask', task)
expect(store.getters.tasks['A']).toBeDefined()
})
})
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment