From c0fc5cdde5042d5421001495aef35d846e26d433 Mon Sep 17 00:00:00 2001 From: Caleb Miller Date: Thu, 6 Jun 2024 14:13:26 -0600 Subject: [PATCH 1/3] Add ETL exercise --- config.json | 8 ++ exercises/practice/etl/.docs/instructions.md | 27 +++++ exercises/practice/etl/.docs/introduction.md | 16 +++ exercises/practice/etl/.meta/config.json | 22 ++++ exercises/practice/etl/.meta/example.sql | 10 ++ exercises/practice/etl/.meta/tests.toml | 22 ++++ exercises/practice/etl/canonical-data.json | 105 +++++++++++++++++++ exercises/practice/etl/create_fixture.sql | 8 ++ exercises/practice/etl/data.csv | 4 + exercises/practice/etl/etl.sql | 2 + exercises/practice/etl/etl_test.sql | 29 +++++ exercises/practice/etl/test_reporter.sql | 16 +++ exercises/practice/etl/test_setup.sql | 25 +++++ 13 files changed, 294 insertions(+) create mode 100644 exercises/practice/etl/.docs/instructions.md create mode 100644 exercises/practice/etl/.docs/introduction.md create mode 100644 exercises/practice/etl/.meta/config.json create mode 100644 exercises/practice/etl/.meta/example.sql create mode 100644 exercises/practice/etl/.meta/tests.toml create mode 100644 exercises/practice/etl/canonical-data.json create mode 100644 exercises/practice/etl/create_fixture.sql create mode 100644 exercises/practice/etl/data.csv create mode 100644 exercises/practice/etl/etl.sql create mode 100644 exercises/practice/etl/etl_test.sql create mode 100644 exercises/practice/etl/test_reporter.sql create mode 100644 exercises/practice/etl/test_setup.sql diff --git a/config.json b/config.json index bfe4b2f..9b9d128 100644 --- a/config.json +++ b/config.json @@ -217,6 +217,14 @@ "practices": [], "prerequisites": [], "difficulty": 8 + }, + { + "slug": "etl", + "name": "ETL", + "uuid": "63792af3-0fce-4c90-8cc3-4844ad6b6861", + "practices": [], + "prerequisites": [], + "difficulty": 5 } ] }, diff --git a/exercises/practice/etl/.docs/instructions.md b/exercises/practice/etl/.docs/instructions.md new file mode 100644 index 0000000..802863b --- /dev/null +++ b/exercises/practice/etl/.docs/instructions.md @@ -0,0 +1,27 @@ +# Instructions + +Your task is to change the data format of letters and their point values in the game. + +Currently, letters are stored in groups based on their score, in a one-to-many mapping. + +- 1 point: "A", "E", "I", "O", "U", "L", "N", "R", "S", "T", +- 2 points: "D", "G", +- 3 points: "B", "C", "M", "P", +- 4 points: "F", "H", "V", "W", "Y", +- 5 points: "K", +- 8 points: "J", "X", +- 10 points: "Q", "Z", + +This needs to be changed to store each individual letter with its score in a one-to-one mapping. + +- "a" is worth 1 point. +- "b" is worth 3 points. +- "c" is worth 3 points. +- "d" is worth 2 points. +- etc. + +As part of this change, the team has also decided to change the letters to be lower-case rather than upper-case. + +~~~~exercism/note +If you want to look at how the data was previously structured and how it needs to change, take a look at the examples in the test suite. +~~~~ diff --git a/exercises/practice/etl/.docs/introduction.md b/exercises/practice/etl/.docs/introduction.md new file mode 100644 index 0000000..5be6514 --- /dev/null +++ b/exercises/practice/etl/.docs/introduction.md @@ -0,0 +1,16 @@ +# Introduction + +You work for a company that makes an online multiplayer game called Lexiconia. + +To play the game, each player is given 13 letters, which they must rearrange to create words. +Different letters have different point values, since it's easier to create words with some letters than others. + +The game was originally launched in English, but it is very popular, and now the company wants to expand to other languages as well. + +Different languages need to support different point values for letters. +The point values are determined by how often letters are used, compared to other letters in that language. + +For example, the letter 'C' is quite common in English, and is only worth 3 points. +But in Norwegian it's a very rare letter, and is worth 10 points. + +To make it easier to add new languages, your team needs to change the way letters and their point values are stored in the game. diff --git a/exercises/practice/etl/.meta/config.json b/exercises/practice/etl/.meta/config.json new file mode 100644 index 0000000..5f4b82f --- /dev/null +++ b/exercises/practice/etl/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "Steffan153" + ], + "files": { + "solution": [ + "etl.sql" + ], + "test": [ + "etl_test.sql" + ], + "example": [ + ".meta/example.sql" + ], + "editor": [ + "data.csv" + ] + }, + "blurb": "Change the data format for scoring a game to more easily add other languages.", + "source": "Based on an exercise by the JumpstartLab team for students at The Turing School of Software and Design.", + "source_url": "https://turing.edu" +} diff --git a/exercises/practice/etl/.meta/example.sql b/exercises/practice/etl/.meta/example.sql new file mode 100644 index 0000000..a6e6240 --- /dev/null +++ b/exercises/practice/etl/.meta/example.sql @@ -0,0 +1,10 @@ +UPDATE etl +SET result = ( + SELECT json_group_object(LOWER(value), TRIM(path, '$."') ->> '$') + FROM ( + SELECT value, path + FROM json_tree(input) + WHERE type = 'text' + ORDER BY value + ) +); diff --git a/exercises/practice/etl/.meta/tests.toml b/exercises/practice/etl/.meta/tests.toml new file mode 100644 index 0000000..e937107 --- /dev/null +++ b/exercises/practice/etl/.meta/tests.toml @@ -0,0 +1,22 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[78a7a9f9-4490-4a47-8ee9-5a38bb47d28f] +description = "single letter" + +[60dbd000-451d-44c7-bdbb-97c73ac1f497] +description = "single score with multiple letters" + +[f5c5de0c-301f-4fdd-a0e5-df97d4214f54] +description = "multiple scores with multiple letters" + +[5db8ea89-ecb4-4dcd-902f-2b418cc87b9d] +description = "multiple scores with differing numbers of letters" diff --git a/exercises/practice/etl/canonical-data.json b/exercises/practice/etl/canonical-data.json new file mode 100644 index 0000000..2734af6 --- /dev/null +++ b/exercises/practice/etl/canonical-data.json @@ -0,0 +1,105 @@ +{ + "exercise": "etl", + "comments": [ + "Transforms a set of legacy Lexiconia data stored as letters per score", + "to a set of data stored score per letter.", + "Note: The expected input data for these tests should have", + "integer keys (not stringified numbers as shown in the JSON below", + "Unless the language prohibits that, please implement these tests", + "such that keys are integers. e.g. in JavaScript, it might look ", + "like `transform( { 1: ['A'] } );`" + ], + "cases": [ + { + "uuid": "78a7a9f9-4490-4a47-8ee9-5a38bb47d28f", + "description": "single letter", + "property": "transform", + "input": { + "legacy": { + "1": ["A"] + } + }, + "expected": { + "a": 1 + } + }, + { + "uuid": "60dbd000-451d-44c7-bdbb-97c73ac1f497", + "description": "single score with multiple letters", + "property": "transform", + "input": { + "legacy": { + "1": ["A", "E", "I", "O", "U"] + } + }, + "expected": { + "a": 1, + "e": 1, + "i": 1, + "o": 1, + "u": 1 + } + }, + { + "uuid": "f5c5de0c-301f-4fdd-a0e5-df97d4214f54", + "description": "multiple scores with multiple letters", + "property": "transform", + "input": { + "legacy": { + "1": ["A", "E"], + "2": ["D", "G"] + } + }, + "expected": { + "a": 1, + "d": 2, + "e": 1, + "g": 2 + } + }, + { + "uuid": "5db8ea89-ecb4-4dcd-902f-2b418cc87b9d", + "description": "multiple scores with differing numbers of letters", + "property": "transform", + "input": { + "legacy": { + "1": ["A", "E", "I", "O", "U", "L", "N", "R", "S", "T"], + "2": ["D", "G"], + "3": ["B", "C", "M", "P"], + "4": ["F", "H", "V", "W", "Y"], + "5": ["K"], + "8": ["J", "X"], + "10": ["Q", "Z"] + } + }, + "expected": { + "a": 1, + "b": 3, + "c": 3, + "d": 2, + "e": 1, + "f": 4, + "g": 2, + "h": 4, + "i": 1, + "j": 8, + "k": 5, + "l": 1, + "m": 3, + "n": 1, + "o": 1, + "p": 3, + "q": 10, + "r": 1, + "s": 1, + "t": 1, + "u": 1, + "v": 4, + "w": 4, + "x": 8, + "y": 4, + "z": 10 + } + } + ] +} diff --git a/exercises/practice/etl/create_fixture.sql b/exercises/practice/etl/create_fixture.sql new file mode 100644 index 0000000..4ed6c78 --- /dev/null +++ b/exercises/practice/etl/create_fixture.sql @@ -0,0 +1,8 @@ +DROP TABLE IF EXISTS "etl"; +CREATE TABLE "etl" ( + "input" TEXT, + "result" TEXT +); + +.mode csv +.import ./data.csv etl diff --git a/exercises/practice/etl/data.csv b/exercises/practice/etl/data.csv new file mode 100644 index 0000000..308c0b0 --- /dev/null +++ b/exercises/practice/etl/data.csv @@ -0,0 +1,4 @@ +"{""1"":[""A""]}","" +"{""1"":[""A"",""E"",""I"",""O"",""U""]}","" +"{""1"":[""A"",""E""],""2"":[""D"",""G""]}","" +"{""1"":[""A"",""E"",""I"",""O"",""U"",""L"",""N"",""R"",""S"",""T""],""2"":[""D"",""G""],""3"":[""B"",""C"",""M"",""P""],""4"":[""F"",""H"",""V"",""W"",""Y""],""5"":[""K""],""8"":[""J"",""X""],""10"":[""Q"",""Z""]}","" diff --git a/exercises/practice/etl/etl.sql b/exercises/practice/etl/etl.sql new file mode 100644 index 0000000..a93a702 --- /dev/null +++ b/exercises/practice/etl/etl.sql @@ -0,0 +1,2 @@ +-- Schema: CREATE TABLE "etl" ("input" TEXT, "result" TEXT); +-- Task: update the etl table and set the result based on the input field. diff --git a/exercises/practice/etl/etl_test.sql b/exercises/practice/etl/etl_test.sql new file mode 100644 index 0000000..24f9f46 --- /dev/null +++ b/exercises/practice/etl/etl_test.sql @@ -0,0 +1,29 @@ +-- Setup test table and read in student solution: +.read ./test_setup.sql + +-- Test cases: +INSERT INTO tests (name, uuid, + input, expected) + VALUES + ('single letter', '78a7a9f9-4490-4a47-8ee9-5a38bb47d28f', + '{"1":["A"]}', + '{"a":1}'), + ('single score with multiple letters', '60dbd000-451d-44c7-bdbb-97c73ac1f497', + '{"1":["A","E","I","O","U"]}', + '{"a":1,"e":1,"i":1,"o":1,"u":1}'), + ('multiple scores with multiple letters', 'f5c5de0c-301f-4fdd-a0e5-df97d4214f54', + '{"1":["A","E"],"2":["D","G"]}', + '{"a":1,"d":2,"e":1,"g":2}'), + ('multiple scores with differing numbers of letters', '5db8ea89-ecb4-4dcd-902f-2b418cc87b9d', + '{"1":["A","E","I","O","U","L","N","R","S","T"],"2":["D","G"],"3":["B","C","M","P"],"4":["F","H","V","W","Y"],"5":["K"],"8":["J","X"],"10":["Q","Z"]}', + '{"a":1,"b":3,"c":3,"d":2,"e":1,"f":4,"g":2,"h":4,"i":1,"j":8,"k":5,"l":1,"m":3,"n":1,"o":1,"p":3,"q":10,"r":1,"s":1,"t":1,"u":1,"v":4,"w":4,"x":8,"y":4,"z":10}'); + + +-- Comparison of user input and the tests updates the status for each test: +UPDATE tests +SET status = 'pass' +FROM (SELECT input, result FROM "etl") AS actual +WHERE (actual.input, json(actual.result)) = (tests.input, json(tests.expected)); + +-- Write results and debug info: +.read ./test_reporter.sql diff --git a/exercises/practice/etl/test_reporter.sql b/exercises/practice/etl/test_reporter.sql new file mode 100644 index 0000000..494e0a2 --- /dev/null +++ b/exercises/practice/etl/test_reporter.sql @@ -0,0 +1,16 @@ +-- Update message for failed tests to give helpful information: +UPDATE tests +SET message = 'Result for ' || actual.input || ' is: ' || actual.result || ', but should be: ' || tests.expected +FROM (SELECT input, result FROM etl) AS actual +WHERE actual.input = tests.input AND tests.status = 'fail'; + +-- Save results to ./output.json (needed by the online test-runner) +.mode json +.once './output.json' +SELECT name, status, message, output, test_code, task_id +FROM tests; + +-- Display test results in readable form for the student: +.mode table +SELECT name, status, message +FROM tests; diff --git a/exercises/practice/etl/test_setup.sql b/exercises/practice/etl/test_setup.sql new file mode 100644 index 0000000..0664d80 --- /dev/null +++ b/exercises/practice/etl/test_setup.sql @@ -0,0 +1,25 @@ +-- Create database: +.read ./create_fixture.sql + +-- Read user student solution and save any output as markdown in user_output.md: +.mode markdown +.output user_output.md +.read ./etl.sql +.output + +-- Create a clean testing environment: +DROP TABLE IF EXISTS tests; +CREATE TABLE IF NOT EXISTS tests ( + -- uuid and name (description) are taken from the test.toml file + uuid TEXT PRIMARY KEY, + name TEXT NOT NULL, + -- The following section is needed by the online test-runner + status TEXT DEFAULT 'fail', + message TEXT, + output TEXT, + test_code TEXT, + task_id INTEGER DEFAULT NULL, + -- Here are columns for the actual tests + input TEXT NOT NULL, + expected TEXT NOT NULL +); From a7fa5c4afa72639c6103232c55a84706dd26ea20 Mon Sep 17 00:00:00 2001 From: Caleb Miller Date: Fri, 7 Jun 2024 09:57:02 -0600 Subject: [PATCH 2/3] fix example solution because the ci doesn't like it --- exercises/practice/etl/.meta/example.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/etl/.meta/example.sql b/exercises/practice/etl/.meta/example.sql index a6e6240..404e1f8 100644 --- a/exercises/practice/etl/.meta/example.sql +++ b/exercises/practice/etl/.meta/example.sql @@ -1,6 +1,6 @@ UPDATE etl SET result = ( - SELECT json_group_object(LOWER(value), TRIM(path, '$."') ->> '$') + SELECT json_group_object(LOWER(value), TRIM(path, '$."') + 0) FROM ( SELECT value, path FROM json_tree(input) From 05320cf10676ada718a0db197fd8e87981fd0865 Mon Sep 17 00:00:00 2001 From: Caleb Miller Date: Mon, 10 Jun 2024 11:27:44 -0600 Subject: [PATCH 3/3] Add instructions.append.md --- exercises/practice/etl/.docs/instructions.append.md | 9 +++++++++ exercises/practice/etl/etl.sql | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 exercises/practice/etl/.docs/instructions.append.md diff --git a/exercises/practice/etl/.docs/instructions.append.md b/exercises/practice/etl/.docs/instructions.append.md new file mode 100644 index 0000000..d0a3d61 --- /dev/null +++ b/exercises/practice/etl/.docs/instructions.append.md @@ -0,0 +1,9 @@ +# SQLite specific instructions + +This exercise requires you set the `result` column of the `etl` table to the correct value based on the JSON object found in the `input` column. The keys in the result object must be sorted alphabetically. + +## Table Schema + +```sql +CREATE TABLE "etl" ("input" TEXT, "result" TEXT); +``` diff --git a/exercises/practice/etl/etl.sql b/exercises/practice/etl/etl.sql index a93a702..a8e8285 100644 --- a/exercises/practice/etl/etl.sql +++ b/exercises/practice/etl/etl.sql @@ -1,2 +1,2 @@ -- Schema: CREATE TABLE "etl" ("input" TEXT, "result" TEXT); --- Task: update the etl table and set the result based on the input field. +-- Task: update the etl table and set the result based on the input field. The keys in the result object must be sorted alphabetically.