diff --git a/config.json b/config.json index 86f0d99..5124829 100644 --- a/config.json +++ b/config.json @@ -218,6 +218,14 @@ "prerequisites": [], "difficulty": 5 }, + { + "slug": "grade-school", + "name": "Grade School", + "uuid": "cfa538ab-e637-4836-bb0c-b8fae94fa680", + "practices": [], + "prerequisites": [], + "difficulty": 5 + }, { "slug": "house", "name": "House", diff --git a/exercises/practice/grade-school/.docs/instructions.append.md b/exercises/practice/grade-school/.docs/instructions.append.md new file mode 100644 index 0000000..27d1e44 --- /dev/null +++ b/exercises/practice/grade-school/.docs/instructions.append.md @@ -0,0 +1,7 @@ +# SQLite-specific instructions + +## JSON documentation + +[JSON Functions And Operators][json-docs] + +[json-docs]: https://www.sqlite.org/json1.html diff --git a/exercises/practice/grade-school/.docs/instructions.md b/exercises/practice/grade-school/.docs/instructions.md new file mode 100644 index 0000000..3cb1b5d --- /dev/null +++ b/exercises/practice/grade-school/.docs/instructions.md @@ -0,0 +1,21 @@ +# Instructions + +Given students' names along with the grade they are in, create a roster for the school. + +In the end, you should be able to: + +- Add a student's name to the roster for a grade: + - "Add Jim to grade 2." + - "OK." +- Get a list of all students enrolled in a grade: + - "Which students are in grade 2?" + - "We've only got Jim right now." +- Get a sorted list of all students in all grades. + Grades should be sorted as 1, 2, 3, etc., and students within a grade should be sorted alphabetically by name. + - "Who is enrolled in school right now?" + - "Let me think. + We have Anna, Barb, and Charlie in grade 1, Alex, Peter, and Zoe in grade 2, and Jim in grade 5. + So the answer is: Anna, Barb, Charlie, Alex, Peter, Zoe, and Jim." + +Note that all our students only have one name (it's a small town, what do you want?), and each student cannot be added more than once to a grade or the roster. +If a test attempts to add the same student more than once, your implementation should indicate that this is incorrect. diff --git a/exercises/practice/grade-school/.meta/config.json b/exercises/practice/grade-school/.meta/config.json new file mode 100644 index 0000000..1e48da8 --- /dev/null +++ b/exercises/practice/grade-school/.meta/config.json @@ -0,0 +1,18 @@ +{ + "authors": [ + "jimmytty" + ], + "files": { + "solution": [ + "grade-school.sql" + ], + "test": [ + "grade-school_test.sql" + ], + "example": [ + ".meta/example.sql" + ] + }, + "blurb": "Given students' names along with the grade that they are in, create a roster for the school.", + "source": "A pairing session with Phil Battos at gSchool" +} diff --git a/exercises/practice/grade-school/.meta/example.sql b/exercises/practice/grade-school/.meta/example.sql new file mode 100644 index 0000000..519a096 --- /dev/null +++ b/exercises/practice/grade-school/.meta/example.sql @@ -0,0 +1,64 @@ +UPDATE "grade-school" + SET result = ( + WITH + list (row_id, student) AS ( + SELECT j.key, JSON_EXTRACT(j.value, '$[0]') + FROM JSON_EACH(JSON_EXTRACT(input, '$.students')) j + ), + duplicates AS ( + SELECT student, COUNT(*) count FROM list GROUP BY student + ), + can_add (bool) AS ( + SELECT (count = 1 + OR row_id = (SELECT MIN(row_id) + FROM list + WHERE list.student = tbl.student) + ) + FROM ( + SELECT row_id, student, count + FROM list LEFT JOIN duplicates USING(student) + ) tbl + ) + SELECT JSON_GROUP_ARRAY(JSON(IIF(bool = 1, 'true', 'false'))) + FROM can_add + ) + WHERE property = 'add' +; + +UPDATE "grade-school" + SET result = ( + WITH rows (student, grade) AS ( + SELECT JSON_EXTRACT(j.value, '$[0]'), + JSON_EXTRACT(j.value, '$[1]') + FROM JSON_EACH(JSON_EXTRACT(input, '$.students')) j + ) + SELECT JSON_GROUP_ARRAY(student) + FROM ( + SELECT student + FROM rows + GROUP BY student + ORDER BY grade, student + ) + ) + WHERE property = 'roster' +; + +UPDATE "grade-school" + SET result = ( + WITH list (student, grade) AS ( + SELECT JSON_EXTRACT(j.value, '$[0]'), + JSON_EXTRACT(j.value, '$[1]') + FROM JSON_EACH(JSON_EXTRACT(input, '$.students')) j + ) + SELECT JSON_GROUP_ARRAY(student) + FROM ( + SELECT student, JSON_GROUP_ARRAY(grade) grades + FROM list + GROUP BY student + ORDER BY student + ) + WHERE JSON_EXTRACT(grades, '$[0]') = + JSON_EXTRACT(input, '$.desiredGrade') + ) + WHERE property = 'grade' +; diff --git a/exercises/practice/grade-school/.meta/tests.toml b/exercises/practice/grade-school/.meta/tests.toml new file mode 100644 index 0000000..50c9e2e --- /dev/null +++ b/exercises/practice/grade-school/.meta/tests.toml @@ -0,0 +1,86 @@ +# 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. + +[a3f0fb58-f240-4723-8ddc-e644666b85cc] +description = "Roster is empty when no student is added" + +[9337267f-7793-4b90-9b4a-8e3978408824] +description = "Add a student" + +[6d0a30e4-1b4e-472e-8e20-c41702125667] +description = "Student is added to the roster" + +[73c3ca75-0c16-40d7-82f5-ed8fe17a8e4a] +description = "Adding multiple students in the same grade in the roster" + +[233be705-dd58-4968-889d-fb3c7954c9cc] +description = "Multiple students in the same grade are added to the roster" + +[87c871c1-6bde-4413-9c44-73d59a259d83] +description = "Cannot add student to same grade in the roster more than once" + +[c125dab7-2a53-492f-a99a-56ad511940d8] +description = "A student can't be in two different grades" +include = false + +[a0c7b9b8-0e89-47f8-8b4a-c50f885e79d1] +description = "A student can only be added to the same grade in the roster once" +include = false +reimplements = "c125dab7-2a53-492f-a99a-56ad511940d8" + +[d7982c4f-1602-49f6-a651-620f2614243a] +description = "Student not added to same grade in the roster more than once" +reimplements = "a0c7b9b8-0e89-47f8-8b4a-c50f885e79d1" + +[e70d5d8f-43a9-41fd-94a4-1ea0fa338056] +description = "Adding students in multiple grades" + +[75a51579-d1d7-407c-a2f8-2166e984e8ab] +description = "Students in multiple grades are added to the roster" + +[7df542f1-57ce-433c-b249-ff77028ec479] +description = "Cannot add same student to multiple grades in the roster" + +[6a03b61e-1211-4783-a3cc-fc7f773fba3f] +description = "A student cannot be added to more than one grade in the sorted roster" +include = false +reimplements = "c125dab7-2a53-492f-a99a-56ad511940d8" + +[c7ec1c5e-9ab7-4d3b-be5c-29f2f7a237c5] +description = "Student not added to multiple grades in the roster" +reimplements = "6a03b61e-1211-4783-a3cc-fc7f773fba3f" + +[d9af4f19-1ba1-48e7-94d0-dabda4e5aba6] +description = "Students are sorted by grades in the roster" + +[d9fb5bea-f5aa-4524-9d61-c158d8906807] +description = "Students are sorted by name in the roster" + +[180a8ff9-5b94-43fc-9db1-d46b4a8c93b6] +description = "Students are sorted by grades and then by name in the roster" + +[5e67aa3c-a3c6-4407-a183-d8fe59cd1630] +description = "Grade is empty if no students in the roster" + +[1e0cf06b-26e0-4526-af2d-a2e2df6a51d6] +description = "Grade is empty if no students in that grade" + +[2bfc697c-adf2-4b65-8d0f-c46e085f796e] +description = "Student not added to same grade more than once" + +[66c8e141-68ab-4a04-a15a-c28bc07fe6b9] +description = "Student not added to multiple grades" + +[c9c1fc2f-42e0-4d2c-b361-99271f03eda7] +description = "Student not added to other grade for multiple grades" + +[1bfbcef1-e4a3-49e8-8d22-f6f9f386187e] +description = "Students are sorted by name in a grade" diff --git a/exercises/practice/grade-school/create_fixture.sql b/exercises/practice/grade-school/create_fixture.sql new file mode 100644 index 0000000..712ebf6 --- /dev/null +++ b/exercises/practice/grade-school/create_fixture.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS "grade-school"; +CREATE TABLE "grade-school" ( + property TEXT NOT NULL, + input TEXT NOT NULL, -- json object + result TEXT -- json array +); + +.mode csv +.import ./data.csv "grade-school" + +UPDATE "grade-school" SET result = NULL; diff --git a/exercises/practice/grade-school/create_test_table.sql b/exercises/practice/grade-school/create_test_table.sql new file mode 100644 index 0000000..8e8387f --- /dev/null +++ b/exercises/practice/grade-school/create_test_table.sql @@ -0,0 +1,39 @@ +DROP TABLE IF EXISTS tests; +CREATE TABLE IF NOT EXISTS tests ( + -- uuid and description are taken from the test.toml file + uuid TEXT PRIMARY KEY, + description 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 + property TEXT NOT NULL, + input TEXT NOT NULL, -- json object + expected TEXT NOT NULL -- json array +); + +INSERT INTO tests (uuid, description, property, input, expected) +VALUES +('a3f0fb58-f240-4723-8ddc-e644666b85cc', 'Roster is empty when no student is added', 'roster', '{"students":[]}', '[]'), +('9337267f-7793-4b90-9b4a-8e3978408824', 'Add a student', 'add', '{"students":[["Aimee",2]]}', '[true]'), +('6d0a30e4-1b4e-472e-8e20-c41702125667', 'Student is added to the roster', 'roster', '{"students":[["Aimee",2]]}', '["Aimee"]'), +('73c3ca75-0c16-40d7-82f5-ed8fe17a8e4a', 'Adding multiple students in the same grade in the roster', 'add', '{"students":[["Blair",2],["James",2],["Paul",2]]}', '[true,true,true]'), +('233be705-dd58-4968-889d-fb3c7954c9cc', 'Multiple students in the same grade are added to the roster', 'roster', '{"students":[["Blair",2],["James",2],["Paul",2]]}', '["Blair","James","Paul"]'), +('87c871c1-6bde-4413-9c44-73d59a259d83', 'Cannot add student to same grade in the roster more than once', 'add', '{"students":[["Blair",2],["James",2],["James",2],["Paul",2]]}', '[true,true,false,true]'), +('d7982c4f-1602-49f6-a651-620f2614243a', 'Student not added to same grade in the roster more than once', 'roster', '{"students":[["Blair",2],["James",2],["James",2],["Paul",2]]}', '["Blair","James","Paul"]'), +('e70d5d8f-43a9-41fd-94a4-1ea0fa338056', 'Adding students in multiple grades', 'add', '{"students":[["Chelsea",3],["Logan",7]]}', '[true,true]'), +('75a51579-d1d7-407c-a2f8-2166e984e8ab', 'Students in multiple grades are added to the roster', 'roster', '{"students":[["Chelsea",3],["Logan",7]]}', '["Chelsea","Logan"]'), +('7df542f1-57ce-433c-b249-ff77028ec479', 'Cannot add same student to multiple grades in the roster', 'add', '{"students":[["Blair",2],["James",2],["James",3],["Paul",3]]}', '[true,true,false,true]'), +('c7ec1c5e-9ab7-4d3b-be5c-29f2f7a237c5', 'Student not added to multiple grades in the roster', 'roster', '{"students":[["Blair",2],["James",2],["James",3],["Paul",3]]}', '["Blair","James","Paul"]'), +('d9af4f19-1ba1-48e7-94d0-dabda4e5aba6', 'Students are sorted by grades in the roster', 'roster', '{"students":[["Jim",3],["Peter",2],["Anna",1]]}', '["Anna","Peter","Jim"]'), +('d9fb5bea-f5aa-4524-9d61-c158d8906807', 'Students are sorted by name in the roster', 'roster', '{"students":[["Peter",2],["Zoe",2],["Alex",2]]}', '["Alex","Peter","Zoe"]'), +('180a8ff9-5b94-43fc-9db1-d46b4a8c93b6', 'Students are sorted by grades and then by name in the roster', 'roster', '{"students":[["Peter",2],["Anna",1],["Barb",1],["Zoe",2],["Alex",2],["Jim",3],["Charlie",1]]}', '["Anna","Barb","Charlie","Alex","Peter","Zoe","Jim"]'), +('5e67aa3c-a3c6-4407-a183-d8fe59cd1630', 'Grade is empty if no students in the roster', 'grade', '{"students":[],"desiredGrade":1}', '[]'), +('1e0cf06b-26e0-4526-af2d-a2e2df6a51d6', 'Grade is empty if no students in that grade', 'grade', '{"students":[["Peter",2],["Zoe",2],["Alex",2],["Jim",3]],"desiredGrade":1}', '[]'), +('2bfc697c-adf2-4b65-8d0f-c46e085f796e', 'Student not added to same grade more than once', 'grade', '{"students":[["Blair",2],["James",2],["James",2],["Paul",2]],"desiredGrade":2}', '["Blair","James","Paul"]'), +('66c8e141-68ab-4a04-a15a-c28bc07fe6b9', 'Student not added to multiple grades', 'grade', '{"students":[["Blair",2],["James",2],["James",3],["Paul",3]],"desiredGrade":2}', '["Blair","James"]'), +('c9c1fc2f-42e0-4d2c-b361-99271f03eda7', 'Student not added to other grade for multiple grades', 'grade', '{"students":[["Blair",2],["James",2],["James",3],["Paul",3]],"desiredGrade":3}', '["Paul"]'), +('1bfbcef1-e4a3-49e8-8d22-f6f9f386187e', 'Students are sorted by name in a grade', 'grade', '{"students":[["Franklin",5],["Bradley",5],["Jeff",1]],"desiredGrade":5}', '["Bradley","Franklin"]'); diff --git a/exercises/practice/grade-school/data.csv b/exercises/practice/grade-school/data.csv new file mode 100644 index 0000000..c7d2c25 --- /dev/null +++ b/exercises/practice/grade-school/data.csv @@ -0,0 +1,20 @@ +roster,"{""students"":[]}", +add,"{""students"":[[""Aimee"",2]]}", +roster,"{""students"":[[""Aimee"",2]]}", +add,"{""students"":[[""Blair"",2],[""James"",2],[""Paul"",2]]}", +roster,"{""students"":[[""Blair"",2],[""James"",2],[""Paul"",2]]}", +add,"{""students"":[[""Blair"",2],[""James"",2],[""James"",2],[""Paul"",2]]}", +roster,"{""students"":[[""Blair"",2],[""James"",2],[""James"",2],[""Paul"",2]]}", +add,"{""students"":[[""Chelsea"",3],[""Logan"",7]]}", +roster,"{""students"":[[""Chelsea"",3],[""Logan"",7]]}", +add,"{""students"":[[""Blair"",2],[""James"",2],[""James"",3],[""Paul"",3]]}", +roster,"{""students"":[[""Blair"",2],[""James"",2],[""James"",3],[""Paul"",3]]}", +roster,"{""students"":[[""Jim"",3],[""Peter"",2],[""Anna"",1]]}", +roster,"{""students"":[[""Peter"",2],[""Zoe"",2],[""Alex"",2]]}", +roster,"{""students"":[[""Peter"",2],[""Anna"",1],[""Barb"",1],[""Zoe"",2],[""Alex"",2],[""Jim"",3],[""Charlie"",1]]}", +grade,"{""students"":[],""desiredGrade"":1}", +grade,"{""students"":[[""Peter"",2],[""Zoe"",2],[""Alex"",2],[""Jim"",3]],""desiredGrade"":1}", +grade,"{""students"":[[""Blair"",2],[""James"",2],[""James"",2],[""Paul"",2]],""desiredGrade"":2}", +grade,"{""students"":[[""Blair"",2],[""James"",2],[""James"",3],[""Paul"",3]],""desiredGrade"":2}", +grade,"{""students"":[[""Blair"",2],[""James"",2],[""James"",3],[""Paul"",3]],""desiredGrade"":3}", +grade,"{""students"":[[""Franklin"",5],[""Bradley"",5],[""Jeff"",1]],""desiredGrade"":5}", diff --git a/exercises/practice/grade-school/grade-school.sql b/exercises/practice/grade-school/grade-school.sql new file mode 100644 index 0000000..b4de6c4 --- /dev/null +++ b/exercises/practice/grade-school/grade-school.sql @@ -0,0 +1,8 @@ +-- Schema: +-- CREATE TABLE "grade-school" ( +-- property TEXT NOT NULL, +-- input TEXT NOT NULL, -- json object +-- result TEXT -- json array +-- ); +-- +-- Task: update the grade-school and update the result column based on the property and the input. diff --git a/exercises/practice/grade-school/grade-school_test.sql b/exercises/practice/grade-school/grade-school_test.sql new file mode 100644 index 0000000..f400c42 --- /dev/null +++ b/exercises/practice/grade-school/grade-school_test.sql @@ -0,0 +1,53 @@ +-- 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 ./grade-school.sql +.output + +-- Create a clean testing environment: +.read ./create_test_table.sql + +-- Comparison of user input and the tests updates the status for each test: +UPDATE tests + SET status = 'pass' + FROM (SELECT property, input, result FROM "grade-school") AS actual + WHERE (actual.property, actual.input, actual.result) = + ( tests.property, tests.input, tests.expected) +; + +-- Update message for failed tests to give helpful information: +UPDATE tests + SET message = ( + 'Result for "' + || PRINTF('property=%s, input=%s', tests.property, tests.input) + || '"' || ' is <' || COALESCE(actual.result, 'NULL') + || '> but should be <' || tests.expected || '>' + ) + FROM (SELECT property, input, result FROM "grade-school") AS actual + WHERE (actual.property, actual.input) = (tests.property, tests.input) + AND tests.status = 'fail'; + +-- Save results to ./output.json (needed by the online test-runner) +.mode json +.once './output.json' +SELECT + description, + status, + message, + output, + test_code, + task_id +FROM + tests; + +-- Display test results in readable form for the student: +.mode table +SELECT + description, + status, + message +FROM + tests;