Index: sources/gui/qml/pages/pretreatment/create/PreTreatmentCreate.qml =================================================================== diff -u -r18c39727da06312b90d15751e6a27e03c7b6742a -r83b9d737cd495b34a7b42f5409962a9442f3b8f4 --- sources/gui/qml/pages/pretreatment/create/PreTreatmentCreate.qml (.../PreTreatmentCreate.qml) (revision 18c39727da06312b90d15751e6a27e03c7b6742a) +++ sources/gui/qml/pages/pretreatment/create/PreTreatmentCreate.qml (.../PreTreatmentCreate.qml) (revision 83b9d737cd495b34a7b42f5409962a9442f3b8f4) @@ -1,13 +1,13 @@ /*! * - * Copyright (c) 2021-2023 Diality Inc. - All Rights Reserved. + * Copyright (c) 2021-2024 Diality Inc. - All Rights Reserved. * \copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file PreTreatmentCreate.qml * \author (last) Behrouz NematiPour - * \date (last) 14-Mar-2023 + * \date (last) 08-Feb-2024 * \author (original) Behrouz NematiPour * \date (original) 12-Jan-2021 * @@ -31,30 +31,103 @@ objectName: "_PreTreatmentCreate" // SquishQt testability header.confirmEnabled: - _bloodFlowRate .active && - _dialysateFlowRate .active && - _duration .active && - _heparinDispensingRate .active && - _heparinBolusVolume .active && - _heparinStopTime .active && - _salineBolus .active && - _heparinType .active && - _acidConcentrate .active && - _bicarbonateConcentrate .active && - _dialyzerType .active && - _dialysateTemperature .active && - _arterialPressureLimits .minAdjusted && - _arterialPressureLimits .maxAdjusted && - _venousPressureLimits .minAdjusted && - _venousPressureLimits .maxAdjusted && - _bloodPressureInterval .active && - _rinsebackFlowRate .active + _bloodFlowRate .active && _bloodFlowRate . valid && + _dialysateFlowRate .active && _dialysateFlowRate . valid && + _duration .active && _duration . valid && + _heparinDispensingRate .active && _heparinDispensingRate . valid && + _heparinBolusVolume .active && _heparinBolusVolume . valid && + _heparinStopTime .active && _heparinStopTime . valid && + _salineBolus .active && _salineBolus . valid && + _heparinType._heparinTypeActive && + _acidConcentrate .active && + _bicarbonateConcentrate .active && + _dialyzerType .active && + _dialysateTemperature .active && _dialysateTemperature . valid && + _bloodPressureInterval .active && _bloodPressureInterval . valid + Connections{ target:vTreatmentCreate + // Update flicker location to show the invalid parameter + function onDidValidationFail() { + if ( vTreatmentCreate. bloodFlowRateRejectReason ) { scrollTo( _bloodFlowRate ); return } + if ( vTreatmentCreate. dialysateFlowRateRejectReason ) { scrollTo( _dialysateFlowRate ); return } + if ( vTreatmentCreate. treatmentDurationRejectReason ) { scrollTo( _duration ); return } + if ( vTreatmentCreate. heparinDispensingRateRejectReason ) { scrollTo( _heparinDispensingRate ); return } + if ( vTreatmentCreate. heparinBolusVolumeRejectReason ) { scrollTo( _heparinBolusVolume ); return } + if ( vTreatmentCreate. heparinStopTimeRejectReason ) { scrollTo( _heparinStopTime ); return } + if ( vTreatmentCreate. salineBolusVolumeRejectReason ) { scrollTo( _salineBolus ); return } + if ( vTreatmentCreate. heparinTypeRejectReason ) { scrollTo( _heparinType ); return } + if ( vTreatmentCreate. acidConcentrateRejectReason ) { scrollTo( _acidConcentrate ); return } + if ( vTreatmentCreate. bicarbonateConcentrateRejectReason ) { scrollTo( _bicarbonateConcentrate ); return } + if ( vTreatmentCreate. dialyzerTypeRejectReason ) { scrollTo( _dialyzerType ); return } + + if ( vTreatmentCreate. dialysateTempRejectReason ) { scrollTo( _dialysateTemperature ); return } + if ( vTreatmentCreate.bloodPressureMeasureIntervalRejectReason ) { scrollTo( _bloodPressureInterval ); return } + } + } + function setInteractive(vInteractive) { _flickable.interactive = vInteractive } + function scrollTo(vItem) { + _flickable.contentY = vItem.mapToItem(_flickable.contentItem, 0, 0).y + } + + function clear() { + _flickable.contentY = 0 + _bloodFlowRate .clear() + _dialysateFlowRate .clear() + _duration .clear() + _heparinDispensingRate .clear() + _heparinBolusVolume .clear() + _heparinStopTime .clear() + _salineBolus .clear() + _heparinType .clear() + _acidConcentrate .clear() + _bicarbonateConcentrate .clear() + _dialyzerType .clear() + _dialysateTemperature .clear() + _bloodPressureInterval .clear() + } + + property bool prepopulated: false + function prepopulate() { + // not complete yet and not part of the current DNBUG + // the issue is the On/Off switches whcih has not been stored in the view to restore/prepopulate it, + // which is needed for three sliders to be set properly. + _bloodFlowRate .reset(vTreatmentCreate.bloodFlowRate ) + _dialysateFlowRate .reset(vTreatmentCreate.dialysateFlowRate ) + _duration .reset(vTreatmentCreate.treatmentDuration ) + _heparinDispensingRate .reset(vTreatmentCreate.heparinDispensingRate ) + _heparinBolusVolume .reset(vTreatmentCreate.heparinBolusVolume ) + _heparinStopTime .reset(vTreatmentCreate.heparinStopTime ) + _salineBolus .reset(vTreatmentCreate.salineBolusVolume ) + _heparinType .reset(vTreatmentCreate.heparinType ) + _acidConcentrate .reset(vTreatmentCreate.acidConcentrate ) + _bicarbonateConcentrate .reset(vTreatmentCreate.bicarbonateConcentrate ) + _dialyzerType .reset(vTreatmentCreate.heparinType ) + _dialysateTemperature .reset(vTreatmentCreate.dialysateTemp ) + _bloodPressureInterval .reset(vTreatmentCreate.bloodPressureMeasureInterval ) + } + + ConfirmButton { id : _prepopulateButton + objectName : "_prepopulateButton" + text.text : prepopulated ? qsTr("RESET") : qsTr("PREPOPULATE") + anchors.top : title.top + anchors.topMargin : 0 + visible : false // true // not readu yet and has been hidded. + onClicked : { + if ( prepopulated ) { + clear() + } + else { + prepopulate() + } + prepopulated = ! prepopulated + } + } + ScrollBar { flickable : _flickable anchors.fill: _flickable @@ -97,8 +170,15 @@ minimum : vTreatmentRanges.bloodFlowRateMin maximum : vTreatmentRanges.bloodFlowRateMax step : vTreatmentRanges.bloodFlowRateRes - value : vTreatmentRanges.bloodFlowRateDef - onValueChanged: vTreatmentCreate.bloodFlowRate = value + defaultValue: vTreatmentRanges.bloodFlowRateDef + valid : !vTreatmentCreate.bloodFlowRateRejectReason + onValueChanged : { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!valid) { + vTreatmentCreate.bloodFlowRateRejectReason = Variables.noRejectReason + } + vTreatmentCreate.bloodFlowRate = value + } } SliderCreateTreatment { id: _dialysateFlowRate @@ -109,8 +189,15 @@ minimum : vTreatmentRanges.dialysateFlowRateMin maximum : vTreatmentRanges.dialysateFlowRateMax step : vTreatmentRanges.dialysateFlowRateRes - value : vTreatmentRanges.dialysateFlowRateDef - onValueChanged : vTreatmentCreate.dialysateFlowRate = value + defaultValue: vTreatmentRanges.dialysateFlowRateDef + valid : !vTreatmentCreate.dialysateFlowRateRejectReason + onValueChanged : { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!valid) { + vTreatmentCreate.dialysateFlowRateRejectReason = Variables.noRejectReason + } + vTreatmentCreate.dialysateFlowRate = value + } } SliderCreateTreatment { id: _duration @@ -121,8 +208,15 @@ minimum : vTreatmentRanges.treatmentDurationMin maximum : vTreatmentRanges.treatmentDurationMax step : vTreatmentRanges.treatmentDurationRes - value : vTreatmentRanges.treatmentDurationDef - onValueChanged: vTreatmentCreate.treatmentDuration = value; + defaultValue: vTreatmentRanges.treatmentDurationDef + valid : !vTreatmentCreate.treatmentDurationRejectReason + onValueChanged: { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!valid) { + vTreatmentCreate.treatmentDurationRejectReason = Variables.noRejectReason + } + vTreatmentCreate.treatmentDuration = value + } } Connections { target : _heparinDispensingRateSwitch @@ -131,8 +225,9 @@ let mObject = _heparinStopTime if ( ! mActive ) { - mObject.enabled = false - mObject.active = false + mObject.enabled = false + mObject.active = false + mObject.adjustable = false } } @@ -141,17 +236,11 @@ let mChecked = _heparinDispensingRateSwitch.checked let mObject = _heparinStopTime - if ( ! mChecked ) { - mObject.enabled = false - mObject.value = 0 - if ( mActive ) { - mObject.active = true - } - } - else { // already active - mObject.enabled = true - mObject.active = true - } + mObject.enabled = mActive && mChecked + mObject.active = mActive && ! mChecked + mObject.adjustable = mChecked + + if ( ! mChecked ) mObject.reset ( 0 ) } } @@ -165,25 +254,33 @@ minimum : vTreatmentRanges.heparinDispensingRateMin maximum : vTreatmentRanges.heparinDispensingRateMax step : vTreatmentRanges.heparinDispensingRateRes - value : vTreatmentRanges.heparinDispensingRateDef - onValueChanged : vTreatmentCreate.heparinDispensingRate = value + defaultValue: vTreatmentRanges.heparinDispensingRateDef + valid : !vTreatmentCreate.heparinDispensingRateRejectReason adjustable : _heparinDispensingRateSwitch.checked inActiveZero : true enableAdjustButtons: _heparinDispensingRateSwitch.checked - + onValueChanged: { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!valid) { + vTreatmentCreate.heparinDispensingRateRejectReason = Variables.noRejectReason + } + vTreatmentCreate.heparinDispensingRate = _heparinDispensingRateSwitch.checked ? value : 0 + } // ToDo: create a component for Switch, // ToDo: Consider putting the new CheckBox component into the SliderCreateTreatment component and set via boolean property // This is a full implementation of a Switch + toggleSwich: _heparinDispensingRateSwitch Switch { id: _heparinDispensingRateSwitch property bool active: false onCheckedChanged: { if ( ! active ) { active = true checked = ! checked } - vTreatmentCreate.heparinDispensingRate = 0 - _heparinDispensingRate.value = 0 - _heparinDispensingRate.active = ! checked + vTreatmentCreate.heparinDispensingRate = 0 + vTreatmentCreate.heparinDispensingRateOff = ! checked + _heparinDispensingRate.reset ( 0 ) + _heparinDispensingRate.active = ! checked } x : width * -1.5 @@ -236,25 +333,34 @@ minimum : vTreatmentRanges.heparinBolusVolumeMin maximum : vTreatmentRanges.heparinBolusVolumeMax step : vTreatmentRanges.heparinBolusVolumeRes - value : vTreatmentRanges.heparinBolusVolumeDef - onValueChanged : vTreatmentCreate.heparinBolusVolume = value + defaultValue: vTreatmentRanges.heparinBolusVolumeDef + valid : !vTreatmentCreate.heparinBolusVolumeRejectReason adjustable : _heparinBolusVolumeSwitch.checked inActiveZero : true enableAdjustButtons: _heparinBolusVolumeSwitch.checked + onValueChanged : { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!valid) { + vTreatmentCreate.heparinBolusVolumeRejectReason = Variables.noRejectReason + } + vTreatmentCreate.heparinBolusVolume = _heparinBolusVolumeSwitch.checked ? value : 0 + } // ToDo: create a component for this, // ToDo: Consider putting the new CheckBox component into the SliderCreateTreatment component and set via boolean property // This is a full implementation of a CheckBox + toggleSwich: _heparinBolusVolumeSwitch Switch { id: _heparinBolusVolumeSwitch property bool active: false onCheckedChanged: { if ( ! active ) { active = true checked = ! checked } - vTreatmentCreate.heparinBolusVolume = 0 - _heparinBolusVolume.value = 0 - _heparinBolusVolume.active = ! checked + vTreatmentCreate.heparinBolusVolume = 0 + vTreatmentCreate.heparinBolusVolumeOff = ! checked + _heparinBolusVolume.reset ( 0 ) + _heparinBolusVolume.active = ! checked } x : width * -1.5 @@ -305,10 +411,18 @@ minimum : vTreatmentRanges.heparinStopTimeMin maximum : vTreatmentRanges.heparinStopTimeMax step : vTreatmentRanges.heparinStopTimeRes - value : vTreatmentRanges.heparinStopTimeDef - onValueChanged : vTreatmentCreate.heparinStopTime = value + defaultValue: vTreatmentRanges.heparinStopTimeDef + valid : !vTreatmentCreate.heparinStopTimeRejectReason enabled : false // this switch depends on the heparin dispencing + adjustable : false // this switch depends on the heparin dispencing enableAdjustButtons: _heparinDispensingRateSwitch.checked + onValueChanged: { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!valid) { + vTreatmentCreate.heparinStopTimeRejectReason = Variables.noRejectReason + } + vTreatmentCreate.heparinStopTime = value + } } SliderCreateTreatment { id: _salineBolus @@ -319,8 +433,15 @@ minimum : vTreatmentRanges.salineBolusVolumeMin maximum : vTreatmentRanges.salineBolusVolumeMax step : vTreatmentRanges.salineBolusVolumeRes - value : vTreatmentRanges.salineBolusVolumeDef - onValueChanged : vTreatmentCreate.salineBolusVolume = value + defaultValue: vTreatmentRanges.salineBolusVolumeDef + valid : !vTreatmentCreate.salineBolusVolumeRejectReason + onValueChanged: { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!valid) { + vTreatmentCreate.salineBolusVolumeRejectReason = Variables.noRejectReason + } + vTreatmentCreate.salineBolusVolume = value + } } Text { id: _titleTextOperation @@ -332,31 +453,52 @@ } GridSelection { id : _heparinType + readonly property bool _heparinTypeActive : (enabled && active) || (!enabled) + objectName : "_heparinTypeRect" title : qsTr("Heparin Type") labels : vTreatmentRanges.heparinTypeOptions onClicked :{vTreatmentCreate.heparinType = curIndex ; vTreatmentCreate.heparinTypeSet = true; } + enabled : _heparinDispensingRateSwitch.checked || _heparinBolusVolumeSwitch.checked + onEnabledChanged: { + if ( ! enabled ) { + vTreatmentCreate.heparinTypeSet = false + clear() + } + } + + valid : ! vTreatmentCreate.heparinTypeRejectReason + onCurIndexChanged : vTreatmentCreate.heparinTypeRejectReason = Variables.noRejectReason } GridSelection { id : _acidConcentrate objectName : "_acidConcentrateRect" title : qsTr("Acid Concentrate") labels : vTreatmentRanges.acidConcentrateOptions onClicked :{vTreatmentCreate.acidConcentrate = curIndex ; vTreatmentCreate.acidConcentrateSet = true; } + + valid : ! vTreatmentCreate.acidConcentrateRejectReason + onCurIndexChanged : vTreatmentCreate.acidConcentrateRejectReason = Variables.noRejectReason } GridSelection { id : _bicarbonateConcentrate objectName : "_bicarbonateConcentrateRect" title : qsTr("Bicarbonate Concentrate") labels : vTreatmentRanges.bicarbonateConcentrateOptions onClicked :{vTreatmentCreate.bicarbonateConcentrate = curIndex ; vTreatmentCreate.bicarbonateConcentrateSet = true; } + + valid : ! vTreatmentCreate.bicarbonateConcentrateRejectReason + onCurIndexChanged : vTreatmentCreate.bicarbonateConcentrateRejectReason = Variables.noRejectReason } GridSelection { id : _dialyzerType objectName : "_dialyzerTypeRect" title : qsTr("Dialyzer Type") labels : vTreatmentRanges.dialyzerTypeOptions onClicked :{vTreatmentCreate.dialyzerType = curIndex ; vTreatmentCreate.dialyzerTypeSet = true; } + + valid : ! vTreatmentCreate.dialyzerTypeRejectReason + onCurIndexChanged : vTreatmentCreate.dialyzerTypeRejectReason = Variables.noRejectReason } Item { height: 1; width: 1 /* TODO : there is a design flaw in here, this is a workaround */ } @@ -370,10 +512,20 @@ minimum : vTreatmentRanges.dialysateTempMin maximum : vTreatmentRanges.dialysateTempMax step : vTreatmentRanges.dialysateTempRes - value : vTreatmentRanges.dialysateTempDef - onValueChanged : vTreatmentCreate.dialysateTemp = value + defaultValue: vTreatmentRanges.dialysateTempDef + valid : !vTreatmentCreate.dialysateTempRejectReason + + onValueChanged: { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!valid) { + vTreatmentCreate.dialysateTempRejectReason = Variables.noRejectReason + } + vTreatmentCreate.dialysateTemp = value + } } +/* [DEN-15359] Arterial and Venous Pressure has been removed, it has been kept here since it supposed to become a component and don't watnt to loose the coce. + // TODO : This has to be a Component Column { id: _arterialColumn spacing: 45 @@ -385,6 +537,11 @@ if ( slider.minAdjusted && slider.maxAdjusted ) { color = Colors.textMain } + + // if it's invalid upper or lower bound, set invalid color + if (!_arterialPressureLimits.lowerBoundValid || !_arterialPressureLimits.upperBoundValid) { + color = Colors.createTreatmentInvalidParam + } return color } width : parent.width @@ -429,6 +586,9 @@ upperText.anchors.topMargin : -60 upperTextHorizontalCenter : true + lowerBoundValid: !vTreatmentCreate.arterialPressureLimitLowRejectReason + upperBoundValid: !vTreatmentCreate.arterialPressureLimitHighRejectReason + minText.visible : true minText.font.bold : false minVerticalEdgeVisible : false @@ -452,10 +612,25 @@ onPressed : { setInteractive(false) } onDragged : { setInteractive(false) } onReleased : { setInteractive(true ) } - onMinValueChanged : { if ( minAdjusted ) vTreatmentCreate.arterialPressureLimitLow = minValue } - onMaxValueChanged : { if ( maxAdjusted ) vTreatmentCreate.arterialPressureLimitHigh = maxValue } - onClicked : { if ( minAdjusted ) vTreatmentCreate.arterialPressureLimitLow = minValue - if ( maxAdjusted ) vTreatmentCreate.arterialPressureLimitHigh = maxValue } + onMinValueChanged : { + if ( minAdjusted ) { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!lowerBoundValid) { + vTreatmentCreate.arterialPressureLimitLowRejectReason = Variables.noRejectReason + } + vTreatmentCreate.arterialPressureLimitLow = minValue + } + } + onMaxValueChanged : { + if ( maxAdjusted ) { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!upperBoundValid) { + vTreatmentCreate.arterialPressureLimitHighRejectReason = Variables.noRejectReason + } + vTreatmentCreate.arterialPressureLimitHigh = maxValue + } + } + SliderArrows{ id:_arterialPressureLimitsMaxArrows anchors.verticalCenter : _arterialPressureLimits.verticalCenter anchors.left : _arterialPressureLimits.right @@ -485,6 +660,11 @@ if ( slider.minAdjusted && slider.maxAdjusted ) { color = Colors.textMain } + + // if it's invalid upper or lower bound, set invalid color + if (!_venousPressureLimits.lowerBoundValid || !_venousPressureLimits.upperBoundValid) { + color = Colors.createTreatmentInvalidParam + } return color } width : parent.width @@ -534,6 +714,9 @@ upperText.anchors.topMargin : -60 upperTextHorizontalCenter : true + lowerBoundValid : !vTreatmentCreate.venousPressureLimitLowRejectReason + upperBoundValid : !vTreatmentCreate.venousPressureLimitHighRejectReason + minText.visible : true minText.font.bold : false minVerticalEdgeVisible : false @@ -557,10 +740,24 @@ onPressed : { setInteractive(false) } onDragged : { setInteractive(false) } onReleased : { setInteractive(true ) } - onMinValueChanged : { if ( minAdjusted ) vTreatmentCreate.venousPressureLimitLow = minValue } - onMaxValueChanged : { if ( maxAdjusted ) vTreatmentCreate.venousPressureLimitHigh = maxValue } - onClicked : { if ( minAdjusted ) vTreatmentCreate.venousPressureLimitLow = minValue - if ( maxAdjusted ) vTreatmentCreate.venousPressureLimitHigh = maxValue } + onMinValueChanged : { + if ( minAdjusted ) { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!lowerBoundValid) { + vTreatmentCreate.venousPressureLimitLowRejectReason = Variables.noRejectReason + } + vTreatmentCreate.venousPressureLimitLow = minValue + } + } + onMaxValueChanged : { + if ( maxAdjusted ) { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!upperBoundValid) { + vTreatmentCreate.venousPressureLimitHighRejectReason = Variables.noRejectReason + } + vTreatmentCreate.venousPressureLimitHigh = maxValue + } + } SliderArrows{ id:_venousPressureLimitsMaxArrows anchors.verticalCenter : _venousPressureLimits.verticalCenter @@ -580,6 +777,8 @@ } } +*/ + SliderCreateTreatment { id: _bloodPressureInterval objectName : "_bloodPressureMeasurementInterval" label : qsTr("Blood Pressure Measurement Interval") @@ -589,15 +788,23 @@ minimum : vTreatmentRanges.bloodPressureMeasureIntervalMin maximum : vTreatmentRanges.bloodPressureMeasureIntervalMax step : vTreatmentRanges.bloodPressureMeasureIntervalRes - value : vTreatmentRanges.bloodPressureMeasureIntervalDef - onValueChanged: vTreatmentCreate.bloodPressureMeasureInterval = value + defaultValue: vTreatmentRanges.bloodPressureMeasureIntervalDef + valid : !vTreatmentCreate.bloodPressureMeasureIntervalRejectReason adjustable : _bloodPressureIntervalSwitch.checked inActiveZero: true enableAdjustButtons: _bloodPressureIntervalSwitch.checked + onValueChanged: { + // Reset the valid state to allow repositioning to the next invalid parameter + if(!valid) { + vTreatmentCreate.bloodPressureMeasureIntervalRejectReason = Variables.noRejectReason + } + vTreatmentCreate.bloodPressureMeasureInterval = _bloodPressureIntervalSwitch.checked ? value : 0 + } // ToDo: create a component for this, // ToDo: Consider putting the new CheckBox component into the SliderCreateTreatment component and set via boolean property // This is a full implementation of a CheckBox + toggleSwich: _bloodPressureIntervalSwitch Switch { id: _bloodPressureIntervalSwitch property bool active: false onCheckedChanged: { @@ -606,7 +813,7 @@ checked = ! checked } vTreatmentCreate.bloodPressureMeasureInterval = 0 - _bloodPressureInterval.value = 0 + _bloodPressureInterval.reset ( 0 ) _bloodPressureInterval.active = ! checked } @@ -651,17 +858,6 @@ } } - SliderCreateTreatment { id: _rinsebackFlowRate - objectName : "_rinsebackFlowRate" - label : qsTr("Rinseback Flow Rate") - flickable : _flickable - unit : Variables.unitTextFlowRate - minimum : vTreatmentRanges.rinsebackFlowRateMin - maximum : vTreatmentRanges.rinsebackFlowRateMax - step : vTreatmentRanges.rinsebackFlowRateRes - value : vTreatmentRanges.rinsebackFlowRateDef - onValueChanged : vTreatmentCreate.rinsebackFlowRate = value - } Item { width : 50 height : 50