Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/networking/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 })
}
Expand Down
8 changes: 7 additions & 1 deletion src/presentation/experiment/analysis/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -24,6 +28,7 @@ class ExperimentAnalysis extends Component {
fetching={this.props.fetching}
error={this.props.error}
timeline={this.props.timeline}
logEntries={this.props.logEntries}
/>
)
}
Expand All @@ -32,7 +37,8 @@ class ExperimentAnalysis extends Component {
const mapStateToProps = state => {
return {
...selectRoutineFetchingStatus(state),
timeline: selectSelectedRoutineTimeline(state)
timeline: selectSelectedRoutineTimeline(state),
logEntries: selectSelectedRoutineLogEntries(state)
}
}

Expand Down
29 changes: 29 additions & 0 deletions src/presentation/experiment/analysis/log_entry/index.js
Original file line number Diff line number Diff line change
@@ -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 (
<div className='logEntry'>
<div className='heading'>
<p className='title'>{typeToReadableName(logEntry.type)}</p>
<p className='date'>{moment(logEntry.insertedAt).format('DD/MM HH:mm:ss')}</p>
</div>
<p className='description'>{logEntry.description}</p>
</div>
)
}

export default LogEntry
23 changes: 23 additions & 0 deletions src/presentation/experiment/analysis/log_entry/styles.css
Original file line number Diff line number Diff line change
@@ -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;
}
47 changes: 31 additions & 16 deletions src/presentation/experiment/analysis/presenter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Screen loading={fetching || timeline.labels.lenght === 0}>

<Container>
<h2>Temperatura</h2>
<SensorChart
magnitudes={['temp']}
timeline={timeline}
/>
</Container>

<Container>
<h2>pH</h2>
<SensorChart
magnitudes={['ph']}
timeline={timeline}
/>
</Container>
<div className='analysisContent'>
<div className='data'>
<Container>
<h2>Temperatura</h2>
<SensorChart
magnitudes={['temp']}
timeline={timeline}
/>
</Container>

<Container>
<h2>pH</h2>
<SensorChart
magnitudes={['ph']}
timeline={timeline}
/>
</Container>
</div>

<div className='events'>
<Container>
<h2>Eventos</h2>
{ logEntries.map(logEntry =>
<LogEntry key={logEntry.id} logEntry={logEntry} />
)}

</Container>
</div>
</div>

<NavigationChart />
</Screen>
Expand Down
20 changes: 20 additions & 0 deletions src/presentation/experiment/analysis/styles.css
Original file line number Diff line number Diff line change
@@ -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;
}
7 changes: 5 additions & 2 deletions src/redux/root_reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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({
Expand Down
4 changes: 3 additions & 1 deletion src/redux/root_saga.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 [
Expand All @@ -14,6 +15,7 @@ export default function * root () {
...readingSagas,
...phCalibrationSagas,
...pumpCalibrationSagas,
...systemSagas
...systemSagas,
...routineLogEntrySagas
]
}
17 changes: 14 additions & 3 deletions src/redux/routine/redux/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
}
Expand All @@ -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 }) =>
Expand All @@ -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) => {
Expand Down
3 changes: 3 additions & 0 deletions src/redux/routine_log_entry/action_types.js
Original file line number Diff line number Diff line change
@@ -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'
9 changes: 9 additions & 0 deletions src/redux/routine_log_entry/actions.js
Original file line number Diff line number Diff line change
@@ -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 })
9 changes: 9 additions & 0 deletions src/redux/routine_log_entry/redux/action_status.js
Original file line number Diff line number Diff line change
@@ -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
39 changes: 39 additions & 0 deletions src/redux/routine_log_entry/redux/entity.js
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions src/redux/routine_log_entry/redux/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import entity from './entity'
import actionStatus from './action_status'

export default {
entity,
actionStatus
}
17 changes: 17 additions & 0 deletions src/redux/routine_log_entry/sagas/index.js
Original file line number Diff line number Diff line change
@@ -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)
]
14 changes: 14 additions & 0 deletions src/redux/routine_log_entry/sagas/perform.js
Original file line number Diff line number Diff line change
@@ -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))
}
}
18 changes: 18 additions & 0 deletions src/redux/routine_log_entry/selector.js
Original file line number Diff line number Diff line change
@@ -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])
}
)
Loading