diff --git a/src/client/app/components/FormFileUploaderComponent.tsx b/src/client/app/components/FormFileUploaderComponent.tsx index 813569e43..be8032ee6 100644 --- a/src/client/app/components/FormFileUploaderComponent.tsx +++ b/src/client/app/components/FormFileUploaderComponent.tsx @@ -3,14 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Col, Input, FormGroup, FormText, Label } from 'reactstrap'; +import { Col, Input, FormGroup, Label } from 'reactstrap'; +import translate from '../utils/translate'; interface FileUploader { - reference: React.RefObject; - required: boolean; - formText: string; - labelStyle?: React.CSSProperties; + isInvalid: boolean; + onFileChange: (file: File | null) => void; } /** @@ -19,17 +17,28 @@ interface FileUploader { * @returns File uploader element */ export default function FileUploaderComponent(props: FileUploader) { + + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0] || null; + props.onFileChange(file); + }; + return ( - ); } diff --git a/src/client/app/components/HeaderButtonsComponent.tsx b/src/client/app/components/HeaderButtonsComponent.tsx index 39fda6430..a48b18f56 100644 --- a/src/client/app/components/HeaderButtonsComponent.tsx +++ b/src/client/app/components/HeaderButtonsComponent.tsx @@ -58,7 +58,8 @@ export default function HeaderButtonsComponent() { shouldGroupsButtonDisabled: true, shouldMetersButtonDisabled: true, shouldMapsButtonDisabled: true, - shouldCSVButtonDisabled: true, + shouldCSVMetersButtonDisabled: true, + shouldCSVReadingsButtonDisabled: true, shouldUnitsButtonDisabled: true, shouldConversionsButtonDisabled: true, // Translated menu title that depend on whether logged in. @@ -93,7 +94,8 @@ export default function HeaderButtonsComponent() { shouldGroupsButtonDisabled: pathname === '/groups', shouldMetersButtonDisabled: pathname === '/meters', shouldMapsButtonDisabled: pathname === '/maps', - shouldCSVButtonDisabled: pathname === '/csv', + shouldCSVMetersButtonDisabled: pathname === '/csvMeters', + shouldCSVReadingsButtonDisabled: pathname === '/csvReadings', shouldUnitsButtonDisabled: pathname === '/units', shouldConversionsButtonDisabled: pathname === '/conversions' })); @@ -172,12 +174,19 @@ export default function HeaderButtonsComponent() { to="/conversions"> + + + - + to="/csvReadings"> + }, { path: 'calibration', element: }, + { path: 'conversions', element: }, + { path: 'csvMeters', element: }, { path: 'maps', element: }, { path: 'units', element: }, - { path: 'conversions', element: }, { path: 'users', element: } ] }, { element: , children: [ - { path: 'csv', element: } + { path: 'csvReadings', element: } ] }, { path: '*', element: } ] } -]); \ No newline at end of file +]); diff --git a/src/client/app/components/TooltipHelpComponent.tsx b/src/client/app/components/TooltipHelpComponent.tsx index 6f446a65e..10a56d79a 100644 --- a/src/client/app/components/TooltipHelpComponent.tsx +++ b/src/client/app/components/TooltipHelpComponent.tsx @@ -44,8 +44,9 @@ export default function TooltipHelpComponent(props: TooltipHelpProps) { 'help.admin.unitcreate': { link: `${helpUrl}/adminUnitCreating/` }, 'help.admin.unitedit': { link: `${helpUrl}/adminUnitEditing/` }, 'help.admin.unitview': { link: `${helpUrl}/adminUnitViewing/` }, - 'help.admin.user': { link: `${helpUrl}/adminUser/` }, - 'help.csv.header': { link: `${helpUrl}/adminDataAcquisition/` }, + 'help.admin.users': { link: `${helpUrl}/adminUser/` }, + 'help.csv.meters': { link: `${helpUrl}/adminMetersImport/` }, + 'help.csv.readings': { link: `${helpUrl}/adminReadingsImport/` }, 'help.home.area.normalize': { link: `${helpUrl}/areaNormalization/` }, 'help.home.bar.days.tip': { link: `${helpUrl}/barGraphic/#usage` }, 'help.home.bar.interval.tip': { link: `${helpUrl}/barGraphic/#usage` }, diff --git a/src/client/app/components/csv/MetersCSVUploadComponent.tsx b/src/client/app/components/csv/MetersCSVUploadComponent.tsx index 29ca0444d..d7414bab1 100644 --- a/src/client/app/components/csv/MetersCSVUploadComponent.tsx +++ b/src/client/app/components/csv/MetersCSVUploadComponent.tsx @@ -3,108 +3,207 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Button, Form, FormGroup, Input, Label } from 'reactstrap'; -import { MODE } from '../../containers/csv/UploadCSVContainer'; -import { MetersCSVUploadProps } from '../../types/csvUploadForm'; +import { Button, Col, Container, Form, FormGroup, Input, Label, Row } from 'reactstrap'; +import { MetersCSVUploadPreferences } from '../../types/csvUploadForm'; +import { submitMeters } from '../../utils/api/UploadCSVApi'; +import { MetersCSVUploadDefaults } from '../../utils/csvUploadDefaults'; import { showErrorNotification, showSuccessNotification } from '../../utils/notifications'; +import translate from '../../utils/translate'; import FormFileUploaderComponent from '../FormFileUploaderComponent'; -import { AppDispatch } from '../../store'; -import { baseApi } from '../../redux/api/baseApi'; -import { connect } from 'react-redux'; -const mapDispatchToProps = (dispatch: AppDispatch) => { - return { - resetApiCache: () => dispatch(baseApi.util.invalidateTags(['MeterData'])) - }; -}; -type ResetProp = { resetApiCache: () => void } -type MetersCsvUploadPropWithCacheDispatch = MetersCSVUploadProps & ResetProp -class MetersCSVUploadComponent extends React.Component { - private fileInput: React.RefObject; +import TooltipHelpComponent from '../TooltipHelpComponent'; +import TooltipMarkerComponent from '../TooltipMarkerComponent'; +import { useAppDispatch, useAppSelector } from '../../redux/reduxHooks'; +import { authApi, authPollInterval } from '../../redux/api/authApi'; +import { selectIsAdmin } from '../../redux/slices/currentUserSlice'; +import { selectVisibleMeterAndGroupData } from '../../redux/selectors/adminSelectors'; - constructor(props: MetersCsvUploadPropWithCacheDispatch) { - super(props); - this.handleSubmit = this.handleSubmit.bind(this); - this.handleSetMeterName = this.handleSetMeterName.bind(this); - this.fileInput = React.createRef(); - } +/** + * Defines the CSV Meters page + * @returns CSV Meters page element + */ +export default function MetersCSVUploadComponent() { + const [meterData, setMeterData] = React.useState(MetersCSVUploadDefaults); + const [selectedFile, setSelectedFile] = React.useState(null); + const [isValidFileType, setIsValidFileType] = React.useState(false); + const dispatch = useAppDispatch(); + // Check for admin status + const isAdmin = useAppSelector(selectIsAdmin); + // page may contain admin info so verify admin status while admin is authenticated. + authApi.useTokenPollQuery(undefined, { skip: !isAdmin, pollingInterval: authPollInterval }); + // We only want displayable meters if non-admins because they still have + // non-displayable in state. + const { visibleMeters } = useAppSelector(selectVisibleMeterAndGroupData); + // tracks whether or not a meter has been selected + const meterIsSelected = meterData.meterIdentifier !== ''; - private async handleSubmit(e: React.MouseEvent) { - try { - e.preventDefault(); - const current = this.fileInput.current as HTMLInputElement; - const { files } = current; - if (files && (files as FileList).length !== 0) { - await this.props.submitCSV(files[0]); - // TODO Using an alert is not the best. At some point this should be integrated - // with react. - showSuccessNotification('

SUCCESS

The meter upload was a success.'); - } - } catch (error) { - // A failed axios request should result in an error. - showErrorNotification(error.response.data as string); - } - // Refetch meters details by invalidating its api cache. - this.props.resetApiCache(); + const handleSelectedMeterChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setMeterData(prevData => ({ + ...prevData, + [name]: value + })); + }; - } + const handleCheckboxChange = (e: React.ChangeEvent) => { + const { name, checked } = e.target; + setMeterData(prevData => ({ + ...prevData, + [name]: checked + })); + }; - private handleSetMeterName(e: React.ChangeEvent) { - const target = e.target; - this.props.setMeterName(MODE.meters, target.value); - } + const handleFileChange = (file: File) => { + setSelectedFile(file); + if (file.name.slice(-4) === '.csv' || file.name.slice(-3) === '.gz') { + setIsValidFileType(true); + } else { + setIsValidFileType(false); + setSelectedFile(null); + showErrorNotification(translate('csv.file.error') + file.name); + } + }; - public render() { - const titleStyle: React.CSSProperties = { - fontWeight: 'bold', - paddingBottom: '5px' - }; + const handleClear = () => { + setMeterData(MetersCSVUploadDefaults); + setIsValidFileType(false); + }; - const checkboxStyle: React.CSSProperties = { - paddingBottom: '15px' - }; + const handleSubmit = async (e: React.MouseEvent) => { + e.preventDefault(); + if (selectedFile) { + const { success, message } = await submitMeters(meterData, selectedFile, dispatch); + if (success) { + showSuccessNotification(message); + } else { + showErrorNotification(message); + } + } + }; - const formStyle: React.CSSProperties = { - display: 'flex', - justifyContent: 'center', - padding: '20px' - }; + const tooltipStyle = { + display: 'inline-block', + fontSize: '50%', + tooltipReadings: 'help.csv.meters' + }; - return ( -
-
- - - - - - - - - - - - - - - - -
- ); - } + const checkBox = { + display: 'flex' + }; -} -export default connect(null, mapDispatchToProps)(MetersCSVUploadComponent); + return ( + + +
+ + +
+

+ {translate('csv.upload.meters')} +
+ +
+

+
+ + + + + + + + + + + + + + + + + + + {meterData.update && ( + + + + { + + } + { + Array.from(visibleMeters).map(meter => { + return (); + }) + } + + + )} +
+
+ +
+
+ +
+
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx b/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx index d99c9112c..a1f34fc1a 100644 --- a/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx +++ b/src/client/app/components/csv/ReadingsCSVUploadComponent.tsx @@ -2,351 +2,551 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { range } from 'lodash'; import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Button, Col, Form, FormGroup, Input, Label } from 'reactstrap'; -import { MODE } from '../../containers/csv/UploadCSVContainer'; -import { BooleanMeterTypes, ReadingsCSVUploadProps, TimeSortTypes } from '../../types/csvUploadForm'; +import { useEffect, useState } from 'react'; +import { Button, Col, Container, Form, FormGroup, Input, Label, Row } from 'reactstrap'; +import { authApi, authPollInterval } from '../../redux/api/authApi'; +import { useAppDispatch, useAppSelector } from '../../redux/reduxHooks'; +import { selectVisibleMeterAndGroupData } from '../../redux/selectors/adminSelectors'; +import { selectIsAdmin } from '../../redux/slices/currentUserSlice'; +import { ReadingsCSVUploadPreferences } from '../../types/csvUploadForm'; +import { TrueFalseType } from '../../types/items'; +import { MeterData, MeterTimeSortType } from '../../types/redux/meters'; +import { submitReadings } from '../../utils/api/UploadCSVApi'; import { ReadingsCSVUploadDefaults } from '../../utils/csvUploadDefaults'; -import { showErrorNotification, showInfoNotification } from '../../utils/notifications'; +import { showErrorNotification, showSuccessNotification } from '../../utils/notifications'; import translate from '../../utils/translate'; import FormFileUploaderComponent from '../FormFileUploaderComponent'; +import TooltipHelpComponent from '../TooltipHelpComponent'; +import TooltipMarkerComponent from '../TooltipMarkerComponent'; +import CreateMeterModalComponent from '../meters/CreateMeterModalComponent'; /** - * Returns a range of values between the specified lower and upper bounds. - * @param lower The lower bound, which will be included in the range. - * @param upper The upper bound, which will be excluded from the range. - * @returns An array of values between starting from the lower bound and up to and excluding the upper bound. + * Defines the CSV Readings page + * @returns CSV Readings page element */ -function range(lower: number, upper: number): number[] { - const arr = []; - for (let i = lower; i < upper; i++) { - arr.push(i); - } - return arr; -} +export default function ReadingsCSVUploadComponent() { -export default class ReadingsCSVUploadComponent extends React.Component { - private fileInput: React.RefObject; - constructor(props: ReadingsCSVUploadProps) { - super(props); - this.handleSetMeterName = this.handleSetMeterName.bind(this); - this.handleSetTimeSort = this.handleSetTimeSort.bind(this); - this.handleSetDuplications = this.handleSetDuplications.bind(this); - this.handleSetCumulative = this.handleSetCumulative.bind(this); - this.handleSetCumulativeReset = this.handleSetCumulativeReset.bind(this); - this.handleSetCumulativeResetStart = this.handleSetCumulativeResetStart.bind(this); - this.handleSetCumulativeResetEnd = this.handleSetCumulativeResetEnd.bind(this); - this.handleSetLengthGap = this.handleSetLengthGap.bind(this); - this.handleSetLengthVariation = this.handleSetLengthVariation.bind(this); - this.handleSetEndOnly = this.handleSetEndOnly.bind(this); - this.handleSetHonorDst = this.handleSetHonorDst.bind(this); - this.handleSetRelaxedParsing = this.handleSetRelaxedParsing.bind(this); - this.handleSetUseMeterZone = this.handleSetUseMeterZone.bind(this); - this.handleSubmit = this.handleSubmit.bind(this); - this.fileInput = React.createRef(); - } + const dispatch = useAppDispatch(); + // Check for admin status + const isAdmin = useAppSelector(selectIsAdmin); + // page may contain admin info so verify admin status while admin is authenticated. + authApi.useTokenPollQuery(undefined, { skip: !isAdmin, pollingInterval: authPollInterval }); + // We only want displayable meters if non-admins because they still have + // non-displayable in state. + const { visibleMeters } = useAppSelector(selectVisibleMeterAndGroupData); + // This is the state for the form data for readings + const [readingsData, setReadingsData] = useState(ReadingsCSVUploadDefaults); + // This is the state for the file to be uploaded + const [selectedFile, setSelectedFile] = useState(null); + // tracks whether or not a meter has been selected + const meterIsSelected = readingsData.meterIdentifier !== ''; + // tracks if a new meter was created + const [newMeterIdentifier, setNewMeterIdentifier] = useState(''); + // tracks if file has .gzip or .csv extension + const [isValidFileType, setIsValidFileType] = React.useState(false); - private async handleSubmit(e: React.MouseEvent) { - try { - e.preventDefault(); - const current = this.fileInput.current as HTMLInputElement; - const { files } = current; - if (files && (files as FileList).length !== 0) { - const msg = await this.props.submitCSV(files[0]); - // If the meter was created then update the meter state so it is available. - // Getting this needed state is a pain with the old React system. Thus, - // this fetches the meter state in all cases. This should be fairly fast, - // esp. compared to the upload time. - // TODO When this is converted to React hooks it is hoped that getting the - // value about whether the meter was created will be easier and can be done then. - // Also, you cannot dispatch with Hooks so this is left until then. For now, - // we reload after the alert. - // dispatch(fetchMetersDetails()); - // TODO Using an alert is not the best. At some point this should be integrated - // with react. - // There should be a message (not void) but that is not the type so overriding. - showInfoNotification(msg as unknown as string); - // TODO remove when the above TODO is done. - window.location.reload(); - } - } catch (error) { - // A failed axios request should result in an error. - showErrorNotification(error.response.data as string); - } - } - - private handleSetMeterName(e: React.ChangeEvent) { - const target = e.target; - this.props.setMeterName(MODE.readings, target.value); - } - - private handleSetTimeSort(e: React.ChangeEvent) { - const target = e.target; - this.props.selectTimeSort(target.value as TimeSortTypes); - } + /* Handlers for each type of input change */ + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setReadingsData(prevData => ({ + ...prevData, + [name]: value + })); + }; - private handleSetDuplications(e: React.ChangeEvent) { - const target = e.target; - this.props.selectDuplications(target.value); - } + const handleNumberChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setReadingsData(prevData => ({ + ...prevData, + [name]: Number(value) + })); + }; - private handleSetCumulative(e: React.ChangeEvent) { - const target = e.target; - this.props.selectCumulative(target.value as BooleanMeterTypes); - } + const handleCheckboxChange = (e: React.ChangeEvent) => { + const { name, checked } = e.target; + setReadingsData(prevData => ({ + ...prevData, + [name]: checked + })); + }; - private handleSetCumulativeReset(e: React.ChangeEvent) { - const target = e.target; - this.props.selectCumulativeReset(target.value as BooleanMeterTypes); - } + const handleTimeSortChange = (e: React.ChangeEvent) => { + const newTimeSort = MeterTimeSortType[e.target.value as keyof typeof MeterTimeSortType]; + setReadingsData(prevData => ({ + ...prevData, + timeSort: newTimeSort + })); + }; - private handleSetCumulativeResetStart(e: React.ChangeEvent) { - const target = e.target; - this.props.setCumulativeResetStart(target.value); - } + // need this change function because input type select expects a string as value + const handleTrueFalseSelectChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setReadingsData(prevData => ({ + ...prevData, + [name]: value === 'true' + })); + }; - private handleSetCumulativeResetEnd(e: React.ChangeEvent) { - const target = e.target; - this.props.setCumulativeResetEnd(target.value); - } + const handleFileChange = (file: File) => { + if (file) { + setSelectedFile(file); + if (file.name.slice(-4) === '.csv' || file.name.slice(-3) === '.gz') { + setIsValidFileType(true); + } else { + setIsValidFileType(false); + setSelectedFile(null); + showErrorNotification(translate('csv.file.error') + file.name); + } + } + }; + /* END of Handlers for each type of input change */ - private handleSetLengthGap(e: React.ChangeEvent) { - const target = e.target; - this.props.setLengthGap(target.value); - } + useEffect(() => { + if (newMeterIdentifier) { + const foundMeter = visibleMeters.find(meter => meter.identifier === newMeterIdentifier); + if (foundMeter) { + updateReadingsData(newMeterIdentifier); + } + } + }, [visibleMeters, newMeterIdentifier]); - private handleSetLengthVariation(e: React.ChangeEvent) { - const target = e.target; - this.props.setLengthVariation(target.value); - } + useEffect(() => { + if (readingsData.cumulative === false) { + setReadingsData(prevData => ({ + ...prevData, + cumulativeReset: false + })); + } + }, [readingsData.cumulative]); - private handleSetEndOnly(e: React.ChangeEvent) { - const target = e.target; - this.props.selectEndOnly(target.value as BooleanMeterTypes); - } + // This gets the meter identifier from a newly created meter and updates the readingsData settings + // with the meterData settings, although the settings only update if user is an admin because a + // CSV user doesn't have access to this data + const handleCreateMeter = async (meterIdentifier: string) => { + setNewMeterIdentifier(meterIdentifier); + }; - private handleSetHonorDst() { - this.props.toggleHonorDst(); - } + const handleSelectedMeterChange = (e: React.ChangeEvent) => { + updateReadingsData(e.target.value); + }; - private handleSetRelaxedParsing() { - this.props.toggleRelaxedParsing(); - } + // method to update readingData state + const updateReadingsData = (meterIdentifier: string) => { + const selectedMeter = visibleMeters.find(meter => meter.identifier === meterIdentifier) as MeterData; + setReadingsData(prevData => ({ + ...prevData, + meterIdentifier: selectedMeter.identifier, + cumulative: selectedMeter.cumulative, + cumulativeReset: selectedMeter.cumulativeReset, + cumulativeResetStart: selectedMeter.cumulativeResetStart, + cumulativeResetEnd: selectedMeter.cumulativeResetEnd, + duplications: Number(selectedMeter.readingDuplication), + lengthGap: selectedMeter.readingGap, + lengthVariation: selectedMeter.readingVariation, + endOnly: selectedMeter.endOnlyTime, + timeSort: MeterTimeSortType[selectedMeter.timeSort as keyof typeof MeterTimeSortType], + useMeterZone: false + })); + }; - private handleSetUseMeterZone() { - this.props.toggleUseMeterZone(); - } + const handleClear = () => { + setReadingsData(ReadingsCSVUploadDefaults); + setIsValidFileType(false); + }; - public render() { - const titleStyle: React.CSSProperties = { - fontWeight: 'bold', - paddingBottom: '5px' - }; + const handleSubmit = async (e: React.MouseEvent) => { + e.preventDefault(); + if (selectedFile) { + const { success, message } = await submitReadings(readingsData, selectedFile, dispatch); + if (success) { + showSuccessNotification(message); + } else { + showErrorNotification(message); + } + } + }; - const checkboxStyle: React.CSSProperties = { - paddingBottom: '15px' - }; + const tooltipStyle = { + display: 'inline-block', + fontSize: '50%', + tooltipReadings: 'help.csv.readings' + }; - const formStyle: React.CSSProperties = { - display: 'flex', - justifyContent: 'center', - padding: '20px' - }; + const checkBox = { + display: 'flex' + }; - return ( -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + { + + } + { + Array.from(visibleMeters).map(meter => { + return (); + }) + } + +
+ {isAdmin && <>} +
+ + + + + +
+ + +
+ + +
+ + +
+ +
+ + +
+ + +
+ + +
+ + +
+ +
+ + +
+ + +
+ + +
+ + +
+ +
+
+ + + + + + {Object.keys(TrueFalseType).map(key => { + return (); + })} - - - - - + + + + + + {readingsData.cumulative === true ? ( + + {Object.keys(TrueFalseType).map(key => { + return (); + })} + + ) : ( + + + + )} + + + + + + + - - - - - + + + + + - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {/* TODO This feature is not working perfectly so disabling from web page but allowing in curl. Rest of changes left so easy to add back in. */} - {/* - - */} - - -
- ); - } -} + + + + + + + + + {Object.keys(TrueFalseType).map(key => { + return (); + })} + + + + + + + + + + + + + + + + + + + + + + {range(1, 10).map(i => ( + + ))} + + + + + + + + + + {Object.keys(MeterTimeSortType).map(key => { + return (); + })} + + + + + {/* TODO This feature is not working perfectly so disabling from web page but allowing in curl. + Rest of changes left so easy to add back in. */} + {/* + + */} +
+
+ +
+
+ +
+
+
+
+ + + + + ); +} \ No newline at end of file diff --git a/src/client/app/components/meters/CreateMeterModalComponent.tsx b/src/client/app/components/meters/CreateMeterModalComponent.tsx index ab7125bb5..3a53cb3e4 100644 --- a/src/client/app/components/meters/CreateMeterModalComponent.tsx +++ b/src/client/app/components/meters/CreateMeterModalComponent.tsx @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +import { range } from 'lodash'; import * as moment from 'moment'; import * as React from 'react'; import { useState } from 'react'; @@ -14,7 +15,8 @@ import { MAX_ERRORS, MAX_VAL, MIN_DATE, MIN_DATE_MOMENT, MIN_VAL, isValidCreateMeter, - selectDefaultCreateMeterValues, selectCreateMeterUnitCompatibility + selectCreateMeterUnitCompatibility, + selectDefaultCreateMeterValues } from '../../redux/selectors/adminSelectors'; import '../../styles/modal.css'; import { tooltipBaseStyle } from '../../styles/modalStyle'; @@ -28,11 +30,16 @@ import TimeZoneSelect from '../TimeZoneSelect'; import TooltipHelpComponent from '../TooltipHelpComponent'; import TooltipMarkerComponent from '../TooltipMarkerComponent'; +interface CreateMeterModalProps { + onCreateMeter?: (meterIdentifier: string) => void; // Define the type of the callback function +} + /** * Defines the create meter modal form + * @param props for create meter to return the identifier * @returns Meter create element */ -export default function CreateMeterModalComponent() { +export default function CreateMeterModalComponent(props: CreateMeterModalProps): React.JSX.Element { // Tracks whether a unit/ default unit has been selected. // RTKQ Mutation to submit add meter const [submitAddMeter] = metersApi.endpoints.addMeter.useMutation(); @@ -152,6 +159,15 @@ export default function CreateMeterModalComponent() { // if successful, the mutation will invalidate existing cache causing all meter details to be retrieved showSuccessNotification(translate('meter.successfully.create.meter')); resetState(); + // if props exist, then return the identifier + // or return the name if identifier is not set because the identifier will be set from the name + if (props.onCreateMeter) { + if (meterDetails.identifier === '') { + props.onCreateMeter(meterDetails.name); + } else { + props.onCreateMeter(meterDetails.identifier); + } + } }) .catch(err => { showErrorNotification(translate('meter.failed.to.create.meter') + '"' + err.data + '"'); @@ -162,7 +178,6 @@ export default function CreateMeterModalComponent() { } }; - const tooltipStyle = { ...tooltipBaseStyle, // Only an admin can create a meter. @@ -367,11 +382,15 @@ export default function CreateMeterModalComponent() { {/* Area input */} - handleNumberChange(e)} - invalid={meterDetails.area < 0} /> + invalid={meterDetails.area < 0} + /> @@ -495,15 +514,9 @@ export default function CreateMeterModalComponent() { handleNumberChange(e)}> - - - - - - - - - + {range(1, 10).map(i => ( + + ))} diff --git a/src/client/app/components/meters/EditMeterModalComponent.tsx b/src/client/app/components/meters/EditMeterModalComponent.tsx index 517715a78..6d1db44e1 100644 --- a/src/client/app/components/meters/EditMeterModalComponent.tsx +++ b/src/client/app/components/meters/EditMeterModalComponent.tsx @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { cloneDeep, isEqual } from 'lodash'; +import { cloneDeep, isEqual, range } from 'lodash'; import * as moment from 'moment'; import * as React from 'react'; import { useEffect, useState } from 'react'; @@ -538,15 +538,9 @@ export default function EditMeterModalComponent(props: EditMeterModalComponentPr handleNumberChange(e)} defaultValue={localMeterEdits?.readingDuplication} > - - - - - - - - - + {range(1, 10).map(i => ( + + ))} diff --git a/src/client/app/containers/csv/UploadCSVContainer.tsx b/src/client/app/containers/csv/UploadCSVContainer.tsx deleted file mode 100644 index ddbdffeb9..000000000 --- a/src/client/app/containers/csv/UploadCSVContainer.tsx +++ /dev/null @@ -1,337 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; -import { Nav, NavItem, NavLink, TabContent, TabPane } from 'reactstrap'; -import TooltipMarkerComponent from '../../components/TooltipMarkerComponent'; -import MetersCSVUploadComponent from '../../components/csv/MetersCSVUploadComponent'; -import ReadingsCSVUploadComponent from '../../components/csv/ReadingsCSVUploadComponent'; -import { BooleanMeterTypes, MetersCSVUploadPreferencesItem, ReadingsCSVUploadPreferencesItem, TimeSortTypes } from '../../types/csvUploadForm'; -import { uploadCSVApi } from '../../utils/api'; -import { MetersCSVUploadDefaults, ReadingsCSVUploadDefaults } from '../../utils/csvUploadDefaults'; -import TooltipHelpComponent from '../../components/TooltipHelpComponent'; - -export const enum MODE { - meters = 'meters', - readings = 'readings' -} - -interface UploadCSVContainerState { - uploadReadingsPreferences: ReadingsCSVUploadPreferencesItem; - uploadMetersPreferences: MetersCSVUploadPreferencesItem; - activeTab: MODE; -} - -const tooltipStyle = { - display: 'inline-block', - fontSize: '100%' -}; - -export default class UploadCSVContainer extends React.Component<{}, UploadCSVContainerState> { - constructor(props: {}) { - super(props); - this.setMeterName = this.setMeterName.bind(this); - this.selectTimeSort = this.selectTimeSort.bind(this); - this.selectDuplications = this.selectDuplications.bind(this); - this.selectCumulative = this.selectCumulative.bind(this); - this.selectCumulativeReset = this.selectCumulativeReset.bind(this); - this.setCumulativeResetStart = this.setCumulativeResetStart.bind(this); - this.setCumulativeResetEnd = this.setCumulativeResetEnd.bind(this); - this.setLengthGap = this.setLengthGap.bind(this); - this.setLengthVariation = this.setLengthVariation.bind(this); - this.selectEndOnly = this.selectEndOnly.bind(this); - this.submitReadings = this.submitReadings.bind(this); - this.submitMeters = this.submitMeters.bind(this); - this.toggleCreateMeter = this.toggleCreateMeter.bind(this); - this.toggleGzip = this.toggleGzip.bind(this); - this.toggleHeaderRow = this.toggleHeaderRow.bind(this); - this.toggleRefreshHourlyReadings = this.toggleRefreshHourlyReadings.bind(this); - this.toggleRefreshReadings = this.toggleRefreshReadings.bind(this); - this.toggleUpdate = this.toggleUpdate.bind(this); - this.toggleTab = this.toggleTab.bind(this); - this.toggleHonorDst = this.toggleHonorDst.bind(this); - this.toggleRelaxedParsing = this.toggleRelaxedParsing.bind(this); - this.toggleUseMeterZone = this.toggleUseMeterZone.bind(this); - } - - state = { - activeTab: MODE.readings, - uploadMetersPreferences: { - ...MetersCSVUploadDefaults - }, - uploadReadingsPreferences: { - ...ReadingsCSVUploadDefaults - } - }; - - private setMeterName(mode: MODE, value: string) { - const preference = (mode === MODE.readings) ? 'uploadReadingsPreferences' : 'uploadMetersPreferences'; - this.setState(previousState => ({ - ...previousState, - [preference]: { - ...previousState[preference], - meterName: value - } - })); - } - private selectTimeSort(value: TimeSortTypes) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - timeSort: value - } - })); - } - private selectDuplications(value: string) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - duplications: value - } - })); - } - private selectCumulative(value: BooleanMeterTypes) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - cumulative: value - } - })); - } - private selectCumulativeReset(value: BooleanMeterTypes) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - cumulativeReset: value - } - })); - } - private setCumulativeResetStart(value: string) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - cumulativeResetStart: value - } - })); - } - private setCumulativeResetEnd(value: string) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - cumulativeResetEnd: value - } - })); - } - private setLengthGap(value: string) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - lengthGap: value - } - })); - } - private setLengthVariation(value: string) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - lengthVariation: value - } - })); - } - private selectEndOnly(value: BooleanMeterTypes) { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - endOnly: value - } - })); - } - private toggleCreateMeter() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - createMeter: !previousState.uploadReadingsPreferences.createMeter - } - })); - } - private toggleGzip(mode: MODE) { - const preference = (mode === MODE.readings) ? 'uploadReadingsPreferences' : 'uploadMetersPreferences'; - return () => { - this.setState(previousState => ({ - ...previousState, - [preference]: { - ...previousState[preference], - gzip: !previousState[preference].gzip - } - })); - }; - } - private toggleHeaderRow(mode: MODE) { - const preference = (mode === MODE.readings) ? 'uploadReadingsPreferences' : 'uploadMetersPreferences'; - return () => { - this.setState(previousState => ({ - ...previousState, - [preference]: { - ...previousState[preference], - headerRow: !previousState[preference].headerRow - } - })); - }; - } - - private toggleRefreshHourlyReadings() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - refreshHourlyReadings: !previousState.uploadReadingsPreferences.refreshHourlyReadings - } - })); - } - - private toggleRefreshReadings() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - refreshReadings: !previousState.uploadReadingsPreferences.refreshReadings - } - })); - } - - private toggleHonorDst() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - honorDst: !previousState.uploadReadingsPreferences.honorDst - } - })); - } - - private toggleRelaxedParsing() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - relaxedParsing: !previousState.uploadReadingsPreferences.relaxedParsing - } - })); - } - - private toggleUseMeterZone() { - this.setState(previousState => ({ - ...previousState, - uploadReadingsPreferences: { - ...previousState.uploadReadingsPreferences, - useMeterZone: !previousState.uploadReadingsPreferences.useMeterZone - } - })); - } - - private toggleUpdate(mode: MODE) { - const preference = (mode === MODE.readings) ? 'uploadReadingsPreferences' : 'uploadMetersPreferences'; - return () => { - this.setState(previousState => ({ - ...previousState, - [preference]: { - ...previousState[preference], - update: !previousState[preference].update - } - })); - }; - } - - private async submitReadings(file: File) { - const uploadPreferences = this.state.uploadReadingsPreferences; - return await uploadCSVApi.submitReadings(uploadPreferences, file); - } - - private async submitMeters(file: File) { - const uploadPreferences = this.state.uploadMetersPreferences; - await uploadCSVApi.submitMeters(uploadPreferences, file); - } - - private toggleTab(tab: MODE) { - if (this.state.activeTab !== tab) { - this.setState({ - activeTab: tab - }); - } - } - - public render() { - const navStyle: React.CSSProperties = { - cursor: 'pointer' - }; - return ( -
- - - - - - - - - - -
- ); - } -} diff --git a/src/client/app/translations/data.ts b/src/client/app/translations/data.ts index 1ffba4030..740311d8c 100644 --- a/src/client/app/translations/data.ts +++ b/src/client/app/translations/data.ts @@ -86,7 +86,11 @@ const LocaleTranslationData = { "create.user": "Create a User", "create.unit": "Create a Unit", "csv": "CSV", - "csv.file": "CSV File", + "csvMeters": "CSV Meters", + "csvReadings": "CSV Readings", + "csv.file": "CSV File:", + "csv.file.error": "File must be in CSV format or GZIP format (.csv or .gz). ", + "csv.clear.button": "Clear Form", "csv.common.param.gzip": "Gzip", "csv.common.param.header.row": "Header Row", "csv.common.param.update": "Update", @@ -94,28 +98,19 @@ const LocaleTranslationData = { "csv.download.size.warning.size": "Total size of all files will be about (usually within 10% for large exports).", "csv.download.size.warning.verify": "Are you sure you want to download", "csv.readings.param.create.meter": "Create Meter", - "csv.readings.param.cumulative": "Cumulative", - "csv.readings.param.cumulative.reset": "Cumulative Reset", - "csv.readings.param.cumulative.reset.end": "Cumulative Reset End", - "csv.readings.param.cumulative.reset.start": "Cumulative Reset Start", - "csv.readings.param.duplications": "Duplications", - "csv.readings.param.endOnly": "End Only times", "csv.readings.param.honor.dst": "Honor Daylight Savings Time", - "csv.readings.param.lengthGap": "Length Gap", - "csv.readings.param.length.variation": "Length Variation", - "csv.readings.param.meter.name": "Meter name", - "csv.readings.param.refresh.hourlyReadings": "Refresh Hourly Readings", - "csv.readings.param.refresh.readings": "Refresh Daily Readings", + "csv.readings.param.meter.identifier": "Meter Identifier:", + "csv.readings.param.refresh.readings": "Refresh Readings", "csv.readings.param.relaxed.parsing": "Relaxed Parsing", - "csv.readings.param.time.sort": "Time Sort", + "csv.readings.param.time.sort": "Time Sort:", "csv.readings.param.use.meter.zone": "Use Meter Zone", "csv.readings.section.cumulative.data": "Cumulative Data", "csv.readings.section.time.gaps": "Time Gaps", "csv.submit.button": "Submit CSV Data", "csv.tab.meters": "Meters", "csv.tab.readings": "Readings", - "csv.upload.meters": "Upload Meters CSV ", - "csv.upload.readings": "Upload Readings CSV ", + "csv.upload.meters": "Upload Meters", + "csv.upload.readings": "Upload Readings", "custom.value": "Custom value", "date.range": 'Date Range', "day": "Day", @@ -233,7 +228,8 @@ const LocaleTranslationData = { "help.admin.unitedit": "This page allows admins to edit units. Please visit {link} for further details and information.", "help.admin.unitview": "This page shows information on units. Please visit {link} for further details and information.", "help.admin.users": "This page allows admins to view and edit users. Please visit {link} for further details and information.", - "help.csv.header": "This page allows certain users to upload meters and readings via a CSV file. Please visit {link} for further details and information.", + "help.csv.meters": "This page allows admins to upload meters via a CSV file. Please visit {link} for further details and information.", + "help.csv.readings": "This page allows certain users to upload readings via a CSV file. Please visit {link} for further details and information.", "help.groups.groupdetails": "This page shows detailed information on a group. Please visit {link} for further details and information.", "help.groups.groupview": "This page shows information on groups. Please visit {link} for further details and information.", "help.groups.area.calculate": "This will sum together the area of all meters in this group with a nonzero area with an area unit. It will ignore any meters which have no area or area unit. If this group has no area unit, it will do nothing.", @@ -439,7 +435,6 @@ const LocaleTranslationData = { "threeD.y.axis.label": "Days of Calendar Year", "TimeSortTypes.decreasing": "decreasing", "TimeSortTypes.increasing": "increasing", - "TimeSortTypes.meter": "meter value or default", "today": "Today", "toggle.link": "Toggle chart link", "total": "total", @@ -594,36 +589,29 @@ const LocaleTranslationData = { "create.unit": "Create a Unit\u{26A1}", "create.user": "Créer un utilisateur", "csv": "CSV", - "csv.file": "Fichier CSV", - "csv.common.param.gzip": "GzipComment", + "csv.file": "Fichier CSV:", + "csv.file.error": "Le fichier doit être au format CSV ou GZIP (.csv ou .gz). ", + "csv.clear.button": "Forme claire", + "csv.common.param.gzip": "Gzip", "csv.common.param.header.row": "Ligne d'en-tête", "csv.common.param.update": "Mise à jour", "csv.download.size.limit": "Sorry you don't have permissions to download due to large number of points.\u{26A1}", "csv.download.size.warning.size": "Total size of all files will be about (usually within 10% for large exports).\u{26A1}", "csv.download.size.warning.verify": "Are you sure you want to download\u{26A1}", "csv.readings.param.create.meter": "Créer un compteur", - "csv.readings.param.cumulative": "Cumulatif", - "csv.readings.param.cumulative.reset": "Réinitialisation cumulative", - "csv.readings.param.cumulative.reset.end": "Fin de la réinitialisation cumulative", - "csv.readings.param.cumulative.reset.start": "Début de réinitialisation cumulée", - "csv.readings.param.duplications": "Duplications", - "csv.readings.param.endOnly": "Heures de fin uniquement", "csv.readings.param.honor.dst": "Honor Daylight Savings Time\u{26A1}", - "csv.readings.param.lengthGap": "Écart de longueur", - "csv.readings.param.length.variation": "Variation de longueur", - "csv.readings.param.meter.name": "Nom du compteur", - "csv.readings.param.refresh.hourlyReadings": "Actualiser les relevés horaires", - "csv.readings.param.refresh.readings": "Actualiser les lectures quotidiennes", + "csv.readings.param.meter.identifier": "Identifiant du compteur:", + "csv.readings.param.refresh.readings": "Actualiser les lectures", "csv.readings.param.relaxed.parsing": "Relaxed Parsing\u{26A1}", - "csv.readings.param.time.sort": "Tri de l'heure", + "csv.readings.param.time.sort": "Tri de l'heure:", "csv.readings.param.use.meter.zone": "Use Meter Zone\u{26A1}", "csv.readings.section.cumulative.data": "Données cumulées", "csv.readings.section.time.gaps": "Intervalles de temps", "csv.submit.button": "Soumettre les données CSV", "csv.tab.meters": "Mètres", "csv.tab.readings": "Lectures", - "csv.upload.meters": "Télécharger les compteurs au format CSV", - "csv.upload.readings": "Télécharger les lectures CSV", + "csv.upload.meters": "Téléverser Mètres", + "csv.upload.readings": "Téléverser Lectures", "custom.value": "Custom value\u{26A1}", "date.range": 'Plage de dates', "day": "Journée", @@ -741,7 +729,8 @@ const LocaleTranslationData = { "help.admin.unitedit": "This page allows admins to edit units. Please visit {link} for further details and information.", "help.admin.unitview": "This page shows information on units. Please visit {link} for further details and information.\u{26A1}", "help.admin.users": "This page allows admins to view and edit users. Please visit {link} for further details and information.\u{26A1}", - "help.csv.header": "This page allows certain users to upload meters and readings via a CSV file. Please visit {link} for further details and information.\u{26A1}", + "help.csv.meters": "Cette page permet aux administrateurs de téléverser des mètres via un fichier CSV. Veuillez visiter {link} pour plus de détails et d'informations.\u{26A1}", + "help.csv.readings": "Cette page permet à certains utilisateurs de télécharger des lectures via un fichier CSV. Veuillez visiter {link} pour plus de détails et d'informations.\u{26A1}", "help.groups.groupdetails": "This page shows detailed information on a group. Please visit {link} for further details and information.\u{26A1}", "help.groups.groupview": "This page shows information on groups. Please visit {link} for further details and information.\u{26A1}", "help.groups.area.calculate": "This will sum together the area of all meters in this group with a nonzero area with an area unit. It will ignore any meters which have no area or area unit. If this group has no area unit, it will do nothing.\u{26A1}", @@ -947,7 +936,6 @@ const LocaleTranslationData = { "timezone.no": "Pas de fuseau horaire", "TimeSortTypes.decreasing": "décroissant", "TimeSortTypes.increasing": "en augmentant", - "TimeSortTypes.meter": "valeur du compteur ou valeur par défaut", "today": "Aujourd'hui", "toggle.link": "Bascule du lien du diagramme", "total": "total", @@ -1102,7 +1090,9 @@ const LocaleTranslationData = { "create.unit": "Crear una unidad", "create.user": "Crear un usuario", "csv": "CSV", - "csv.file": "Archivo CSV", + "csv.file": "Archivo CSV:", + "csv.file.error": "El archivo debe estar en formato CSV o GZIP (.csv o .gz). ", + "csv.clear.button": "Forma clara", "csv.common.param.gzip": "Gzip", "csv.common.param.header.row": "Fila de cabecera", "csv.common.param.update": "Actualización", @@ -1110,20 +1100,11 @@ const LocaleTranslationData = { "csv.download.size.warning.size": "El tamaño todos los archivos juntos será de unos (usualmente dentro de 10% para exportaciones largas).", "csv.download.size.warning.verify": "Estás seguro que quieres descargar", "csv.readings.param.create.meter": "Crear medidor", - "csv.readings.param.cumulative": "Acumulado", - "csv.readings.param.cumulative.reset": "Reinicio acumulativo", - "csv.readings.param.cumulative.reset.end": "Fin del reinicio acumulativo", - "csv.readings.param.cumulative.reset.start": "Comienzo del reinicio acumulativo", - "csv.readings.param.duplications": "Duplicaciones", - "csv.readings.param.endOnly": "Únicamente tiempos finales", "csv.readings.param.honor.dst": "Seguir el horario de verano", - "csv.readings.param.lengthGap": "Duración entre mediciones", - "csv.readings.param.length.variation": "Variación de longitud de medición", - "csv.readings.param.meter.name": "Nombre de medidor", - "csv.readings.param.refresh.hourlyReadings": "Actualizar lecturas por hora", - "csv.readings.param.refresh.readings": "Actualizar lecturas diarias", + "csv.readings.param.meter.identifier": "Identificador del medidor:", + "csv.readings.param.refresh.readings": "Actualizar lecturas", "csv.readings.param.relaxed.parsing": "Análisis sintactico no estricto", - "csv.readings.param.time.sort": "Ordenar el tiempo", + "csv.readings.param.time.sort": "Ordenar el tiempo:", "csv.readings.param.use.meter.zone": "Use Meter Zone\u{26A1}", "csv.readings.section.cumulative.data": "Datos acumulativos", "csv.readings.section.time.gaps": "Longitud de tiempos", @@ -1250,7 +1231,8 @@ const LocaleTranslationData = { "help.admin.unitedit": "Esta página permite a los administradores editar unidades Por favor, visite {link} para más detalles e información.", "help.admin.unitview": "Esta página muestra información sobre unidades. Por favor, visite {link} para más detalles e información.", "help.admin.users": "Esta página permite a los administradores ver y editar usarios. Por favor, visite {link} para más detalles e información.", - "help.csv.header": "Esta página permite unos usuarios subir medidores y lecturas a través de un archivo CSV. Por favor, visite {link} para más detalles e información.", + "help.csv.meters": "Esta página permite a los administradores cargar medidores a través de un archivo CSV. Visite {enlace} para obtener más detalles e información.", + "help.csv.readings": "Esta página permite a ciertos usuarios cargar lecturas a través de un archivo CSV. Visite {link} para obtener más detalles e información.", "help.groups.groupdetails": "Esta página muestra información detallada de un grupo. Por favor visite {link} para obtener más detalles e información.", "help.groups.groupview": "Esta página muestra información sobre grupos. Por favor, visite {link} para más detalles e información.", "help.groups.area.calculate": "Esto sumará el área de todos los medidores en este grupo que tengan un área distinta a cero y una unidad de área. Ignorará cualquier medidor que no tiene área o unidad de área. Si este grupo no tiene unidad de área, no hará nada.", @@ -1456,7 +1438,6 @@ const LocaleTranslationData = { "threeD.y.axis.label": "Días del año calendario", "TimeSortTypes.decreasing": "decreciente", "TimeSortTypes.increasing": "creciente", - "TimeSortTypes.meter": "valor del medidor o predeterminado", "today": "Hoy", "toggle.link": "Alternar enlace de gráfico", "total": "total", diff --git a/src/client/app/types/csvUploadForm.ts b/src/client/app/types/csvUploadForm.ts index aa1cd8d84..e85217c01 100644 --- a/src/client/app/types/csvUploadForm.ts +++ b/src/client/app/types/csvUploadForm.ts @@ -2,118 +2,33 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { MODE } from '../containers/csv/UploadCSVContainer'; +import { MeterTimeSortType } from '../types/redux/meters'; -interface CSVUploadPreferences { - meterName: string; +export interface CSVUploadPreferences { + meterIdentifier: string; gzip: boolean; headerRow: boolean; update: boolean; } -// Very similar to CSVUploadPreferences but uses a different Boolean type that is expected when the form is submitted. -export interface CSVUploadPreferencesForm { - meterName: string; - gzip: BooleanTypes; - headerRow: BooleanTypes; - update: BooleanTypes; -} - -interface CSVUploadProps extends CSVUploadPreferences { - submitCSV: (file: File) => Promise; - setMeterName: (mode: MODE, value: string) => void; - toggleGzip: () => void; - toggleHeaderRow: () => void; - toggleUpdate: () => void; -} - -// This relates to MeterTimeSortTypes in src/client/app/types/redux/meters.ts but also has 'meter value or default'. -// They should be kept in sync. -export const enum TimeSortTypes { - // Normally the values here are not used when displayed to user but the ones in data.js so translated. - increasing = 'increasing', - decreasing = 'decreasing', - // meter means to use value stored on meter or the default if not. - meter = 'meter value or default' -} - -export const enum BooleanTypes { - // Normally the values here are not used when displayed to user but the ones in data.js so translated. - true = 'yes', - false = 'no' -} - -// Unusual boolean that also allows for meter so 3-way. -export const enum BooleanMeterTypes { - // Normally the values here are not used when displayed to user but the ones in data.js so translated. - true = 'yes', - false = 'no', - // meter means to use value stored on meter or the default if not. - meter = 'meter value or default' -} - -export interface ReadingsCSVUploadPreferencesItem extends CSVUploadPreferences { - createMeter: boolean; - cumulative: BooleanMeterTypes; - cumulativeReset: BooleanMeterTypes; +export interface ReadingsCSVUploadPreferences extends CSVUploadPreferences { + cumulative: boolean; + cumulativeReset: boolean; cumulativeResetStart: string; cumulativeResetEnd: string; - duplications: string; // Not sure how to type this an integer string; - meterName: string; - lengthGap: string; - lengthVariation: string; - endOnly: BooleanMeterTypes; - refreshHourlyReadings: boolean; - refreshReadings: boolean; - timeSort: TimeSortTypes; + duplications: number; + endOnly: boolean; honorDst: boolean; + lengthGap: number; + lengthVariation: number; + meterIdentifier: string; + refreshReadings: boolean; relaxedParsing: boolean; + timeSort: MeterTimeSortType; useMeterZone: boolean; } -export interface ReadingsCSVUploadPreferencesForm extends CSVUploadPreferencesForm { - createMeter: BooleanTypes; - cumulative: BooleanMeterTypes; - cumulativeReset: BooleanMeterTypes; - cumulativeResetStart: string; - cumulativeResetEnd: string; - duplications: string; // Not sure how to type this an integer string; - meterName: string; - lengthGap: string; - lengthVariation: string; - endOnly: BooleanMeterTypes; - refreshHourlyReadings: BooleanTypes; - refreshReadings: BooleanTypes; - timeSort: TimeSortTypes; - honorDst: BooleanTypes; - relaxedParsing: BooleanTypes; - useMeterZone: BooleanTypes; -} - -export interface ReadingsCSVUploadProps extends ReadingsCSVUploadPreferencesItem, CSVUploadProps{ - // Note: each of these will have to change in consideration of redux; - selectTimeSort: (value: TimeSortTypes) => void; - selectDuplications: (value: string) => void; - selectCumulative: (value: BooleanMeterTypes) => void; - selectCumulativeReset: (value: BooleanMeterTypes) => void; - setCumulativeResetStart: (value: string) => void; - setCumulativeResetEnd: (value: string) => void; - setLengthGap: (value: string) => void; - setLengthVariation: (value: string) => void; - selectEndOnly: (value: string) => void; - toggleCreateMeter: () => void; - toggleRefreshHourlyReadings: () => void; - toggleRefreshReadings: () => void; - toggleHonorDst: () => void; - toggleRelaxedParsing: () => void; - toggleUseMeterZone: () => void; -} - // MetersCSVUpload, MetersCSVUploadPreferencesItem, MetersCSVUploadProps should be interfaces. However, at the moment does not add anything new. // Hence, we define a new type rather than a new interface that extends CSVUploadPreferences and CSVUploadProps to pass our linter. -export type MetersCSVUpload = CSVUploadPreferences; - -export type MetersCSVUploadPreferencesItem = MetersCSVUpload; - -export type MetersCSVUploadProps = CSVUploadProps; +export type MetersCSVUploadPreferences = CSVUploadPreferences; \ No newline at end of file diff --git a/src/client/app/types/redux/meters.ts b/src/client/app/types/redux/meters.ts index 686fc31d3..d5490568e 100644 --- a/src/client/app/types/redux/meters.ts +++ b/src/client/app/types/redux/meters.ts @@ -15,7 +15,7 @@ export enum MeterType { OTHER = 'other' } -// This relates to TimeSortTypes in src/client/app/types/csvUploadForm.ts but does not have 'meter value or default'. +// This relates to TimeSortTypes in src/client/app/types/csvUploadForm.ts // They should be kept in sync. export enum MeterTimeSortType { increasing = 'increasing', diff --git a/src/client/app/utils/api/UploadCSVApi.ts b/src/client/app/utils/api/UploadCSVApi.ts index 0845ae672..a810d0bec 100644 --- a/src/client/app/utils/api/UploadCSVApi.ts +++ b/src/client/app/utils/api/UploadCSVApi.ts @@ -4,53 +4,73 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import ApiBackend from './ApiBackend'; +import { Dispatch } from '@reduxjs/toolkit'; +import { baseApi } from '../../redux/api/baseApi'; import { - ReadingsCSVUploadPreferencesItem, MetersCSVUploadPreferencesItem, BooleanTypes, CSVUploadPreferencesForm, ReadingsCSVUploadPreferencesForm + CSVUploadPreferences, + MetersCSVUploadPreferences, + ReadingsCSVUploadPreferences } from '../../types/csvUploadForm'; +import ApiBackend from './ApiBackend'; + +interface ApiResponse { + success: boolean, + message: string +} -export default class UploadCSVApi { - private readonly backend: ApiBackend; +export const submitReadings = async (uploadPreferences: ReadingsCSVUploadPreferences, readingsFile: File, + dispatch: Dispatch): Promise => { + const backend = new ApiBackend(); + const formData = new FormData(); + // The Boolean values in state must be converted to the submitted values of yes and no. + const uploadPreferencesForm: ReadingsCSVUploadPreferences = { + ...uploadPreferences, + gzip: uploadPreferences.gzip, + headerRow: uploadPreferences.headerRow, + update: uploadPreferences.update, + refreshReadings: uploadPreferences.refreshReadings, + honorDst: uploadPreferences.honorDst, + relaxedParsing: uploadPreferences.relaxedParsing, + useMeterZone: uploadPreferences.useMeterZone + }; + for (const [preference, value] of Object.entries(uploadPreferencesForm)) { + formData.append(preference, value.toString()); + } + formData.append('csvfile', readingsFile); // It is important for the server that the file is attached last. - constructor(backend: ApiBackend) { - this.backend = backend; + let message = ''; + try { + message = await backend.doPostRequest('/api/csv/readings', formData); + dispatch(baseApi.util.invalidateTags(['Readings'])); + return { success: true, message: message }; + } catch (error) { + return { success: false, message: error.response.data }; } +}; - public async submitReadings(uploadPreferences: ReadingsCSVUploadPreferencesItem, readingsFile: File): Promise { - const formData = new FormData(); - // The Boolean values in state must be converted to the submitted values of yes and no. - const uploadPreferencesForm: ReadingsCSVUploadPreferencesForm = { - ...uploadPreferences, - gzip: uploadPreferences.gzip ? BooleanTypes.true : BooleanTypes.false, - headerRow: uploadPreferences.headerRow ? BooleanTypes.true : BooleanTypes.false, - update: uploadPreferences.update ? BooleanTypes.true : BooleanTypes.false, - createMeter: uploadPreferences.createMeter ? BooleanTypes.true : BooleanTypes.false, - refreshHourlyReadings: uploadPreferences.refreshHourlyReadings ? BooleanTypes.true : BooleanTypes.false, - refreshReadings: uploadPreferences.refreshReadings ? BooleanTypes.true : BooleanTypes.false, - honorDst: uploadPreferences.honorDst ? BooleanTypes.true : BooleanTypes.false, - relaxedParsing: uploadPreferences.relaxedParsing ? BooleanTypes.true : BooleanTypes.false, - useMeterZone: uploadPreferences.useMeterZone ? BooleanTypes.true : BooleanTypes.false - }; - for (const [preference, value] of Object.entries(uploadPreferencesForm)) { - formData.append(preference, value.toString()); - } - formData.append('csvfile', readingsFile); // It is important for the server that the file is attached last. - return await this.backend.doPostRequest('/api/csv/readings', formData); +export const submitMeters = async (uploadPreferences: MetersCSVUploadPreferences, metersFile: File, + dispatch: Dispatch): Promise => { + const backend = new ApiBackend(); + const formData = new FormData(); + // The Boolean values in state must be converted to the submitted values of yes and no. + const uploadPreferencesForm: CSVUploadPreferences = { + ...uploadPreferences, + gzip: uploadPreferences.gzip, + headerRow: uploadPreferences.headerRow, + update: uploadPreferences.update + }; + for (const [preference, value] of Object.entries(uploadPreferencesForm)) { + formData.append(preference, value.toString()); } + formData.append('csvfile', metersFile); // It is important for the server that the file is attached last. - public async submitMeters(uploadPreferences: MetersCSVUploadPreferencesItem, metersFile: File): Promise { - const formData = new FormData(); - // The Boolean values in state must be converted to the submitted values of yes and no. - const uploadPreferencesForm: CSVUploadPreferencesForm = { - ...uploadPreferences, - gzip: uploadPreferences.gzip ? BooleanTypes.true : BooleanTypes.false, - headerRow: uploadPreferences.headerRow ? BooleanTypes.true : BooleanTypes.false, - update: uploadPreferences.update ? BooleanTypes.true : BooleanTypes.false - }; - for (const [preference, value] of Object.entries(uploadPreferencesForm)) { - formData.append(preference, value.toString()); - } - formData.append('csvfile', metersFile); // It is important for the server that the file is attached last. - await this.backend.doPostRequest('/api/csv/meters', formData); + try { + const response = await backend.doPostRequest('/api/csv/meters', formData); + // Meter Data was sent to the DB, invalidate meters for now + dispatch(baseApi.util.invalidateTags(['MeterData'])); + // meters were invalidated so all meter changes will now reflect in Redux state, now return + return { success: true, message: response }; + } catch (error) { + return { success: false, message: error.response.data }; } -} +}; diff --git a/src/client/app/utils/api/index.ts b/src/client/app/utils/api/index.ts index 436a1e970..4330a2919 100644 --- a/src/client/app/utils/api/index.ts +++ b/src/client/app/utils/api/index.ts @@ -5,20 +5,17 @@ */ import ApiBackend from './ApiBackend'; -import UploadCSVApi from './UploadCSVApi'; import MapsApi from './MapsApi'; import LogsApi from './LogsApi'; const apiBackend = new ApiBackend(); // All specific backends share the same ApiBackend -const uploadCSVApi = new UploadCSVApi(apiBackend); const mapsApi = new MapsApi(apiBackend); const logsApi = new LogsApi(apiBackend); export { mapsApi, - logsApi, - uploadCSVApi + logsApi }; diff --git a/src/client/app/utils/csvUploadDefaults.ts b/src/client/app/utils/csvUploadDefaults.ts index 8053bd03e..49a73ff57 100644 --- a/src/client/app/utils/csvUploadDefaults.ts +++ b/src/client/app/utils/csvUploadDefaults.ts @@ -2,35 +2,34 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { ReadingsCSVUploadPreferencesItem, MetersCSVUploadPreferencesItem, TimeSortTypes, BooleanMeterTypes } from '../types/csvUploadForm'; +import { ReadingsCSVUploadPreferences, MetersCSVUploadPreferences } from '../types/csvUploadForm'; +import { MeterTimeSortType } from '../types/redux/meters'; // This file contains the default parameters for uploading readings and meters CSV files. These defaults should be consistent with the defaults // specified in /src/server/services/csvPipeline/validateCsvUploadParams and with /src/server/sql/create_meters_table.sql. -export const ReadingsCSVUploadDefaults: ReadingsCSVUploadPreferencesItem = { - meterName: '', - timeSort: TimeSortTypes.meter, - duplications: '', - cumulative: BooleanMeterTypes.meter, - cumulativeReset: BooleanMeterTypes.meter, +export const ReadingsCSVUploadDefaults: ReadingsCSVUploadPreferences = { + cumulative: false, + cumulativeReset: false, cumulativeResetStart: '', cumulativeResetEnd: '', - lengthGap: '', - lengthVariation: '', - endOnly: BooleanMeterTypes.meter, - createMeter: false, + duplications: 1, + endOnly: false, gzip: false, headerRow: false, - refreshHourlyReadings: false, - refreshReadings: false, - update: false, honorDst: false, + lengthGap: 0, + lengthVariation: 0, + meterIdentifier: '', + refreshReadings: false, relaxedParsing: false, + timeSort: MeterTimeSortType.increasing, + update: false, useMeterZone: false }; -export const MetersCSVUploadDefaults: MetersCSVUploadPreferencesItem = { - meterName: '', +export const MetersCSVUploadDefaults: MetersCSVUploadPreferences = { gzip: false, headerRow: false, + meterIdentifier: '', update: false -}; +}; \ No newline at end of file diff --git a/src/server/migrations/1.0.0-1.1.0/index.js b/src/server/migrations/1.0.0-2.0.0/index.js similarity index 55% rename from src/server/migrations/1.0.0-1.1.0/index.js rename to src/server/migrations/1.0.0-2.0.0/index.js index 6273473bb..0e796f33d 100644 --- a/src/server/migrations/1.0.0-1.1.0/index.js +++ b/src/server/migrations/1.0.0-2.0.0/index.js @@ -7,17 +7,17 @@ const sqlFile = database.sqlFile; module.exports = { fromVersion: '1.0.0', - toVersion: '1.1.0', + toVersion: '2.0.0', up: async db => { - await db.none(sqlFile('../migrations/1.0.0-1.1.0/sql/readings/create_reading_views.sql')); - await db.none(sqlFile('../migrations/1.0.0-1.1.0/sql/meter/add_meter_pipeline_checks.sql')); - await db.none(sqlFile('../migrations/1.0.0-1.1.0/sql/preferences/add_preferences_pipeline_checks.sql')); - await db.none(sqlFile('../migrations/1.0.0-1.1.0/sql/preferences/add_graph_type.sql')); - await db.none(sqlFile('../migrations/1.0.0-1.1.0/sql/preferences/add_preferences_help_url.sql')); - await db.none(sqlFile('../migrations/1.0.0-1.1.0/sql/users/add_users_note.sql')); - await db.none(sqlFile('../migrations/1.0.0-1.1.0/sql/users/alter_users_table.sql')); + await db.none(sqlFile('../migrations/1.0.0-2.0.0/sql/readings/create_reading_views.sql')); + await db.none(sqlFile('../migrations/1.0.0-2.0.0/sql/meter/add_meter_pipeline_checks.sql')); + await db.none(sqlFile('../migrations/1.0.0-2.0.0/sql/preferences/add_preferences_pipeline_checks.sql')); + await db.none(sqlFile('../migrations/1.0.0-2.0.0/sql/preferences/add_graph_type.sql')); + await db.none(sqlFile('../migrations/1.0.0-2.0.0/sql/preferences/add_preferences_help_url.sql')); + await db.none(sqlFile('../migrations/1.0.0-2.0.0/sql/users/add_users_note.sql')); + await db.none(sqlFile('../migrations/1.0.0-2.0.0/sql/users/alter_users_table.sql')); // It should not matter but first rename cik and then do units. - await db.none(sqlFile('../migrations/1.0.0-1.1.0/sql/cik/alter_cik_table.sql')); - await db.none(sqlFile('../migrations/1.0.0-1.1.0/sql/units/alter_units_table.sql')); + await db.none(sqlFile('../migrations/1.0.0-2.0.0/sql/cik/alter_cik_table.sql')); + await db.none(sqlFile('../migrations/1.0.0-2.0.0/sql/units/alter_units_table.sql')); } }; diff --git a/src/server/migrations/1.0.0-1.1.0/sql/cik/alter_cik_table.sql b/src/server/migrations/1.0.0-2.0.0/sql/cik/alter_cik_table.sql similarity index 100% rename from src/server/migrations/1.0.0-1.1.0/sql/cik/alter_cik_table.sql rename to src/server/migrations/1.0.0-2.0.0/sql/cik/alter_cik_table.sql diff --git a/src/server/migrations/1.0.0-1.1.0/sql/meter/add_meter_pipeline_checks.sql b/src/server/migrations/1.0.0-2.0.0/sql/meter/add_meter_pipeline_checks.sql similarity index 100% rename from src/server/migrations/1.0.0-1.1.0/sql/meter/add_meter_pipeline_checks.sql rename to src/server/migrations/1.0.0-2.0.0/sql/meter/add_meter_pipeline_checks.sql diff --git a/src/server/migrations/1.0.0-1.1.0/sql/preferences/add_graph_type.sql b/src/server/migrations/1.0.0-2.0.0/sql/preferences/add_graph_type.sql similarity index 100% rename from src/server/migrations/1.0.0-1.1.0/sql/preferences/add_graph_type.sql rename to src/server/migrations/1.0.0-2.0.0/sql/preferences/add_graph_type.sql diff --git a/src/server/migrations/1.0.0-1.1.0/sql/preferences/add_preferences_help_url.sql b/src/server/migrations/1.0.0-2.0.0/sql/preferences/add_preferences_help_url.sql similarity index 100% rename from src/server/migrations/1.0.0-1.1.0/sql/preferences/add_preferences_help_url.sql rename to src/server/migrations/1.0.0-2.0.0/sql/preferences/add_preferences_help_url.sql diff --git a/src/server/migrations/1.0.0-1.1.0/sql/preferences/add_preferences_pipeline_checks.sql b/src/server/migrations/1.0.0-2.0.0/sql/preferences/add_preferences_pipeline_checks.sql similarity index 100% rename from src/server/migrations/1.0.0-1.1.0/sql/preferences/add_preferences_pipeline_checks.sql rename to src/server/migrations/1.0.0-2.0.0/sql/preferences/add_preferences_pipeline_checks.sql diff --git a/src/server/migrations/1.0.0-1.1.0/sql/readings/create_reading_views.sql b/src/server/migrations/1.0.0-2.0.0/sql/readings/create_reading_views.sql similarity index 100% rename from src/server/migrations/1.0.0-1.1.0/sql/readings/create_reading_views.sql rename to src/server/migrations/1.0.0-2.0.0/sql/readings/create_reading_views.sql diff --git a/src/server/migrations/1.0.0-1.1.0/sql/units/alter_units_table.sql b/src/server/migrations/1.0.0-2.0.0/sql/units/alter_units_table.sql similarity index 100% rename from src/server/migrations/1.0.0-1.1.0/sql/units/alter_units_table.sql rename to src/server/migrations/1.0.0-2.0.0/sql/units/alter_units_table.sql diff --git a/src/server/models/Meter.js b/src/server/models/Meter.js index 758210541..a8a7dcd36 100644 --- a/src/server/models/Meter.js +++ b/src/server/models/Meter.js @@ -166,6 +166,17 @@ class Meter { return Meter.mapRow(row); } + /** + * Returns a promise to retrieve the meter with the given identifier from the database. + * @param identifier the meter's identifier + * @param conn the connection to be used. + * @returns {Promise.} + */ + static async getByIdentifier(identifier, conn) { + const row = await conn.one(sqlFile('meter/get_meter_by_identifier.sql'), { identifier: identifier }); + return Meter.mapRow(row); + } + /** * Returns a promise to get all of the meters from the database * @param conn the connection to be used. diff --git a/src/server/routes/csv.js b/src/server/routes/csv.js index 9ccf0e631..df4701b15 100644 --- a/src/server/routes/csv.js +++ b/src/server/routes/csv.js @@ -21,13 +21,11 @@ const saveCsv = require('../services/csvPipeline/saveCsv'); const uploadMeters = require('../services/csvPipeline/uploadMeters'); const uploadReadings = require('../services/csvPipeline/uploadReadings'); const zlib = require('zlib'); -const { refreshReadingViews } = require('../services/refreshReadingViews'); -const { refreshHourlyReadingViews } = require('../services/refreshHourlyReadingViews'); +const { refreshAllReadingViews } = require('../services/refreshAllReadingViews'); const { success, failure } = require('../services/csvPipeline/success'); -const { BooleanTypesJS } = require('../services/csvPipeline/validateCsvUploadParams'); /** Middleware validation */ -const { validateMetersCsvUploadParams, validateReadingsCsvUploadParams } = require('../services/csvPipeline/validateCsvUploadParams'); +const { normalizeBoolean, validateMetersCsvUploadParams, validateReadingsCsvUploadParams } = require('../services/csvPipeline/validateCsvUploadParams'); const { CSVPipelineError } = require('../services/csvPipeline/CustomErrors'); const { isTokenAuthorized, isUserAuthorized } = require('../util/userRoles'); @@ -73,7 +71,7 @@ router.use(function (req, res, next) { // Allowing for backwards compatibility if any users are still using the 'email' parameter instead of // the 'username' parameter to login. Developers need to decide in the future if we should deprecate email // or continue to allow this backwards compatibility - const user = username || email; + const user = username || email; const verifiedUser = await verifyCredentials(user, password, true); if (verifiedUser) { isUserAuthorized(verifiedUser, csvRole) ? cb(null, true) : cb(new Error('Invalid credentials')); @@ -110,7 +108,7 @@ router.use(function (req, res, next) { }); router.post('/meters', validateMetersCsvUploadParams, async (req, res) => { - const isGzip = req.body.gzip === BooleanTypesJS.true; + const isGzip = normalizeBoolean(req.body.gzip); const uploadedFilepath = req.file.path; let csvFilepath; try { @@ -133,6 +131,7 @@ router.post('/meters', validateMetersCsvUploadParams, async (req, res) => { success(req, res, 'Successfully inserted the meters.'); } catch (error) { failure(req, res, error); + } finally { // Clean up files fs.unlink(uploadedFilepath) // Delete the uploaded file. @@ -154,9 +153,8 @@ router.post('/meters', validateMetersCsvUploadParams, async (req, res) => { }); router.post('/readings', validateReadingsCsvUploadParams, async (req, res) => { - const isGzip = req.body.gzip === BooleanTypesJS.true; - const isRefreshReadings = req.body.refreshReadings === BooleanTypesJS.true; - const isRefreshHourlyReadings = req.body.refreshHourlyReadings === BooleanTypesJS.true; + const isGzip = normalizeBoolean(req.body.gzip); + const isRefreshReadings = normalizeBoolean(req.body.refreshReadings); const uploadedFilepath = req.file.path; let csvFilepath; let isAllReadingsOk; @@ -179,11 +177,7 @@ router.post('/readings', validateReadingsCsvUploadParams, async (req, res) => { ({ isAllReadingsOk, msgTotal } = await uploadReadings(req, res, csvFilepath, conn)); if (isRefreshReadings) { // Refresh readings so show when daily data is used. - await refreshReadingViews(); - } - if (isRefreshHourlyReadings) { - // Refresh readings so show when hourly data is used. - await refreshHourlyReadingViews(); + await refreshAllReadingViews(); } } catch (error) { failure(req, res, error); diff --git a/src/server/routes/meters.js b/src/server/routes/meters.js index e7aaea313..5bd411ca5 100644 --- a/src/server/routes/meters.js +++ b/src/server/routes/meters.js @@ -25,10 +25,10 @@ router.use(optionalAuthenticator); /** * Defines the format in which we want to send meters and controls what information we send to the client, if logged in and an Admin or not. * @param meter - * @param loggedInAsAdmin + * @param hasFullAccess * @returns {{id, name}} */ -function formatMeterForResponse(meter, loggedInAsAdmin) { +function formatMeterForResponse(meter, hasFullAccess) { const formattedMeter = { id: meter.id, name: null, @@ -68,7 +68,7 @@ function formatMeterForResponse(meter, loggedInAsAdmin) { // Only logged in Admins can see url, types, timezones, and internal names // and lots of other items now. - if (loggedInAsAdmin) { + if (hasFullAccess) { formattedMeter.name = meter.name; formattedMeter.url = meter.url; formattedMeter.meterType = meter.type; @@ -108,13 +108,13 @@ router.get('/', async (req, res) => { const conn = getConnection(); let query; const token = req.headers.token || req.body.token || req.query.token; - const loggedInAsAdmin = req.hasValidAuthToken && (await isTokenAuthorized(token, User.role.ADMIN)); + const isAuthorizedCSV = req.hasValidAuthToken && (await isTokenAuthorized(token, User.role.CSV)); // Because groups can use hidden meters, everyone gets all meters but we filter the // information given about the meter after getting it. query = Meter.getAll; const rows = await query(conn); - res.json(rows.map(row => formatMeterForResponse(row, loggedInAsAdmin))); + res.json(rows.map(row => formatMeterForResponse(row, isAuthorizedCSV))); } catch (err) { log.error(`Error while performing GET all meters query: ${err}`, err); } diff --git a/src/server/services/csvPipeline/uploadMeters.js b/src/server/services/csvPipeline/uploadMeters.js index db80dd14f..78f8cb521 100644 --- a/src/server/services/csvPipeline/uploadMeters.js +++ b/src/server/services/csvPipeline/uploadMeters.js @@ -7,7 +7,7 @@ const { CSVPipelineError } = require('./CustomErrors'); const Meter = require('../../models/Meter'); const readCsv = require('../pipeline-in-progress/readCsv'); const Unit = require('../../models/Unit'); -const { BooleanTypesJS } = require('./validateCsvUploadParams'); +const { normalizeBoolean } = require('./validateCsvUploadParams'); /** * Middleware that uploads meters via the pipeline. This should be the final stage of the CSV Pipeline. @@ -28,7 +28,7 @@ async function uploadMeters(req, res, filepath, conn) { }); // If there is a header row, we remove and ignore it for now. - const meters = (req.body.headerRow === BooleanTypesJS.true) ? temp.slice(1) : temp; + const meters = normalizeBoolean(req.body.headerRow) ? temp.slice(1) : temp; // The original code used a Promise.all to run through the meters. The issue is that the promises are run in parallel. // If the meters are independent as expected then this works fine. However, in the error case where one CSV file has // the same meter name listed twice, the order of the attempts to add to the database was arbitrary. This meant one of them @@ -37,6 +37,7 @@ async function uploadMeters(req, res, filepath, conn) { // as this makes the most logical sense (no update here) and it is consistent. To make this happen a for loop is used as it // is sequential. A small negative is the database requests do not run in parallel in the usual case without an error. // However, uploading meters is not common so slowing it down slightly seems a reasonable price to get this behavior. + try { for (let i = 0; i < meters.length; i++) { let meter = meters[i]; @@ -75,34 +76,40 @@ async function uploadMeters(req, res, filepath, conn) { // Replace the default graphic unit's name by its id. meter[24] = defaultGraphicUnitId; - if (req.body.update === BooleanTypesJS.true) { + if (normalizeBoolean(req.body.update)) { // Updating the new meters. // First get its id. - let nameOfMeter = req.body.meterName; - if (!nameOfMeter) { - // Seems no name provided so use one in CSV file. - nameOfMeter = meter[0]; + let identifierOfMeter = req.body.meterIdentifier; + if (!identifierOfMeter) { + // Seems no identifier provided so use one in CSV file. + if (!meter[7]) { + // There is no identifier given for meter in CSV so use name as identifier since would be automatically set. + identifierOfMeter = meter[0]; + } else { + identifierOfMeter = meter[7]; + } } else if (meters.length !== 1) { // This error could be thrown a number of times, one per meter in CSV, but should only see one of them. - throw new CSVPipelineError(`Meter name provided (\"${nameOfMeter}\") in request with update for meters but more than one meter in CSV so not processing`, undefined, 500); + throw new CSVPipelineError(`Meter identifier provided (\"${identifierOfMeter}\") in request with update for meters but more than one meter in CSV so not processing`, undefined, 500); } let currentMeter; - currentMeter = await Meter.getByName(nameOfMeter, conn) + currentMeter = await Meter.getByIdentifier(identifierOfMeter, conn) .catch(error => { // Did not find the meter. - let msg = `Meter name of \"${nameOfMeter}\" does not seem to exist with update for meters and got DB error of: ${error.message}`; + let msg = `Meter identifier of \"${identifierOfMeter}\" does not seem to exist with update for meters and got DB error of: ${error.message}`; throw new CSVPipelineError(msg, undefined, 500); }); currentMeter.merge(...meter); await currentMeter.update(conn); } else { - // Inserting the new meters. + // Inserting the new meter await new Meter(undefined, ...meter).insert(conn) .catch(error => { // Probably duplicate meter. throw new CSVPipelineError( `Meter name of \"${meter[0]}\" got database error of: ${error.message}`, undefined, 500); - }); + } + ); } } } catch (error) { diff --git a/src/server/services/csvPipeline/uploadReadings.js b/src/server/services/csvPipeline/uploadReadings.js index f27b35fc2..1bdcfd947 100644 --- a/src/server/services/csvPipeline/uploadReadings.js +++ b/src/server/services/csvPipeline/uploadReadings.js @@ -5,11 +5,8 @@ const express = require('express'); const { CSVPipelineError } = require('./CustomErrors'); const { loadCsvInput } = require('../pipeline-in-progress/loadCsvInput'); -const { TimeSortTypesJS, BooleanMeterTypesJS, BooleanTypesJS } = require('./validateCsvUploadParams'); +const { normalizeBoolean, MeterTimeSortTypesJS } = require('./validateCsvUploadParams'); const Meter = require('../../models/Meter'); -const { log } = require('../../log'); -const moment = require('moment'); -const Preferences = require('../../models/Preferences'); /** * Middleware that uploads readings via the pipeline. This should be the final stage of the CSV Pipeline. @@ -20,73 +17,35 @@ const Preferences = require('../../models/Preferences'); * @returns */ async function uploadReadings(req, res, filepath, conn) { - const { meterName, createMeter, headerRow, update, honorDst, relaxedParsing, useMeterZone } = req.body; // extract query parameters + const { meterIdentifier, meterName, headerRow, update, honorDst, relaxedParsing, useMeterZone } = req.body; // extract query parameters // The next few have no value in the DB for a meter so always use the value passed. - const hasHeaderRow = (headerRow === BooleanTypesJS.true); - const shouldUpdate = (update === BooleanTypesJS.true); - let shouldHonorDst = (honorDst === BooleanTypesJS.true); - let shouldRelaxedParsing = (relaxedParsing === BooleanTypesJS.true); - let shouldUseMeterZone = (useMeterZone === BooleanTypesJS.true); - let meterCreated = false; - let meter = await Meter.getByName(meterName, conn) - .catch(async err => { - // Meter#getByNames throws an error when no meter is found. We need the catch clause to account for this error. - if (createMeter !== BooleanTypesJS.true) { - // If createMeter is not set to true, we do not know what to do with the readings so we error out. - throw new CSVPipelineError( - `User Error: Meter with name '${meterName}' not found. createMeter needs to be set true in order to automatically create meter.`, - err.message - ); - } else { - const preferences = await Preferences.get(conn); - // If createMeter is true, we will create the meter for the user. - // The meter type is unknown so set to other. Most parameters take on default values. - const tempMeter = new Meter( - undefined, // id - meterName, // name - undefined, // URL - false, // enabled - false, // displayable - Meter.type.OTHER, // type - undefined, // timezone - undefined, // gps - meterName, // identifier - 'created via reading upload on ' + moment().format(), // note - undefined, //area - undefined, // cumulative - undefined, // cumulativeReset - undefined, // cumulativeResetStart - undefined, // cumulativeResetEnd - preferences.defaultMeterReadingGap, // readingGap - undefined, // readingVariation - undefined, // readingDuplication - undefined, // timeSort - undefined, // endOnlyTime - undefined, // reading - undefined, // startTimestamp - undefined, // endTimestamp - undefined, // previousEnd - undefined, // unit - undefined, // default graphic unit - undefined, // area unit - preferences.defaultMeterReadingFrequency, // reading frequency - preferences.defaultMeterMinimumValue, // minVal - preferences.defaultMeterMaximumValue, // maxVal - preferences.defaultMeterMinimumDate, // minDate - preferences.defaultMeterMaximumDate, // maxDate - preferences.defaultMeterMaximumErrors, // maxError - preferences.defaultMeterDisableChecks // disableChecks - ) - await tempMeter.insert(conn); - meterCreated = true; - log.info('Creating meter ' + tempMeter.name); - return await Meter.getByName(tempMeter.name, conn); // Get meter from DB after insert because some defaults are set within the DB. - } - }); - if (!meterCreated && createMeter === BooleanTypesJS.true) { - log.warn('The create meter was set but the meter already existed for meter ' + meter.name); - } + const hasHeaderRow = normalizeBoolean(headerRow); + const shouldUpdate = normalizeBoolean(update); + let shouldHonorDst = normalizeBoolean(honorDst); + let shouldRelaxedParsing = normalizeBoolean(relaxedParsing); + let shouldUseMeterZone = normalizeBoolean(useMeterZone); + // TODO: + // Allowing for backwards compatibility if any users are still using the 'meterName' parameter instead of + // the 'meterIdentifier' parameter to login. Developers need to decide in the future if we should deprecate + // using 'meterName' or continue to allow this backwards compatibility + let meter; + try { + if (meterIdentifier) { + meter = await Meter.getByIdentifier(meterIdentifier, conn); + } else { + meter = await Meter.getByName(meterName, conn); + } + } catch (error) { + // If Meter does not exist, we do not know what to do with the readings so we error out. + let errorMessage = meterIdentifier + ? `User Error: Meter with identifier '${meterIdentifier}' not found.` + : `User Error: Meter with name '${meterName}' not found.`; + throw new CSVPipelineError( + errorMessage, + error.message + ); + } // Handle other parameter defaults let { timeSort, duplications, cumulative, cumulativeReset, cumulativeResetStart, cumulativeResetEnd, lengthGap, lengthVariation, endOnly } = req.body; @@ -99,7 +58,7 @@ async function uploadReadings(req, res, filepath, conn) { let readingGap; let readingLengthVariation; let readingEndOnly; - // For the parameters, they either have one of the desired values or a "empty" value. + // For the parameters, they either have one of the desired values or an "empty" value. // An empty value means use the value from the DB meter or, in the unlikely event it is not set, // then use the default value. For values coming from the web page: // In the case of timeSort, cumulative, cumulativeReset & endOnly empty is the enum 'meter' value. @@ -126,43 +85,43 @@ async function uploadReadings(req, res, filepath, conn) { readingRepetition = parseInt(duplications, 10); } - if (timeSort === undefined || timeSort === TimeSortTypesJS.meter) { + if (timeSort === undefined) { if (meter.timeSort === null) { // This probably should not happen with a new DB but keep just in case. // No variation allowed. - readingTimeSort = TimeSortTypesJS.increasing; + readingTimeSort = MeterTimeSortTypesJS.increasing; } else { - readingTimeSort = TimeSortTypesJS[meter.timeSort]; + readingTimeSort = MeterTimeSortTypesJS[meter.timeSort]; } } else { readingTimeSort = timeSort; } - if (cumulative === undefined || cumulative === BooleanMeterTypesJS.meter) { + if (cumulative === undefined) { if (meter.cumulative === null) { // This probably should not happen with a new DB but keep just in case. // No variation allowed. - readingsCumulative = BooleanMeterTypesJS.false; + readingsCumulative = false; } else { - readingsCumulative = BooleanMeterTypesJS[meter.cumulative]; + readingsCumulative = meter.cumulative; } } else { - readingsCumulative = cumulative; + readingsCumulative = normalizeBoolean(cumulative); } - const areReadingsCumulative = (readingsCumulative === BooleanMeterTypesJS.true); + const areReadingsCumulative = readingsCumulative; - if (cumulativeReset === undefined || cumulativeReset === BooleanMeterTypesJS.meter) { + if (cumulativeReset === undefined) { if (meter.cumulativeReset === null) { // This probably should not happen with a new DB but keep just in case. // No variation allowed. - readingsReset = BooleanMeterTypesJS.false; + readingsReset = false; } else { - readingsReset = BooleanMeterTypesJS[meter.cumulativeReset]; + readingsReset = meter.cumulativeReset; } } else { - readingsReset = cumulativeReset; + readingsReset = normalizeBoolean(cumulativeReset); } - const doReadingsReset = (readingsReset === BooleanMeterTypesJS.true); + const doReadingsReset = readingsReset; if (cumulativeResetStart === undefined || cumulativeResetStart === '') { if (meter.cumulativeResetStart === null) { @@ -214,18 +173,18 @@ async function uploadReadings(req, res, filepath, conn) { readingLengthVariation = parseFloat(lengthVariation); } - if (endOnly === undefined || endOnly === BooleanMeterTypesJS.meter) { + if (endOnly === undefined) { if (meter.endOnlyTime === null) { // This probably should not happen with a new DB but keep just in case. // No variation allowed. - readingEndOnly = BooleanMeterTypesJS.false; + readingEndOnly = false; } else { - readingEndOnly = BooleanMeterTypesJS[meter.endOnlyTime]; + readingEndOnly = meter.endOnlyTime; } } else { - readingEndOnly = endOnly; + readingEndOnly = normalizeBoolean(endOnly); } - const areReadingsEndOnly = (readingEndOnly === BooleanMeterTypesJS.true); + const areReadingsEndOnly = readingEndOnly; const mapRowToModel = row => { return row; }; // STUB function to satisfy the parameter of loadCsvInput. @@ -238,7 +197,7 @@ async function uploadReadings(req, res, filepath, conn) { maxError: meter.maxError, disableChecks: meter.disableChecks } - + return await loadCsvInput( filepath, meter.id, diff --git a/src/server/services/csvPipeline/validateCsvUploadParams.js b/src/server/services/csvPipeline/validateCsvUploadParams.js index 159c2df11..d2bca74ad 100644 --- a/src/server/services/csvPipeline/validateCsvUploadParams.js +++ b/src/server/services/csvPipeline/validateCsvUploadParams.js @@ -8,18 +8,6 @@ const { Param, EnumParam, BooleanParam, StringParam } = require('./ValidationSch const failure = require('./failure'); const validate = require('jsonschema').validate; -/** - * Enum of CSV input type sorting. - * This enum needs to be kept in sync with the enum in src/client/app/types/csvUploadForm.ts - * @enum {string} - */ -TimeSortTypesJS = Object.freeze({ - increasing: 'increasing', - decreasing: 'decreasing', - // meter means to use value stored on meter or the default if not. - meter: 'meter value or default' -}); - // This is only used for meter page inputs but put here so next one above that related to. /** * Enum of CSV input type sorting. @@ -31,27 +19,30 @@ MeterTimeSortTypesJS = Object.freeze({ decreasing: 'decreasing', }); -/** - * Enum of Boolean types. - * This enum needs to be kept in sync with the enum in src/client/app/types/csvUploadForm.ts - * @enum {string} - */ -BooleanTypesJS = Object.freeze({ - true: 'yes', - false: 'no', -}); +// This function allows for curl users to continue to use 'yes' or 'no' and also allows string +// values of true or false if the change is made. +const normalizeBoolean = (input) => { + if (typeof input === 'string') { + input = input.toLowerCase(); + } + + switch (input) { + case 'yes': + case 'true': + case true: + return true; + case 'no': + case 'false': + case false: + return false; + default: + return 'normalizeBoolean error'; // Return the original value if it does not match any of the boolean representations + } +}; -/** - * Enum of Boolean types. - * This enum needs to be kept in sync with the enum in src/client/app/types/csvUploadForm.ts - * @enum {string} - */ -BooleanMeterTypesJS = Object.freeze({ - true: 'yes', - false: 'no', - // meter means to use value stored on meter or the default if not. - meter: 'meter value or default' -}); +// This is to allow curl users to still send 'yes' and 'no' for parameters sent, +// The frontend will just use standard boolean values to send to the API +const BooleanCheckArray = ['yes', 'no', 'true', 'false', true, false]; // These are the default values of CSV Pipeline upload parameters. If a user does not specify // a choice for a particular parameter, then these defaults will be used. @@ -66,28 +57,26 @@ BooleanMeterTypesJS = Object.freeze({ // we should change these strings to booleans. const DEFAULTS = { common: { - gzip: BooleanTypesJS.true, - headerRow: BooleanTypesJS.false, - update: BooleanTypesJS.false + gzip: true, + headerRow: false, + update: false }, meters: { }, readings: { - timeSort: undefined, - duplications: undefined, - cumulative: BooleanMeterTypesJS.meter, - cumulativeReset: BooleanMeterTypesJS.meter, + cumulative: undefined, + cumulativeReset: undefined, cumulativeResetStart: undefined, cumulativeResetEnd: undefined, + duplications: undefined, + endOnly: undefined, + honorDst: false, lengthGap: undefined, lengthVariation: undefined, - endOnly: undefined, - createMeter: BooleanTypesJS.false, - refreshReadings: BooleanTypesJS.false, - refreshHourlyReadings: BooleanTypesJS.false, - honorDst: BooleanTypesJS.false, - relaxedParsing: BooleanTypesJS.false, - useMeterZone: BooleanTypesJS.false + refreshReadings: false, + relaxedParsing: false, + timeSort: undefined, + useMeterZone: false } } @@ -97,17 +86,18 @@ const DEFAULTS = { // (i.e. when the user performs a curl request to the pipeline). Thus, we list these properties // here so that they do not falsely trigger the 'additionalProperties' User Error. const COMMON_PROPERTIES = { - meterName: new StringParam('meterName', undefined, undefined), + meterIdentifier: new StringParam('meterIdentifier', undefined, undefined), username: new StringParam('username', undefined, undefined), // TODO: // Allowing for backwards compatibility to allow for curl users to use the 'email' parameter instead of - // the 'username' parameter to login. Developers need to decide in the future if we should deprecate email - // or continue to allow this backwards compatibility + // the 'username' parameter to login. Also allowing to use 'meterName' vs 'meterIdentifier'. Developers need + // to decide in the future if we should deprecate email & meterName or continue to allow this backwards compatibility. + meterName: new StringParam('meterName', undefined, undefined), email: new StringParam('email', undefined, undefined), password: new StringParam('password', undefined, undefined), - gzip: new BooleanParam('gzip'), - headerRow: new BooleanParam('headerRow'), - update: new BooleanParam('update') + gzip: new EnumParam('gzip', BooleanCheckArray), + headerRow: new EnumParam('headerRow', BooleanCheckArray), + update: new EnumParam('update', BooleanCheckArray) } // This sets the validation schemas for jsonschema. @@ -122,25 +112,26 @@ const VALIDATION = { }, readings: { type: 'object', - required: ['meterName'], properties: { ...COMMON_PROPERTIES, - timeSort: new EnumParam('timeSort', [TimeSortTypesJS.increasing, TimeSortTypesJS.decreasing, TimeSortTypesJS.meter]), - duplications: new StringParam('duplications', '^\\d+$|^(?![\s\S])', 'duplications must be an integer or empty.'), - cumulative: new EnumParam('cumulative', [BooleanMeterTypesJS.true, BooleanMeterTypesJS.false, BooleanMeterTypesJS.meter]), - cumulativeReset: new EnumParam('cumulativeReset', [BooleanMeterTypesJS.true, BooleanMeterTypesJS.false, BooleanMeterTypesJS.meter]), + cumulative: new EnumParam('cumulative', BooleanCheckArray), + cumulativeReset: new EnumParam('cumulativeReset', BooleanCheckArray), cumulativeResetStart: new StringParam('cumulativeResetStart', undefined, undefined), cumulativeResetEnd: new StringParam('cumulativeResetEnd', undefined, undefined), + duplications: new StringParam('duplications', '^\\d+$|^(?![\s\S])', 'duplications must be an integer or empty.'), + endOnly: new EnumParam('endOnly', BooleanCheckArray), + honorDst: new EnumParam('honorDst', BooleanCheckArray), lengthGap: new StringParam('lengthGap', undefined, undefined), lengthVariation: new StringParam('lengthVariation', undefined, undefined), - endOnly: new EnumParam('endOnly', [BooleanMeterTypesJS.true, BooleanMeterTypesJS.false, BooleanMeterTypesJS.meter]), - createMeter: new BooleanParam('createMeter'), - refreshReadings: new BooleanParam('refreshReadings'), - refreshHourlyReadings: new BooleanParam('refreshHourlyReadings'), - honorDst: new BooleanParam('honorDst'), - relaxedParsing: new BooleanParam('relaxedParsing'), - useMeterZone: new BooleanParam('useMeterZone') + refreshReadings: new EnumParam('refreshReadings', BooleanCheckArray), + relaxedParsing: new EnumParam('relaxedParsing', BooleanCheckArray), + timeSort: new EnumParam('timeSort', [MeterTimeSortTypesJS.increasing, MeterTimeSortTypesJS.decreasing]), + useMeterZone: new EnumParam('useMeterZone', BooleanCheckArray), }, + anyOf: [ + { required: ['meterIdentifier'] }, + { required: ['meterName'] } + ], additionalProperties: false // This protects us from unintended parameters as well as typos. } } @@ -158,7 +149,7 @@ function validateRequestParams(body, schema) { } else if (err.name === 'required') { responseMessage = 'User Error: ' + responseMessage + err.path + ': ' + `${err.argument} must be provided as the field ${err.argument}=.\n`; } else if (err.name === 'additionalProperties') { - responseMessage = 'User Error: ' + responseMessage + err.path + ': '+ err.argument + ' is an unexpected argument.\n'; + responseMessage = 'User Error: ' + responseMessage + err.path + ': ' + err.argument + ' is an unexpected argument.\n'; } else { responseMessage = responseMessage + err.path + ': ' + 'has message: ' + err.message; } @@ -174,7 +165,6 @@ function validateRequestParams(body, schema) { } } - /** * Middleware that validates a request to upload readings via the CSV Pipeline and sets defaults for upload parameters. * @param {express.Request} req @@ -188,38 +178,39 @@ function validateReadingsCsvUploadParams(req, res, next) { failure(req, res, new CSVPipelineError(responseMessage)); return; } + + const { cumulative, cumulativeReset, duplications, gzip, headerRow, timeSort, update, honorDst, + relaxedParsing, useMeterZone } = req.body; // extract query parameters - const { createMeter, cumulative, duplications, - gzip, headerRow, timeSort, update, honorDst, relaxedParsing, useMeterZone } = req.body; // extract query parameters // Set default values of not supplied parameters. - if (!createMeter) { - req.body.createMeter = DEFAULTS.readings.createMeter; - } - if (!cumulative) { + if (cumulative === undefined) { req.body.cumulative = DEFAULTS.readings.cumulative; } - if (!duplications) { + if (cumulativeReset === undefined) { + req.body.cumulativeReset = DEFAULTS.readings.cumulativeReset; + } + if (duplications === undefined) { req.body.duplications = DEFAULTS.readings.duplications; } - if (!gzip) { + if (gzip === undefined) { req.body.gzip = DEFAULTS.common.gzip; } - if (!headerRow) { + if (headerRow === undefined) { req.body.headerRow = DEFAULTS.common.headerRow; } - if (!timeSort) { + if (timeSort === undefined) { req.body.timeSort = DEFAULTS.readings.timeSort; } - if (!update) { + if (update === undefined) { req.body.update = DEFAULTS.common.update; } - if (!honorDst) { + if (honorDst === undefined) { req.body.honorDst = DEFAULTS.readings.honorDst; } - if (!relaxedParsing) { + if (relaxedParsing === undefined) { req.body.relaxedParsing = DEFAULTS.readings.relaxedParsing; } - if (!useMeterZone) { + if (useMeterZone === undefined) { req.body.useMeterZone = DEFAULTS.readings.useMeterZone; } next(); @@ -242,13 +233,13 @@ function validateMetersCsvUploadParams(req, res, next) { const { gzip, headerRow, update } = req.body; // Extract query parameters // Set default values of not supplied parameters. - if (!gzip) { + if (gzip === undefined) { req.body.gzip = DEFAULTS.common.gzip; } - if (!headerRow) { + if (headerRow === undefined) { req.body.headerRow = DEFAULTS.common.headerRow; } - if (!update) { + if (update === undefined) { req.body.update = DEFAULTS.common.update; } next(); @@ -257,8 +248,6 @@ function validateMetersCsvUploadParams(req, res, next) { module.exports = { validateMetersCsvUploadParams, validateReadingsCsvUploadParams, - TimeSortTypesJS, MeterTimeSortTypesJS, - BooleanMeterTypesJS, - BooleanTypesJS + normalizeBoolean }; diff --git a/src/server/services/pipeline-in-progress/processData.js b/src/server/services/pipeline-in-progress/processData.js index 55cf8557d..f458e1d3c 100644 --- a/src/server/services/pipeline-in-progress/processData.js +++ b/src/server/services/pipeline-in-progress/processData.js @@ -10,7 +10,7 @@ const Meter = require('../../models/Meter'); const Reading = require('../../models/Reading'); const handleCumulativeReset = require('./handleCumulativeReset'); const { validateReadings } = require('./validateReadings'); -const { TimeSortTypesJS } = require('../csvPipeline/validateCsvUploadParams'); +const { MeterTimeSortTypesJS } = require('../csvPipeline/validateCsvUploadParams'); const { meterTimezone } = require('../meterTimezone'); // The default start/end timestamps that are set to the first @@ -53,7 +53,7 @@ const E0 = moment(0).utc() * be avoided except in special circumstances. * @returns {object[]} {array of readings accepted, true if all readings accepted and false otherwise, all messages from processing} */ -async function processData(rows, meterID, timeSort = TimeSortTypesJS.increasing, readingRepetition, isCumulative, cumulativeReset, +async function processData(rows, meterID, timeSort = MeterTimeSortTypesJS.increasing, readingRepetition, isCumulative, cumulativeReset, resetStart = '00:00:00.000', resetEnd = '23:59:99.999', readingGap = 0, readingLengthVariation = 0, isEndTime = false, conditionSet, conn, honorDst = false, relaxedParsing = false, useMeterZone = false) { // Holds all the warning message to pass back to inform user. @@ -71,7 +71,7 @@ async function processData(rows, meterID, timeSort = TimeSortTypesJS.increasing, // Usually holds current message(s) that are yet to be added to msgTotal. let errMsg; // Tells sorted order of readings. - const isAscending = (timeSort === TimeSortTypesJS.increasing); + const isAscending = (timeSort === MeterTimeSortTypesJS.increasing); // Convert readingGap and readingLengthVariation to milliseconds to stay consistent with moment.diff() which returns the difference in milliseconds const msReadingGap = readingGap * 1000; const msReadingLengthVariation = readingLengthVariation * 1000; diff --git a/src/server/services/pipeline-in-progress/validateReadings.js b/src/server/services/pipeline-in-progress/validateReadings.js index 882626bdd..7e9affa49 100644 --- a/src/server/services/pipeline-in-progress/validateReadings.js +++ b/src/server/services/pipeline-in-progress/validateReadings.js @@ -11,12 +11,12 @@ const { log } = require('../../log'); * Validate an array of Readings value according to certain criteria * @param {Reading[]} arrayToValidate * @param {dict} conditionSet used to validate readings (minVal, maxVal, minDate, maxDate, threshold, maxError) - * @param {string} meterName name of meter being checked + * @param {string} meterIdentifier identifier of meter being checked */ -function validateReadings(arrayToValidate, conditionSet, meterName = undefined) { +function validateReadings(arrayToValidate, conditionSet, meterIdentifier = undefined) { /* tslint:disable:no-string-literal */ - const { validDates, errMsg: errMsgDate } = checkDate(arrayToValidate, conditionSet['minDate'], conditionSet['maxDate'], conditionSet['maxError'] / 2, meterName); - const { validValues, errMsg: errMsgValue } = checkValue(arrayToValidate, conditionSet['minVal'], conditionSet['maxVal'], conditionSet['maxError'] / 2, meterName); + const { validDates, errMsg: errMsgDate } = checkDate(arrayToValidate, conditionSet['minDate'], conditionSet['maxDate'], conditionSet['maxError'] / 2, meterIdentifier); + const { validValues, errMsg: errMsgValue } = checkValue(arrayToValidate, conditionSet['minVal'], conditionSet['maxVal'], conditionSet['maxError'] / 2, meterIdentifier); /* tslint:enable:no-string-literal */ const errMsg = errMsgDate + errMsgValue; return { @@ -31,8 +31,9 @@ function validateReadings(arrayToValidate, conditionSet, meterName = undefined) * @param {Moment} minDate inclusive earliest acceptable date (won't be rejected) * @param {Moment} maxDate inclusive latest acceptable date (won't be rejected) * @param {number} maxError maximum number of errors to be reported, ignore the rest + * @param {string} meterIdentifier identifier of meter being checked. */ -function checkDate(arrayToValidate, minDate, maxDate, maxError, meterName) { +function checkDate(arrayToValidate, minDate, maxDate, maxError, meterIdentifier) { let validDates = true; let errMsg = ''; if (minDate === null && maxDate === null) { @@ -45,7 +46,7 @@ function checkDate(arrayToValidate, minDate, maxDate, maxError, meterName) { break; } if (reading.startTimestamp < minDate) { - const newErrMsg = `error when checking reading time for #${readingNumber} on meter ${meterName}: ` + + const newErrMsg = `error when checking reading time for #${readingNumber} on meter ${meterIdentifier}: ` + `time ${reading.startTimestamp} is earlier than lower bound ${minDate} ` + `with reading ${reading.reading} and endTimestamp ${reading.endTimestamp}`; log.error(newErrMsg); @@ -54,9 +55,9 @@ function checkDate(arrayToValidate, minDate, maxDate, maxError, meterName) { validDates = false; } if (reading.endTimestamp > maxDate) { - const newErrMsg = `error when checking reading time for #${readingNumber} on meter ${meterName}: ` + - `time ${reading.endTimestamp} is later than upper bound ${maxDate} ` + - `with reading ${reading.reading} and startTimestamp ${reading.startTimestamp}`; + const newErrMsg = `error when checking reading time for #${readingNumber} on meter ${meterIdentifier}: ` + + `time ${reading.endTimestamp} is later than upper bound ${maxDate} ` + + `with reading ${reading.reading} and startTimestamp ${reading.startTimestamp}`; log.error(newErrMsg); errMsg += '
' + newErrMsg + '
'; --maxError; @@ -72,8 +73,9 @@ function checkDate(arrayToValidate, minDate, maxDate, maxError, meterName) { * @param {number} minVal inclusive minimum acceptable reading value (won't be rejected) * @param {number} maxVal inclusive maximum acceptable reading value (won't be rejected) * @param {number} maxError maximum number of errors to be reported, ignore the rest + * @param {string} meterIdentifier identifier of meter being checked. */ -function checkValue(arrayToValidate, minVal, maxVal, maxError, meterName) { +function checkValue(arrayToValidate, minVal, maxVal, maxError, meterIdentifier) { let validValues = true; let errMsg = ''; let readingNumber = 0; @@ -83,17 +85,17 @@ function checkValue(arrayToValidate, minVal, maxVal, maxError, meterName) { break; } if (reading.reading < minVal) { - const newErrMsg = `error when checking reading value for #${readingNumber} on meter ${meterName}: ` + - `value ${reading.reading} is smaller than lower bound ${minVal} ` + - `with startTimestamp ${reading.startTimestamp} and endTimestamp ${reading.endTimestamp}`; + const newErrMsg = `error when checking reading value for #${readingNumber} on meter ${meterIdentifier}: ` + + `value ${reading.reading} is smaller than lower bound ${minVal} ` + + `with startTimestamp ${reading.startTimestamp} and endTimestamp ${reading.endTimestamp}`; log.error(newErrMsg); errMsg += '
' + newErrMsg + '
'; --maxError; validValues = false; } else if (reading.reading > maxVal) { - const newErrMsg = `error when checking reading value for #${readingNumber} on meter ${meterName}: ` + - `value ${reading.reading} is larger than upper bound ${maxVal} ` + - `with startTimestamp ${reading.startTimestamp} and endTimestamp ${reading.endTimestamp}`; + const newErrMsg = `error when checking reading value for #${readingNumber} on meter ${meterIdentifier}: ` + + `value ${reading.reading} is larger than upper bound ${maxVal} ` + + `with startTimestamp ${reading.startTimestamp} and endTimestamp ${reading.endTimestamp}`; log.error(newErrMsg); errMsg += '
' + newErrMsg + '
'; --maxError; @@ -107,8 +109,9 @@ function checkValue(arrayToValidate, minVal, maxVal, maxError, meterName) { * Check and report unequal intervals. Can be ignore by passing null interval * @param {Readings[]} arrayToValidate * @param {number} threshold the maximum allowed difference between consecutive data points' intervals + * @param {string} meterIdentifier identifier of meter being checked. */ -function checkIntervals(arrayToValidate, threshold, meterName) { +function checkIntervals(arrayToValidate, threshold, meterIdentifier) { let validIntervals = true; let errMsg = ''; @@ -130,9 +133,9 @@ function checkIntervals(arrayToValidate, threshold, meterName) { const currGap = reading.startTimestamp.diff(lastTime, 'seconds'); // Compare the current time gap with the expected interval. Terminate if the difference is larger than the accepted threshold if (Math.abs(currGap - interval) > threshold) { - const newErrMsg = `warning when checking reading gap for #${readingNumber} on meter ${meterName}: ` + - `time gap is detected between current start time ${reading.startTimestamp} and previous end time ${lastTime} that exceeds threshold of ${threshold} ` + - `with current reading ${reading.reading} and endTimestamp ${reading.endTimestamp}`; + const newErrMsg = `warning when checking reading gap for #${readingNumber} on meter ${meterIdentifier}: ` + + `time gap is detected between current start time ${reading.startTimestamp} and previous end time ${lastTime} that exceeds threshold of ${threshold} ` + + `with current reading ${reading.reading} and endTimestamp ${reading.endTimestamp}`; log.error(newErrMsg); errMsg += '
' + newErrMsg + '
'; validIntervals = false; diff --git a/src/server/sql/meter/get_meter_by_identifier.sql b/src/server/sql/meter/get_meter_by_identifier.sql new file mode 100644 index 000000000..3c4f22dd8 --- /dev/null +++ b/src/server/sql/meter/get_meter_by_identifier.sql @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +SELECT * FROM meters WHERE identifier=${identifier}; diff --git a/src/server/test/web/csvPipelineTest.js b/src/server/test/web/csvPipelineTest.js index 0366dec06..7abc7fcea 100644 --- a/src/server/test/web/csvPipelineTest.js +++ b/src/server/test/web/csvPipelineTest.js @@ -9,7 +9,6 @@ const Point = require('../../models/Point'); const Unit = require('../../models/Unit'); const { insertStandardUnits, insertStandardConversions, insertUnits, insertConversions } = require('../../util/insertData') const { redoCik } = require('../../services/graph/redoCik'); -const { BooleanTypesJS } = require('../../services/csvPipeline/validateCsvUploadParams'); const util = require('util'); const fs = require('fs'); const csv = require('csv'); @@ -42,189 +41,216 @@ const CHAI_METERS_REQUEST_EMAIL = `chai.request(app).post('${UPLOAD_METERS_ROUTE const testCases = { pipe1: { description: 'Ascending time readings', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('createMeter', BooleanTypesJS.true).field('meterName', 'pipe1').field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier', 'pipe1').field('gzip', false)"], fileName: ['pipe1Input.csv'], + createMeter: true, responseCode: [200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

'] }, pipe2: { description: 'Descending time readings', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('createMeter', BooleanTypesJS.true).field('meterName', 'pipe2').field('gzip', BooleanTypesJS.false).field('timeSort', 'decreasing')"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier', 'pipe2').field('gzip', false).field('timeSort', 'decreasing')"], fileName: ['pipe2Input.csv'], + createMeter: true, responseCode: [200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

'] }, pipe3: { description: 'Cumulative time readings', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('createMeter', BooleanTypesJS.true).field('meterName', 'pipe3').field('gzip', BooleanTypesJS.false).field('cumulative', BooleanTypesJS.true)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier', 'pipe3').field('gzip', false).field('cumulative', true)"], fileName: ['pipe3Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe3: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe3 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe3
'] }, pipe4: { description: 'Cumulative, descending time readings', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('timeSort','decreasing').field('cumulative',BooleanTypesJS.true).field('meterName','pipe4').field('createMeter',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('timeSort','decreasing').field('cumulative',true).field('meterIdentifier','pipe4').field('gzip', false)"], fileName: ['pipe4Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe4: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe4 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort decreasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe4
'] }, pipe5: { description: 'Cumulative time readings with reset with default cumulative reset', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('createMeter', BooleanTypesJS.true).field('meterName', 'pipe5').field('gzip', BooleanTypesJS.false).field('cumulative', BooleanTypesJS.true).field('cumulativeReset',BooleanTypesJS.true)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier', 'pipe5').field('gzip', false).field('cumulative', true).field('cumulativeReset',true)"], + createMeter: true, fileName: ['pipe5Input.csv'], responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe5: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe5 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe5
'] }, pipe6: { description: 'Cumulative time readings with reset with cumulative reset around midnight', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('cumulativeReset',BooleanTypesJS.true).field('cumulativeResetStart','23:45').field('cumulativeResetEnd','00:15').field('meterName','pipe6').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('cumulativeReset',true).field('cumulativeResetStart','23:45').field('cumulativeResetEnd','00:15').field('meterIdentifier','pipe6').field('gzip',false)"], fileName: ['pipe6Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe6: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe6 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 23:45; cumulativeResetEnd 00:15; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe6
'] }, pipe7: { description: 'Cumulative time readings with reset with cumulative reset around noon which is incorrect', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('createMeter', BooleanTypesJS.true).field('meterName', 'pipe7').field('gzip', BooleanTypesJS.false).field('cumulative', BooleanTypesJS.true).field('cumulativeReset',BooleanTypesJS.true).field('cumulativeResetStart','11:45').field('cumulativeResetEnd','12:15')"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier', 'pipe7').field('gzip', false).field('cumulative', true).field('cumulativeReset',true).field('cumulativeResetStart','11:45').field('cumulativeResetEnd','12:15')"], fileName: ['pipe7Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe7: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe7 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 11:45; cumulativeResetEnd 12:15; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe7: Error parsing Reading #4. Reading value of 96 gives -48 with error message:
A negative meterReading has been detected but either cumulativeReset is not enabled, or the start time and end time of this reading is out of the reset range. Reject all readings.
For reading #4 on meter pipe7 in pipeline: previous reading has value 72 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z and current reading has value -48 start time 2021-06-04T00:00:00Z end time 2021-06-05T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 11:45; cumulativeResetEnd 12:15; lengthGap 0; lengthVariation 0; onlyEndTime false
'], }, pipe8: { description: 'Cumulative time readings with reset with cumulative reset tight around midnight', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('cumulativeReset',BooleanTypesJS.true).field('cumulativeResetStart','00:00').field('cumulativeResetEnd','00:00.001').field('meterName','pipe8').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('cumulativeReset',true).field('cumulativeResetStart','00:00').field('cumulativeResetEnd','00:00.001').field('meterIdentifier','pipe8').field('gzip',false)"], fileName: ['pipe8Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe8: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe8 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 00:00; cumulativeResetEnd 00:00.001; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe8
'] }, pipe9: { description: 'Cumulative time readings with reset without cumulative reset which is incorrect', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('meterName','pipe9').field('createMeter',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('meterIdentifier','pipe9').field('gzip', false)"], fileName: ['pipe9Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe9: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe9 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe9: Error parsing Reading #4. Reading value of 96 gives -48 with error message:
A negative meterReading has been detected but either cumulativeReset is not enabled, or the start time and end time of this reading is out of the reset range. Reject all readings.
For reading #4 on meter pipe9 in pipeline: previous reading has value 72 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z and current reading has value -48 start time 2021-06-04T00:00:00Z end time 2021-06-05T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe10: { description: 'Cumulative time readings changing at noon with default cumulative reset', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('cumulativeReset',BooleanTypesJS.true).field('meterName','pipe10').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('cumulativeReset',true).field('meterIdentifier','pipe10').field('gzip',false)"], fileName: ['pipe10Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe10: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe10 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T12:00:00Z end time 2021-06-02T12:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe10
'] }, pipe11: { description: 'Cumulative time readings changing at noon without cumulative reset which is incorrect', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('meterName','pipe11').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('meterIdentifier','pipe11').field('gzip',false)"], fileName: ['pipe11Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe11: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe11 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T12:00:00Z end time 2021-06-02T12:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe11: Error parsing Reading #4. Reading value of 96 gives -48 with error message:
A negative meterReading has been detected but either cumulativeReset is not enabled, or the start time and end time of this reading is out of the reset range. Reject all readings.
For reading #4 on meter pipe11 in pipeline: previous reading has value 72 start time 2021-06-03T12:00:00Z end time 2021-06-04T12:00:00Z and current reading has value -48 start time 2021-06-04T12:00:00Z end time 2021-06-05T12:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe12: { description: 'Cumulative time readings changing at noon with cumulative reset at noon', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('cumulativeReset',BooleanTypesJS.true).field('cumulativeResetStart','11:45').field('cumulativeResetEnd','12:15').field('meterName','pipe12').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('cumulativeReset',true).field('cumulativeResetStart','11:45').field('cumulativeResetEnd','12:15').field('meterIdentifier','pipe12').field('gzip',false)"], fileName: ['pipe12Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe12: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe12 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T12:00:00Z end time 2021-06-02T12:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 11:45; cumulativeResetEnd 12:15; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe12
'] }, pipe13: { description: 'Cumulative time readings changing at noon with cumulative reset at midnight which is incorrect', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('cumulativeReset',BooleanTypesJS.true).field('cumulativeResetStart','23:45').field('cumulativeResetEnd','00:15').field('meterName','pipe13').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('cumulativeReset',true).field('cumulativeResetStart','23:45').field('cumulativeResetEnd','00:15').field('meterIdentifier','pipe13').field('gzip',false)"], fileName: ['pipe13Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe13: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe13 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T12:00:00Z end time 2021-06-02T12:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 23:45; cumulativeResetEnd 00:15; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe13: Error parsing Reading #4. Reading value of 96 gives -48 with error message:
A negative meterReading has been detected but either cumulativeReset is not enabled, or the start time and end time of this reading is out of the reset range. Reject all readings.
For reading #4 on meter pipe13 in pipeline: previous reading has value 72 start time 2021-06-03T12:00:00Z end time 2021-06-04T12:00:00Z and current reading has value -48 start time 2021-06-04T12:00:00Z end time 2021-06-05T12:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 23:45; cumulativeResetEnd 00:15; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe14: { description: 'Ascending time readings with length variation and default time variation', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe14').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe14').field('gzip',false)"], fileName: ['pipe14Input.csv'], + createMeter: true, responseCode: [200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe14: Warning parsing Reading #2. Reading value gives 48 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 0 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #2 on meter pipe14 in pipeline: previous reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z and current reading has value 48 start time 2021-06-02T00:00:00Z end time 2021-06-03T00:01:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe14: Warning parsing Reading #3. Reading value gives 72 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 0 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #3 on meter pipe14 in pipeline: previous reading has value 48 start time 2021-06-02T00:00:00Z end time 2021-06-03T00:01:00Z and current reading has value 72 start time 2021-06-03T00:01:00Z end time 2021-06-04T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe14: Warning parsing Reading #4. Reading value gives 96 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 0 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #4 on meter pipe14 in pipeline: previous reading has value 72 start time 2021-06-03T00:01:00Z end time 2021-06-04T00:00:00Z and current reading has value 96 start time 2021-06-04T00:00:00Z end time 2021-06-04T23:58:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe14: Warning parsing Reading #5. Reading value gives 120 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 0 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #5 on meter pipe14 in pipeline: previous reading has value 96 start time 2021-06-04T00:00:00Z end time 2021-06-04T23:58:00Z and current reading has value 120 start time 2021-06-04T23:58:00Z end time 2021-06-06T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe15: { description: 'Ascending time readings with length variation where length variation set small so warns on 2 readings', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe15').field('lengthVariation','60').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe15').field('lengthVariation','60').field('gzip',false)"], fileName: ['pipe15Input.csv'], + createMeter: true, responseCode: [200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe15: Warning parsing Reading #3. Reading value gives 72 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 60 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #3 on meter pipe15 in pipeline: previous reading has value 48 start time 2021-06-02T00:00:00Z end time 2021-06-03T00:01:00Z and current reading has value 72 start time 2021-06-03T00:01:00Z end time 2021-06-04T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 60; onlyEndTime false

For meter pipe15: Warning parsing Reading #5. Reading value gives 120 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 60 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #5 on meter pipe15 in pipeline: previous reading has value 96 start time 2021-06-04T00:00:00Z end time 2021-06-04T23:58:00Z and current reading has value 120 start time 2021-06-04T23:58:00Z end time 2021-06-06T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 60; onlyEndTime false
'] }, pipe16: { description: 'Ascending time readings with length variation and length variation set so warns on 1 reading', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe16').field('lengthVariation','120').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe16').field('lengthVariation','120').field('gzip',false)"], fileName: ['pipe16Input.csv'], + createMeter: true, responseCode: [200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe16: Warning parsing Reading #5. Reading value gives 120 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 120 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #5 on meter pipe16 in pipeline: previous reading has value 96 start time 2021-06-04T00:00:00Z end time 2021-06-04T23:58:00Z and current reading has value 120 start time 2021-06-04T23:58:00Z end time 2021-06-06T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 120; onlyEndTime false
'] }, pipe17: { description: 'Ascending time readings with length variation and gap where length variation set so all pass but gap not big enough so warns on 2 reading', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe17').field('lengthVariation','121').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe17').field('lengthVariation','121').field('gzip',false)"], fileName: ['pipe17Input.csv'], + createMeter: true, responseCode: [200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe17: Warning parsing Reading #2. Reading value gives 48 with warning message:
There is a gap in time between this reading and the previous reading that exceeds the allowed amount of 0 seconds.
For reading #2 on meter pipe17 in pipeline: previous reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z and current reading has value 48 start time 2021-06-02T00:01:00Z end time 2021-06-03T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 121; onlyEndTime false

For meter pipe17: Warning parsing Reading #4. Reading value gives 96 with warning message:
There is a gap in time between this reading and the previous reading that exceeds the allowed amount of 0 seconds.
For reading #4 on meter pipe17 in pipeline: previous reading has value 72 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z and current reading has value 96 start time 2021-06-04T00:02:00Z end time 2021-06-05T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 121; onlyEndTime false
'] }, pipe18: { description: 'Ascending time readings with gaps and small time gap so 1 passes and 1 warns', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe18').field('lengthGap','60').field('lengthVariation','121').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe18').field('lengthGap','60').field('lengthVariation','121').field('gzip',false)"], fileName: ['pipe18Input.csv'], + createMeter: true, responseCode: [200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe18: Warning parsing Reading #4. Reading value gives 96 with warning message:
There is a gap in time between this reading and the previous reading that exceeds the allowed amount of 60 seconds.
For reading #4 on meter pipe18 in pipeline: previous reading has value 72 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z and current reading has value 96 start time 2021-06-04T00:02:00Z end time 2021-06-05T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 60; lengthVariation 121; onlyEndTime false
'] }, pipe19: { description: 'Ascending time readings with gap and just right size time gap', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe19').field('lengthGap','120').field('lengthVariation','121').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe19').field('lengthGap','120').field('lengthVariation','121').field('gzip',false)"], fileName: ['pipe19Input.csv'], + createMeter: true, responseCode: [200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

'] }, pipe20: { description: 'Cumulative time readings with header', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('cumulative',BooleanTypesJS.true).field('meterName','pipe20').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('headerRow',true).field('cumulative',true).field('meterIdentifier','pipe20').field('gzip',false)"], fileName: ['pipe20Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe20: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe20 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe20
'] }, pipe21: { description: 'Cumulative time readings with duplication', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('duplications','3').field('cumulative',BooleanTypesJS.true).field('meterName','pipe21').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('duplications','3').field('cumulative',true).field('meterIdentifier','pipe21').field('gzip',false)"], fileName: ['pipe21Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe21: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe21 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 3; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe21
'] }, pipe22: { description: 'Cumulative time readings with default cumulative reset with negative reading which in incorrect', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('cumulativeReset',BooleanTypesJS.true).field('meterName','pipe22').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('cumulativeReset',true).field('meterIdentifier','pipe22').field('gzip',false)"], fileName: ['pipe22Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe22: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe22 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
For meter pipe22:
Error parsing Reading #4. Detected a negative value while handling cumulative readings so all reading are rejected.
For reading #4 on meter pipe22 in pipeline: previous reading has value 72 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z and current reading has value -145 start time 2021-06-04T00:00:00Z end time 2021-06-05T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe23: { description: 'Ascending time readings that are end only', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('endOnly',BooleanTypesJS.true).field('meterName','pipe23').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('endOnly',true).field('meterIdentifier','pipe23').field('gzip',false)"], fileName: ['pipe23Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe23: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing only with endTimestamps.
For reading #1 on meter pipe23 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 1970-01-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe23
'] }, pipe24: { description: 'Descending time readings that are end only', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('timeSort','decreasing').field('endOnly',BooleanTypesJS.true).field('meterName','pipe24').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('timeSort','decreasing').field('endOnly',true).field('meterIdentifier','pipe24').field('gzip',false)"], fileName: ['pipe24Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe24: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing only with endTimestamps.
For reading #1 on meter pipe24 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 1970-01-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort decreasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe24
'] }, pipe25: { description: 'Descending, cumulative time readings that are end only', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('timeSort','decreasing').field('endOnly',BooleanTypesJS.true).field('meterName','pipe25').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('timeSort','decreasing').field('endOnly',true).field('meterIdentifier','pipe25').field('gzip',false)"], fileName: ['pipe25Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe25: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
The first ever reading must be dropped when dealing only with endTimestamps.
For reading #1 on meter pipe25 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 1970-01-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort decreasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe25
'] }, pipe26: { description: 'Ascending time readings with bad start date/time which is incorrect', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe26').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe26').field('gzip',false)"], fileName: ['pipe26Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):

For meter pipe26: Error parsing Reading #2 The start (2021-06-02 00:00:00 x) and/or end time (2021-06-03 00:00:00) provided did not parse into a valid date/time so all reading are rejected.
For reading #2 on meter pipe26 in pipeline: previous reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z and current reading has value unknown start time Invalid date end time 2021-06-03T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe27: { description: 'Ascending time readings with bad end date/time which is incorrect', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe27').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe27').field('gzip',false)"], + createMeter: true, fileName: ['pipe27Input.csv'], responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):

For meter pipe27: Error parsing Reading #2 The start (2021-06-02 00:00:00) and/or end time (2021-06-32 00:00:00) provided did not parse into a valid date/time so all reading are rejected.
For reading #2 on meter pipe27 in pipeline: previous reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z and current reading has value unknown start time 2021-06-02T00:00:00Z end time Invalid date with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] @@ -232,255 +258,268 @@ const testCases = { // Pipe28 removed since stronger tests on dates cause it to fail. pipe29: { description: 'Cumulative time readings with bad reading value which in incorrect', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('meterName','pipe29').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('meterIdentifier','pipe29').field('gzip',false)"], fileName: ['pipe29Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe29: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe29 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
For meter pipe29: Error parsing Reading #4 with cumulative data. The reading value provided of 240.x is not considered a number so all reading are rejected.
'] }, pipe30: { description: 'Ascending time readings with bad reading value which in incorrect', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe30').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe30').field('gzip',false)"], fileName: ['pipe30Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):

For meter pipe30: Error parsing Reading #4 The reading value provided of 9a is not considered a number so all reading are rejected.
'] }, pipe31: { description: 'Cumulative time readings with gaps with length variation but still needs to drop 2 readings with gap', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe31').field('cumulative',BooleanTypesJS.true).field('lengthVariation','121').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe31').field('cumulative',true).field('lengthVariation','121').field('gzip',false)"], fileName: ['pipe31Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe31: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe31 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 121; onlyEndTime false

For meter pipe31: Error parsing Reading #2. Reading value gives 48 with error message:
The end of the previous reading is too far from the start of the next readings in cumulative data so drop this reading.
For reading #2 on meter pipe31 in pipeline: previous reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z and current reading has value 48 start time 2021-06-02T00:01:00Z end time 2021-06-03T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 121; onlyEndTime false

For meter pipe31: Error parsing Reading #4. Reading value gives 96 with error message:
The end of the previous reading is too far from the start of the next readings in cumulative data so drop this reading.
For reading #4 on meter pipe31 in pipeline: previous reading has value 72 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z and current reading has value 96 start time 2021-06-04T00:02:00Z end time 2021-06-05T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 121; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe31
  2. Dropped Reading #2 for meter pipe31
  3. Dropped Reading #4 for meter pipe31
'] }, pipe32: { description: 'Cumulative time readings with one reading start before end of previous so dropped', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe32').field('cumulative',BooleanTypesJS.true).field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe32').field('cumulative',true).field('gzip',false)"], fileName: ['pipe32Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe32: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe32 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe32: Error parsing Reading #2. Reading value gives 48 with error message:
The reading start time is before the previous end time and the data is cumulative so OED cannot use this reading.
For reading #2 on meter pipe32 in pipeline: previous reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z and current reading has value 48 start time 2021-06-01T23:59:59Z end time 2021-06-03T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe32: Warning parsing Reading #3. Reading value gives 72 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 0 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #3 on meter pipe32 in pipeline: previous reading has value 48 start time 2021-06-01T23:59:59Z end time 2021-06-03T00:00:00Z and current reading has value 72 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe32
  2. Dropped Reading #2 for meter pipe32
'] }, pipe33: { description: 'Cumulative time readings with negative reading which is incorrect', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe33').field('cumulative',BooleanTypesJS.true).field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe33').field('cumulative',true).field('gzip',false)"], fileName: ['pipe33Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe33: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe33 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
For meter pipe33:
Error parsing Reading #3. Detected a negative value while handling cumulative readings so all reading are rejected.
For reading #3 on meter pipe33 in pipeline: previous reading has value 48 start time 2021-06-02T00:00:00Z end time 2021-06-03T00:00:00Z and current reading has value -73 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe34: { description: 'Ascending time readings with one reading start/end the same so dropped', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe34').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe34').field('gzip',false)"], fileName: ['pipe34Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe34: Error parsing Reading #2. Reading value gives 48 with error message:
The reading end time is not after the start time so we must drop the reading.
For reading #2 on meter pipe34 in pipeline: previous reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z and current reading has value 48 start time 2021-06-02T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe34: Warning parsing Reading #3. Reading value gives 72 with warning message:
There is a gap in time between this reading and the previous reading that exceeds the allowed amount of 0 seconds.
The previous reading has a different time length than the current reading and exceeds the tolerance of 0 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #3 on meter pipe34 in pipeline: previous reading has value 48 start time 2021-06-02T00:00:00Z end time 2021-06-02T00:00:00Z and current reading has value 72 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #2 for meter pipe34
'] }, pipe35: { description: 'Ascending time readings that are end only with two readings time the same so dropped', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('refreshReadings',BooleanTypesJS.true).field('meterName','pipe35').field('endOnly',BooleanTypesJS.true).field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('refreshReadings',true).field('meterIdentifier','pipe35').field('endOnly',true).field('gzip',false)"], fileName: ['pipe35Input.csv'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe35: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing only with endTimestamps.
For reading #1 on meter pipe35 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 1970-01-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

For meter pipe35: Error parsing Reading #2. Reading value gives 48 with error message:
The reading end time is not after the start time so we must drop the reading. The start time came from the previous readings end time.
For reading #2 on meter pipe35 in pipeline: previous reading has value 24 start time 1970-01-01T00:00:00Z end time 2021-06-02T00:00:00Z and current reading has value 48 start time 2021-06-02T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

For meter pipe35: Warning parsing Reading #3. Reading value gives 72 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 0 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #3 on meter pipe35 in pipeline: previous reading has value 48 start time 2021-06-02T00:00:00Z end time 2021-06-02T00:00:00Z and current reading has value 72 start time 2021-06-02T00:00:00Z end time 2021-06-04T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

For meter pipe35: Warning parsing Reading #4. Reading value gives 96 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 0 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #4 on meter pipe35 in pipeline: previous reading has value 72 start time 2021-06-02T00:00:00Z end time 2021-06-04T00:00:00Z and current reading has value 96 start time 2021-06-04T00:00:00Z end time 2021-06-05T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe35
  2. Dropped Reading #2 for meter pipe35
'] }, pipe40: { description: 'Cumulative time zipped readings with header', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('cumulative',BooleanTypesJS.true).field('createMeter',BooleanTypesJS.true).field('meterName', 'pipe40')"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('headerRow',true).field('cumulative',true).field('meterIdentifier', 'pipe40')"], fileName: ['pipe40Input.csv.gz'], + createMeter: true, responseCode: [400], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe40: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe40 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe40
'] }, pipe50: { description: 'Ascending time readings with two readings uploads where update readings', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('createMeter',BooleanTypesJS.true).field('meterName', 'pipe50')", CHAI_READINGS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('update',BooleanTypesJS.true).field('meterName', 'pipe50')"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('gzip', false).field('meterIdentifier', 'pipe50')", CHAI_READINGS_REQUEST + ".field('gzip', false).field('update',true).field('meterIdentifier', 'pipe50')"], fileName: ['pipe50AInput.csv', 'pipe50BInput.csv'], + createMeter: true, responseCode: [200, 200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

', '

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe50: Warning parsing Reading #1. Reading value gives 0 with warning message:
The current reading startTime is not after the previous reading\'s end time. Note this is treated only as a warning since readings may be sent out of order.
There is a gap in time between this reading and the previous reading that exceeds the allowed amount of 0 seconds.
For reading #1 on meter pipe50 in pipeline: previous reading has value 120 start time 2021-06-05T00:00:00Z end time 2021-06-06T00:00:00Z and current reading has value 0 start time 2021-05-31T00:00:00Z end time 2021-06-01T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe51: { description: 'Ascending time readings with two readings uploads without update so no changes', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterName','pipe51').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe51').field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe51').field('gzip',false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe51').field('gzip',false)"], fileName: ['pipe51AInput.csv', 'pipe51BInput.csv'], + createMeter: true, responseCode: [200, 200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

', '

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe51: Warning parsing Reading #1. Reading value gives 0 with warning message:
The current reading startTime is not after the previous reading\'s end time. Note this is treated only as a warning since readings may be sent out of order.
There is a gap in time between this reading and the previous reading that exceeds the allowed amount of 0 seconds.
For reading #1 on meter pipe51 in pipeline: previous reading has value 120 start time 2021-06-05T00:00:00Z end time 2021-06-06T00:00:00Z and current reading has value 0 start time 2021-05-31T00:00:00Z end time 2021-06-01T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe52: { description: 'Cumulative time readings with default reset with two uploads to add more readings with a reset', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('meterName','pipe52').field('createMeter',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('cumulativeReset',BooleanTypesJS.true).field('meterName','pipe52').field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('cumulative',true).field('meterIdentifier','pipe52').field('gzip',false)", CHAI_READINGS_REQUEST + ".field('cumulative',true).field('cumulativeReset',true).field('meterIdentifier','pipe52').field('gzip',false)"], fileName: ['pipe52AInput.csv', 'pipe52BInput.csv'], + createMeter: true, responseCode: [400, 200], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe52: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe52 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe52
', '

SUCCESS

It looks like the insert of the readings was a success.

'] }, pipe60: { description: 'Cumulative time readings with three readings uploads', - chaiRequest: [CHAI_READINGS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('createMeter',BooleanTypesJS.true).field('meterName', 'pipe60').field('cumulative', BooleanTypesJS.true)", CHAI_READINGS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('meterName', 'pipe60').field('cumulative', BooleanTypesJS.true)", CHAI_READINGS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('meterName', 'pipe60').field('cumulative', BooleanTypesJS.true)"], + chaiRequest: [CHAI_READINGS_REQUEST + ".field('gzip', false).field('meterIdentifier', 'pipe60').field('cumulative', true)", CHAI_READINGS_REQUEST + ".field('gzip', false).field('meterIdentifier', 'pipe60').field('cumulative', true)", CHAI_READINGS_REQUEST + ".field('gzip', false).field('meterIdentifier', 'pipe60').field('cumulative', true)"], fileName: ['pipe60AInput.csv', 'pipe60BInput.csv', 'pipe60CInput.csv'], + createMeter: true, responseCode: [400, 200, 200], responseString: ['

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe60: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe60 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe60
', '

SUCCESS

It looks like the insert of the readings was a success.

', '

SUCCESS

It looks like the insert of the readings was a success.

'] }, pipe70: { description: 'Create meter cumulative reset around noon with reading upload with reset at midnight which is incorrect', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe70').field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip',false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe70').field('gzip',false)"], fileName: ['pipe70AInputMeter.csv', 'pipe70BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe70: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe70 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 11:45:00; cumulativeResetEnd 12:15:00; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe70: Error parsing Reading #4. Reading value of 96 gives -48 with error message:
A negative meterReading has been detected but either cumulativeReset is not enabled, or the start time and end time of this reading is out of the reset range. Reject all readings.
For reading #4 on meter pipe70 in pipeline: previous reading has value 72 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z and current reading has value -48 start time 2021-06-04T00:00:00Z end time 2021-06-05T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 11:45:00; cumulativeResetEnd 12:15:00; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe71: { description: 'Create meter cumulative reset around midnight with reading upload with reset at midnight', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('cumulativeReset',BooleanTypesJS.true).field('cumulativeResetStart','23:45').field('cumulativeResetEnd','00:15').field('meterName','pipe71').field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip',false)", CHAI_READINGS_REQUEST + ".field('cumulative',true).field('cumulativeReset',true).field('cumulativeResetStart','23:45').field('cumulativeResetEnd','00:15').field('meterIdentifier','pipe71').field('gzip',false)"], fileName: ['pipe71AInputMeter.csv', 'pipe71BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe71: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe71 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 23:45; cumulativeResetEnd 00:15; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe71
'] }, pipe72: { description: 'Create meter with modest length variation and gap with reading upload where warn on larger gaps', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe72').field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip',false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe72').field('gzip',false)"], fileName: ['pipe72AInputMeter.csv', 'pipe72BInput.csv'], responseCode: [200, 200], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe72: Warning parsing Reading #4. Reading value gives 96 with warning message:
There is a gap in time between this reading and the previous reading that exceeds the allowed amount of 60 seconds.
For reading #4 on meter pipe72 in pipeline: previous reading has value 72 start time 2021-06-03T00:00:00Z end time 2021-06-04T00:00:00Z and current reading has value 96 start time 2021-06-04T00:02:00Z end time 2021-06-05T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 60; lengthVariation 120; onlyEndTime false

For meter pipe72: Warning parsing Reading #5. Reading value gives 120 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 120 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #5 on meter pipe72 in pipeline: previous reading has value 96 start time 2021-06-04T00:02:00Z end time 2021-06-05T00:00:00Z and current reading has value 120 start time 2021-06-05T00:00:00Z end time 2021-06-06T00:04:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 60; lengthVariation 120; onlyEndTime false
'] }, pipe73: { description: 'Create meter with modest length variation and gap with reading upload that changes these values so fewer warnings; also checks floating point values in reading upload', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe73').field('lengthGap','120.1').field('lengthVariation','120.2').field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip',false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe73').field('lengthGap','120.1').field('lengthVariation','120.2').field('gzip',false)"], fileName: ['pipe73AInputMeter.csv', 'pipe73BInput.csv'], responseCode: [200, 200], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe73: Warning parsing Reading #5. Reading value gives 120 with warning message:
The previous reading has a different time length than the current reading and exceeds the tolerance of 120.2 seconds. Note this is treated only as a warning since this may be expected for certain meters.
For reading #5 on meter pipe73 in pipeline: previous reading has value 96 start time 2021-06-04T00:02:00Z end time 2021-06-05T00:00:00Z and current reading has value 120 start time 2021-06-05T00:00:00Z end time 2021-06-06T00:04:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 120.1; lengthVariation 120.2; onlyEndTime false
'] }, pipe74: { description: 'Create meter with duplication with reading upload', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('cumulative',BooleanTypesJS.true).field('meterName','pipe74').field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip',false)", CHAI_READINGS_REQUEST + ".field('cumulative',true).field('meterIdentifier','pipe74').field('gzip',false)"], fileName: ['pipe74AInputMeter.csv', 'pipe74BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe74: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe74 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 3; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe74
'] }, pipe75: { description: 'Create meter with decreasing with reading upload', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe75').field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe75').field('gzip', false)"], fileName: ['pipe75AInputMeter.csv', 'pipe75BInput.csv'], responseCode: [200, 200], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

It looks like the insert of the readings was a success.

'] }, pipe76: { description: 'Create meter with end only with reading upload', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip',BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe76').field('gzip',BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip',false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe76').field('gzip',false)"], fileName: ['pipe76AInputMeter.csv', 'pipe76BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe76: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing only with endTimestamps.
For reading #1 on meter pipe76 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 1970-01-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe76
'] }, pipe80: { description: 'Two meter uploads where second sets cumulative with reading upload; all without headers', - chaiRequest: [CHAI_METERS_REQUEST + ".field('gzip', BooleanTypesJS.false)", CHAI_METERS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('update',BooleanTypesJS.true).field('meterName', 'pipe80')", CHAI_READINGS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('meterName', 'pipe80')"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('gzip', false)", CHAI_METERS_REQUEST + ".field('gzip', false).field('update',true).field('meterIdentifier', 'pipe80')", CHAI_READINGS_REQUEST + ".field('gzip', false).field('meterIdentifier', 'pipe80')"], fileName: ['pipe80AInputMeter.csv', 'pipe80BInputMeter.csv', 'pipe80CInput.csv'], responseCode: [200, 200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe80: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe80 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe80
'] }, pipe90: { description: 'Two meter uploads with header and zipped where second sets cumulative & reset, renames meter then reading upload', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true)", CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('update',BooleanTypesJS.true).field('meterName', 'pipe90x')", CHAI_READINGS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('meterName', 'pipe90')"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true)", CHAI_METERS_REQUEST + ".field('headerRow',true).field('update',true).field('meterIdentifier', 'pipe90x')", CHAI_READINGS_REQUEST + ".field('gzip', false).field('meterIdentifier', 'pipe90')"], fileName: ['pipe90AInputMeter.csv.gz', 'pipe90BInputMeter.csv.gz', 'pipe90CInput.csv'], responseCode: [200, 200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe90: Error parsing Reading #1. Reading value gives 24 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe90 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 24 start time 2021-06-01T00:00:00Z end time 2021-06-02T00:00:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset true; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe90
'] }, pipe110: { description: 'Create meter with timezone with hourly reading upload into DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe110').field('honorDst', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe110').field('honorDst', true).field('gzip', false)"], fileName: ['pipe110AInputMeter.csv', 'pipe110BInput.csv'], responseCode: [200, 200], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe110: Warning parsing Reading #2. Reading value gives 120 with warning message:
Reading #2 crossed into daylight savings so it needs to be split where the first part is now being used. The original reading had startTimestamp of 2022-03-13T01:00:00-06:00 endTimestamp of 2022-03-13T03:00:00-05:00 reading value of 120 and the first part has a startTimestamp of 2022-03-13T01:00:00Z endTimestamp of 2022-03-13T02:00:00Z reading value of 120. This is only a notification and should not be an issue.
For reading #2 on meter pipe110 in pipeline: previous reading has value 60 start time 2022-03-13T00:00:00Z end time 2022-03-13T01:00:00Z and current reading has value 120 start time 2022-03-13T02:00:00Z end time 2022-03-13T03:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe111: { description: 'Create meter with timezone with daily reading upload into DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe111').field('honorDst', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe111').field('honorDst', true).field('gzip', false)"], fileName: ['pipe111AInputMeter.csv', 'pipe111BInput.csv'], responseCode: [200, 200], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe111: Warning parsing Reading #2. Reading value gives 2760 with warning message:
Reading #2 crossed into daylight savings so it needs to be split where the first part is now being used. The original reading had startTimestamp of 2022-03-13T00:00:00-06:00 endTimestamp of 2022-03-14T00:00:00-05:00 reading value of 2760 and the first part has a startTimestamp of 2022-03-13T00:00:00Z endTimestamp of 2022-03-13T02:00:00Z reading value of 240. This is only a notification and should not be an issue.
Reading #2 crossed into daylight savings so it needs to be split where the second part is now being used. The original reading had startTimestamp of 2022-03-13T00:00:00-06:00 endTimestamp of 2022-03-14T00:00:00-05:00 reading value of 2760 and the second part has a startTimestamp of 2022-03-13T03:00:00Z endTimestamp of 2022-03-14T00:00:00Z reading value of 2520. This is only a notification and should not be an issue.
For reading #2 on meter pipe111 in pipeline: previous reading has value 1440 start time 2022-03-12T00:00:00Z end time 2022-03-13T00:00:00Z and current reading has value 2760 start time 2022-03-13T00:00:00Z end time 2022-03-14T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe112: { description: 'Create meter with timezone with 15-minute reading upload into DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe112').field('honorDst', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe112').field('honorDst', true).field('gzip', false)"], fileName: ['pipe112AInputMeter.csv', 'pipe112BInput.csv'], responseCode: [200, 200], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe112: Warning parsing Reading #2. Reading value gives 30 with warning message:
Reading #2 crossed into daylight savings so it needs to be split where the first part is now being used. The original reading had startTimestamp of 2022-03-13T01:45:00-06:00 endTimestamp of 2022-03-13T03:00:00-05:00 reading value of 30 and the first part has a startTimestamp of 2022-03-13T01:45:00Z endTimestamp of 2022-03-13T02:00:00Z reading value of 30. This is only a notification and should not be an issue.
For reading #2 on meter pipe112 in pipeline: previous reading has value 15 start time 2022-03-13T01:30:00Z end time 2022-03-13T01:45:00Z and current reading has value 30 start time 2022-03-13T02:45:00Z end time 2022-03-13T03:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe113: { description: 'Create meter with timezone with 23-minute reading upload into DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe113').field('honorDst', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe113').field('honorDst', true).field('gzip', false)"], fileName: ['pipe113AInputMeter.csv', 'pipe113BInput.csv'], responseCode: [200, 200], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe113: Warning parsing Reading #2. Reading value gives 46 with warning message:
Reading #2 crossed into daylight savings so it needs to be split where the first part is now being used. The original reading had startTimestamp of 2022-03-13T01:46:00-06:00 endTimestamp of 2022-03-13T03:09:00-05:00 reading value of 46 and the first part has a startTimestamp of 2022-03-13T01:46:00Z endTimestamp of 2022-03-13T02:00:00Z reading value of 28. This is only a notification and should not be an issue.
Reading #2 crossed into daylight savings so it needs to be split where the second part is now being used. The original reading had startTimestamp of 2022-03-13T01:46:00-06:00 endTimestamp of 2022-03-13T03:09:00-05:00 reading value of 46 and the second part has a startTimestamp of 2022-03-13T03:00:00Z endTimestamp of 2022-03-13T03:09:00Z reading value of 18. This is only a notification and should not be an issue.
For reading #2 on meter pipe113 in pipeline: previous reading has value 23 start time 2022-03-13T01:23:00Z end time 2022-03-13T01:46:00Z and current reading has value 46 start time 2022-03-13T02:46:00Z end time 2022-03-13T03:09:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe114: { description: 'Create cumulative meter with timezone with 23-minute reading upload into DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe114').field('honorDst', BooleanTypesJS.true).field('cumulative',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe114').field('honorDst', true).field('cumulative',true).field('gzip', false)"], fileName: ['pipe114AInputMeter.csv', 'pipe114BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe114: Error parsing Reading #1. Reading value gives 0 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe114 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 0 start time 2022-03-13T01:00:00Z end time 2022-03-13T01:23:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe114: Warning parsing Reading #3. Reading value gives 46 with warning message:
Reading #3 crossed into daylight savings so it needs to be split where the first part is now being used. The original reading had startTimestamp of 2022-03-13T01:46:00-06:00 endTimestamp of 2022-03-13T03:09:00-05:00 reading value of 46 and the first part has a startTimestamp of 2022-03-13T01:46:00Z endTimestamp of 2022-03-13T02:00:00Z reading value of 28. This is only a notification and should not be an issue.
Reading #3 crossed into daylight savings so it needs to be split where the second part is now being used. The original reading had startTimestamp of 2022-03-13T01:46:00-06:00 endTimestamp of 2022-03-13T03:09:00-05:00 reading value of 46 and the second part has a startTimestamp of 2022-03-13T03:00:00Z endTimestamp of 2022-03-13T03:09:00Z reading value of 18. This is only a notification and should not be an issue.
For reading #3 on meter pipe114 in pipeline: previous reading has value 23 start time 2022-03-13T01:23:00Z end time 2022-03-13T01:46:00Z and current reading has value 46 start time 2022-03-13T02:46:00Z end time 2022-03-13T03:09:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe114
'] }, pipe115: { description: 'Create meter with timezone with end-only reading upload into DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe115').field('honorDst', BooleanTypesJS.true).field('endOnly', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe115').field('honorDst', true).field('endOnly', true).field('gzip', false)"], fileName: ['pipe115AInputMeter.csv', 'pipe115BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe115: Error parsing Reading #1. Reading value gives -99 with error message:
The first ever reading must be dropped when dealing only with endTimestamps.
For reading #1 on meter pipe115 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value -99 start time 1970-01-01T00:00:00Z end time 2022-03-13T01:23:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

For meter pipe115: Warning parsing Reading #3. Reading value gives 46 with warning message:
Reading #3 crossed into daylight savings so it needs to be split where the first part is now being used. The original reading had startTimestamp of 2022-03-13T01:46:00-06:00 endTimestamp of 2022-03-13T03:09:00-05:00 reading value of 46 and the first part has a startTimestamp of 2022-03-13T01:46:00Z endTimestamp of 2022-03-13T02:00:00Z reading value of 28. This is only a notification and should not be an issue.
Reading #3 crossed into daylight savings so it needs to be split where the second part is now being used. The original reading had startTimestamp of 2022-03-13T01:46:00-06:00 endTimestamp of 2022-03-13T03:09:00-05:00 reading value of 46 and the second part has a startTimestamp of 2022-03-13T03:00:00Z endTimestamp of 2022-03-13T03:09:00Z reading value of 18. This is only a notification and should not be an issue.
For reading #3 on meter pipe115 in pipeline: previous reading has value 23 start time 2022-03-13T01:23:00Z end time 2022-03-13T02:46:00Z and current reading has value 46 start time 2022-03-13T02:46:00Z end time 2022-03-13T03:09:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe115
'] }, pipe116: { description: 'Create meter with timezone with hourly reading upload from DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe116').field('honorDst', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe116').field('honorDst', true).field('gzip', false)"], fileName: ['pipe116AInputMeter.csv', 'pipe116BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe116: Error parsing Reading #2. Reading value gives -1 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:00:00-05:00 and endTimestamp of 2022-11-06T01:00:00-06:00 and value of -1. This should not be an issue but the reading is lost.
For reading #2 on meter pipe116 in pipeline: previous reading has value 60 start time 2022-11-06T00:00:00Z end time 2022-11-06T01:00:00Z and current reading has value -1 start time 2022-11-06T00:00:00Z end time 2022-11-06T01:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe116: Warning parsing Reading #3. Reading value gives 120 with warning message:
This or a previous reading crossed from daylight savings time and is the first one that does not entirely overlap a previous reading so its reading will be prorated where the original values were: startTimestamp of 2022-11-06T01:00:00-06:00 endTimestamp of 2022-11-06T02:00:00-06:00 reading value of 120. The used part has startTimestamp of 2022-11-06T01:00:00Z and endTimestamp of 2022-11-06T02:00:00Z and value of 120. This is only a notification and should not be an issue.
For reading #3 on meter pipe116 in pipeline: previous reading has value -1 start time 2022-11-06T00:00:00Z end time 2022-11-06T01:00:00Z and current reading has value 120 start time 2022-11-06T01:00:00Z end time 2022-11-06T02:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #2 for meter pipe116
'] }, pipe117: { description: 'Create meter with timezone with daily reading upload from DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe117').field('honorDst', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe117').field('honorDst', true).field('gzip', false)"], fileName: ['pipe117AInputMeter.csv', 'pipe117BInput.csv'], responseCode: [200, 200], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe117: Warning parsing Reading #2. Reading value gives 2880 with warning message:
This or a previous reading crossed from daylight savings time and is the first one that does not entirely overlap a previous reading so its reading will be prorated where the original values were: startTimestamp of 2022-11-06T00:00:00-05:00 endTimestamp of 2022-11-07T00:00:00-06:00 reading value of 3000. The used part has startTimestamp of 2022-11-06T00:00:00Z and endTimestamp of 2022-11-07T00:00:00Z and value of 2880. This is only a notification and should not be an issue.
For reading #2 on meter pipe117 in pipeline: previous reading has value 1440 start time 2022-11-05T00:00:00Z end time 2022-11-06T00:00:00Z and current reading has value 2880 start time 2022-11-06T00:00:00Z end time 2022-11-07T00:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe118: { description: 'Create meter with timezone with 15-minute reading upload from DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe118').field('honorDst', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe118').field('honorDst', true).field('gzip', false)"], fileName: ['pipe118AInputMeter.csv', 'pipe118BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe118: Error parsing Reading #2. Reading value gives -1 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:45:00-05:00 and endTimestamp of 2022-11-06T01:00:00-06:00 and value of -1. This should not be an issue but the reading is lost.
For reading #2 on meter pipe118 in pipeline: previous reading has value 15 start time 2022-11-06T01:30:00Z end time 2022-11-06T01:45:00Z and current reading has value -1 start time 2022-11-06T00:45:00Z end time 2022-11-06T01:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe118: Error parsing Reading #3. Reading value gives -2 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:00:00-06:00 and endTimestamp of 2022-11-06T01:15:00-06:00 and value of -2. This should not be an issue but the reading is lost.
For reading #3 on meter pipe118 in pipeline: previous reading has value -1 start time 2022-11-06T00:45:00Z end time 2022-11-06T01:00:00Z and current reading has value -2 start time 2022-11-06T01:00:00Z end time 2022-11-06T01:15:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe118: Error parsing Reading #4. Reading value gives -3 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:15:00-06:00 and endTimestamp of 2022-11-06T01:30:00-06:00 and value of -3. This should not be an issue but the reading is lost.
For reading #4 on meter pipe118 in pipeline: previous reading has value -2 start time 2022-11-06T01:00:00Z end time 2022-11-06T01:15:00Z and current reading has value -3 start time 2022-11-06T01:15:00Z end time 2022-11-06T01:30:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe118: Error parsing Reading #5. Reading value gives -4 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:30:00-06:00 and endTimestamp of 2022-11-06T01:45:00-06:00 and value of -4. This should not be an issue but the reading is lost.
For reading #5 on meter pipe118 in pipeline: previous reading has value -3 start time 2022-11-06T01:15:00Z end time 2022-11-06T01:30:00Z and current reading has value -4 start time 2022-11-06T01:30:00Z end time 2022-11-06T01:45:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe118: Warning parsing Reading #6. Reading value gives 30 with warning message:
This or a previous reading crossed from daylight savings time and is the first one that does not entirely overlap a previous reading so its reading will be prorated where the original values were: startTimestamp of 2022-11-06T01:45:00-06:00 endTimestamp of 2022-11-06T02:00:00-06:00 reading value of 30. The used part has startTimestamp of 2022-11-06T01:45:00Z and endTimestamp of 2022-11-06T02:00:00Z and value of 30. This is only a notification and should not be an issue.
For reading #6 on meter pipe118 in pipeline: previous reading has value -4 start time 2022-11-06T01:30:00Z end time 2022-11-06T01:45:00Z and current reading has value 30 start time 2022-11-06T01:45:00Z end time 2022-11-06T02:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #2 for meter pipe118
  2. Dropped Reading #3 for meter pipe118
  3. Dropped Reading #4 for meter pipe118
  4. Dropped Reading #5 for meter pipe118
'] }, pipe119: { description: 'Create meter with timezone with 23-minute reading upload from DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe119').field('honorDst', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe119').field('honorDst', true).field('gzip', false)"], fileName: ['pipe119AInputMeter.csv', 'pipe119BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe119: Error parsing Reading #2. Reading value gives -1 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:46:00-05:00 and endTimestamp of 2022-11-06T01:09:00-06:00 and value of -1. This should not be an issue but the reading is lost.
For reading #2 on meter pipe119 in pipeline: previous reading has value 23 start time 2022-11-06T01:23:00Z end time 2022-11-06T01:46:00Z and current reading has value -1 start time 2022-11-06T00:46:00Z end time 2022-11-06T01:09:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe119: Error parsing Reading #3. Reading value gives -2 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:09:00-06:00 and endTimestamp of 2022-11-06T01:32:00-06:00 and value of -2. This should not be an issue but the reading is lost.
For reading #3 on meter pipe119 in pipeline: previous reading has value -1 start time 2022-11-06T00:46:00Z end time 2022-11-06T01:09:00Z and current reading has value -2 start time 2022-11-06T01:09:00Z end time 2022-11-06T01:32:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe119: Warning parsing Reading #4. Reading value gives 18 with warning message:
This or a previous reading crossed from daylight savings time and is the first one that does not entirely overlap a previous reading so its reading will be prorated where the original values were: startTimestamp of 2022-11-06T01:32:00-06:00 endTimestamp of 2022-11-06T01:55:00-06:00 reading value of 46. The used part has startTimestamp of 2022-11-06T01:46:00Z and endTimestamp of 2022-11-06T01:55:00Z and value of 18. This is only a notification and should not be an issue.
For reading #4 on meter pipe119 in pipeline: previous reading has value -2 start time 2022-11-06T01:09:00Z end time 2022-11-06T01:32:00Z and current reading has value 18 start time 2022-11-06T01:32:00Z end time 2022-11-06T01:55:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #2 for meter pipe119
  2. Dropped Reading #3 for meter pipe119
'] }, pipe120: { description: 'Create cumulative meter with timezone with 23-minute reading upload from DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe120').field('honorDst', BooleanTypesJS.true).field('cumulative',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe120').field('honorDst', true).field('cumulative',true).field('gzip', false)"], fileName: ['pipe120AInputMeter.csv', 'pipe120BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe120: Error parsing Reading #1. Reading value gives 0 with error message:
The first ever reading must be dropped when dealing with cumulative data.
For reading #1 on meter pipe120 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value 0 start time 2022-11-06T01:00:00Z end time 2022-11-06T01:23:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe120: Error parsing Reading #3. Reading value gives 0 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:46:00-05:00 and endTimestamp of 2022-11-06T01:09:00-06:00 and value of 0. This should not be an issue but the reading is lost.
For reading #3 on meter pipe120 in pipeline: previous reading has value 23 start time 2022-11-06T01:23:00Z end time 2022-11-06T01:46:00Z and current reading has value 0 start time 2022-11-06T00:46:00Z end time 2022-11-06T01:09:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe120: Error parsing Reading #4. Reading value gives 0 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:09:00-06:00 and endTimestamp of 2022-11-06T01:32:00-06:00 and value of 0. This should not be an issue but the reading is lost.
For reading #4 on meter pipe120 in pipeline: previous reading has value 0 start time 2022-11-06T00:46:00Z end time 2022-11-06T01:09:00Z and current reading has value 0 start time 2022-11-06T01:09:00Z end time 2022-11-06T01:32:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

For meter pipe120: Warning parsing Reading #5. Reading value gives 18 with warning message:
This or a previous reading crossed from daylight savings time and is the first one that does not entirely overlap a previous reading so its reading will be prorated where the original values were: startTimestamp of 2022-11-06T01:32:00-06:00 endTimestamp of 2022-11-06T01:55:00-06:00 reading value of 46. The used part has startTimestamp of 2022-11-06T01:46:00Z and endTimestamp of 2022-11-06T01:55:00Z and value of 18. This is only a notification and should not be an issue.
For reading #5 on meter pipe120 in pipeline: previous reading has value 0 start time 2022-11-06T01:09:00Z end time 2022-11-06T01:32:00Z and current reading has value 18 start time 2022-11-06T01:32:00Z end time 2022-11-06T01:55:00Z with timeSort increasing; duplications 1; cumulative true; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe120
  2. Dropped Reading #3 for meter pipe120
  3. Dropped Reading #4 for meter pipe120
'] }, pipe121: { description: 'Create end-only meter with timezone with 23-minute reading upload from DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe121').field('honorDst', BooleanTypesJS.true).field('endOnly', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe121').field('honorDst', true).field('endOnly', true).field('gzip', false)"], fileName: ['pipe121AInputMeter.csv', 'pipe121BInput.csv'], responseCode: [200, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe121: Error parsing Reading #1. Reading value gives -99 with error message:
The first ever reading must be dropped when dealing only with endTimestamps.
For reading #1 on meter pipe121 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value -99 start time 1970-01-01T00:00:00Z end time 2022-11-06T01:23:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

For meter pipe121: Error parsing Reading #3. Reading value gives -1 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:46:00-05:00 and endTimestamp of 2022-11-06T01:09:00-06:00 and value of -1. This should not be an issue but the reading is lost.
For reading #3 on meter pipe121 in pipeline: previous reading has value 23 start time 2022-11-06T01:23:00Z end time 2022-11-06T01:46:00Z and current reading has value -1 start time 2022-11-06T00:46:00Z end time 2022-11-06T01:09:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

For meter pipe121: Error parsing Reading #4. Reading value gives -2 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:09:00-06:00 and endTimestamp of 2022-11-06T01:32:00-06:00 and value of -2. This should not be an issue but the reading is lost.
For reading #4 on meter pipe121 in pipeline: previous reading has value -1 start time 2022-11-06T00:46:00Z end time 2022-11-06T01:09:00Z and current reading has value -2 start time 2022-11-06T01:09:00Z end time 2022-11-06T01:32:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

For meter pipe121: Warning parsing Reading #5. Reading value gives 18 with warning message:
This or a previous reading crossed from daylight savings time and is the first one that does not entirely overlap a previous reading so its reading will be prorated where the original values were: startTimestamp of 2022-11-06T01:32:00-06:00 endTimestamp of 2022-11-06T01:55:00-06:00 reading value of 46. The used part has startTimestamp of 2022-11-06T01:46:00Z and endTimestamp of 2022-11-06T01:55:00Z and value of 18. This is only a notification and should not be an issue.
For reading #5 on meter pipe121 in pipeline: previous reading has value -2 start time 2022-11-06T01:09:00Z end time 2022-11-06T01:32:00Z and current reading has value 18 start time 2022-11-06T01:32:00Z end time 2022-11-06T01:55:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe121
  2. Dropped Reading #3 for meter pipe121
  3. Dropped Reading #4 for meter pipe121
'] }, pipe122: { description: 'Create end-only meter with timezone with two 23-minute reading upload from DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe122').field('honorDst', BooleanTypesJS.true).field('endOnly', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe122').field('honorDst', BooleanTypesJS.true).field('endOnly', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe122').field('honorDst', true).field('endOnly', true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe122').field('honorDst', true).field('endOnly', true).field('gzip', false)"], fileName: ['pipe122AInputMeter.csv', 'pipe122BInput.csv', 'pipe122CInput.csv'], responseCode: [200, 400, 400], responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe122: Error parsing Reading #1. Reading value gives -99 with error message:
The first ever reading must be dropped when dealing only with endTimestamps.
For reading #1 on meter pipe122 in pipeline: previous reading has value 0 start time 1970-01-01T00:00:00Z end time 1970-01-01T00:00:00Z and current reading has value -99 start time 1970-01-01T00:00:00Z end time 2022-11-06T01:23:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

For meter pipe122: Error parsing Reading #3. Reading value gives -1 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:46:00-05:00 and endTimestamp of 2022-11-06T01:09:00-06:00 and value of -1. This should not be an issue but the reading is lost.
For reading #3 on meter pipe122 in pipeline: previous reading has value 23 start time 2022-11-06T01:23:00Z end time 2022-11-06T01:46:00Z and current reading has value -1 start time 2022-11-06T00:46:00Z end time 2022-11-06T01:09:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe122
  2. Dropped Reading #3 for meter pipe122
', '

FAILURE

It looks like the insert of the readings had issues with some or all of the readings where the processing of the readings returned these warning(s)/error(s):


For meter pipe122: Error parsing Reading #1. Reading value gives -2 with error message:
This reading is entirely within the shift time from daylight savings to standard time so it is dropped. The dropped reading had startTimestamp of 2022-11-06T01:09:00-06:00 and endTimestamp of 2022-11-06T01:32:00-06:00 and value of -2. This should not be an issue but the reading is lost.
For reading #1 on meter pipe122 in pipeline: previous reading has value -1 start time 2022-11-06T01:46:00Z end time 2022-11-06T01:09:00Z and current reading has value -2 start time 2022-11-06T01:09:00Z end time 2022-11-06T01:32:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

For meter pipe122: Warning parsing Reading #2. Reading value gives 18 with warning message:
This or a previous reading crossed from daylight savings time and is the first one that does not entirely overlap a previous reading so its reading will be prorated where the original values were: startTimestamp of 2022-11-06T01:32:00-06:00 endTimestamp of 2022-11-06T01:55:00-06:00 reading value of 46. The used part has startTimestamp of 2022-11-06T01:46:00Z and endTimestamp of 2022-11-06T01:55:00Z and value of 18. This is only a notification and should not be an issue.
For reading #2 on meter pipe122 in pipeline: previous reading has value -2 start time 2022-11-06T01:09:00Z end time 2022-11-06T01:32:00Z and current reading has value 18 start time 2022-11-06T01:32:00Z end time 2022-11-06T01:55:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime true

Readings Dropped and should have previous messages

  1. Dropped Reading #1 for meter pipe122
'] }, pipe123: { description: 'Create meter with timezone with 30-minute reading with gap upload from DST', - chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)", CHAI_READINGS_REQUEST + ".field('meterName','pipe123').field('honorDst', BooleanTypesJS.true).field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('headerRow',true).field('gzip', false)", CHAI_READINGS_REQUEST + ".field('meterIdentifier','pipe123').field('honorDst', true).field('gzip', false)"], fileName: ['pipe123AInputMeter.csv', 'pipe123BInput.csv'], responseCode: [200, 200], responseString: ['

SUCCESS

Successfully inserted the meters.', '

SUCCESS

It looks like the insert of the readings was a success.

However, note that the processing of the readings returned these warning(s):


For meter pipe123: Warning parsing Reading #2. Reading value gives 30 with warning message:
The reading start time is shifted and within the DST shift so it is possible that the crossing to standard time was missed and readings overlap. The current reading startTime is not after the previous reading\'s end time. Note this is treated only as a warning since readings may be sent out of order.
There is a gap in time between this reading and the previous reading that exceeds the allowed amount of 0 seconds.
For reading #2 on meter pipe123 in pipeline: previous reading has value 15 start time 2022-11-06T01:25:00Z end time 2022-11-06T01:55:00Z and current reading has value 30 start time 2022-11-06T01:30:00Z end time 2022-11-06T02:00:00Z with timeSort increasing; duplications 1; cumulative false; cumulativeReset false; cumulativeResetStart 00:00:00; cumulativeResetEnd 23:59:59.999999; lengthGap 0; lengthVariation 0; onlyEndTime false
'] }, pipe130: { description: 'Testing Readings Upload using Email', - chaiRequest: [CHAI_READINGS_REQUEST_EMAIL + ".field('createMeter', BooleanTypesJS.true).field('meterName', 'pipe130').field('gzip', BooleanTypesJS.false)"], + chaiRequest: [CHAI_READINGS_REQUEST_EMAIL + ".field('meterIdentifier', 'pipe130').field('gzip', false)"], fileName: ['pipe130Input.csv'], + createMeter: true, responseCode: [200], responseString: ['

SUCCESS

It looks like the insert of the readings was a success.

'] } @@ -499,10 +538,10 @@ for (let fileKey in testCases) { name: 'Electric_Utility', identifier: '', unitRepresent: Unit.unitRepresentType.QUANTITY, - secInRate: 3600, - typeOfUnit: Unit.unitType.METER, + secInRate: 3600, + typeOfUnit: Unit.unitType.METER, suffix: '', - displayable: Unit.displayableType.NONE, + displayable: Unit.displayableType.NONE, preferredDisplay: false, note: 'for testing' } @@ -529,10 +568,24 @@ for (let fileKey in testCases) { let expectedFile = `${fileKey}Expected.csv`; let expectedPath = `${__dirname}/csvPipeline/${expectedFile}`; let expectedBuffer = fs.readFileSync(expectedPath); + const conn = testDB.getConnection(); for (let index = 0; index < numUploads; index++) { // It would be nice to put a mocha.describe inside the loop to tell the upload starting // but that breaks the tests. // Each set of uploads must be in one mocha because the DB is reset with each test. + if (index == 0 && testCases[fileKey].hasOwnProperty('createMeter') && testCases[fileKey]['createMeter']) { + // The key to create meter is present and true so create the meter if first request. + const meter = new Meter( + undefined, // id + fileKey, // name + undefined, // url + false, // enabled + false, // displayable + Meter.type.OTHER, // type + null, // timezone + ) + meter.insert(conn); + } let inputFile = testCases[fileKey]['fileName'][index]; let inputPath = `${__dirname}/csvPipeline/${inputFile}`; let inputBuffer = fs.readFileSync(inputPath); @@ -545,7 +598,6 @@ for (let fileKey in testCases) { expect(res.text).to.equal(testCases[fileKey]['responseString'][index]); } // You do not want to check the database until all the uploads are done. - const conn = testDB.getConnection(); // Get every meter to be sure only one with correct name. const meters = await Meter.getAll(conn); expect(meters.length).to.equal(1); @@ -577,11 +629,11 @@ metersUpload: an array where each entry is a meter object that is what should be */ const testMeters = { pipe100: { - description: 'Second meter upload where incorrectly provides meter name so fails', - chaiRequest: [CHAI_METERS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('headerRow',BooleanTypesJS.true)", CHAI_METERS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('update',BooleanTypesJS.true).field('meterName', 'pipe100').field('headerRow',BooleanTypesJS.true)"], + description: 'Second meter upload where incorrectly provides meter identifier so fails', + chaiRequest: [CHAI_METERS_REQUEST + ".field('gzip', false).field('headerRow',true)", CHAI_METERS_REQUEST + ".field('gzip', false).field('update',true).field('meterIdentifier', 'pipe100').field('headerRow',true)"], fileName: ['pipe100InputMeter.csv', 'pipe100InputMeter.csv'], responseCode: [200, 400], - responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

CSVPipelineError: Failed to upload meters due to internal OED Error: Meter name provided ("pipe100") in request with update for meters but more than one meter in CSV so not processing'], + responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

CSVPipelineError: Failed to upload meters due to internal OED Error: Meter identifier provided ("pipe100") in request with update for meters but more than one meter in CSV so not processing'], metersUploaded: [ new Meter( undefined, // id @@ -648,7 +700,7 @@ const testMeters = { }, pipe101: { description: 'Second meter with same name so fails but first meter exists', - chaiRequest: [CHAI_METERS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('headerRow',BooleanTypesJS.true)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('gzip', false).field('headerRow',true)"], fileName: ['pipe101InputMeter.csv'], responseCode: [400], responseString: ['

FAILURE

CSVPipelineError: Failed to upload meters due to internal OED Error: Meter name of "pipe101" got database error of: duplicate key value violates unique constraint "meters_name_key"'], @@ -687,18 +739,18 @@ const testMeters = { }, pipe102: { description: 'Update meter where name does not exist so fails', - chaiRequest: [CHAI_METERS_REQUEST + ".field('gzip', BooleanTypesJS.false).field('headerRow',BooleanTypesJS.true).field('update',BooleanTypesJS.true)"], + chaiRequest: [CHAI_METERS_REQUEST + ".field('gzip', false).field('headerRow',true).field('update',true)"], fileName: ['pipe102InputMeter.csv'], responseCode: [400], - responseString: ['

FAILURE

CSVPipelineError: Failed to upload meters due to internal OED Error: Meter name of "pipe102" does not seem to exist with update for meters and got DB error of: No data returned from the query.'], + responseString: ['

FAILURE

CSVPipelineError: Failed to upload meters due to internal OED Error: Meter identifier of "pipe102" does not seem to exist with update for meters and got DB error of: No data returned from the query.'], metersUploaded: [] }, pipe103: { - description: 'Uploading meters using Email. First succeeds then the second meter upload fails because it incorrectly provides meter name', - chaiRequest: [CHAI_METERS_REQUEST_EMAIL + ".field('gzip', BooleanTypesJS.false).field('headerRow',BooleanTypesJS.true)", CHAI_METERS_REQUEST_EMAIL + ".field('gzip', BooleanTypesJS.false).field('update',BooleanTypesJS.true).field('meterName', 'pipe100').field('headerRow',BooleanTypesJS.true)"], + description: 'Uploading meters using Email. First succeeds then the second meter upload fails because it incorrectly provides meter identifier', + chaiRequest: [CHAI_METERS_REQUEST_EMAIL + ".field('gzip', false).field('headerRow',true)", CHAI_METERS_REQUEST_EMAIL + ".field('gzip', false).field('update',true).field('meterIdentifier', 'pipe100').field('headerRow',true)"], fileName: ['pipe100InputMeter.csv', 'pipe100InputMeter.csv'], responseCode: [200, 400], - responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

CSVPipelineError: Failed to upload meters due to internal OED Error: Meter name provided ("pipe100") in request with update for meters but more than one meter in CSV so not processing'], + responseString: ['

SUCCESS

Successfully inserted the meters.', '

FAILURE

CSVPipelineError: Failed to upload meters due to internal OED Error: Meter identifier provided ("pipe100") in request with update for meters but more than one meter in CSV so not processing'], metersUploaded: [ new Meter( undefined, // id @@ -777,21 +829,21 @@ for (let fileKey in testMeters) { name: 'Electric_Utility', identifier: '', unitRepresent: Unit.unitRepresentType.QUANTITY, - secInRate: 3600, - typeOfUnit: Unit.unitType.METER, + secInRate: 3600, + typeOfUnit: Unit.unitType.METER, suffix: '', - displayable: Unit.displayableType.NONE, + displayable: Unit.displayableType.NONE, preferredDisplay: false, note: 'for teting' }, - { + { name: 'kWh', identifier: '', unitRepresent: Unit.unitRepresentType.QUANTITY, - secInRate: 3600, - typeOfUnit: Unit.unitType.UNIT, + secInRate: 3600, + typeOfUnit: Unit.unitType.UNIT, suffix: '', - displayable: Unit.displayableType.ALL, + displayable: Unit.displayableType.ALL, preferredDisplay: true, note: 'for testing' } diff --git a/src/server/test/web/meters.js b/src/server/test/web/meters.js index 5a4a2203d..ea3f137ad 100644 --- a/src/server/test/web/meters.js +++ b/src/server/test/web/meters.js @@ -136,45 +136,50 @@ mocha.describe('meters API', () => { expect(res.body).to.have.lengthOf(4); expectMetersToBeEquivalent(res.body, 4, false, unitId); }); - mocha.describe('Admin role:', () => { - let token; - // Since this .before is in the middle of tests, it should not have issues as - // documented in usersTest.js. - mocha.before(async () => { - let res = await chai.request(app).post('/api/login') - .send({ username: testUser.username, password: testUser.password }); - token = res.body.token; - }); - mocha.it('returns all meters', async () => { - const conn = testDB.getConnection(); - await new Meter(undefined, 'Meter 1', '1.1.1.1', true, true, Meter.type.MAMAC, '+01', gps, - 'Identified 1', 'notes 1', 10.0, true, true, '01:01:25', '05:05:05', 5.1, 7.3, 1, 'increasing', false, - 1.0, '0001-01-01 23:59:59', '2020-07-02 01:00:10', '2020-03-05 13:15:13', unitId, unitId, - Unit.areaUnitType.METERS, '13:57:19').insert(conn); - await new Meter(undefined, 'Meter 2', '1.1.1.1', true, true, Meter.type.MAMAC, '+02', gps, - 'Identified 2', 'notes 2', 20.0, true, true, '01:01:25', '05:05:05', 5.1, 7.3, 1, 'increasing', false, - 2.0, '0001-01-01 23:59:59', '2020-07-02 01:00:10', '2020-03-05 13:15:13', unitId, unitId, - Unit.areaUnitType.METERS, '13:57:19').insert(conn); - await new Meter(undefined, 'Meter 3', '1.1.1.1', true, true, Meter.type.MAMAC, '+03', gps, - 'Identified 3', 'notes 3', 30.0, true, true, '01:01:25', '05:05:05', 5.1, 7.3, 1, 'increasing', false, - 3.0, '0001-01-01 23:59:59', '2020-07-02 01:00:10', '2020-03-05 13:15:13', unitId, unitId, - Unit.areaUnitType.METERS, '13:57:19').insert(conn); - await new Meter(undefined, 'Not Visible', '1.1.1.1', true, false, Meter.type.MAMAC, '+04', gps, - 'Identified 4', 'notes 4', 40.0, true, true, '01:01:25', '05:05:05', 5.1, 7.3, 1, 'increasing', false, - 4.0, '0001-01-01 23:59:59', '2020-07-02 01:00:10', '2020-03-05 13:15:13', unitId, unitId, - Unit.areaUnitType.METERS, '13:57:19').insert(conn); - const res = await chai.request(app).get('/api/meters').set('token', token); - expect(res).to.have.status(200); - expect(res).to.be.json; - expect(res.body).to.have.lengthOf(4); - expectMetersToBeEquivalent(res.body, 4, true, unitId); - }); + mocha.describe('Admin role & CSV role:', () => { + for (const role in User.role) { + if (User.role[role] !== User.role.OBVIUS && User.role[role] !== User.role.EXPORT) { + let token; + // Since this .before is in the middle of tests, it should not have issues as + // documented in usersTest.js. + mocha.before(async () => { + let res = await chai.request(app).post('/api/login') + .send({ username: testUser.username, password: testUser.password }); + token = res.body.token; + }); + mocha.it('returns all meters', async () => { + const conn = testDB.getConnection(); + await new Meter(undefined, 'Meter 1', '1.1.1.1', true, true, Meter.type.MAMAC, '+01', gps, + 'Identified 1', 'notes 1', 10.0, true, true, '01:01:25', '05:05:05', 5.1, 7.3, 1, 'increasing', false, + 1.0, '0001-01-01 23:59:59', '2020-07-02 01:00:10', '2020-03-05 13:15:13', unitId, unitId, + Unit.areaUnitType.METERS, '13:57:19').insert(conn); + await new Meter(undefined, 'Meter 2', '1.1.1.1', true, true, Meter.type.MAMAC, '+02', gps, + 'Identified 2', 'notes 2', 20.0, true, true, '01:01:25', '05:05:05', 5.1, 7.3, 1, 'increasing', false, + 2.0, '0001-01-01 23:59:59', '2020-07-02 01:00:10', '2020-03-05 13:15:13', unitId, unitId, + Unit.areaUnitType.METERS, '13:57:19').insert(conn); + await new Meter(undefined, 'Meter 3', '1.1.1.1', true, true, Meter.type.MAMAC, '+03', gps, + 'Identified 3', 'notes 3', 30.0, true, true, '01:01:25', '05:05:05', 5.1, 7.3, 1, 'increasing', false, + 3.0, '0001-01-01 23:59:59', '2020-07-02 01:00:10', '2020-03-05 13:15:13', unitId, unitId, + Unit.areaUnitType.METERS, '13:57:19').insert(conn); + await new Meter(undefined, 'Not Visible', '1.1.1.1', true, false, Meter.type.MAMAC, '+04', gps, + 'Identified 4', 'notes 4', 40.0, true, true, '01:01:25', '05:05:05', 5.1, 7.3, 1, 'increasing', false, + 4.0, '0001-01-01 23:59:59', '2020-07-02 01:00:10', '2020-03-05 13:15:13', unitId, unitId, + Unit.areaUnitType.METERS, '13:57:19').insert(conn); + + const res = await chai.request(app).get('/api/meters').set('token', token); + expect(res).to.have.status(200); + expect(res).to.be.json; + expect(res.body).to.have.lengthOf(4); + expectMetersToBeEquivalent(res.body, 4, true, unitId); + }); + } + } }); - mocha.describe('Non-Admin role:', () => { + mocha.describe('Export role & Obvius role:', () => { for (const role in User.role) { - if (User.role[role] !== User.role.ADMIN) { + if (User.role[role] !== User.role.ADMIN && User.role[role] !== User.role.CSV) { let token; mocha.beforeEach(async () => { // insert test user