.. _dev-developing-vcdat: ================= Developing vCDAT ================= Flask ------ While I have been trying to move functionality from flask to vcs-js, there are still some critical functions provided by the flask server. For example: * When loading the vcdat page, html and static files are currently served by the flask server stored in the `backend `__. * Browsers are unable to query the local filesystem in detail. We use flask to provide `file browser functions `__, * When editing a template, we display an example plot to demonstrate the changes. `This is done with flask `__. All flask functionality can be found `here `__. Redux ------ It is highly recommended that you familiarize yourself with redux via the `official documentation `__, but I have supplied some basic information here with an emphasis on how we use Redux in vCDAT. .. _connect: Connect ~~~~~~~ .. note:: Any time a component is wrapped by something like Redux, it can affect how a component is unit tested. In some instances, you will only need to provide some additional props. In other, more complex cases, it may be easier to export an unwrapped version for testing. Historically, these components have been named "PureComponents" in vCDAT. For example, if the component was named "Example", we would export the wrapped "Example" as the default and the unwrapped one would look like ``export { Example as PureExample }`` A component that uses Redux does not 'inherit' from Redux like you might expect. Instead, a component is 'wrapped' by the connect function. .. code:: javascript export default connect(mapStateToProps, mapDispatchToProps)(Example); Where mapStateToProps and mapDispatchToProps are functions. mapStateToProps ^^^^^^^^^^^^^^^^ mapStateToProps does exactly what it says. It will take state from redux and hand it to the component as props. The function is expected to return an object containing the bits of state we want. .. code:: javascript const mapStateToProps = (state) => { return { selected_template: state.present.ui.selected_template, } } We could then access this via ``this.props.selected_template``. mapDispatchToProps ^^^^^^^^^^^^^^^^^^^ The dispatch function is what tells Redux that the store needs to change. The function should return an object where each key maps to a function. .. code:: javascript const mapDispatchToProps = (dispatch) => { return { deselectCell: function(){ dispatch(Actions.deselectCell()) }, } } We could then deselect a cell by calling ``this.props.deselectCell()`` Actions ~~~~~~~~ An action is simply an object that contains data describing how the store should change. Actions are always given to the store by the dispatch function that comes from :ref:`connect`. We keep all of the actions that can be dispatched in the `Actions.js `__ file. The only thing required by any action is that it has a ``type`` key. How it works ~~~~~~~~~~~~~ Once loaded, the redux `store `__ will initialize it's state. It does this by calling ``getInitialState()`` on each `model `__. Once initialized, Redux follows a very simple pattern. When something in the store (The top level object that holds everything redux knows about our application) needs to be updated, a component will dispatch an 'Action'. This 'Action' essentially boils down to a javascript object with a 'type' key. Redux then looks to hand this action to every reducer it knows about. We identify the reducers inside the `Store.js `__ file by pointing to the reduce function of each model. Each reduce function will look at the action type to determine what, if anything, needs to be done. Let's look at an example. Here is what happens when a user clicks a cell to select it. Inside the `Cell component `__ the following code segment runs in response to the browser event. .. code:: javascript selectCell(){ let id = this.getOwnCellId() if(this.props.selected_cell_id == id){ // this.props.deselectCell() // if a cell is selected, a user clicking on it should deselect it. // Turning this feature off since a user manipulating an interactive plot toggles the selection too much return } else{ this.props.selectCell(id) } } What is important to look at here is ``this.props.selectCell(id)``. This function actually comes from the way we connect this component to Redux. Look `further down `__ and you will see that selectCell comes from the mapDispatchToProps function: .. code:: javascript const mapDispatchToProps = (dispatch) => { return { selectCell: function(cell_id){ dispatch(Actions.selectCell(cell_id)) }, } } The action that is 'dispatched' comes from the `Actions.js `__ file. This file simply gives us a single reliable place to reuse our actions, and helps assure that the formatting of these objects is consistent. Here is what this action does: .. code:: javascript selectCell(cell_id) { return { type: "SELECT_CELL", cell_id: cell_id }; }, *Remember, an action is really just an object* When ``dispatch()`` is called with an action. Every reducer is given the action for processing. In this case, only the `Spreadhseet model `__ will make any changes to the store. This is because each reducer looks at the ``type`` of the action and compares that with the types that it handles Inside the Spreadsheet model we see that it indeed handles an action of type ``SELECT_CELL`` .. code::javascript switch (action.type) { case 'SELECT_CELL': new_state = jQuery.extend(true, {}, state); new_state.selected_cell_id = action.cell_id return new_state Once that completes, our store will finish updating and any component that relied on ``selected_cell_id`` will recieve the new value. Vcs-js ------- Kitware has provided some basic documentation `here `__. Vcs-js is included via a script tag inside the `index.html `__ file. Vcs-js provides it's own server with which the vcs object maintains a websocket connection to. If you need to debug or develop a new feature for Vcs-js, the following is a quick run down of how a call to the ``vcs`` variable is processed. All functions that the vcs object exposes are listed in the `src/index.js `__ file, and in addition to being defined here, each must be exported at the bottom of the file. Notice that each operation makes a function call with a string value such as ``'vcs.createtemplate'``. Each of these strings corresponds to a function in the `vcs-server directory `__ which has been decorated with the same string. ``'vcs.createtemplate'`` corresponds to the function decorated like this: .. code:: javascript @exportRpc('vcs.createtemplate') def somefunction(self, arugment, other_argument): ... In this case, that function lives in `Vizualizer.py `__. In order to write a new function that vCDAT can call to do operations in Vcs-js, simply fill out the following: 1. Add a new function in `src/index.js `__ 2. Export that function at the bottom of `src/index.js `__ 3. Create a new function in the `vcs-server directory `__ and decorate it with the same string used in step 1. 4. Run ``python setup.py install`` using the same conda env as vCDAT 5. Test with vCDAT (You will need to restart any instances if they were already running) Since ``vcs`` is global you can actually call it from the dev tools directly.