diff --git a/wres-config/src/wres/config/yaml/DeclarationFactory.java b/wres-config/src/wres/config/yaml/DeclarationFactory.java index 40813fd64a..785835495a 100644 --- a/wres-config/src/wres/config/yaml/DeclarationFactory.java +++ b/wres-config/src/wres/config/yaml/DeclarationFactory.java @@ -56,6 +56,7 @@ import wres.config.yaml.components.DatasetOrientation; import wres.config.yaml.components.EvaluationDeclaration; import wres.config.yaml.components.Threshold; +import wres.config.yaml.components.ThresholdBuilder; import wres.config.yaml.components.ThresholdOperator; import wres.config.yaml.components.ThresholdOrientation; import wres.config.yaml.components.ThresholdType; @@ -131,7 +132,9 @@ public class DeclarationFactory .setOperator( DEFAULT_THRESHOLD_OPERATOR.canonical() ) .build(); /** Default wrapped threshold. */ - public static final Threshold DEFAULT_THRESHOLD = new Threshold( DEFAULT_CANONICAL_THRESHOLD, null, null, null ); + public static final Threshold DEFAULT_THRESHOLD = + ThresholdBuilder.builder().threshold( DEFAULT_CANONICAL_THRESHOLD ) + .build(); /** To stringify protobufs into presentable JSON. */ public static final Function PROTBUF_STRINGIFIER = message -> { diff --git a/wres-config/src/wres/config/yaml/DeclarationInterpolator.java b/wres-config/src/wres/config/yaml/DeclarationInterpolator.java index 098fab2d25..314f2605ac 100644 --- a/wres-config/src/wres/config/yaml/DeclarationInterpolator.java +++ b/wres-config/src/wres/config/yaml/DeclarationInterpolator.java @@ -1676,6 +1676,11 @@ private static Metric addThresholdsToMetric( Map migrateCsvThresholdsInline( ThresholdsConfig.Sourc .build(); Set toMigrate = nextEntry.getValue(); Set innerMigrated = toMigrate.stream() - .map( next -> new Threshold( next, - canonicalType, - feature, - featureNameFrom ) ) + .map( next -> ThresholdBuilder.builder() + .threshold( next ) + .type( canonicalType ) + .feature( feature ) + .featureNameFrom( featureNameFrom ) + .build() ) .collect( Collectors.toCollection( LinkedHashSet::new ) ); migrated.addAll( innerMigrated ); } diff --git a/wres-config/src/wres/config/yaml/DeclarationUtilities.java b/wres-config/src/wres/config/yaml/DeclarationUtilities.java index 66995c18f1..12ae787757 100644 --- a/wres-config/src/wres/config/yaml/DeclarationUtilities.java +++ b/wres-config/src/wres/config/yaml/DeclarationUtilities.java @@ -62,6 +62,7 @@ import wres.config.yaml.components.SourceBuilder; import wres.config.yaml.components.SourceInterface; import wres.config.yaml.components.Threshold; +import wres.config.yaml.components.ThresholdBuilder; import wres.config.yaml.components.ThresholdType; import wres.config.yaml.components.TimeInterval; import wres.config.yaml.components.TimePools; @@ -84,14 +85,19 @@ public class DeclarationUtilities /** All data threshold. */ public static final Threshold ALL_DATA_THRESHOLD = - new Threshold( wres.statistics.generated.Threshold.newBuilder() - .setLeftThresholdValue( Double.NEGATIVE_INFINITY ) - .setOperator( wres.statistics.generated.Threshold.ThresholdOperator.GREATER ) - .setDataType( wres.statistics.generated.Threshold.ThresholdDataType.LEFT_AND_RIGHT ) - .build(), - wres.config.yaml.components.ThresholdType.VALUE, - null, - null ); + ThresholdBuilder.builder() + .threshold( wres.statistics.generated.Threshold.newBuilder() + .setLeftThresholdValue( Double.NEGATIVE_INFINITY ) + .setOperator( wres.statistics.generated.Threshold.ThresholdOperator.GREATER ) + .setDataType( wres.statistics.generated.Threshold.ThresholdDataType.LEFT_AND_RIGHT ) + .build() ) + .type( wres.config.yaml.components.ThresholdType.VALUE ) + // All data threshold, as declared or read from a source, not generated by the software. + // Distinguishing between an all data threshold as declared vs. generated internally is + // important in some contexts, such as when establishing if the threshold was declared + // explicitly as an override for a specific metric + .generated( false ) + .build(); /** Re-used message. */ private static final String CANNOT_DETERMINE_TIME_WINDOWS_FROM_MISSING_DECLARATION = "Cannot determine time " @@ -1040,7 +1046,10 @@ public static EvaluationDeclaration addThresholds( EvaluationDeclaration declara /** * Removes from the declaration any features for which featureful thresholds are undefined. Features are matched on - * name only. + * name only. Only filters against thresholds available within the declaration (i.e., internal). Thus, any + * thresholds from external sources, such as files or threshold services, should be "read into" the declaration + * before using this utility. + * * @param declaration the declaration * @return the adjusted declaration * @throws NullPointerException if the declaration is null @@ -1081,27 +1090,20 @@ public static EvaluationDeclaration removeFeaturesWithoutThresholds( EvaluationD // Create a filter. Only filter when the above sets contain some features Predicate retain = geoTuple -> { - if ( geoTuple.hasLeft() - && !leftFeatureNamesWithThresholds.isEmpty() - && !leftFeatureNamesWithThresholds.contains( geoTuple.getLeft() - .getName() ) ) - { - return false; - } - - if ( geoTuple.hasRight() - && !rightFeatureNamesWithThresholds.isEmpty() - && !rightFeatureNamesWithThresholds.contains( geoTuple.getRight() - .getName() ) ) - { - return false; - } - - return !DeclarationUtilities.hasBaseline( declaration ) - || !geoTuple.hasBaseline() - || baselineFeatureNamesWithThresholds.isEmpty() - || baselineFeatureNamesWithThresholds.contains( geoTuple.getBaseline() - .getName() ); + boolean hasLeft = geoTuple.hasLeft() + && !leftFeatureNamesWithThresholds.isEmpty() + && leftFeatureNamesWithThresholds.contains( geoTuple.getLeft() + .getName() ); + boolean hasRight = geoTuple.hasRight() + && !rightFeatureNamesWithThresholds.isEmpty() + && rightFeatureNamesWithThresholds.contains( geoTuple.getRight() + .getName() ); + boolean hasBaseline = geoTuple.hasBaseline() + && !baselineFeatureNamesWithThresholds.isEmpty() + && baselineFeatureNamesWithThresholds.contains( geoTuple.getBaseline() + .getName() ); + + return hasLeft || hasRight || hasBaseline; }; // Remove the features in all contexts diff --git a/wres-config/src/wres/config/yaml/DeclarationValidator.java b/wres-config/src/wres/config/yaml/DeclarationValidator.java index 82222256f2..c67cbc7b3c 100644 --- a/wres-config/src/wres/config/yaml/DeclarationValidator.java +++ b/wres-config/src/wres/config/yaml/DeclarationValidator.java @@ -3746,15 +3746,15 @@ private static List validateFeaturefulThresholds( Set thresholdFeatureNames = thresholds.stream() - .filter( n -> n.featureNameFrom() == orientation ) - // Ignore all data, which was added automagically - .filter( n -> !DeclarationUtilities.ALL_DATA_THRESHOLD.threshold() - .equals( - n.threshold() ) ) - .map( Threshold::feature ) - .map( wres.statistics.generated.Geometry::getName ) - .collect( Collectors.toSet() ); + Set thresholdFeatureNames = + thresholds.stream() + .filter( n -> n.featureNameFrom() == orientation ) + // Ignore all data, which was added automagically + .filter( n -> !DeclarationUtilities.ALL_DATA_THRESHOLD.threshold() + .equals( n.threshold() ) ) + .map( Threshold::feature ) + .map( wres.statistics.generated.Geometry::getName ) + .collect( Collectors.toSet() ); if ( thresholdFeatureNames.isEmpty() ) { diff --git a/wres-config/src/wres/config/yaml/components/Threshold.java b/wres-config/src/wres/config/yaml/components/Threshold.java index 43298c61d0..8788fb9ad1 100644 --- a/wres-config/src/wres/config/yaml/components/Threshold.java +++ b/wres-config/src/wres/config/yaml/components/Threshold.java @@ -17,12 +17,14 @@ * @param type the threshold type to help identify the declaration context * @param feature a feature * @param featureNameFrom the orientation of the data from which the named feature is taken + * @param generated true if this threshold was generated by the software, false if declared or read from a source */ @RecordBuilder public record Threshold( wres.statistics.generated.Threshold threshold, ThresholdType type, Geometry feature, - DatasetOrientation featureNameFrom ) + DatasetOrientation featureNameFrom, + boolean generated ) { /** Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger( Threshold.class ); @@ -39,7 +41,8 @@ public record Threshold( wres.statistics.generated.Threshold threshold, */ public Threshold { - if ( Objects.nonNull( feature ) && Objects.isNull( featureNameFrom ) ) + if ( Objects.nonNull( feature ) + && Objects.isNull( featureNameFrom ) ) { LOGGER.debug( "Discovered a threshold for feature {}, but the orientation of the feature name was not " + "supplied. Assuming an orientation of {}.", diff --git a/wres-config/src/wres/config/yaml/deserializers/ThresholdsDeserializer.java b/wres-config/src/wres/config/yaml/deserializers/ThresholdsDeserializer.java index db5850f436..1e3c837e83 100644 --- a/wres-config/src/wres/config/yaml/deserializers/ThresholdsDeserializer.java +++ b/wres-config/src/wres/config/yaml/deserializers/ThresholdsDeserializer.java @@ -20,6 +20,7 @@ import wres.config.yaml.DeclarationUtilities; import wres.config.yaml.components.DatasetOrientation; import wres.config.yaml.components.Threshold; +import wres.config.yaml.components.ThresholdBuilder; import wres.config.yaml.components.ThresholdOrientation; import wres.config.yaml.components.ThresholdType; import wres.statistics.generated.Geometry; @@ -178,7 +179,10 @@ private Set getSingletonThreshold( JsonNode thresholdNode, } wres.statistics.generated.Threshold nextThreshold = builder.build(); - Threshold wrappedThreshold = new Threshold( nextThreshold, type, null, null ); + Threshold wrappedThreshold = ThresholdBuilder.builder() + .threshold( nextThreshold ) + .type( type ) + .build(); return Set.of( wrappedThreshold ); } @@ -345,7 +349,12 @@ private Set getEmbellishedThresholds( ObjectReader reader, } Threshold nextThreshold = - new Threshold( builder.build(), type, feature, featureNameFrom ); + ThresholdBuilder.builder() + .threshold( builder.build() ) + .type( type ) + .feature( feature ) + .featureNameFrom( featureNameFrom ) + .build(); thresholds.add( nextThreshold ); } @@ -386,7 +395,10 @@ private Set getThresholdsFromArray( ObjectReader reader, } wres.statistics.generated.Threshold nextThreshold = thresholdBuilder.build(); - Threshold nextWrappedThreshold = new Threshold( nextThreshold, type, null, null ); + Threshold nextWrappedThreshold = ThresholdBuilder.builder() + .threshold( nextThreshold ) + .type( type ) + .build(); thresholds.add( nextWrappedThreshold ); } diff --git a/wres-config/src/wres/config/yaml/serializers/ThresholdSetsSerializer.java b/wres-config/src/wres/config/yaml/serializers/ThresholdSetsSerializer.java index a28bc90448..133378581b 100644 --- a/wres-config/src/wres/config/yaml/serializers/ThresholdSetsSerializer.java +++ b/wres-config/src/wres/config/yaml/serializers/ThresholdSetsSerializer.java @@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory; import wres.config.yaml.components.Threshold; +import wres.config.yaml.components.ThresholdBuilder; import wres.config.yaml.components.ThresholdType; /** @@ -37,7 +38,7 @@ public void serialize( Set thresholds, LOGGER.debug( "Discovered threshold sets with {} members.", grouped.size() ); - if( !grouped.isEmpty() ) + if ( !grouped.isEmpty() ) { // Start the threshold sets writer.writeStartArray(); @@ -102,7 +103,12 @@ private Map> groupThresholdsByType( Set thr .clearLeftThresholdProbability() .build(); - Threshold outer = new Threshold( nextInner, next.type(), next.feature(), next.featureNameFrom() ); + Threshold outer = ThresholdBuilder.builder() + .threshold( nextInner ) + .type( next.type() ) + .feature( next.feature() ) + .featureNameFrom( next.featureNameFrom() ) + .build(); if ( grouped.containsKey( outer ) ) { diff --git a/wres-config/src/wres/config/yaml/serializers/ThresholdsSerializer.java b/wres-config/src/wres/config/yaml/serializers/ThresholdsSerializer.java index 2ac760eadc..9393e02cbb 100644 --- a/wres-config/src/wres/config/yaml/serializers/ThresholdsSerializer.java +++ b/wres-config/src/wres/config/yaml/serializers/ThresholdsSerializer.java @@ -19,6 +19,7 @@ import wres.config.yaml.DeclarationFactory; import wres.config.yaml.DeclarationUtilities; import wres.config.yaml.components.Threshold; +import wres.config.yaml.components.ThresholdBuilder; import wres.config.yaml.components.ThresholdOrientation; import wres.config.yaml.components.ThresholdType; import wres.statistics.generated.Geometry; @@ -329,7 +330,9 @@ private boolean hasDefaultMetadata( Set thresholds ) .clearRightThresholdValue() .build(); - Threshold blankOuter = new Threshold( blank, null, null, null ); + Threshold blankOuter = ThresholdBuilder.builder() + .threshold( blank ) + .build(); if ( !blankOuter.equals( DeclarationFactory.DEFAULT_THRESHOLD ) || Objects.nonNull( next.feature() ) ) diff --git a/wres-config/test/wres/config/yaml/DeclarationInterpolatorTest.java b/wres-config/test/wres/config/yaml/DeclarationInterpolatorTest.java index a27bf8f885..f05194af9a 100644 --- a/wres-config/test/wres/config/yaml/DeclarationInterpolatorTest.java +++ b/wres-config/test/wres/config/yaml/DeclarationInterpolatorTest.java @@ -162,13 +162,15 @@ class DeclarationInterpolatorTest /** All data threshold. */ private static final wres.config.yaml.components.Threshold ALL_DATA_THRESHOLD = - new wres.config.yaml.components.Threshold( Threshold.newBuilder() - .setLeftThresholdValue( Double.NEGATIVE_INFINITY ) - .setOperator( Threshold.ThresholdOperator.GREATER ) - .setDataType( Threshold.ThresholdDataType.LEFT_AND_RIGHT ) - .build(), - ThresholdType.VALUE, - null, null ); + wres.config.yaml.components.ThresholdBuilder.builder() + .threshold( Threshold.newBuilder() + .setLeftThresholdValue( Double.NEGATIVE_INFINITY ) + .setOperator( Threshold.ThresholdOperator.GREATER ) + .setDataType( Threshold.ThresholdDataType.LEFT_AND_RIGHT ) + .build() ) + .type( ThresholdType.VALUE ) + .generated( true ) + .build(); /** Default list of observed sources in the old-style declaration. */ List observedSources; /** Default list of predicted sources in the old-style declaration. */ @@ -1904,22 +1906,22 @@ void testDeserializeAndInterpolateAddsUnitTothresholds() throws IOException assertAll( () -> assertTrue( actualInterpolated.thresholds() .stream() - .filter( next -> next - != DeclarationUtilities.ALL_DATA_THRESHOLD ) + .filter( next -> next.threshold() + != DeclarationUtilities.ALL_DATA_THRESHOLD.threshold() ) .allMatch( next -> "foo".equals( next.threshold() .getThresholdValueUnits() ) ) ), () -> assertTrue( actualInterpolated.thresholdSets() .stream() - .filter( next -> next - != DeclarationUtilities.ALL_DATA_THRESHOLD ) + .filter( next -> next.threshold() + != DeclarationUtilities.ALL_DATA_THRESHOLD.threshold() ) .allMatch( next -> "foo".equals( next.threshold() .getThresholdValueUnits() ) ) ), () -> assertTrue( actualInterpolated.metrics() .stream() .map( Metric::parameters ) .flatMap( next -> next.thresholds().stream() ) - .filter( next -> next - != DeclarationUtilities.ALL_DATA_THRESHOLD ) + .filter( next -> next.threshold() + != DeclarationUtilities.ALL_DATA_THRESHOLD.threshold() ) .allMatch( next -> "foo".equals( next.threshold() .getThresholdValueUnits() ) ) ) ); diff --git a/wres-config/test/wres/config/yaml/DeclarationUtilitiesTest.java b/wres-config/test/wres/config/yaml/DeclarationUtilitiesTest.java index dff762955d..66803db3f7 100644 --- a/wres-config/test/wres/config/yaml/DeclarationUtilitiesTest.java +++ b/wres-config/test/wres/config/yaml/DeclarationUtilitiesTest.java @@ -2566,6 +2566,75 @@ void testRemoveFeaturesWithoutThresholds() assertEquals( expected, actual ); } + @Test + void testRemoveFeaturesWithoutThresholdsRemovesNoFeaturesWhenEachFeatureHasAThresholdWithADifferentFeatureNameOrientation() + { + // Tests GitHub issue #319 + Geometry oneObserved = Geometry.newBuilder() + .setName( "one_observed" ) + .build(); + Geometry onePredicted = Geometry.newBuilder() + .setName( "one_predicted" ) + .build(); + Geometry twoObserved = Geometry.newBuilder() + .setName( "two_observed" ) + .build(); + Geometry twoPredicted = Geometry.newBuilder() + .setName( "two_predicted" ) + .build(); + + GeometryTuple one = GeometryTuple.newBuilder() + .setLeft( oneObserved ) + .setRight( onePredicted ) + .build(); + GeometryTuple two = GeometryTuple.newBuilder() + .setLeft( twoObserved ) + .setRight( twoPredicted ) + .build(); + + Threshold threshold = Threshold.newBuilder() + .setLeftThresholdValue( 1 ) + .build(); + wres.config.yaml.components.Threshold wrappedThresholdOne = + ThresholdBuilder.builder() + .threshold( threshold ) + .feature( oneObserved ) + .featureNameFrom( DatasetOrientation.LEFT ) + .build(); + wres.config.yaml.components.Threshold wrappedThresholdTwo = + ThresholdBuilder.builder() + .threshold( threshold ) + .feature( twoPredicted ) + .featureNameFrom( DatasetOrientation.RIGHT ) + .build(); + + Set geometryTuples = Set.of( one, two ); + Features features = FeaturesBuilder.builder() + .geometries( geometryTuples ) + .build(); + + EvaluationDeclaration declaration = + EvaluationDeclarationBuilder.builder() + .features( features ) + .thresholds( Set.of( wrappedThresholdOne, + wrappedThresholdTwo ) ) + .build(); + + EvaluationDeclaration actual = DeclarationUtilities.removeFeaturesWithoutThresholds( declaration ); + + Features expectedFeatures = FeaturesBuilder.builder() + .geometries( geometryTuples ) + .build(); + + EvaluationDeclaration expected = EvaluationDeclarationBuilder.builder() + .features( expectedFeatures ) + .thresholds( Set.of( wrappedThresholdOne, + wrappedThresholdTwo ) ) + .build(); + + assertEquals( expected, actual ); + } + @Test void testRemoveFeaturesWithoutThresholdsWhenThresholdsContainSingleDataOrientation() { diff --git a/wres-config/test/wres/config/yaml/DeclarationValidatorTest.java b/wres-config/test/wres/config/yaml/DeclarationValidatorTest.java index c69887e583..d5e6e7551f 100644 --- a/wres-config/test/wres/config/yaml/DeclarationValidatorTest.java +++ b/wres-config/test/wres/config/yaml/DeclarationValidatorTest.java @@ -1752,10 +1752,10 @@ void testRealValuedThresholdWithoutUnitProducesWarning() .build(); wres.config.yaml.components.Threshold wrapped = - new wres.config.yaml.components.Threshold( threshold, - ThresholdType.VALUE, - null, - null ); + wres.config.yaml.components.ThresholdBuilder.builder() + .threshold( threshold ) + .type( ThresholdType.VALUE ) + .build(); EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() diff --git a/wres-reading/src/wres/reading/wrds/geography/FeatureFiller.java b/wres-reading/src/wres/reading/wrds/geography/FeatureFiller.java index f2ec5d1944..65efaaa043 100644 --- a/wres-reading/src/wres/reading/wrds/geography/FeatureFiller.java +++ b/wres-reading/src/wres/reading/wrds/geography/FeatureFiller.java @@ -157,7 +157,7 @@ private static EvaluationDeclaration fillFeatures( EvaluationDeclaration evaluat FeatureAuthority rightAuthority, FeatureAuthority baselineAuthority ) { - // Is this an actual feature service request or a response from a filesystem? If the latter, then any other + // Is this an actual feature service request or a response from a file system? If the latter, then any other // service declaration, such as groups, must be ignored and the response read as singleton features Set filledSingletonFeatures; Set filledGroupedFeatures = Collections.emptySet(); @@ -168,11 +168,32 @@ private static EvaluationDeclaration fillFeatures( EvaluationDeclaration evaluat + "feature service declaration, including 'groups', 'group' and 'pool', will be ignored.", featureService.uri() ); - filledSingletonFeatures = FeatureFiller.readWrdsFeatures( featureService.uri(), - leftAuthority, - rightAuthority, - baselineAuthority, - DeclarationUtilities.hasBaseline( evaluation ) ); + // Start with any existing, dense features from the declaration. Note that sparse features cannot be + // declared without a web/feature service, and there is no feature service in this pathway, as tested above + filledSingletonFeatures = new HashSet<>(); + if ( Objects.nonNull( evaluation.features() ) ) + { + boolean hasBaseline = DeclarationUtilities.hasBaseline( evaluation ); + + Set dense = evaluation.features() + .geometries() + .stream() + .filter( f -> f.hasLeft() + && f.hasRight() + && ( !hasBaseline || f.hasBaseline() ) ) + .collect( Collectors.toSet() ); + + filledSingletonFeatures.addAll( dense ); + } + + // Now read the WRDS features + Set wrdsFeatures = + FeatureFiller.readWrdsFeatures( featureService.uri(), + leftAuthority, + rightAuthority, + baselineAuthority, + DeclarationUtilities.hasBaseline( evaluation ) ); + filledSingletonFeatures.addAll( wrdsFeatures ); } else { diff --git a/wres-reading/src/wres/reading/wrds/nwm/WrdsNwmReader.java b/wres-reading/src/wres/reading/wrds/nwm/WrdsNwmReader.java index d89e80d2da..fefbe9bb9e 100644 --- a/wres-reading/src/wres/reading/wrds/nwm/WrdsNwmReader.java +++ b/wres-reading/src/wres/reading/wrds/nwm/WrdsNwmReader.java @@ -678,17 +678,6 @@ private static Pair toBasicISO8601String( Instant left, Instant { String dateFormat = "yyyyMMdd'T'HH'Z'"; - ZonedDateTime zonedLeft = left.atZone( ReaderUtilities.UTC ); - ZonedDateTime zonedRight = right.atZone( ReaderUtilities.UTC ); - - if ( zonedLeft.getMinute() > 0 - || zonedLeft.getSecond() > 0 - || zonedRight.getMinute() > 0 - || zonedRight.getSecond() > 0 ) - { - dateFormat = "yyyyMMdd'T'HHmmss'Z'"; - } - DateTimeFormatter formatter = DateTimeFormatter.ofPattern( dateFormat ) .withZone( ReaderUtilities.UTC ); String leftString = formatter.format( left ); diff --git a/wres-reading/src/wres/reading/wrds/thresholds/WrdsThresholdReader.java b/wres-reading/src/wres/reading/wrds/thresholds/WrdsThresholdReader.java index 9bd8ec5128..9f1bd906c6 100644 --- a/wres-reading/src/wres/reading/wrds/thresholds/WrdsThresholdReader.java +++ b/wres-reading/src/wres/reading/wrds/thresholds/WrdsThresholdReader.java @@ -618,7 +618,8 @@ else if ( LOGGER.isDebugEnabled() ) } } - if ( LOGGER.isDebugEnabled() && !featuresNotRequired.isEmpty() ) + if ( LOGGER.isDebugEnabled() + && !featuresNotRequired.isEmpty() ) { LOGGER.debug( "Thresholds were discovered for the following features whose thresholds were not " + "required: {}", @@ -711,9 +712,9 @@ private static void validate( URI uri, "evaluate with the features for which thresholds ", "are available, found ", featureNames.size(), - " features to evaluate and ", + " features to evaluate, ", featureNamesWithThresholds.size(), - " features for which thresholds were found, but ", + " features for which thresholds were found, and ", featureNamesWithoutThresholds.size(), " features for which thresholds could not be ", "reconciled with features to evaluate. Features without ", diff --git a/wres-reading/test/wres/reading/wrds/nwm/WrdsNwmReaderTest.java b/wres-reading/test/wres/reading/wrds/nwm/WrdsNwmReaderTest.java index b51643b179..f6833c36a3 100644 --- a/wres-reading/test/wres/reading/wrds/nwm/WrdsNwmReaderTest.java +++ b/wres-reading/test/wres/reading/wrds/nwm/WrdsNwmReaderTest.java @@ -15,7 +15,6 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.mockserver.integration.ClientAndServer; @@ -570,87 +569,4 @@ void testReadDoesNotThrowClassCastExceptionWhenChunkingFeatures() } } ); } - - @Test - @Disabled - void testReadRequestsDateRangeWithMinutesAndSeconds() - { - this.mockServer.when( HttpRequest.request() - .withPath( ANALYSIS_PATH ) - .withMethod( GET ) ) - .respond( HttpResponse.response( ANALYSIS_RESPONSE ) ); - - URI fakeUri = URI.create( "http://localhost:" - + this.mockServer.getLocalPort() - + "/api/v1/nwm/ops/analysis_assim/" - + ANALYSIS_PARAMS ); - - Source fakeDeclarationSource = SourceBuilder.builder() - .uri( fakeUri ) - .sourceInterface( SourceInterface.WRDS_NWM ) - .build(); - - Dataset dataset = DatasetBuilder.builder() - .sources( List.of( fakeDeclarationSource ) ) - .variable( VariableBuilder.builder() - .name( "streamflow" ) - .build() ) - .build(); - - DataSource fakeSource = DataSource.of( DataSource.DataDisposition.JSON_WRDS_NWM, - fakeDeclarationSource, - dataset, - Collections.emptyList(), - fakeUri, - DatasetOrientation.LEFT, - null ); - - SystemSettings systemSettings = Mockito.mock( SystemSettings.class ); - Mockito.when( systemSettings.getMaximumWebClientThreads() ) - .thenReturn( 6 ); - Mockito.when( systemSettings.getPoolObjectLifespan() ) - .thenReturn( 30_000 ); - - // Create a declaration with a short period of valid dates to create one chunked request where the first and - // last datetimes both contain minutes and seconds - Instant minimum = Instant.parse( "2024-09-01T00:13:00Z" ); - Instant maximum = Instant.parse( "2024-09-03T00:27:59Z" ); - TimeInterval interval = TimeIntervalBuilder.builder() - .minimum( minimum ) - .maximum( maximum ) - .build(); - Set geometries - = Set.of( GeometryTuple.newBuilder() - .setLeft( Geometry.newBuilder() - .setName( Integer.toString( NWM_FEATURE_ID ) ) ) - .build() ); - Features features = FeaturesBuilder.builder() - .geometries( geometries ) - .build(); - EvaluationDeclaration declaration = EvaluationDeclarationBuilder.builder() - .validDates( interval ) - .features( features ) - .build(); - - WrdsNwmReader reader = WrdsNwmReader.of( declaration, systemSettings ); - - Parameters parametersOne = new Parameters( new Parameter( "proj", "UNKNOWN_PROJECT_USING_WRES" ), - new Parameter( "reference_time", - "(20240901T000000Z,20240903T002759Z]" ), - new Parameter( "forecast_type", "deterministic" ) ); - - try ( Stream tupleStream = reader.read( fakeSource ) ) - { - List> actual = tupleStream.map( TimeSeriesTuple::getSingleValuedTimeSeries ) - .toList(); - - assertEquals( 1, actual.size() ); - - // One request made with parameters one - this.mockServer.verify( request().withMethod( GET ) - .withPath( ANALYSIS_PATH ) - .withQueryStringParameters( parametersOne ), - VerificationTimes.exactly( 1 ) ); - } - } } \ No newline at end of file