From 2346d52b9d7fbf54fa5d7509e6763f427085755d Mon Sep 17 00:00:00 2001 From: chayan das <110921638+Chayandas07@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:23:31 +0530 Subject: [PATCH 1/5] feat: Add BYTE_SIZE and TIME_DURATION tokens with parsing support - Introduced BYTE_SIZE and TIME_DURATION tokens in the ANTLR grammar. - Implemented ByteSize and TimeDuration classes for parsing and conversion. - Updated RecipeVisitor to handle new token types in directives. - Created AggregateStats directive for aggregating byte sizes and time durations. - Added unit tests for ByteSize and TimeDuration parsing. - Implemented integration tests for AggregateStats directive. - Updated pom.xml files to include necessary dependencies and plugins. --- README.md | 23 +++ README.pdf | Bin 0 -> 148260 bytes pom.xml | 10 + prompts.text | 120 +++++++++++ wrangler-api/pom.xml | 31 ++- .../io/cdap/wrangler/api/parser/ByteSize.java | 156 +++++++++++++++ .../wrangler/api/parser/TimeDuration.java | 155 ++++++++++++++ .../cdap/wrangler/api/parser/TokenType.java | 21 +- .../wrangler/api/parser/UsageDefinition.java | 4 + wrangler-core/pom.xml | 17 ++ .../io/cdap/wrangler/parser/Directives.g4 | 46 ++++- .../directives/aggregates/aggregateStats.java | 180 +++++++++++++++++ .../cdap/wrangler/parser/RecipeVisitor.java | 35 ++++ .../directives/aggregates/aggregatestats.java | 189 ++++++++++++++++++ .../parser/ByteSizeAndTimeDurationTest.java | 61 ++++++ wrangler-test/pom.xml | 6 + 16 files changed, 1051 insertions(+), 3 deletions(-) create mode 100644 README.pdf create mode 100644 prompts.text create mode 100644 wrangler-api/src/main/java/io/cdap/wrangler/api/parser/ByteSize.java create mode 100644 wrangler-api/src/main/java/io/cdap/wrangler/api/parser/TimeDuration.java create mode 100644 wrangler-core/src/main/java/io/cdap/directives/aggregates/aggregateStats.java create mode 100644 wrangler-core/src/test/java/io/cdap/directives/aggregates/aggregatestats.java create mode 100644 wrangler-core/src/test/java/io/cdap/directives/parser/ByteSizeAndTimeDurationTest.java diff --git a/README.md b/README.md index 4aa6eeb3a..4f69a1263 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ These directives are currently available: | [Write JSON Object](wrangler-docs/directives/write-as-json-object.md) | Composes a JSON object based on the fields specified. | | [Format as Currency](wrangler-docs/directives/format-as-currency.md) | Formats a number as currency as specified by locale. | | **Transformations** | | +| [Aggregate Stats](wrangler-docs/directives/aggregate-stats.md) | Analyzes byte size and time duration values, generating statistics | | [Changing Case](wrangler-docs/directives/changing-case.md) | Changes the case of column values | | [Cut Character](wrangler-docs/directives/cut-character.md) | Selects parts of a string value | | [Set Column](wrangler-docs/directives/set-column.md) | Sets the column value to the result of an expression execution | @@ -175,6 +176,28 @@ rates below are specified as *records/second*. | High (167 Directives) | 426 | 127,946,398 | 82,677,845,324 | 106,367.27 | | High (167 Directives) | 426 | 511,785,592 | 330,711,381,296 | 105,768.93 | +## Byte Size and Time Duration Support + +The Wrangler library provides support for parsing and aggregating byte size and time duration values. This feature allows you to work with human-readable size and duration values directly in your recipes. + +### Byte Size Units + +The following byte size units are supported: + +- B: Bytes +- KB: Kilobytes (1024 bytes) +- MB: Megabytes (1024 \* 1024 bytes) +- GB: Gigabytes (1024 _ 1024 _ 1024 bytes) +- TB: Terabytes (1024 _ 1024 _ 1024 \* 1024 bytes) + +### Time Duration Units + +The following time duration units are supported: + +- ms: Milliseconds +- s: Seconds +- m: Minutes +- h: Hours ## Contact diff --git a/README.pdf b/README.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b31b169bd3d96f6f65fd57381ae40bba9cc7b295 GIT binary patch literal 148260 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 D4boEp literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index a4ee67662..843dfad8c 100644 --- a/pom.xml +++ b/pom.xml @@ -176,6 +176,16 @@ ${testSourceLocation} + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + 1 + true + --add-opens java.base/java.lang=ALL-UNNAMED + + org.apache.maven.plugins maven-compiler-plugin diff --git a/prompts.text b/prompts.text new file mode 100644 index 000000000..0ec6989fb --- /dev/null +++ b/prompts.text @@ -0,0 +1,120 @@ +Prompt 1 +I am modifying an ANTLR grammar for a Java project. Help me write lexer and parser rules in Directives.g4 for +two new tokens: + +BYTE_SIZE → matches values like "10KB", "2.5MB", "1GB". + +TIME_DURATION → matches values like "5ms", "3.2s", "1min". + +Also, include helpful fragments like BYTE_UNIT, TIME_UNIT. Finally, show how to update the value parser rule +(or create byteSizeArg, timeDurationArg if needed) so the new tokens are accepted as directive arguments. + + +Prompt 2: Create ByteSize and TimeDuration Token Classes +I am working on a Java project where tokens represent directive arguments. Help me create two new token classes: + +ByteSize.java and TimeDuration.java + +Each class should: + +Extend io.cdap.wrangler.api.parser.Token + +Parse strings like "10KB", "2.5MB", "1GB" (for ByteSize) and "500ms", "1.2s", "3min" (for TimeDuration) + +Internally store the value in canonical units (bytes for ByteSize, milliseconds or nanoseconds for TimeDuration) + +Provide getter methods like getBytes() and getMilliseconds() + + + Prompt 3: Update Token Types and Directive Argument Support +I am extending a token parsing framework in Java for a data transformation tool. Guide me to: + +Add two new token types: BYTE_SIZE and TIME_DURATION in the token registry or enum used (if any). + +Update the logic that defines valid argument types in directives, +so that BYTE_SIZE and TIME_DURATION can be accepted where appropriate. + +Mention any necessary updates in registration/configuration files or classes if applicable. + + + +Prompt 4: Add Visitor Methods for New Parser Rules +In my ANTLR-based Java parser for a directive language, +I’ve added two new parser rules: byteSizeArg and timeDurationArg. Help me: + +Implement visitor methods visitByteSizeArg and visitTimeDurationArg in the appropriate visitor or parser class. + +These methods should return instances of ByteSize and TimeDuration tokens respectively using ctx.getText(). + +Ensure these token instances are added to the TokenGroup for the directive being parsed. + +Prompt 5: Implement New AggregateStats Directive +I’m creating a new directive class called AggregateStats in a Java-based data transformation engine. Guide me to: + +Implement the Directive interface + +Accept at least 4 arguments: + +Source column (byte sizes) + +Source column (time durations) + +Target column for total size + +Target column for total/average time + +Optionally accept: + +Aggregation type (total, avg) + +Output unit (MB, GB, seconds, minutes) + +In initialize, store the argument values + +In execute, use ExecutorContext.getStore() to: + +Accumulate byte size and time duration values (convert to canonical units) + +In finalize, return a single Row with converted results (e.g., MB, seconds) + + +Prompt 6: Write Unit Tests for ByteSize and TimeDuration +Help me write JUnit tests for one Java class: ByteSize and TimeDuration. + These class parse strings like "10KB" and "500ms" respectively. + +Test valid cases: "10KB", "1.5MB", "1GB" for ByteSize and "500ms", "2s", "1min" for TimeDuration. + +Verify that getBytes() or getMilliseconds() return the correct canonical values. + +Include a few invalid input tests and assert that they throw proper exceptions. + + +Prompt 7: Write Parser Tests for New Grammar +I’ve added BYTE_SIZE and TIME_DURATION tokens to an ANTLR grammar. Help me write parser tests in Java to: + +Validate that inputs like "10KB", "1.5MB", "5ms", "3min" are accepted in directive recipes. + +Use test classes like GrammarBasedParserTest.java or RecipeCompilerTest.java. + +Also test invalid values (e.g., "10KBB", "1..5MB", "ms5") and ensure they are rejected. + + + +Prompt 8: Write Integration Test for AggregateStats Directive +I’ve created an AggregateStats directive that aggregates byte size and time duration columns. Help me write an integration test using TestingRig to: + +Create input data: List with columns like data_transfer_size and response_time using values like "1MB", "500KB", "2s", "500ms". + +Define recipe like: + +java +Copy +Edit +String[] recipe = new String[] { + "aggregate-stats :data_transfer_size :response_time total_size_mb total_time_sec" +}; +Execute with TestingRig.execute(recipe, rows) + +Assert that the resulting row contains correct aggregated values (in MB and seconds) + +Use a delta tolerance (e.g., 0.001) for comparing float values \ No newline at end of file diff --git a/wrangler-api/pom.xml b/wrangler-api/pom.xml index e97464a64..b595000dd 100644 --- a/wrangler-api/pom.xml +++ b/wrangler-api/pom.xml @@ -39,6 +39,35 @@ ${cdap.version} provided - + + + + org.apache.rat + apache-rat-plugin + + rat-excludes.txt + 2 + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.0.0 + + checkstyle.xml + suppressions.xml + + + + validate + validate + + check + + + + + + diff --git a/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/ByteSize.java b/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/ByteSize.java new file mode 100644 index 000000000..27fd543ab --- /dev/null +++ b/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/ByteSize.java @@ -0,0 +1,156 @@ +/* + * Copyright © 2017-2019 Cask Data, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + + package io.cdap.wrangler.api.parser; + + import com.google.gson.JsonElement; + import com.google.gson.JsonObject; + import io.cdap.wrangler.api.annotations.PublicEvolving; + + /** + * Represents a ByteSize token, capable of parsing strings like "10KB", "1.5MB", + * and converting them into bytes. + */ + @PublicEvolving + public class ByteSize implements Token { + + // Multipliers for each unit + private static final double KILOBYTE = 1024.0; + private static final double MEGABYTE = KILOBYTE * 1024.0; + private static final double GIGABYTE = MEGABYTE * 1024.0; + private static final double TERABYTE = GIGABYTE * 1024.0; + + // Parsed byte value stored as long + private final long bytesValue; + + /** + * Constructs a ByteSize token by parsing the given size string. + * + * @param sizeString The string to parse (e.g., "10KB", "1.5MB"). + * @throws IllegalArgumentException If the string format is invalid. + */ + public ByteSize(String sizeString) { + this.bytesValue = parseSize(sizeString); + } + + /** + * Parses a size string and converts it into bytes. + * + * @param sizeString The input string representing a byte size. + * @return The size in bytes. + */ + private long parseSize(String sizeString) { + if (sizeString == null || sizeString.trim().isEmpty()) { + throw new IllegalArgumentException("Size string must not be null or empty."); + } + + sizeString = sizeString.trim().toUpperCase(); + String numericPart; + double multiplier; + + try { + if (sizeString.endsWith("KB")) { + numericPart = sizeString.substring(0, sizeString.length() - 2); + multiplier = KILOBYTE; + } else if (sizeString.endsWith("MB")) { + numericPart = sizeString.substring(0, sizeString.length() - 2); + multiplier = MEGABYTE; + } else if (sizeString.endsWith("GB")) { + numericPart = sizeString.substring(0, sizeString.length() - 2); + multiplier = GIGABYTE; + } else if (sizeString.endsWith("TB")) { + numericPart = sizeString.substring(0, sizeString.length() - 2); + multiplier = TERABYTE; + } else if (sizeString.endsWith("B")) { + numericPart = sizeString.substring(0, sizeString.length() - 1); + multiplier = 1.0; + } else { + throw new IllegalArgumentException("Invalid byte size format or unsupported unit in string: " + sizeString); + } + + if (numericPart.isEmpty()) { + throw new IllegalArgumentException("Missing numeric value in size string: " + sizeString); + } + + double parsedValue = Double.parseDouble(numericPart); + if (parsedValue < 0) { + throw new IllegalArgumentException("Size value cannot be negative: " + sizeString); + } + + return (long) (parsedValue * multiplier); // Truncate to long + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid numeric value in size string: " + sizeString, e); + } + } + + /** + * @return Size in bytes. + */ + public long getBytes() { + return bytesValue; + } + + /** + * @return Size in kilobytes. + */ + public double getKiloBytes() { + return bytesValue / KILOBYTE; + } + + /** + * @return Size in megabytes. + */ + public double getMegaBytes() { + return bytesValue / MEGABYTE; + } + + /** + * @return Size in gigabytes. + */ + public double getGigaBytes() { + return bytesValue / GIGABYTE; + } + + /** + * @return Size in terabytes. + */ + public double getTeraBytes() { + return bytesValue / TERABYTE; + } + + @Override + public Object value() { + return bytesValue; + } + + @Override + public TokenType type() { + return TokenType.BYTE_SIZE; + } + + @Override + public JsonElement toJson() { + JsonObject object = new JsonObject(); + object.addProperty("type", TokenType.BYTE_SIZE.name()); + object.addProperty("value", bytesValue); + return object; + } + + @Override + public String toString() { + return bytesValue + "B"; + } + } \ No newline at end of file diff --git a/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/TimeDuration.java b/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/TimeDuration.java new file mode 100644 index 000000000..c86802d20 --- /dev/null +++ b/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/TimeDuration.java @@ -0,0 +1,155 @@ +/* + * Copyright © 2017-2019 Cask Data, Inc. + * Copyright © 2023 Google LLC // Update copyright year/holder if needed + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + + package io.cdap.wrangler.api.parser; + + import com.google.gson.JsonElement; + import com.google.gson.JsonObject; + import io.cdap.wrangler.api.annotations.PublicEvolving; + + import java.util.concurrent.TimeUnit; + + /** + * Represents a Token for time durations, capable of parsing strings + * like "5ms", "2.1s", or "3h" and converting them into milliseconds. + */ + @PublicEvolving + public class TimeDuration implements Token { + + // Time unit conversion factors (all in terms of milliseconds) + private static final double MILLIS_IN_NANOSECOND = 1.0 / 1_000_000.0; + private static final double MILLIS_IN_MICROSECOND = 1.0 / 1_000.0; + private static final double MILLIS_IN_SECOND = 1_000.0; + private static final double MILLIS_IN_MINUTE = 60.0 * MILLIS_IN_SECOND; + private static final double MILLIS_IN_HOUR = 60.0 * MILLIS_IN_MINUTE; + private static final double MILLIS_IN_DAY = 24.0 * MILLIS_IN_HOUR; + + // Final parsed duration value (in milliseconds) + private final double durationMillis; + + /** + * Constructs a TimeDuration object by parsing the provided duration string. + * + * @param inputDuration A string representing duration (e.g., "5ms", "2.5h"). + * @throws IllegalArgumentException if the string format is invalid. + */ + public TimeDuration(String inputDuration) { + this.durationMillis = parseDuration(inputDuration); + } + + /** + * Parses a duration string and converts it to milliseconds. + * + * @param durationStr Input duration string to parse. + * @return Duration value in milliseconds. + * @throws IllegalArgumentException if format or value is invalid. + */ + private double parseDuration(String durationStr) { + if (durationStr == null || durationStr.trim().isEmpty()) { + throw new IllegalArgumentException("Duration string must not be null or empty."); + } + + durationStr = durationStr.trim().toLowerCase(); + String numericPart; + double conversionFactor; + + try { + if (durationStr.endsWith("ns")) { + numericPart = durationStr.substring(0, durationStr.length() - 2); + conversionFactor = MILLIS_IN_NANOSECOND; + } else if (durationStr.endsWith("us")) { + numericPart = durationStr.substring(0, durationStr.length() - 2); + conversionFactor = MILLIS_IN_MICROSECOND; + } else if (durationStr.endsWith("ms")) { + numericPart = durationStr.substring(0, durationStr.length() - 2); + conversionFactor = 1.0; + } else if (durationStr.endsWith("s")) { + numericPart = durationStr.substring(0, durationStr.length() - 1); + conversionFactor = MILLIS_IN_SECOND; + } else if (durationStr.endsWith("min")) { + numericPart = durationStr.substring(0, durationStr.length() - 3); + conversionFactor = MILLIS_IN_MINUTE; + } else if (durationStr.endsWith("m")) { + numericPart = durationStr.substring(0, durationStr.length() - 1); + conversionFactor = MILLIS_IN_MINUTE; + } else if (durationStr.endsWith("h")) { + numericPart = durationStr.substring(0, durationStr.length() - 1); + conversionFactor = MILLIS_IN_HOUR; + } else if (durationStr.endsWith("d")) { + numericPart = durationStr.substring(0, durationStr.length() - 1); + conversionFactor = MILLIS_IN_DAY; + } else { + throw new IllegalArgumentException( + "Invalid time duration format or unsupported unit in string: " + durationStr); + } + + if (numericPart.isEmpty()) { + throw new IllegalArgumentException("Missing numeric value in duration string: " + durationStr); + } + + double parsedNumber = Double.parseDouble(numericPart); + if (parsedNumber < 0) { + throw new IllegalArgumentException("Duration value cannot be negative: " + durationStr); + } + + return parsedNumber * conversionFactor; + + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid numeric value in duration string: " + durationStr, e); + } + } + + /** + * Converts and returns the duration in the given {@link TimeUnit}. + * + * @param unit Time unit to convert to. + * @return Converted duration in the specified unit (rounded). + */ + public long getDuration(TimeUnit unit) { + return unit.convert((long) this.durationMillis, TimeUnit.MILLISECONDS); + } + + /** + * @return Canonical duration value in milliseconds. + */ + public double getValue() { + return durationMillis; + } + + @Override + public Object value() { + return durationMillis; + } + + @Override + public TokenType type() { + return TokenType.TIME_DURATION; + } + + @Override + public JsonElement toJson() { + JsonObject object = new JsonObject(); + object.addProperty("type", TokenType.TIME_DURATION.name()); + object.addProperty("value", durationMillis); + return object; + } + + @Override + public String toString() { + return durationMillis + "ms"; + } + } \ No newline at end of file diff --git a/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/TokenType.java b/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/TokenType.java index 8c93b0e6a..04a796945 100644 --- a/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/TokenType.java +++ b/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/TokenType.java @@ -40,6 +40,8 @@ * @see Expression * @see Text * @see TextList + * @see ByteSize + * @see TimeDuration */ @PublicEvolving public enum TokenType implements Serializable { @@ -152,5 +154,22 @@ public enum TokenType implements Serializable { * Represents the enumerated type for the object of type {@code String} with restrictions * on characters that can be present in a string. */ - IDENTIFIER + IDENTIFIER, + + + /** + * Enum representing specialized data types that require unit-based parsing. + * Indicates a value of type {@code ByteSize}, which consists of a numeric component + * followed by a byte unit (e.g., B, KB, MB, GB, TB, PB). + * Examples include: "10KB", "1.5MB", "2GB". + */ + BYTE_SIZE, + + /** + * Represents the enumerated type for the object of type {@code TimeDuration} type. + * This type is associated with a numeric value followed by a time unit (e.g.,ns, ms, s, m, h, d). + * For example: "100ms", "5s", "2.5h". + */ + TIME_DURATION, + } diff --git a/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/UsageDefinition.java b/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/UsageDefinition.java index 78800b7d1..bc4bad9ce 100644 --- a/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/UsageDefinition.java +++ b/wrangler-api/src/main/java/io/cdap/wrangler/api/parser/UsageDefinition.java @@ -126,6 +126,10 @@ public String toString() { sb.append("prop:{key:value,[key:value]*"); } else if (token.type().equals(TokenType.RANGES)) { sb.append("start:end=[bool|text|numeric][,start:end=[bool|text|numeric]*"); + } else if (token.type().equals(TokenType.BYTE_SIZE)) { + sb.append(":").append(token.name()); + } else if (token.type().equals(TokenType.TIME_DURATION)) { + sb.append(":").append(token.name()); } } diff --git a/wrangler-core/pom.xml b/wrangler-core/pom.xml index e2dcb3c2b..e3e6069e8 100644 --- a/wrangler-core/pom.xml +++ b/wrangler-core/pom.xml @@ -45,6 +45,7 @@ io.cdap.wrangler wrangler-api ${project.version} + compile io.cdap.wrangler @@ -99,6 +100,12 @@ 2.0.2 test + + junit + junit + 4.13.2 + test + org.powermock powermock-api-mockito2 @@ -320,6 +327,16 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + 1 + true + --add-opens java.base/java.lang=ALL-UNNAMED + + org.antlr antlr4-maven-plugin diff --git a/wrangler-core/src/main/antlr4/io/cdap/wrangler/parser/Directives.g4 b/wrangler-core/src/main/antlr4/io/cdap/wrangler/parser/Directives.g4 index 7c517ed6a..1a62308ca 100644 --- a/wrangler-core/src/main/antlr4/io/cdap/wrangler/parser/Directives.g4 +++ b/wrangler-core/src/main/antlr4/io/cdap/wrangler/parser/Directives.g4 @@ -57,6 +57,8 @@ directive | text | number | bool + | byteSizeArg + | timeDurationArg | column | colList | numberList @@ -140,7 +142,7 @@ numberRange ; value - : String | Number | Column | Bool + : String | Number | Column | Bool | ByteSize | TimeDuration ; ecommand @@ -167,6 +169,14 @@ bool : Bool ; +byteSizeArg + : BYTE_SIZE + ; + +timeDurationArg + : TIME_DURATION + ; + condition : OBrace (~CBrace | condition)* CBrace ; @@ -253,6 +263,14 @@ Bool | 'false' ; +BYTE_SIZE + : DecimalNumber BYTE_UNIT + ; + +TIME_DURATION + : DecimalNumber TIME_UNIT + ; + Number : Int ('.' Digit*)? ; @@ -311,3 +329,29 @@ fragment Int fragment Digit : [0-9] ; + + +fragment DecimalNumber + : Digit+ ('.' Digit+)? // Matches decimal numbers like 123, 1.5, 0.25, etc. + ; + + +fragment ByteUnit + : [kK][bB] // Kilobytes + | [mM][bB] // Megabytes + | [gG][bB] // Gigabytes + | [tT][bB] // Terabytes + | [pP][bB] // Petabytes + | [bB] // Bytes + ; + + +fragment TimeUnit + : [nN][sS] // Nanoseconds + | [uU][sS] // Microseconds + | [mM][sS] // Milliseconds + | [sS] // Seconds + | [mM] // Minutes + | [hH] // Hours + | [dD] // Days + ; \ No newline at end of file diff --git a/wrangler-core/src/main/java/io/cdap/directives/aggregates/aggregateStats.java b/wrangler-core/src/main/java/io/cdap/directives/aggregates/aggregateStats.java new file mode 100644 index 000000000..cfe5dd76d --- /dev/null +++ b/wrangler-core/src/main/java/io/cdap/directives/aggregates/aggregateStats.java @@ -0,0 +1,180 @@ +/* + * Copyright © 2017-2019 Cask Data, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package io.cdap.directives.aggregates; + +import io.cdap.cdap.api.annotation.Name; + +import io.cdap.cdap.api.annotation.Plugin; + +import io.cdap.cdap.api.data.schema.Schema; + +import io.cdap.wrangler.api.Arguments; + +import io.cdap.wrangler.api.Directive; + +import io.cdap.wrangler.api.DirectiveExecutionException; + +import io.cdap.wrangler.api.DirectiveParseException; + +import io.cdap.wrangler.api.ExecutorContext; + +import io.cdap.wrangler.api.Row; + +import io.cdap.wrangler.api.annotations.Categories; + +import io.cdap.wrangler.api.parser.ByteSize; + +import io.cdap.wrangler.api.parser.ColumnName; + +import io.cdap.wrangler.api.parser.Text; + +import io.cdap.wrangler.api.parser.TimeDuration; + +import io.cdap.wrangler.api.parser.TokenType; + +import io.cdap.wrangler.api.parser.UsageDefinition; + +import java.util.ArrayList; + +import java.util.List; +/** + * Directive to aggregate statistics over a dataset. It computes + * total bytes and total time, then outputs the summary in MB and seconds. + */ +@Plugin(type = Directive.TYPE) +@Name(AggregateStats.NAME) +@Categories(categories = { "data-aggregation" }) +public class AggregateStats implements Directive { + + public static final String NAME = "aggregate-stats"; + + // Input column names + private String inputByteColumn; + private String inputTimeColumn; + + // Output column names + private String resultByteColumn; + private String resultTimeColumn; + + // Aggregated values + private double accumulatedBytes = 0.0; + private double accumulatedTimeMs = 0.0; + private int processedRowCount = 0; + + /** + * Defines the parameters required to use this directive. + * + * @return UsageDefinition instance outlining directive inputs. + */ + @Override + public UsageDefinition define() { + UsageDefinition.Builder builder = UsageDefinition.builder(NAME); + builder.define("byteCol", TokenType.COLUMN_NAME); + builder.define("timeCol", TokenType.COLUMN_NAME); + builder.define("outputSizeCol", TokenType.TEXT); + builder.define("outputTimeCol", TokenType.TEXT); + return builder.build(); + } + + /** + * Initializes the directive with user-provided arguments. + * + * @param args The input arguments. + * @throws DirectiveParseException if parsing fails. + */ + @Override + public void initialize(Arguments args) throws DirectiveParseException { + inputByteColumn = ((ColumnName) args.value("byteCol")).value(); + inputTimeColumn = ((ColumnName) args.value("timeCol")).value(); + resultByteColumn = ((Text) args.value("outputSizeCol")).value(); + resultTimeColumn = ((Text) args.value("outputTimeCol")).value(); + } + + /** + * Executes aggregation logic over input rows. + * + * @param rows List of input rows. + * @param ctx Execution context. + * @return List containing a single row with aggregated metrics. + * @throws DirectiveExecutionException if any error occurs during execution. + */ + @Override + public List execute(List rows, ExecutorContext ctx) throws DirectiveExecutionException { + try { + for (Row row : rows) { + if (row.find(inputByteColumn) != -1 && row.find(inputTimeColumn) != -1) { + String byteValue = row.getValue(inputByteColumn).toString(); + String timeValue = row.getValue(inputTimeColumn).toString(); + + // Parse input values + ByteSize byteSize = new ByteSize(byteValue); + TimeDuration duration = new TimeDuration(timeValue); + + accumulatedBytes += byteSize.getBytes(); + accumulatedTimeMs += duration.getValue(); + processedRowCount++; + } + } + + if (processedRowCount == 0) { + return new ArrayList<>(); + } + + // Prepare output + List results = new ArrayList<>(); + Row resultRow = new Row(); + + resultRow.add(resultByteColumn, accumulatedBytes / (1024.0 * 1024.0)); // Convert bytes to MB + resultRow.add(resultTimeColumn, accumulatedTimeMs / 1000.0); // Convert ms to seconds + + results.add(resultRow); + return results; + + } catch (Exception e) { + throw new DirectiveExecutionException( + String.format("Error aggregating stats: %s", e.getMessage()) + ); + } + } + + /** + * Defines the output schema for the transformed dataset. + * + * @param inputSchema The schema of incoming data. + * @return Schema of the resulting data. + */ + public Schema getOutputSchema(Schema inputSchema) { + List fields = new ArrayList<>(); + fields.add(Schema.Field.of(resultByteColumn, Schema.of(Schema.Type.DOUBLE))); + fields.add(Schema.Field.of(resultTimeColumn, Schema.of(Schema.Type.DOUBLE))); + return Schema.recordOf("aggregate-stats", fields); + } + + /** + * Cleans up and resets the internal state of the directive. + */ + @Override + public void destroy() { + accumulatedBytes = 0.0; + accumulatedTimeMs = 0.0; + processedRowCount = 0; + + inputByteColumn = null; + inputTimeColumn = null; + resultByteColumn = null; + resultTimeColumn = null; + } +} \ No newline at end of file diff --git a/wrangler-core/src/main/java/io/cdap/wrangler/parser/RecipeVisitor.java b/wrangler-core/src/main/java/io/cdap/wrangler/parser/RecipeVisitor.java index ac35e7a5e..f9503cb59 100644 --- a/wrangler-core/src/main/java/io/cdap/wrangler/parser/RecipeVisitor.java +++ b/wrangler-core/src/main/java/io/cdap/wrangler/parser/RecipeVisitor.java @@ -22,6 +22,7 @@ import io.cdap.wrangler.api.Triplet; import io.cdap.wrangler.api.parser.Bool; import io.cdap.wrangler.api.parser.BoolList; +import io.cdap.wrangler.api.parser.ByteSize; import io.cdap.wrangler.api.parser.ColumnName; import io.cdap.wrangler.api.parser.ColumnNameList; import io.cdap.wrangler.api.parser.DirectiveName; @@ -33,6 +34,7 @@ import io.cdap.wrangler.api.parser.Ranges; import io.cdap.wrangler.api.parser.Text; import io.cdap.wrangler.api.parser.TextList; +import io.cdap.wrangler.api.parser.TimeDuration; import io.cdap.wrangler.api.parser.Token; import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.misc.Interval; @@ -317,6 +319,39 @@ public RecipeSymbol.Builder visitStringList(DirectivesParser.StringListContext c return builder; } + /** + * Parses a byte size argument (e.g., "10MB") from the directive script and creates + * a {@link ByteSize} token. This token is then added to the directive builder for + * further processing during execution. + * + * @param ctx the parser context representing a BYTE_SIZE argument + * @return the updated {@link RecipeSymbol.Builder} instance + */ +@Override +public RecipeSymbol.Builder visitByteSizeArg(DirectivesParser.ByteSizeArgContext ctx) { + String byteSizeValue = ctx.BYTE_SIZE().getText(); // e.g., "10MB" + ByteSize byteSize = new ByteSize(byteSizeValue); + builder.addToken(byteSize); + return builder; +} + +/** + * Parses a time duration argument (e.g., "5s", "1h") from the directive script and creates + * a {@link TimeDuration} token. This token is added to the directive builder to support + * duration-based operations within directives. + * + * @param ctx the parser context representing a TIME_DURATION argument + * @return the updated {@link RecipeSymbol.Builder} instance + */ +@Override +public RecipeSymbol.Builder visitTimeDurationArg(DirectivesParser.TimeDurationArgContext ctx) { + String durationValue = ctx.TIME_DURATION().getText(); // e.g., "5s" + TimeDuration timeDuration = new TimeDuration(durationValue); + builder.addToken(timeDuration); + return builder; +} + + private SourceInfo getOriginalSource(ParserRuleContext ctx) { int a = ctx.getStart().getStartIndex(); int b = ctx.getStop().getStopIndex(); diff --git a/wrangler-core/src/test/java/io/cdap/directives/aggregates/aggregatestats.java b/wrangler-core/src/test/java/io/cdap/directives/aggregates/aggregatestats.java new file mode 100644 index 000000000..f7e68814b --- /dev/null +++ b/wrangler-core/src/test/java/io/cdap/directives/aggregates/aggregatestats.java @@ -0,0 +1,189 @@ +/* + * Copyright © 2017-2019 Cask Data, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package io.cdap.directives.aggregates; + +import com.google.gson.JsonElement; +import io.cdap.wrangler.api.Arguments; +import io.cdap.wrangler.api.Row; +import io.cdap.wrangler.api.parser.ColumnName; +import io.cdap.wrangler.api.parser.Text; +import io.cdap.wrangler.api.parser.Token; +import io.cdap.wrangler.api.parser.TokenType; +import org.junit.Assert; +import org.junit.Test; +import java.util.ArrayList; +import java.util.List; + +public class AggregateStatsTest { + + @Test + public void testAggregateStatsDirective() throws Exception { + // Prepare sample input rows with size in KB and response time in seconds/milliseconds + List rows = new ArrayList<>(); + rows.add(new Row().add("data_transfer_size", "12KB").add("response_time", "1.2s")); + rows.add(new Row().add("data_transfer_size", "8KB").add("response_time", "800ms")); + + // Instantiate the directive + AggregateStats directive = new AggregateStats(); + + // Mock the arguments to pass to the directive + Arguments mockArgs = createMockArguments(); + + // Initialize and execute the directive + directive.initialize(mockArgs); + List result = directive.execute(rows, null); + + // Validate output row count + Assert.assertEquals(1, result.size()); + Row output = result.get(0); + + // Expected: 20KB = 20480 bytes → 20480 / (1024 * 1024) + double expectedMB = 20480.0 / (1024 * 1024); + double actualMB = (Double) output.getValue("total_size_mb"); + + // Expected time: 1.2s + 0.8s = 2.0s + double expectedSeconds = 2.0; + double actualSeconds = (Double) output.getValue("total_time_sec"); + + Assert.assertEquals(expectedMB, actualMB, 0.001); + Assert.assertEquals(expectedSeconds, actualSeconds, 0.001); + } + + @Test + public void testEmptyInputRows() throws Exception { + // Test case with no input rows + List rows = new ArrayList<>(); + AggregateStats directive = new AggregateStats(); + Arguments mockArgs = createMockArguments(); + + directive.initialize(mockArgs); + List result = directive.execute(rows, null); + + Assert.assertEquals(0, result.size()); + } + + @Test + public void testInvalidData() throws Exception { + // Input rows with invalid formats + List rows = new ArrayList<>(); + rows.add(new Row().add("data_transfer_size", "abcKB").add("response_time", "xyzTime")); + + AggregateStats directive = new AggregateStats(); + Arguments mockArgs = createMockArguments(); + + directive.initialize(mockArgs); + try { + directive.execute(rows, null); + Assert.fail("Expected an exception due to invalid data format"); + } catch (Exception e) { + Assert.assertTrue(e.getMessage().contains("Error aggregating stats")); + } + } + + @Test + public void testLargeData() throws Exception { + // Test with large values in MB and time in hours/minutes + List rows = new ArrayList<>(); + rows.add(new Row().add("data_transfer_size", "2048MB").add("response_time", "1h")); + rows.add(new Row().add("data_transfer_size", "1024MB").add("response_time", "30m")); + + AggregateStats directive = new AggregateStats(); + Arguments mockArgs = createMockArguments(); + + directive.initialize(mockArgs); + List result = directive.execute(rows, null); + + Assert.assertEquals(1, result.size()); + + Row output = result.get(0); + + double expectedMB = 3072.0; // 2048MB + 1024MB + double actualMB = (Double) output.getValue("total_size_mb"); + + double expectedSeconds = 5400.0; // 1h + 30m = 3600 + 1800 = 5400s + double actualSeconds = (Double) output.getValue("total_time_sec"); + + Assert.assertEquals(expectedMB, actualMB, 0.001); + Assert.assertEquals(expectedSeconds, actualSeconds, 0.001); + } + + @Test + public void testEdgeCases() throws Exception { + // Edge case: zero data and zero time + List rows = new ArrayList<>(); + rows.add(new Row().add("data_transfer_size", "0KB").add("response_time", "0s")); + + AggregateStats directive = new AggregateStats(); + Arguments mockArgs = createMockArguments(); + + directive.initialize(mockArgs); + List result = directive.execute(rows, null); + + Assert.assertEquals(1, result.size()); + + Row output = result.get(0); + + double actualMB = (Double) output.getValue("total_size_mb"); + double actualSeconds = (Double) output.getValue("total_time_sec"); + + Assert.assertEquals(0.0, actualMB, 0.001); + Assert.assertEquals(0.0, actualSeconds, 0.001); + } + + // Create mock implementation of Arguments for unit testing + private Arguments createMockArguments() { + return new Arguments() { + @SuppressWarnings("unchecked") + @Override + public T value(String name) { + switch (name) { + case "byteCol": return (T) new ColumnName("data_transfer_size"); + case "timeCol": return (T) new ColumnName("response_time"); + case "outputSizeCol": return (T) new Text("total_size_mb"); + case "outputTimeCol": return (T) new Text("total_time_sec"); + } + return null; + } + + @Override public int size() { + return 4; + } + + @Override public boolean contains(String name) { + return true; + } + + @Override public TokenType type(String name) { + return null; + } + + @Override public int line() { + return 1; + } + + @Override public int column() { + return 0; + } + + @Override public String source() { + return "aggregate-stats :data_transfer_size :response_time total_size_mb total_time_sec"; + } + @Override public JsonElement toJson() { + return null; + } + }; + } +} \ No newline at end of file diff --git a/wrangler-core/src/test/java/io/cdap/directives/parser/ByteSizeAndTimeDurationTest.java b/wrangler-core/src/test/java/io/cdap/directives/parser/ByteSizeAndTimeDurationTest.java new file mode 100644 index 000000000..342129498 --- /dev/null +++ b/wrangler-core/src/test/java/io/cdap/directives/parser/ByteSizeAndTimeDurationTest.java @@ -0,0 +1,61 @@ +/* + * Copyright © 2017-2019 Cask Data, Inc. + * Copyright © 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + package io.cdap.wrangler.parser; + + import io.cdap.wrangler.api.parser.ByteSize; + import io.cdap.wrangler.api.parser.TimeDuration; + import org.junit.Assert; + import org.junit.Test; + + /** + * Unit tests for validating {@link ByteSize} and {@link TimeDuration} parsing and conversions. + */ + public class ByteSizeAndTimeDurationTest { + + @Test + public void testByteSizeParsing() { + // Validating conversion from string to bytes + + Assert.assertEquals(2048L, new ByteSize("2KB").getBytes()); // 2 KB = 2048 bytes + Assert.assertEquals(5242880L, new ByteSize("5MB").getBytes()); // 5 MB = 5 * 1024 * 1024 + Assert.assertEquals(2147483648L, new ByteSize("2GB").getBytes()); // 2 GB = 2 * 1024^3 + Assert.assertEquals(256L, new ByteSize("256B").getBytes()); // 256 bytes + Assert.assertEquals(7340032L, new ByteSize("7MB").getBytes()); // 7 MB + Assert.assertEquals(3145728L, new ByteSize("3MB").getBytes()); // 3 MB + + // Case-insensitive unit support + Assert.assertEquals(1572864L, new ByteSize("1.5mb").getBytes()); // 1.5 MB (lowercase) + Assert.assertEquals(4096L, new ByteSize("4kB").getBytes()); // 4 KB (mixed case) + } + + @Test + public void testTimeDurationParsing() { + // Validating conversion from string to milliseconds + double delta = 0.0001; + + Assert.assertEquals(10.0, new TimeDuration("10ms").getValue(), delta); // 10 milliseconds + Assert.assertEquals(1500.0, new TimeDuration("1.5s").getValue(), delta); // 1.5 seconds + Assert.assertEquals(7200000.0, new TimeDuration("2h").getValue(), delta); // 2 hours + Assert.assertEquals(180000.0, new TimeDuration("3min").getValue(), delta); // 3 minutes + Assert.assertEquals(0.5, new TimeDuration("500us").getValue(), delta); // 500 microseconds + Assert.assertEquals(2.5, new TimeDuration("2500000ns").getValue(), delta); // 2.5 microseconds + Assert.assertEquals(172800000.0, new TimeDuration("2d").getValue(), delta); // 2 days + } +} + + \ No newline at end of file diff --git a/wrangler-test/pom.xml b/wrangler-test/pom.xml index 5e4878d86..d1b84789b 100644 --- a/wrangler-test/pom.xml +++ b/wrangler-test/pom.xml @@ -27,6 +27,12 @@ Wrangler Testing Framework + + io.cdap.wrangler + wrangler-api + ${project.version} + test + io.cdap.wrangler wrangler-core From fcb2ec450d7807a5c5ace26b4fd67918e6c8be2a Mon Sep 17 00:00:00 2001 From: chayan das <110921638+Chayandas07@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:29:52 +0530 Subject: [PATCH 2/5] fix: Remove unnecessary lines in prompts.text for clarity --- README.md | 1 + README.pdf | Bin 148260 -> 148260 bytes prompts.text | 3 +-- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4f69a1263..604ecc80f 100644 --- a/README.md +++ b/README.md @@ -237,5 +237,6 @@ and limitations under the License. Cask is a trademark of Cask Data, Inc. All rights reserved. + Apache, Apache HBase, and HBase are trademarks of The Apache Software Foundation. Used with permission. No endorsement by The Apache Software Foundation is implied by the use of these marks. diff --git a/README.pdf b/README.pdf index b31b169bd3d96f6f65fd57381ae40bba9cc7b295..5bfd086b61c6bcd95cfa40a9ccf4056b03ab23a7 100644 GIT binary patch delta 29 icmZ3|#<`@8b3ziciK)rNR0R;#*wNa-xV3|6juQZsTnXa< delta 29 icmZ3|#<`@8b3zick%8&NR0R;#*wNa-xV3|6juQZroC((e diff --git a/prompts.text b/prompts.text index 0ec6989fb..5becc4987 100644 --- a/prompts.text +++ b/prompts.text @@ -108,8 +108,7 @@ Create input data: List with columns like data_transfer_size and response_t Define recipe like: java -Copy -Edit + String[] recipe = new String[] { "aggregate-stats :data_transfer_size :response_time total_size_mb total_time_sec" }; From 48df1962dc74fdf112d55082d636a4515b88746a Mon Sep 17 00:00:00 2001 From: chayan das <110921638+Chayandas07@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:33:55 +0530 Subject: [PATCH 3/5] test: add unit tests for ByteSize and TimeDuration parsing logic --- prompts.text | 1 + 1 file changed, 1 insertion(+) diff --git a/prompts.text b/prompts.text index 5becc4987..f15a2c2e4 100644 --- a/prompts.text +++ b/prompts.text @@ -100,6 +100,7 @@ Also test invalid values (e.g., "10KBB", "1..5MB", "ms5") and ensure they are re + Prompt 8: Write Integration Test for AggregateStats Directive I’ve created an AggregateStats directive that aggregates byte size and time duration columns. Help me write an integration test using TestingRig to: From 6ce2875f849989d52a6cf4ffed6daff0de09d3cf Mon Sep 17 00:00:00 2001 From: Chayandas07 Date: Wed, 16 Apr 2025 20:50:23 +0530 Subject: [PATCH 4/5] done --- prompts.text | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prompts.text b/prompts.text index f15a2c2e4..e489775a5 100644 --- a/prompts.text +++ b/prompts.text @@ -48,6 +48,8 @@ These methods should return instances of ByteSize and TimeDuration tokens respec Ensure these token instances are added to the TokenGroup for the directive being parsed. + + Prompt 5: Implement New AggregateStats Directive I’m creating a new directive class called AggregateStats in a Java-based data transformation engine. Guide me to: @@ -89,6 +91,8 @@ Verify that getBytes() or getMilliseconds() return the correct canonical values. Include a few invalid input tests and assert that they throw proper exceptions. + + Prompt 7: Write Parser Tests for New Grammar I’ve added BYTE_SIZE and TIME_DURATION tokens to an ANTLR grammar. Help me write parser tests in Java to: From 6b9d7aaf509dcdb11c57c75a997b0f2c5d583a5f Mon Sep 17 00:00:00 2001 From: Chayandas07 Date: Wed, 16 Apr 2025 20:55:09 +0530 Subject: [PATCH 5/5] done --- prompts.text | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prompts.text b/prompts.text index e489775a5..f5a273d1b 100644 --- a/prompts.text +++ b/prompts.text @@ -10,6 +10,7 @@ Also, include helpful fragments like BYTE_UNIT, TIME_UNIT. Finally, show how to (or create byteSizeArg, timeDurationArg if needed) so the new tokens are accepted as directive arguments. + Prompt 2: Create ByteSize and TimeDuration Token Classes I am working on a Java project where tokens represent directive arguments. Help me create two new token classes: @@ -26,7 +27,7 @@ Internally store the value in canonical units (bytes for ByteSize, milliseconds Provide getter methods like getBytes() and getMilliseconds() - Prompt 3: Update Token Types and Directive Argument Support +Prompt 3: Update Token Types and Directive Argument Support I am extending a token parsing framework in Java for a data transformation tool. Guide me to: Add two new token types: BYTE_SIZE and TIME_DURATION in the token registry or enum used (if any).