diff --git a/src/networking/index.js b/src/networking/index.js
index ce8f051..6508faf 100644
--- a/src/networking/index.js
+++ b/src/networking/index.js
@@ -74,6 +74,10 @@ class HttpService {
return this._get(`routines/${routine.id}`)
}
+ getRoutineLogEntries (routine) {
+ return this._get(`routines/${routine.id}/log_entries`)
+ }
+
createRoutine (routine) {
return this._post('routines', { routine })
}
diff --git a/src/presentation/experiment/analysis/index.js b/src/presentation/experiment/analysis/index.js
index f8c2311..be1da8b 100644
--- a/src/presentation/experiment/analysis/index.js
+++ b/src/presentation/experiment/analysis/index.js
@@ -6,6 +6,10 @@ import ExperimentAnalysisPresenter from './presenter'
import {
selectRoutineFetchingStatus
} from '../../../redux/routine/selector'
+
+import {
+ selectSelectedRoutineLogEntries
+} from '../../../redux/routine_log_entry/selector'
import {
selectSelectedRoutineTimeline
} from '../../../redux/reading/selector'
@@ -24,6 +28,7 @@ class ExperimentAnalysis extends Component {
fetching={this.props.fetching}
error={this.props.error}
timeline={this.props.timeline}
+ logEntries={this.props.logEntries}
/>
)
}
@@ -32,7 +37,8 @@ class ExperimentAnalysis extends Component {
const mapStateToProps = state => {
return {
...selectRoutineFetchingStatus(state),
- timeline: selectSelectedRoutineTimeline(state)
+ timeline: selectSelectedRoutineTimeline(state),
+ logEntries: selectSelectedRoutineLogEntries(state)
}
}
diff --git a/src/presentation/experiment/analysis/log_entry/index.js b/src/presentation/experiment/analysis/log_entry/index.js
new file mode 100644
index 0000000..3d63d10
--- /dev/null
+++ b/src/presentation/experiment/analysis/log_entry/index.js
@@ -0,0 +1,29 @@
+import React from 'react'
+import moment from 'moment'
+import './styles.css'
+
+const typeToReadableName = type => {
+ switch (type) {
+ case 'reading_error': return 'Error de lectura'
+ case 'base_cal': return 'Bombeo de base'
+ case 'acid_cal': return 'Bombeo de ácido'
+ case 'temp_change': return 'Cambio de temperatura'
+ case 'system_error': return 'Error del sistema'
+
+ default: return 'Evento'
+ }
+}
+
+const LogEntry = ({ logEntry }) => {
+ return (
+
+
+
{typeToReadableName(logEntry.type)}
+
{moment(logEntry.insertedAt).format('DD/MM HH:mm:ss')}
+
+
{logEntry.description}
+
+ )
+}
+
+export default LogEntry
diff --git a/src/presentation/experiment/analysis/log_entry/styles.css b/src/presentation/experiment/analysis/log_entry/styles.css
new file mode 100644
index 0000000..63393dc
--- /dev/null
+++ b/src/presentation/experiment/analysis/log_entry/styles.css
@@ -0,0 +1,23 @@
+@value primary, accent1, accent2 from '../../../constants/colors.css';
+
+.logEntry {
+ margin-bottom: 20px;
+}
+
+.logEntry .heading {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+.logEntry .heading * {
+ margin-bottom: 0;
+}
+
+.logEntry .title {
+ font-weight: 600 !important;
+ color: accent1;
+}
+
+.logEntry .date {
+ color: accent2;
+}
\ No newline at end of file
diff --git a/src/presentation/experiment/analysis/presenter.js b/src/presentation/experiment/analysis/presenter.js
index d31ae26..2ea2109 100644
--- a/src/presentation/experiment/analysis/presenter.js
+++ b/src/presentation/experiment/analysis/presenter.js
@@ -6,26 +6,41 @@ import Container from '../../common/container'
import SensorChart from '../../common/sensor_chart'
import NavigationChart from './navigation_chart'
+import LogEntry from './log_entry'
-const ExperimentAnalysisPresenter = ({ timeline, fetching, error, onAnalyzeData, onUpdate, onStart }) => {
+const ExperimentAnalysisPresenter = ({ timeline, logEntries, fetching, error }) => {
return (
-
- Temperatura
-
-
-
-
- pH
-
-
+
+
+
+ Temperatura
+
+
+
+
+ pH
+
+
+
+
+
+
+ Eventos
+ { logEntries.map(logEntry =>
+
+ )}
+
+
+
+
diff --git a/src/presentation/experiment/analysis/styles.css b/src/presentation/experiment/analysis/styles.css
index e69de29..0c84f79 100644
--- a/src/presentation/experiment/analysis/styles.css
+++ b/src/presentation/experiment/analysis/styles.css
@@ -0,0 +1,20 @@
+@value border from '../../constants/border.css';
+
+.analysisContent {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ min-height: 100vh;
+}
+
+.analysisContent .data {
+ flex: 1;
+ padding-right: 20px;
+}
+
+.analysisContent .events {
+ flex: 0.3;
+ border-left: border;
+ padding-left: 20px;
+ padding-bottom: 20px;
+}
\ No newline at end of file
diff --git a/src/redux/root_reducer.js b/src/redux/root_reducer.js
index 6da4085..3c3bb2a 100644
--- a/src/redux/root_reducer.js
+++ b/src/redux/root_reducer.js
@@ -8,11 +8,13 @@ import bootReducer from './boot/redux'
import phCalibrationReducer from './calibration/ph/redux'
import pumpCalibrationReducer from './calibration/pump/redux'
import systemReducer from './system/redux'
+import routineLogEntryReducer from './routine_log_entry/redux'
const entities = combineReducers({
routine: routineReducer.entity,
reading: readingReducer.entity,
- alert: alertReducer.entity
+ alert: alertReducer.entity,
+ routineLogEntry: routineLogEntryReducer.entity
})
const actionStatus = combineReducers({
@@ -22,7 +24,8 @@ const actionStatus = combineReducers({
sensors: sensorsReducer.actionStatus,
phCalibration: phCalibrationReducer.actionStatus,
pumpCalibration: pumpCalibrationReducer.actionStatus,
- system: systemReducer.actionStatus
+ system: systemReducer.actionStatus,
+ routineLogEntry: routineLogEntryReducer.actionStatus
})
const rootReducer = combineReducers({
diff --git a/src/redux/root_saga.js b/src/redux/root_saga.js
index a2a5f3a..dd691c1 100644
--- a/src/redux/root_saga.js
+++ b/src/redux/root_saga.js
@@ -5,6 +5,7 @@ import readingSagas from './reading/sagas'
import systemSagas from './system/sagas'
import phCalibrationSagas from './calibration/ph/sagas'
import pumpCalibrationSagas from './calibration/pump/sagas'
+import routineLogEntrySagas from './routine_log_entry/sagas'
export default function * root () {
yield [
@@ -14,6 +15,7 @@ export default function * root () {
...readingSagas,
...phCalibrationSagas,
...pumpCalibrationSagas,
- ...systemSagas
+ ...systemSagas,
+ ...routineLogEntrySagas
]
}
diff --git a/src/redux/routine/redux/entity.js b/src/redux/routine/redux/entity.js
index f6d3f5a..45b1999 100644
--- a/src/redux/routine/redux/entity.js
+++ b/src/redux/routine/redux/entity.js
@@ -9,6 +9,7 @@ import {
DESTROY_ROUTINE_SUCCESS
} from '../action_types'
import * as readingActionTypes from '../../reading/action_types'
+import * as routineLogEntryActionTypes from '../../routine_log_entry/action_types'
import {
merge,
replaceByIdEntries,
@@ -35,6 +36,7 @@ const routinesById = (state = INITIAL_STATE_BY_ID, action) => {
case readingActionTypes.FETCH_ROUTINE_READINGS_SUCCESS: return replaceRoutineReadings(state, action)
case readingActionTypes.ADD_READING: return addRoutineReading(state, action)
+ case routineLogEntryActionTypes.FETCH_ROUTINE_LOG_ENTRIES_SUCCESS: return replaceRoutineLogEntries(state, action)
default: return state
}
@@ -43,19 +45,22 @@ const routinesById = (state = INITIAL_STATE_BY_ID, action) => {
const replaceRoutines = (state, { routines }) =>
replaceByIdEntries(state, routines.reverse().map(routine => ({
...routine,
- readings: []
+ readings: [],
+ logEntries: []
})))
const addRoutine = (state, { routine }) =>
addByIdEntry(state, {
...routine,
- readings: []
+ readings: [],
+ logEntries: []
})
const updateRoutine = (state, { routine }) =>
updateByIdEntry(state, {
...routine,
- readings: (state[routine.id] || {}).readings || []
+ readings: (state[routine.id] || {}).readings || [],
+ logEntries: (state[routine.id] || {}).logEntries || []
})
const startRoutine = (state, { routine }) =>
@@ -80,6 +85,12 @@ const replaceRoutineReadings = (state, { routine, readings }) =>
readings: replaceAllEntriesIds(state[routine.id].readings, readings)
})
+const replaceRoutineLogEntries = (state, { routine, logEntries }) =>
+ updateByIdEntry(state, {
+ id: routine.id,
+ logEntries: replaceAllEntriesIds(state[routine.id].logEntries, logEntries)
+ })
+
const INITIAL_STATE_ALL_IDS = []
const allRoutinesIds = (state = INITIAL_STATE_ALL_IDS, action) => {
diff --git a/src/redux/routine_log_entry/action_types.js b/src/redux/routine_log_entry/action_types.js
new file mode 100644
index 0000000..631720c
--- /dev/null
+++ b/src/redux/routine_log_entry/action_types.js
@@ -0,0 +1,3 @@
+export const FETCH_ROUTINE_LOG_ENTRIES_REQUEST = 'ROUTINE_LOG_ENTRIES.FETCH_ROUTINE_LOG_ENTRIES_REQUEST'
+export const FETCH_ROUTINE_LOG_ENTRIES_FAILURE = 'ROUTINE_LOG_ENTRIES.FETCH_ROUTINE_LOG_ENTRIES_FAILURE'
+export const FETCH_ROUTINE_LOG_ENTRIES_SUCCESS = 'ROUTINE_LOG_ENTRIES.FETCH_ROUTINE_LOG_ENTRIES_SUCCESS'
diff --git a/src/redux/routine_log_entry/actions.js b/src/redux/routine_log_entry/actions.js
new file mode 100644
index 0000000..31835cf
--- /dev/null
+++ b/src/redux/routine_log_entry/actions.js
@@ -0,0 +1,9 @@
+import {
+ FETCH_ROUTINE_LOG_ENTRIES_REQUEST,
+ FETCH_ROUTINE_LOG_ENTRIES_FAILURE,
+ FETCH_ROUTINE_LOG_ENTRIES_SUCCESS
+} from './action_types'
+
+export const fetchRoutineLogEntriesRequest = routine => ({ type: FETCH_ROUTINE_LOG_ENTRIES_REQUEST, routine })
+export const fetchRoutineLogEntriesFailure = error => ({ type: FETCH_ROUTINE_LOG_ENTRIES_FAILURE, error })
+export const fetchRoutineLogEntriesSuccess = (routine, logEntries) => ({ type: FETCH_ROUTINE_LOG_ENTRIES_SUCCESS, routine, logEntries })
diff --git a/src/redux/routine_log_entry/redux/action_status.js b/src/redux/routine_log_entry/redux/action_status.js
new file mode 100644
index 0000000..29fa871
--- /dev/null
+++ b/src/redux/routine_log_entry/redux/action_status.js
@@ -0,0 +1,9 @@
+import buildActionStatusReducer from '../../helper/action_status_builder'
+
+const requestReducer = buildActionStatusReducer({
+ namespace: 'ROUTINE_LOG_ENTRIES.',
+ prefix: 'ROUTINE_LOG_ENTRIES',
+ get: true
+})
+
+export default requestReducer
diff --git a/src/redux/routine_log_entry/redux/entity.js b/src/redux/routine_log_entry/redux/entity.js
new file mode 100644
index 0000000..09b2eec
--- /dev/null
+++ b/src/redux/routine_log_entry/redux/entity.js
@@ -0,0 +1,39 @@
+import {
+ FETCH_ROUTINE_LOG_ENTRIES_SUCCESS
+} from '../action_types'
+import {
+ addByIdEntries,
+ addEntriesIds
+} from '../../helper'
+import { omitBy } from 'lodash'
+
+const INITIAL_STATE = { byId: {}, allIds: [] }
+
+const reducer = (state = INITIAL_STATE, action) => {
+ switch (action.type) {
+ case 'RESET': return INITIAL_STATE
+
+ case FETCH_ROUTINE_LOG_ENTRIES_SUCCESS: return replaceRoutineLogEntries(state, action)
+
+ default: return state
+ }
+}
+
+const replaceRoutineLogEntries = (state, { routine, logEntries }) => ({
+ byId: addByIdEntries(omitBy(
+ state.byId, ({routineId}) => routineId === routine.id),
+ logEntries.map(({ id, type, insertedAt, description }) => ({
+ id,
+ type,
+ insertedAt,
+ description,
+ routineId: routine.id
+ })
+ )),
+ allIds: addEntriesIds(
+ state.allIds.filter(id => state.byId[id].routineId !== routine.id),
+ logEntries
+ )
+})
+
+export default reducer
diff --git a/src/redux/routine_log_entry/redux/index.js b/src/redux/routine_log_entry/redux/index.js
new file mode 100644
index 0000000..0ba0a71
--- /dev/null
+++ b/src/redux/routine_log_entry/redux/index.js
@@ -0,0 +1,7 @@
+import entity from './entity'
+import actionStatus from './action_status'
+
+export default {
+ entity,
+ actionStatus
+}
diff --git a/src/redux/routine_log_entry/sagas/index.js b/src/redux/routine_log_entry/sagas/index.js
new file mode 100644
index 0000000..3f34553
--- /dev/null
+++ b/src/redux/routine_log_entry/sagas/index.js
@@ -0,0 +1,17 @@
+import { takeEvery } from 'redux-saga/effects'
+import httpService from '../../../networking'
+
+import {
+ FETCH_ROUTINE_LOG_ENTRIES_REQUEST
+} from '../action_types'
+import {
+ FETCH_SUCCESS
+} from '../../routine/action_types.js'
+import {
+ performFetchRoutineLogEntries
+} from './perform'
+
+export default [
+ takeEvery(FETCH_ROUTINE_LOG_ENTRIES_REQUEST, performFetchRoutineLogEntries, httpService),
+ takeEvery(FETCH_SUCCESS, performFetchRoutineLogEntries, httpService)
+]
diff --git a/src/redux/routine_log_entry/sagas/perform.js b/src/redux/routine_log_entry/sagas/perform.js
new file mode 100644
index 0000000..7d61652
--- /dev/null
+++ b/src/redux/routine_log_entry/sagas/perform.js
@@ -0,0 +1,14 @@
+import { call, put } from 'redux-saga/effects'
+import {
+ fetchRoutineLogEntriesFailure,
+ fetchRoutineLogEntriesSuccess
+} from '../actions'
+
+export function * performFetchRoutineLogEntries (httpService, { routine }) {
+ try {
+ const response = yield call([httpService, 'getRoutineLogEntries'], routine)
+ yield put(fetchRoutineLogEntriesSuccess(routine, response.data.data))
+ } catch (error) {
+ yield put(fetchRoutineLogEntriesFailure(error))
+ }
+}
diff --git a/src/redux/routine_log_entry/selector.js b/src/redux/routine_log_entry/selector.js
new file mode 100644
index 0000000..d520670
--- /dev/null
+++ b/src/redux/routine_log_entry/selector.js
@@ -0,0 +1,18 @@
+import { createSelector } from 'reselect'
+import * as routineSelector from '../routine/selector'
+
+const entity = state => state.entities.routineLogEntry
+const actionStatus = state => state.actionStatus.routineLogEntry
+
+export const selectFetchingStatus = createSelector(actionStatus, ({ fetching, error }) => ({ fetching, error }))
+
+export const selectSelectedRoutineLogEntries = createSelector(
+ routineSelector.selectSelectedRoutine,
+ entity,
+ (routine, { byId }) => {
+ if (!routine) {
+ return []
+ }
+ return routine.logEntries.map(id => byId[id])
+ }
+)
diff --git a/src/test/core/alert/add_alert.test.js b/src/test/alert/add_alert.test.js
similarity index 84%
rename from src/test/core/alert/add_alert.test.js
rename to src/test/alert/add_alert.test.js
index 05147a2..99db2f6 100644
--- a/src/test/core/alert/add_alert.test.js
+++ b/src/test/alert/add_alert.test.js
@@ -3,11 +3,11 @@
import Immutable from 'seamless-immutable'
import {
ADD
-} from '../../../redux/alert/action_types'
+} from '../../redux/alert/action_types'
import {
addAlert
-} from '../../../redux/alert/actions'
-import reducer from '../../../redux/alert/redux'
+} from '../../redux/alert/actions'
+import reducer from '../../redux/alert/redux'
describe('actions', () => {
it('should create an action to add a routine alert', () => {
diff --git a/src/test/core/alert/dismiss_alert.test.js b/src/test/alert/dismiss_alert.test.js
similarity index 84%
rename from src/test/core/alert/dismiss_alert.test.js
rename to src/test/alert/dismiss_alert.test.js
index 271d24d..167d67e 100644
--- a/src/test/core/alert/dismiss_alert.test.js
+++ b/src/test/alert/dismiss_alert.test.js
@@ -3,11 +3,11 @@
import Immutable from 'seamless-immutable'
import {
DISMISS
-} from '../../../redux/alert/action_types'
+} from '../../redux/alert/action_types'
import {
dismissAlert
-} from '../../../redux/alert/actions'
-import reducer from '../../../redux/alert/redux'
+} from '../../redux/alert/actions'
+import reducer from '../../redux/alert/redux'
describe('actions', () => {
it('should create an action to add a routine alert', () => {
diff --git a/src/test/log_entries/fetch_log_entries.test.js b/src/test/log_entries/fetch_log_entries.test.js
new file mode 100644
index 0000000..99e1d0c
--- /dev/null
+++ b/src/test/log_entries/fetch_log_entries.test.js
@@ -0,0 +1,162 @@
+/* eslint-env jest */
+
+import { call, put } from 'redux-saga/effects'
+import {
+ FETCH_ROUTINE_LOG_ENTRIES_REQUEST,
+ FETCH_ROUTINE_LOG_ENTRIES_FAILURE,
+ FETCH_ROUTINE_LOG_ENTRIES_SUCCESS
+} from '../../redux/routine_log_entry/action_types'
+import {
+ fetchRoutineLogEntriesRequest,
+ fetchRoutineLogEntriesFailure,
+ fetchRoutineLogEntriesSuccess
+} from '../../redux/routine_log_entry/actions'
+import reducer from '../../redux/routine_log_entry/redux'
+import routineReducer from '../../redux/routine/redux'
+import { performFetchRoutineLogEntries } from '../../redux/routine_log_entry/sagas/perform'
+import httpServiceMock from '../networking_mock'
+
+describe('actions', () => {
+ it('should create an action to request a routine log entries', () => {
+ const routine = { id: 4 }
+ const expectedAction = {
+ type: FETCH_ROUTINE_LOG_ENTRIES_REQUEST,
+ routine
+ }
+ expect(fetchRoutineLogEntriesRequest(routine)).toEqual(expectedAction)
+ })
+
+ it('should create an action for routine log entries request failure', () => {
+ const error = 'an error'
+ const expectedAction = {
+ type: FETCH_ROUTINE_LOG_ENTRIES_FAILURE,
+ error
+ }
+ expect(fetchRoutineLogEntriesFailure(error)).toEqual(expectedAction)
+ })
+
+ it('should create an action for routine log entries request success', () => {
+ const routine = { id: 4 }
+ const logEntries = [{ a: 'a' }]
+ const expectedAction = {
+ type: FETCH_ROUTINE_LOG_ENTRIES_SUCCESS,
+ routine,
+ logEntries
+ }
+ expect(fetchRoutineLogEntriesSuccess(routine, logEntries)).toEqual(expectedAction)
+ })
+})
+
+describe('action status reducer', () => {
+ const DIRTY_STATE = { fetching: false, error: 'error' }
+
+ it('should handle FETCH_ROUTINE_LOG_ENTRIES_REQUEST', () => {
+ expect(
+ reducer.actionStatus(DIRTY_STATE, {
+ type: FETCH_ROUTINE_LOG_ENTRIES_REQUEST
+ })
+ ).toEqual({
+ fetching: true,
+ error: null
+ })
+ })
+
+ it('should handle FETCH_ROUTINE_LOG_ENTRIES_FAILURE', () => {
+ expect(
+ reducer.actionStatus(DIRTY_STATE, {
+ type: FETCH_ROUTINE_LOG_ENTRIES_FAILURE,
+ error: 'an error'
+ })
+ ).toEqual({
+ fetching: false,
+ error: 'an error'
+ })
+ })
+
+ it('should handle FETCH_ROUTINE_LOG_ENTRIES_SUCCESS', () => {
+ expect(
+ reducer.actionStatus(DIRTY_STATE, {
+ type: FETCH_ROUTINE_LOG_ENTRIES_SUCCESS
+ })
+ ).toEqual({
+ fetching: false,
+ error: null
+ })
+ })
+})
+
+describe('entity reducer', () => {
+ const INITIAL_STATE = {
+ byId: {
+ 1: { id: 1, routineId: 1 },
+ 2: { id: 2, routineId: 2 }
+ },
+ allIds: [1, 2]
+ }
+
+ it('should handle FETCH_ROUTINE_LOG_ENTRIES_SUCCESS', () => {
+ const routine = { id: 1 }
+ const logEntries = [{ id: 3, routineId: 1, type: 'temp_change', insertedAt: 'today', description: 'a' }]
+
+ expect(
+ reducer.entity(INITIAL_STATE, {
+ type: FETCH_ROUTINE_LOG_ENTRIES_SUCCESS,
+ routine,
+ logEntries
+ })
+ ).toEqual({
+ byId: {
+ 2: { id: 2, routineId: 2 },
+ 3: { id: 3, routineId: 1, type: 'temp_change', insertedAt: 'today', description: 'a' }
+ },
+ allIds: [2, 3]
+ })
+ })
+})
+
+describe('routine entity reducer', () => {
+ const INITIAL_STATE = {
+ byId: {
+ 4: { id: 4, logEntries: [5] }
+ },
+ allIds: [4]
+ }
+
+ it('should handle FETCH_ROUTINE_LOG_ENTRIES_SUCCESS', () => {
+ const routine = { id: 4 }
+ const logEntries = [{ id: 6, temp: 10, insertedAt: 'today' }]
+
+ expect(
+ routineReducer.entity(INITIAL_STATE, {
+ type: FETCH_ROUTINE_LOG_ENTRIES_SUCCESS,
+ routine,
+ logEntries
+ })
+ ).toEqual({
+ byId: {
+ 4: {
+ id: 4,
+ logEntries: [6]
+ }
+ },
+ allIds: [4]
+ })
+ })
+})
+
+describe('sagas', () => {
+ it('perfom fetch routine log entries success', () => {
+ const routine = { id: 2 }
+ const iterator = performFetchRoutineLogEntries(httpServiceMock, { routine })
+ const response = httpServiceMock.getRoutineLogEntries(routine)
+ expect(iterator.next().value).toEqual(call([httpServiceMock, 'getRoutineLogEntries'], routine))
+ expect(iterator.next(response).value).toEqual(put(fetchRoutineLogEntriesSuccess(routine, response.data.data)))
+ })
+
+ it('perfom fetch routine log entries failure', () => {
+ const routine = { title: 'a title', strain: 30, medium: 'a medium', targetTemp: 1, targetPh: 4, estimatedTimeSeconds: 100, extraNotes: 'some notes' }
+ const iterator = performFetchRoutineLogEntries(httpServiceMock, { routine })
+ expect(iterator.next().value).toEqual(call([httpServiceMock, 'getRoutineLogEntries'], routine))
+ expect(iterator.throw('an error').value).toEqual(put(fetchRoutineLogEntriesFailure('an error')))
+ })
+})
diff --git a/src/test/core/networking_mock.js b/src/test/networking_mock.js
similarity index 86%
rename from src/test/core/networking_mock.js
rename to src/test/networking_mock.js
index 8ee11e9..9173673 100644
--- a/src/test/core/networking_mock.js
+++ b/src/test/networking_mock.js
@@ -61,6 +61,14 @@ class HttpServiceMock {
}
}
}
+
+ getRoutineLogEntries (routine) {
+ return {
+ data: {
+ data: [{ id: 1 }, { id: 2 }]
+ }
+ }
+ }
}
export default new HttpServiceMock()
diff --git a/src/test/core/pump/push_acid.test.js b/src/test/pump/push_acid.test.js
similarity index 90%
rename from src/test/core/pump/push_acid.test.js
rename to src/test/pump/push_acid.test.js
index c742eb8..9fb3e63 100644
--- a/src/test/core/pump/push_acid.test.js
+++ b/src/test/pump/push_acid.test.js
@@ -5,14 +5,14 @@ import {
PUSH_ACID_REQUEST,
PUSH_ACID_FAILURE,
PUSH_ACID_SUCCESS
-} from '../../../redux/calibration/pump/action_types'
+} from '../../redux/calibration/pump/action_types'
import {
pushAcidRequest,
pushAcidFailure,
pushAcidSuccess
-} from '../../../redux/calibration/pump/actions'
-import reducer from '../../../redux/calibration/pump/redux'
-import { performPushAcid } from '../../../redux/calibration/pump/sagas/perform'
+} from '../../redux/calibration/pump/actions'
+import reducer from '../../redux/calibration/pump/redux'
+import { performPushAcid } from '../../redux/calibration/pump/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {
diff --git a/src/test/core/pump/push_base.test.js b/src/test/pump/push_base.test.js
similarity index 90%
rename from src/test/core/pump/push_base.test.js
rename to src/test/pump/push_base.test.js
index 580a10c..beac7eb 100644
--- a/src/test/core/pump/push_base.test.js
+++ b/src/test/pump/push_base.test.js
@@ -5,14 +5,14 @@ import {
PUSH_BASE_REQUEST,
PUSH_BASE_FAILURE,
PUSH_BASE_SUCCESS
-} from '../../../redux/calibration/pump/action_types'
+} from '../../redux/calibration/pump/action_types'
import {
pushBaseRequest,
pushBaseFailure,
pushBaseSuccess
-} from '../../../redux/calibration/pump/actions'
-import reducer from '../../../redux/calibration/pump/redux'
-import { performPushBase } from '../../../redux/calibration/pump/sagas/perform'
+} from '../../redux/calibration/pump/actions'
+import reducer from '../../redux/calibration/pump/redux'
+import { performPushBase } from '../../redux/calibration/pump/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {
diff --git a/src/test/core/pump/test_acid_drop.test.js b/src/test/pump/test_acid_drop.test.js
similarity index 90%
rename from src/test/core/pump/test_acid_drop.test.js
rename to src/test/pump/test_acid_drop.test.js
index 5055533..bee3b3c 100644
--- a/src/test/core/pump/test_acid_drop.test.js
+++ b/src/test/pump/test_acid_drop.test.js
@@ -5,14 +5,14 @@ import {
TEST_ACID_DROP_REQUEST,
TEST_ACID_DROP_FAILURE,
TEST_ACID_DROP_SUCCESS
-} from '../../../redux/calibration/pump/action_types'
+} from '../../redux/calibration/pump/action_types'
import {
testAcidDropRequest,
testAcidDropFailure,
testAcidDropSuccess
-} from '../../../redux/calibration/pump/actions'
-import reducer from '../../../redux/calibration/pump/redux'
-import { performTestAcidDrop } from '../../../redux/calibration/pump/sagas/perform'
+} from '../../redux/calibration/pump/actions'
+import reducer from '../../redux/calibration/pump/redux'
+import { performTestAcidDrop } from '../../redux/calibration/pump/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {
diff --git a/src/test/core/pump/test_base_drop.test.js b/src/test/pump/test_base_drop.test.js
similarity index 90%
rename from src/test/core/pump/test_base_drop.test.js
rename to src/test/pump/test_base_drop.test.js
index b0ae76e..b87cca3 100644
--- a/src/test/core/pump/test_base_drop.test.js
+++ b/src/test/pump/test_base_drop.test.js
@@ -5,14 +5,14 @@ import {
TEST_BASE_DROP_REQUEST,
TEST_BASE_DROP_FAILURE,
TEST_BASE_DROP_SUCCESS
-} from '../../../redux/calibration/pump/action_types'
+} from '../../redux/calibration/pump/action_types'
import {
testBaseDropRequest,
testBaseDropFailure,
testBaseDropSuccess
-} from '../../../redux/calibration/pump/actions'
-import reducer from '../../../redux/calibration/pump/redux'
-import { performTestBaseDrop } from '../../../redux/calibration/pump/sagas/perform'
+} from '../../redux/calibration/pump/actions'
+import reducer from '../../redux/calibration/pump/redux'
+import { performTestBaseDrop } from '../../redux/calibration/pump/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {
diff --git a/src/test/core/reading/add_reading.test.js b/src/test/reading/add_reading.test.js
similarity index 87%
rename from src/test/core/reading/add_reading.test.js
rename to src/test/reading/add_reading.test.js
index 0ea9bb3..d94305d 100644
--- a/src/test/core/reading/add_reading.test.js
+++ b/src/test/reading/add_reading.test.js
@@ -2,12 +2,12 @@
import {
ADD_READING
-} from '../../../redux/reading/action_types'
+} from '../../redux/reading/action_types'
import {
addReading
-} from '../../../redux/reading/actions'
-import reducer from '../../../redux/reading/redux'
-import routineReducer from '../../../redux/routine/redux'
+} from '../../redux/reading/actions'
+import reducer from '../../redux/reading/redux'
+import routineReducer from '../../redux/routine/redux'
describe('actions', () => {
it('should create an action to add a routine reading', () => {
diff --git a/src/test/core/reading/fetch_readings.test.js b/src/test/reading/fetch_readings.test.js
similarity index 94%
rename from src/test/core/reading/fetch_readings.test.js
rename to src/test/reading/fetch_readings.test.js
index b8c5f7c..bc58fbd 100644
--- a/src/test/core/reading/fetch_readings.test.js
+++ b/src/test/reading/fetch_readings.test.js
@@ -5,15 +5,15 @@ import {
FETCH_ROUTINE_READINGS_REQUEST,
FETCH_ROUTINE_READINGS_FAILURE,
FETCH_ROUTINE_READINGS_SUCCESS
-} from '../../../redux/reading/action_types'
+} from '../../redux/reading/action_types'
import {
fetchRoutineReadingsRequest,
fetchRoutineReadingsFailure,
fetchRoutineReadingsSuccess
-} from '../../../redux/reading/actions'
-import reducer from '../../../redux/reading/redux'
-import routineReducer from '../../../redux/routine/redux'
-import { performFetchRoutineReadings } from '../../../redux/reading/sagas/perform'
+} from '../../redux/reading/actions'
+import reducer from '../../redux/reading/redux'
+import routineReducer from '../../redux/routine/redux'
+import { performFetchRoutineReadings } from '../../redux/reading/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {
diff --git a/src/test/core/reading/readings.js b/src/test/reading/readings.js
similarity index 100%
rename from src/test/core/reading/readings.js
rename to src/test/reading/readings.js
diff --git a/src/test/core/routine/create_routine.test.js b/src/test/routine/create_routine.test.js
similarity index 94%
rename from src/test/core/routine/create_routine.test.js
rename to src/test/routine/create_routine.test.js
index 0d6162b..715a7b1 100644
--- a/src/test/core/routine/create_routine.test.js
+++ b/src/test/routine/create_routine.test.js
@@ -5,14 +5,14 @@ import {
CREATE_ROUTINE_REQUEST,
CREATE_ROUTINE_FAILURE,
CREATE_ROUTINE_SUCCESS
-} from '../../../redux/routine/action_types'
+} from '../../redux/routine/action_types'
import {
createRoutineRequest,
createRoutineFailure,
createRoutineSuccess
-} from '../../../redux/routine/actions'
-import reducer from '../../../redux/routine/redux'
-import { performCreateRoutine } from '../../../redux/routine/sagas/perform'
+} from '../../redux/routine/actions'
+import reducer from '../../redux/routine/redux'
+import { performCreateRoutine } from '../../redux/routine/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {
diff --git a/src/test/core/routine/destroy_routine.test.js b/src/test/routine/destroy_routine.test.js
similarity index 93%
rename from src/test/core/routine/destroy_routine.test.js
rename to src/test/routine/destroy_routine.test.js
index 179bd1f..53297f7 100644
--- a/src/test/core/routine/destroy_routine.test.js
+++ b/src/test/routine/destroy_routine.test.js
@@ -5,14 +5,14 @@ import {
DESTROY_ROUTINE_REQUEST,
DESTROY_ROUTINE_FAILURE,
DESTROY_ROUTINE_SUCCESS
-} from '../../../redux/routine/action_types'
+} from '../../redux/routine/action_types'
import {
destroyRoutineRequest,
destroyRoutineFailure,
destroyRoutineSuccess
-} from '../../../redux/routine/actions'
-import reducer from '../../../redux/routine/redux'
-import { performRemoveRoutine } from '../../../redux/routine/sagas/perform'
+} from '../../redux/routine/actions'
+import reducer from '../../redux/routine/redux'
+import { performRemoveRoutine } from '../../redux/routine/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {
diff --git a/src/test/core/routine/start_routine.test.js b/src/test/routine/start_routine.test.js
similarity index 93%
rename from src/test/core/routine/start_routine.test.js
rename to src/test/routine/start_routine.test.js
index 80257f4..5736def 100644
--- a/src/test/core/routine/start_routine.test.js
+++ b/src/test/routine/start_routine.test.js
@@ -5,14 +5,14 @@ import {
START_ROUTINE_REQUEST,
START_ROUTINE_FAILURE,
START_ROUTINE_SUCCESS
-} from '../../../redux/routine/action_types'
+} from '../../redux/routine/action_types'
import {
startRoutineRequest,
startRoutineFailure,
startRoutineSuccess
-} from '../../../redux/routine/actions'
-import reducer from '../../../redux/routine/redux'
-import { performStartRoutine } from '../../../redux/routine/sagas/perform'
+} from '../../redux/routine/actions'
+import reducer from '../../redux/routine/redux'
+import { performStartRoutine } from '../../redux/routine/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {
diff --git a/src/test/core/routine/stop_routine.test.js b/src/test/routine/stop_routine.test.js
similarity index 92%
rename from src/test/core/routine/stop_routine.test.js
rename to src/test/routine/stop_routine.test.js
index 00535e5..964cf90 100644
--- a/src/test/core/routine/stop_routine.test.js
+++ b/src/test/routine/stop_routine.test.js
@@ -5,14 +5,14 @@ import {
STOP_ROUTINE_REQUEST,
STOP_ROUTINE_FAILURE,
STOP_ROUTINE_SUCCESS
-} from '../../../redux/routine/action_types'
+} from '../../redux/routine/action_types'
import {
stopRunningRoutineRequest,
stopRunningRoutineFailure,
stopRunningRoutineSuccess
-} from '../../../redux/routine/actions'
-import reducer from '../../../redux/routine/redux'
-import { performStopRoutine } from '../../../redux/routine/sagas/perform'
+} from '../../redux/routine/actions'
+import reducer from '../../redux/routine/redux'
+import { performStopRoutine } from '../../redux/routine/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {
diff --git a/src/test/core/routine/update_routine.test.js b/src/test/routine/update_routine.test.js
similarity index 94%
rename from src/test/core/routine/update_routine.test.js
rename to src/test/routine/update_routine.test.js
index ace0416..ceac3c3 100644
--- a/src/test/core/routine/update_routine.test.js
+++ b/src/test/routine/update_routine.test.js
@@ -5,14 +5,14 @@ import {
UPDATE_ROUTINE_REQUEST,
UPDATE_ROUTINE_FAILURE,
UPDATE_ROUTINE_SUCCESS
-} from '../../../redux/routine/action_types'
+} from '../../redux/routine/action_types'
import {
updateRoutineRequest,
updateRoutineFailure,
updateRoutineSuccess
-} from '../../../redux/routine/actions'
-import reducer from '../../../redux/routine/redux'
-import { performUpdateRoutine } from '../../../redux/routine/sagas/perform'
+} from '../../redux/routine/actions'
+import reducer from '../../redux/routine/redux'
+import { performUpdateRoutine } from '../../redux/routine/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {
diff --git a/src/test/core/system/restart.test.js b/src/test/system/restart.test.js
similarity index 91%
rename from src/test/core/system/restart.test.js
rename to src/test/system/restart.test.js
index 46e274a..6d5fb5a 100644
--- a/src/test/core/system/restart.test.js
+++ b/src/test/system/restart.test.js
@@ -5,14 +5,14 @@ import {
RESTART_REQUEST,
RESTART_FAILURE,
RESTART_SUCCESS
-} from '../../../redux/system/action_types'
+} from '../../redux/system/action_types'
import {
restartRequest,
restartFailure,
restartSuccess
-} from '../../../redux/system/actions'
-import reducer from '../../../redux/system/redux'
-import { performSystemRestart } from '../../../redux/system/sagas/perform'
+} from '../../redux/system/actions'
+import reducer from '../../redux/system/redux'
+import { performSystemRestart } from '../../redux/system/sagas/perform'
import httpServiceMock from '../networking_mock'
describe('actions', () => {