mumuki-sqlite-runner 1.0.1 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/checker.rb +4 -4
- data/lib/dataset.rb +14 -3
- data/lib/dataset_solution_parser.rb +34 -0
- data/lib/errors.rb +7 -0
- data/lib/extensions/hash_extension.rb +6 -0
- data/lib/html_renderer.rb +40 -8
- data/lib/locales/en.yml +7 -5
- data/lib/locales/es.yml +7 -5
- data/lib/metadata_hook.rb +1 -1
- data/lib/query_solution_parser.rb +25 -0
- data/lib/sqlite_runner.rb +6 -2
- data/lib/test_hook.rb +78 -23
- data/lib/version_hook.rb +2 -2
- data/lib/view/rows_error.html.erb +43 -11
- data/lib/view/rows_success.html.erb +1 -1
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e574ef63ec53b93d7841532738042a3be0899079
|
4
|
+
data.tar.gz: a87d92c79f8af4546ef46eb34e6faa7e0d26572c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28dca88c0eae83a3d5e34173b47b6702d805e1bc3d3ff70a99d7d546001c157386f91ca9d6e8b93c13b43561576caf0b1d2e0e4329b598d79c73e5ab1f8ca4b4
|
7
|
+
data.tar.gz: aeba550111b462b085d48c9b590822ddd03fdefa874303ccadb13c89867c55120c06612ded7402f871fb3cdaffb95c3d2297e45cd4310c2af36a167ed4e66240
|
data/lib/checker.rb
CHANGED
@@ -6,17 +6,17 @@ module Sqlite
|
|
6
6
|
# when distinct_columns: return failed & render error
|
7
7
|
# when distinct_rows: return failed & render error
|
8
8
|
def check(result, solution)
|
9
|
-
name =
|
9
|
+
name = I18n.t 'dataset', number: solution[:id]
|
10
10
|
|
11
11
|
case solution[:dataset].compare result[:dataset]
|
12
12
|
when :equals
|
13
13
|
success(name, result)
|
14
14
|
when :distinct_columns
|
15
|
-
failed(name, result, solution,
|
15
|
+
failed(name, result, solution, (I18n.t 'failure.columns'))
|
16
16
|
when :distinct_rows
|
17
|
-
failed(name, result, solution,
|
17
|
+
failed(name, result, solution, (I18n.t 'failure.rows'))
|
18
18
|
else
|
19
|
-
failed(name, result, solution,
|
19
|
+
failed(name, result, solution, (I18n.t 'failure.query'))
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
data/lib/dataset.rb
CHANGED
@@ -3,9 +3,12 @@ module Sqlite
|
|
3
3
|
attr_reader :header, :rows
|
4
4
|
|
5
5
|
def initialize(data)
|
6
|
-
rows =
|
7
|
-
|
8
|
-
|
6
|
+
@header = @rows = []
|
7
|
+
rows = split_lines data
|
8
|
+
unless rows.empty?
|
9
|
+
@header = split_pipe rows.shift
|
10
|
+
@rows = rows.map { |item| split_pipe item }
|
11
|
+
end
|
9
12
|
end
|
10
13
|
|
11
14
|
def compare(other)
|
@@ -23,6 +26,14 @@ module Sqlite
|
|
23
26
|
|
24
27
|
protected
|
25
28
|
|
29
|
+
def split_lines(str)
|
30
|
+
str.split(/\n+/i)
|
31
|
+
end
|
32
|
+
|
33
|
+
def split_pipe(line)
|
34
|
+
line.split(/\|/)
|
35
|
+
end
|
36
|
+
|
26
37
|
def same_header(other_header)
|
27
38
|
@header.eql? other_header
|
28
39
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Sqlite
|
2
|
+
class DatasetSolutionParser
|
3
|
+
|
4
|
+
# If solutions comes in an explicit datasets,
|
5
|
+
# it was stored in instance variable.
|
6
|
+
# Expected input:
|
7
|
+
# OpenStruct#{
|
8
|
+
# solution_type: 'datasets',
|
9
|
+
# examples: [
|
10
|
+
# {
|
11
|
+
# dataset: "INSERT INTO ...\nINSERT INTO ...",
|
12
|
+
# solution_dataset: "id|field\n1|row1..."
|
13
|
+
# }
|
14
|
+
# ]
|
15
|
+
# }
|
16
|
+
def parse_test(test)
|
17
|
+
data = []
|
18
|
+
@solutions = []
|
19
|
+
solution_query = '-- none'
|
20
|
+
|
21
|
+
test.examples.each do |item|
|
22
|
+
@solutions << item[:solution_dataset].scan(/(?!\|).+(?<!\|)/).join("\n")
|
23
|
+
data.append item[:data]
|
24
|
+
end
|
25
|
+
|
26
|
+
return solution_query, data
|
27
|
+
end
|
28
|
+
|
29
|
+
def choose(_solution)
|
30
|
+
@solutions
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
data/lib/errors.rb
ADDED
data/lib/html_renderer.rb
CHANGED
@@ -11,19 +11,51 @@ module Sqlite
|
|
11
11
|
|
12
12
|
def render_error(result, solution, error)
|
13
13
|
@error = error
|
14
|
-
@result =
|
15
|
-
|
16
|
-
|
17
|
-
}
|
18
|
-
@solution = {
|
19
|
-
header: solution[:dataset].header,
|
20
|
-
rows: solution[:dataset].rows
|
21
|
-
}
|
14
|
+
@result = parse_dataset(result[:dataset].header, result[:dataset].rows)
|
15
|
+
@solution = parse_dataset(solution[:dataset].header, solution[:dataset].rows)
|
16
|
+
|
22
17
|
template_file_error.result binding
|
23
18
|
end
|
24
19
|
|
25
20
|
protected
|
26
21
|
|
22
|
+
def parse_dataset(header, rows)
|
23
|
+
header_sign = header.shift
|
24
|
+
rows = rows.map do |row|
|
25
|
+
{
|
26
|
+
sign: row.shift,
|
27
|
+
row: row
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
{
|
32
|
+
header: {
|
33
|
+
sign: header_sign,
|
34
|
+
class: diff_class_of(header_sign),
|
35
|
+
fields: header
|
36
|
+
},
|
37
|
+
rows: rows.map do |row|
|
38
|
+
{
|
39
|
+
sign:row[:sign],
|
40
|
+
class: diff_class_of(row[:sign]),
|
41
|
+
fields: row[:row]
|
42
|
+
}
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def diff_class_of(value)
|
49
|
+
case value
|
50
|
+
when '+'
|
51
|
+
'required'
|
52
|
+
when '-'
|
53
|
+
'errored'
|
54
|
+
else
|
55
|
+
'nothing'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
27
59
|
def template_file_success
|
28
60
|
ERB.new File.read("#{__dir__}/view/rows_success.html.erb")
|
29
61
|
end
|
data/lib/locales/en.yml
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
en:
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
dataset: "Dataset %{number}"
|
3
|
+
success:
|
4
|
+
query: 'Correct Query!'
|
5
|
+
failure:
|
6
|
+
columns: 'Columns do not match'
|
7
|
+
rows: 'Rows do not match'
|
8
|
+
query: 'Queries do not match'
|
data/lib/locales/es.yml
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
es:
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
dataset: "Set de datos %{number}"
|
3
|
+
success:
|
4
|
+
query: '¡Consulta correcta!'
|
5
|
+
failure:
|
6
|
+
columns: 'Las columnas no coinciden'
|
7
|
+
rows: 'Las filas no coinciden'
|
8
|
+
query: 'Las consultas no coinciden'
|
data/lib/metadata_hook.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sqlite
|
2
|
+
class QuerySolutionParser
|
3
|
+
|
4
|
+
# Expected input:
|
5
|
+
# OpenStruct#{
|
6
|
+
# solution_type: 'query',
|
7
|
+
# solution_query: 'select * from motores;',
|
8
|
+
# examples: [
|
9
|
+
# { dataset: "INSERT INTO ...\nINSERT INTO ..." }
|
10
|
+
# ]
|
11
|
+
# }
|
12
|
+
def parse_test(test)
|
13
|
+
data = test.examples.map do |item|
|
14
|
+
item[:data]
|
15
|
+
end
|
16
|
+
|
17
|
+
return test.solution_query, data
|
18
|
+
end
|
19
|
+
|
20
|
+
def choose(solution)
|
21
|
+
solution
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
data/lib/sqlite_runner.rb
CHANGED
@@ -5,11 +5,12 @@ I18n.load_translations_path File.join(__dir__, 'locales', '*.yml')
|
|
5
5
|
|
6
6
|
Mumukit.runner_name = 'sqlite'
|
7
7
|
Mumukit.configure do |config|
|
8
|
-
config.docker_image = '
|
9
|
-
config.content_type = '
|
8
|
+
config.docker_image = 'mumuki/mumuki-sqlite-worker'
|
9
|
+
config.content_type = 'html'
|
10
10
|
config.structured = true
|
11
11
|
end
|
12
12
|
|
13
|
+
require_relative './extensions/hash_extension'
|
13
14
|
require_relative './version_hook'
|
14
15
|
require_relative './test_hook'
|
15
16
|
require_relative './metadata_hook'
|
@@ -17,3 +18,6 @@ require_relative './checker'
|
|
17
18
|
require_relative './multiple_executions_runner'
|
18
19
|
require_relative './html_renderer'
|
19
20
|
require_relative './dataset'
|
21
|
+
require_relative './dataset_solution_parser'
|
22
|
+
require_relative './query_solution_parser'
|
23
|
+
require_relative './errors'
|
data/lib/test_hook.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'diffy'
|
2
3
|
|
3
4
|
##
|
4
|
-
# This Hook allow to run Sqlite Worker from an
|
5
|
+
# This Hook allow to run Sqlite Worker from an ad-hoc program that receives .json files.
|
5
6
|
|
6
7
|
class SqliteTestHook < Mumukit::Templates::FileHook
|
7
8
|
|
@@ -18,22 +19,22 @@ class SqliteTestHook < Mumukit::Templates::FileHook
|
|
18
19
|
"runsql #{filename}"
|
19
20
|
end
|
20
21
|
|
21
|
-
# Define the .
|
22
|
-
# request = {
|
23
|
-
# test: (string) teacher's code that define which testing verification student code should pass,
|
24
|
-
# extra: (string) teacher's code that prepare field where student code should run,
|
25
|
-
# content: (string) student code,
|
26
|
-
# expectations: [
|
22
|
+
# Define the .json file template from request structure
|
23
|
+
# Input: request = {
|
24
|
+
# test: (yaml string) teacher's code that define which testing verification student code should pass,
|
25
|
+
# extra: (sql string) teacher's code that prepare field where student code should run,
|
26
|
+
# content: (sql string) student code,
|
27
|
+
# expectations: [] not using for now
|
27
28
|
# }
|
28
29
|
#
|
29
30
|
def compile_file_content(request)
|
30
|
-
solution,
|
31
|
+
solution, data = parse_test request.test
|
31
32
|
|
32
33
|
content = {
|
33
34
|
init: request.extra.strip,
|
34
35
|
solution: solution,
|
35
36
|
student: request.content.strip,
|
36
|
-
datasets:
|
37
|
+
datasets: data
|
37
38
|
}
|
38
39
|
|
39
40
|
content.to_json
|
@@ -56,8 +57,7 @@ class SqliteTestHook < Mumukit::Templates::FileHook
|
|
56
57
|
|
57
58
|
case status
|
58
59
|
when :passed
|
59
|
-
results
|
60
|
-
solutions = post_process_datasets(output['solutions'])
|
60
|
+
solutions, results = parse_output output
|
61
61
|
framework.test solutions, results
|
62
62
|
when :failed
|
63
63
|
[output['output'], status]
|
@@ -66,6 +66,42 @@ class SqliteTestHook < Mumukit::Templates::FileHook
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
protected
|
70
|
+
|
71
|
+
def parse_output(output)
|
72
|
+
results = output['results']
|
73
|
+
solutions = output['solutions']
|
74
|
+
unless @solution_parser.nil?
|
75
|
+
solutions = @solution_parser.choose solutions
|
76
|
+
end
|
77
|
+
|
78
|
+
diff(solutions, results)
|
79
|
+
end
|
80
|
+
|
81
|
+
def diff(solutions, results)
|
82
|
+
zipped = solutions.zip(results).map do |solution, result|
|
83
|
+
|
84
|
+
diff = Diffy::SplitDiff.new result << "\n", solution << "\n"
|
85
|
+
|
86
|
+
if diff.left.blank?
|
87
|
+
[solution, result]
|
88
|
+
else
|
89
|
+
res = post_process_diff diff.left
|
90
|
+
sol = post_process_diff diff.right
|
91
|
+
[sol, res]
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
zipped.transpose.map { |dataset| post_process_datasets dataset }
|
97
|
+
end
|
98
|
+
|
99
|
+
def post_process_diff(data)
|
100
|
+
data.scan(/^(\s|-|\+)(.+)/)
|
101
|
+
.map { |mark, content| mark << '|' << content }
|
102
|
+
.join("\n")
|
103
|
+
end
|
104
|
+
|
69
105
|
# Transforms array datasets into hash with :id & :rows
|
70
106
|
def post_process_datasets(datasets)
|
71
107
|
datasets.map.with_index do |dataset, i|
|
@@ -76,18 +112,37 @@ class SqliteTestHook < Mumukit::Templates::FileHook
|
|
76
112
|
end
|
77
113
|
end
|
78
114
|
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
|
89
|
-
|
90
|
-
|
115
|
+
# Test should have one of these formats:
|
116
|
+
#
|
117
|
+
# Solution by query:
|
118
|
+
# { solution_type: 'query',
|
119
|
+
# solution_query: 'SELECT * FROM ...',
|
120
|
+
# examples: [
|
121
|
+
# { data: "INSERT INTO..." }
|
122
|
+
# ]
|
123
|
+
# }
|
124
|
+
#
|
125
|
+
# Solution by datasets:
|
126
|
+
# { solution_type: 'datasets',
|
127
|
+
# examples: [
|
128
|
+
# { data: "INSERT INTO...",
|
129
|
+
# solution: "id|field\n1|row1..."
|
130
|
+
# }
|
131
|
+
# ]
|
132
|
+
# }
|
133
|
+
def parse_test(test)
|
134
|
+
test = OpenStruct.new YAML.load(test).deep_symbolize_keys
|
135
|
+
|
136
|
+
case test.solution_type.to_sym
|
137
|
+
when :query
|
138
|
+
@solution_parser = Sqlite::QuerySolutionParser.new
|
139
|
+
when :datasets
|
140
|
+
@solution_parser = Sqlite::DatasetSolutionParser.new
|
141
|
+
else
|
142
|
+
raise Sqlite::TestSolutionTypeError
|
143
|
+
end
|
144
|
+
|
145
|
+
@solution_parser.parse_test test
|
91
146
|
end
|
92
147
|
|
93
148
|
# Initialize Metatest Framework with Checker & Runner
|
data/lib/version_hook.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module SqliteVersionHook
|
2
|
-
VERSION = '
|
3
|
-
end
|
2
|
+
VERSION = '2.2.1'
|
3
|
+
end
|
@@ -3,13 +3,13 @@
|
|
3
3
|
width: auto;
|
4
4
|
}
|
5
5
|
table.sqlite_result {
|
6
|
-
border:
|
6
|
+
border: none;
|
7
7
|
}
|
8
8
|
table.sqlite_solution {
|
9
|
-
border:
|
9
|
+
border: none;
|
10
10
|
}
|
11
11
|
table.sqlite_error tr:nth-child(even) {
|
12
|
-
background-color: #
|
12
|
+
background-color: #EEEEEE;
|
13
13
|
}
|
14
14
|
table.sqlite_error tr:nth-child(odd) {
|
15
15
|
background-color: #FFFFFF;
|
@@ -23,6 +23,34 @@
|
|
23
23
|
width: auto;
|
24
24
|
text-align: left;
|
25
25
|
}
|
26
|
+
|
27
|
+
.required {
|
28
|
+
color: #5cb85c;
|
29
|
+
font-style: italic;
|
30
|
+
}
|
31
|
+
.errored {
|
32
|
+
color: #d9534f;
|
33
|
+
font-style: italic;
|
34
|
+
}
|
35
|
+
|
36
|
+
.required :first-child,
|
37
|
+
.errored :first-child,
|
38
|
+
.nothing :first-child {
|
39
|
+
font-style: normal;
|
40
|
+
padding-left: 2px;
|
41
|
+
padding-right: 2px;
|
42
|
+
background-color: white;
|
43
|
+
border-left: none;
|
44
|
+
border-bottom: 1px solid white;
|
45
|
+
}
|
46
|
+
.required :first-child {
|
47
|
+
color: white;
|
48
|
+
background-color: #5cb85c;
|
49
|
+
}
|
50
|
+
.errored :first-child {
|
51
|
+
color: white;
|
52
|
+
background-color: #d9534f;
|
53
|
+
}
|
26
54
|
</style>
|
27
55
|
|
28
56
|
<h5><%= @error %></h5>
|
@@ -34,8 +62,9 @@
|
|
34
62
|
<h6>Se esperaba:</h6>
|
35
63
|
<table class="table table-bordered sqlite_error sqlite_result">
|
36
64
|
<thead>
|
37
|
-
<tr>
|
38
|
-
|
65
|
+
<tr class="<%= @result[:header][:class] %>">
|
66
|
+
<th><%= @result[:header][:sign] %></th>
|
67
|
+
<% @result[:header][:fields].each do |field| %>
|
39
68
|
<th><%= field %></th>
|
40
69
|
<% end %>
|
41
70
|
</tr>
|
@@ -43,8 +72,9 @@
|
|
43
72
|
|
44
73
|
<tbody>
|
45
74
|
<% @result[:rows].each do |row| %>
|
46
|
-
<tr>
|
47
|
-
|
75
|
+
<tr class="<%= row[:class] %>">
|
76
|
+
<td><%= row[:sign] %></td>
|
77
|
+
<% row[:fields].each do |field| %>
|
48
78
|
<td><%= field %></td>
|
49
79
|
<% end %>
|
50
80
|
</tr>
|
@@ -58,8 +88,9 @@
|
|
58
88
|
<h6>Se obtuvo:</h6>
|
59
89
|
<table class="table table-bordered sqlite_error sqlite_solution">
|
60
90
|
<thead>
|
61
|
-
<tr>
|
62
|
-
|
91
|
+
<tr class="<%= @result[:header][:class] %>">
|
92
|
+
<th><%= @result[:header][:sign] %></th>
|
93
|
+
<% @solution[:header][:fields].each do |field| %>
|
63
94
|
<th><%= field %></th>
|
64
95
|
<% end %>
|
65
96
|
</tr>
|
@@ -67,8 +98,9 @@
|
|
67
98
|
|
68
99
|
<tbody>
|
69
100
|
<% @solution[:rows].each do |row| %>
|
70
|
-
<tr>
|
71
|
-
|
101
|
+
<tr class="<%= row[:class] %>">
|
102
|
+
<td><%= row[:sign] %></td>
|
103
|
+
<% row[:fields].each do |field| %>
|
72
104
|
<td><%= field %></td>
|
73
105
|
<% end %>
|
74
106
|
</tr>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mumuki-sqlite-runner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Leandro Di Lorenzo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mumukit
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: diffy
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -159,11 +173,15 @@ extra_rdoc_files: []
|
|
159
173
|
files:
|
160
174
|
- lib/checker.rb
|
161
175
|
- lib/dataset.rb
|
176
|
+
- lib/dataset_solution_parser.rb
|
177
|
+
- lib/errors.rb
|
178
|
+
- lib/extensions/hash_extension.rb
|
162
179
|
- lib/html_renderer.rb
|
163
180
|
- lib/locales/en.yml
|
164
181
|
- lib/locales/es.yml
|
165
182
|
- lib/metadata_hook.rb
|
166
183
|
- lib/multiple_executions_runner.rb
|
184
|
+
- lib/query_solution_parser.rb
|
167
185
|
- lib/sqlite_runner.rb
|
168
186
|
- lib/test_hook.rb
|
169
187
|
- lib/version_hook.rb
|
@@ -189,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
189
207
|
version: '0'
|
190
208
|
requirements: []
|
191
209
|
rubyforge_project:
|
192
|
-
rubygems_version: 2.6.
|
210
|
+
rubygems_version: 2.6.14
|
193
211
|
signing_key:
|
194
212
|
specification_version: 4
|
195
213
|
summary: SQLite Runner for Mumuki
|