mumuki-sqlite-runner 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d075a041f2a6a4d3e8ee0c156fb9533443625287
4
+ data.tar.gz: 6d7e335083a7442e80ce5e83ecda14d1e214fa26
5
+ SHA512:
6
+ metadata.gz: 7e8082c65194aba7d81a10db77aac0884cb1773d0604a7eb0e83b87a10a147e284568677fde04cab048ffcaa7485137981af5507e4c15dc6acd73b8428c3f8ab
7
+ data.tar.gz: 57cf7a92f1805f7df7cf2c733c7c8e42e8d065cc66b58ad3947c17497874b6ebe054a55ccf23cada3b79d554d83e8d88d45a20cd3c6c3984c557f6910a28a479
data/lib/checker.rb ADDED
@@ -0,0 +1,47 @@
1
+ module Sqlite
2
+ class Checker < Mumukit::Metatest::Checker
3
+
4
+ # Verify if solution dataset is equals than result
5
+ # when equals: Return passed & render success
6
+ # when distinct_columns: return failed & render error
7
+ # when distinct_rows: return failed & render error
8
+ def check(result, solution)
9
+ name = "Dataset #{solution[:id]}"
10
+
11
+ case solution[:dataset].compare result[:dataset]
12
+ when :equals
13
+ success(name, result)
14
+ when :distinct_columns
15
+ failed(name, result, solution, 'Las columnas no coinciden')
16
+ when :distinct_rows
17
+ failed(name, result, solution, 'Las filas no coinciden')
18
+ else
19
+ failed(name, result, solution, 'Las consultas no coinciden')
20
+ end
21
+ end
22
+
23
+ def success(name, result)
24
+ [name, :passed, render_success(result)]
25
+ end
26
+
27
+ def failed(name, result, solution, error)
28
+ [name, :failed, render_error(result, solution, error)]
29
+ end
30
+
31
+ # Return success page rendered with results
32
+ def render_success(result)
33
+ renderer.render_success result
34
+ end
35
+
36
+ # Return error page rendered with results & solutions
37
+ def render_error(result, solution, error)
38
+ renderer.render_error result, solution, error
39
+ end
40
+
41
+ private
42
+
43
+ def renderer
44
+ @renderer ||= Sqlite::HtmlRenderer.new
45
+ end
46
+ end
47
+ end
data/lib/dataset.rb ADDED
@@ -0,0 +1,43 @@
1
+ module Sqlite
2
+ class Dataset
3
+ attr_reader :header, :rows
4
+
5
+ def initialize(data)
6
+ rows = data.split(/\n+/i)
7
+ @header = rows.shift.split(/\|/)
8
+ @rows = rows.map { |row| row.split(/\|/) }
9
+ end
10
+
11
+ def compare(other)
12
+ same_header = same_header other.header
13
+ same_rows = same_rows other.rows
14
+
15
+ if same_header & same_rows
16
+ :equals
17
+ elsif !same_header
18
+ :distinct_columns
19
+ else
20
+ :distinct_rows
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ def same_header(other_header)
27
+ @header.eql? other_header
28
+ end
29
+
30
+ def same_rows(other_rows)
31
+ same_amount(other_rows) && same_content(other_rows)
32
+ end
33
+
34
+ def same_amount(other_rows)
35
+ @rows.length == other_rows.length
36
+ end
37
+
38
+ def same_content(other_rows)
39
+ @rows.zip(other_rows).all? { |tuple| tuple[0] == tuple[1] }
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ require 'active_support/inflector'
2
+
3
+ module Sqlite
4
+ class HtmlRenderer
5
+
6
+ def render_success(result)
7
+ @header = result[:dataset].header
8
+ @rows = result[:dataset].rows
9
+ template_file_success.result binding
10
+ end
11
+
12
+ def render_error(result, solution, error)
13
+ @error = error
14
+ @result = {
15
+ header: result[:dataset].header,
16
+ rows: result[:dataset].rows
17
+ }
18
+ @solution = {
19
+ header: solution[:dataset].header,
20
+ rows: solution[:dataset].rows
21
+ }
22
+ template_file_error.result binding
23
+ end
24
+
25
+ protected
26
+
27
+ def template_file_success
28
+ ERB.new File.read("#{__dir__}/view/rows_success.html.erb")
29
+ end
30
+
31
+ def template_file_error
32
+ ERB.new File.read("#{__dir__}/view/rows_error.html.erb")
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,6 @@
1
+ en:
2
+ records: 'Records'
3
+ memory: 'Memory'
4
+ special_records: 'Special records'
5
+ flags: 'Flags'
6
+ check_equal_failure: "<b>%{record}</b> should be <b>%{expected}</b>, but was <b>%{actual}</b>"
@@ -0,0 +1,6 @@
1
+ es:
2
+ records: 'Registros'
3
+ memory: 'Memoria'
4
+ special_records: 'Registros especiales'
5
+ flags: 'Banderas'
6
+ check_equal_failure: "<b>%{record}</b> debería contener el valor <b>%{expected}</b>, pero se encontró <b>%{actual}</b>"
@@ -0,0 +1,18 @@
1
+ class SqliteMetadataHook < Mumukit::Hook
2
+ def metadata
3
+ {
4
+ language: {
5
+ name: 'sqlite',
6
+ icon: { type: 'devicon', name: 'sqlite' },
7
+ version: 'v0.2.2',
8
+ extension: 'sql',
9
+ ace_mode: 'assembly_x86',
10
+ graphic: true
11
+ },
12
+ test_framework: {
13
+ name: 'metatest',
14
+ test_extension: 'yml'
15
+ }
16
+ }
17
+ end
18
+ end
@@ -0,0 +1,10 @@
1
+ module Sqlite
2
+ class MultipleExecutionsRunner
3
+ def run(output, example)
4
+ result = output.find { |it| it[:id] == example[:id] }
5
+
6
+ raise Mumukit::Metatest::Errored, result[:error] if result[:error]
7
+ result
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ require 'mumukit'
2
+ require 'erb'
3
+
4
+ I18n.load_translations_path File.join(__dir__, 'locales', '*.yml')
5
+
6
+ Mumukit.runner_name = 'sqlite'
7
+ Mumukit.configure do |config|
8
+ config.docker_image = 'leandrojdl/mumuki-sqlite-worker'
9
+ config.content_type = 'plain'
10
+ config.structured = true
11
+ end
12
+
13
+ require_relative './test_hook'
14
+ require_relative './metadata_hook'
15
+ require_relative './checker'
16
+ require_relative './multiple_executions_runner'
17
+ require_relative './html_renderer'
18
+ require_relative './dataset'
data/lib/test_hook.rb ADDED
@@ -0,0 +1,99 @@
1
+ require 'json'
2
+
3
+ ##
4
+ # This Hook allow to run Sqlite Worker from an adhoc program that receives .sql files.
5
+
6
+ class SqliteTestHook < Mumukit::Templates::FileHook
7
+
8
+ # Define that worker runs on a freshly-cleaned environment
9
+ isolated
10
+
11
+ # Just define file extension
12
+ def tempfile_extension
13
+ '.json'
14
+ end
15
+
16
+ # Define the command to be run by sqlite worker
17
+ def command_line(filename)
18
+ "runsql #{filename}"
19
+ end
20
+
21
+ # Define the .sql file template from request structure
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: [mulang verifications] todo: better explain
27
+ # }
28
+ #
29
+ def compile_file_content(request)
30
+ solution, datasets = parse_test request.test.strip
31
+
32
+ content = {
33
+ init: request.extra.strip,
34
+ solution: solution,
35
+ student: request.content.strip,
36
+ datasets: datasets
37
+ }
38
+
39
+ content.to_json
40
+ end
41
+
42
+ # Define how output results
43
+ # Expected:
44
+ # {
45
+ # "solutions": [
46
+ # "name\nTest 1.1\nTest 1.2\nTest 1.3\n",
47
+ # "name\nTest 2.1\nTest 2.2\nTest 2.3\n"
48
+ # ],
49
+ # "results": [
50
+ # "id|name\n1|Test 1.1\n2|Test 1.2\n3|Test 1.3\n",
51
+ # "id|name\n1|Test 2.1\n2|Test 2.2\n3|Test 2.3\n"
52
+ # ]
53
+ # }
54
+ def post_process_file(_file, result, status)
55
+ output = JSON.parse(result)
56
+
57
+ case status
58
+ when :passed
59
+ results = post_process_datasets(output['results'])
60
+ solutions = post_process_datasets(output['solutions'])
61
+ framework.test solutions, results
62
+ when :failed
63
+ [output['output'], status]
64
+ else
65
+ [output, status]
66
+ end
67
+ end
68
+
69
+ # Transforms array datasets into hash with :id & :rows
70
+ def post_process_datasets(datasets)
71
+ datasets.map.with_index do |dataset, i|
72
+ {
73
+ id: i + 1,
74
+ dataset: Sqlite::Dataset.new(dataset)
75
+ }
76
+ end
77
+ end
78
+
79
+ # Split query by '-- DATASET' line match
80
+ # First match is teacher's solution
81
+ # Rest are Datasets
82
+ # Example:
83
+ # select * from table
84
+ # -- DATASET
85
+ # insert into 1
86
+ # -- dataset
87
+ # insert into 2
88
+ def parse_test(content)
89
+ query = content.split(/\s*--\s*dataset\s*\n+/i)
90
+ return query.shift, query
91
+ end
92
+
93
+ # Initialize Metatest Framework with Checker & Runner
94
+ def framework
95
+ Mumukit::Metatest::Framework.new checker: Sqlite::Checker.new,
96
+ runner: Sqlite::MultipleExecutionsRunner.new
97
+ end
98
+
99
+ end
@@ -0,0 +1,81 @@
1
+ <style>
2
+ table.sqlite_error {
3
+ width: auto;
4
+ }
5
+ table.sqlite_result {
6
+ border: 3px solid #0B465D;
7
+ }
8
+ table.sqlite_solution {
9
+ border: 3px solid darkred;
10
+ }
11
+ table.sqlite_error tr:nth-child(even) {
12
+ background-color: #DDDDDD;
13
+ }
14
+ table.sqlite_error tr:nth-child(odd) {
15
+ background-color: #FFFFFF;
16
+ }
17
+ table.sqlite_error th {
18
+ color: white;
19
+ font-weight: bold;
20
+ background-color: #2E2F30;
21
+ }
22
+ table.sqlite_error td {
23
+ width: auto;
24
+ text-align: left;
25
+ }
26
+ </style>
27
+
28
+ <h5><%= @error %></h5>
29
+
30
+ <div class="row">
31
+
32
+ <!-- Result -->
33
+ <div class="col-md-6">
34
+ <h6>Se esperaba:</h6>
35
+ <table class="table table-bordered sqlite_error sqlite_result">
36
+ <thead>
37
+ <tr>
38
+ <% @result[:header].each do |field| %>
39
+ <th><%= field %></th>
40
+ <% end %>
41
+ </tr>
42
+ </thead>
43
+
44
+ <tbody>
45
+ <% @result[:rows].each do |row| %>
46
+ <tr>
47
+ <% row.each do |field| %>
48
+ <td><%= field %></td>
49
+ <% end %>
50
+ </tr>
51
+ <% end %>
52
+ </tbody>
53
+ </table>
54
+ </div>
55
+
56
+ <!-- Solution -->
57
+ <div class="col-md-6">
58
+ <h6>Se obtuvo:</h6>
59
+ <table class="table table-bordered sqlite_error sqlite_solution">
60
+ <thead>
61
+ <tr>
62
+ <% @solution[:header].each do |field| %>
63
+ <th><%= field %></th>
64
+ <% end %>
65
+ </tr>
66
+ </thead>
67
+
68
+ <tbody>
69
+ <% @solution[:rows].each do |row| %>
70
+ <tr>
71
+ <% row.each do |field| %>
72
+ <td><%= field %></td>
73
+ <% end %>
74
+ </tr>
75
+ <% end %>
76
+ </tbody>
77
+ </table>
78
+ </div>
79
+ </div>
80
+
81
+
@@ -0,0 +1,42 @@
1
+ <style>
2
+ table.sqlite_success {
3
+ width: auto;
4
+ border: none;
5
+ }
6
+ table.sqlite_success tr:nth-child(even) {
7
+ background-color: #DDDDDD;
8
+ }
9
+ table.sqlite_success tr:nth-child(odd) {
10
+ background-color: #FFFFFF;
11
+ }
12
+ table.sqlite_success th {
13
+ color: white;
14
+ font-weight: bold;
15
+ background-color: #BBBBBB;
16
+ }
17
+ table.sqlite_success td {
18
+ width: auto;
19
+ text-align: left;
20
+ }
21
+ </style>
22
+
23
+ <h5>Consulta correcta!</h5>
24
+ <table class="table table-bordered sqlite_success">
25
+ <thead>
26
+ <tr>
27
+ <% @header.each do |field| %>
28
+ <th><%= field %></th>
29
+ <% end %>
30
+ </tr>
31
+ </thead>
32
+
33
+ <tbody>
34
+ <% @rows.each do |row| %>
35
+ <tr>
36
+ <% row.each do |field| %>
37
+ <td><%= field %></td>
38
+ <% end %>
39
+ </tr>
40
+ <% end %>
41
+ </tbody>
42
+ </table>
metadata ADDED
@@ -0,0 +1,195 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mumuki-sqlite-runner
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Leandro Di Lorenzo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mumukit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rouge
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: dotenv
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: codeclimate-test-reporter
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: mumukit-bridge
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.3'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.3'
153
+ description:
154
+ email:
155
+ - leandro.jdl@gmail.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - lib/checker.rb
161
+ - lib/dataset.rb
162
+ - lib/html_renderer.rb
163
+ - lib/locales/en.yml
164
+ - lib/locales/es.yml
165
+ - lib/metadata_hook.rb
166
+ - lib/multiple_executions_runner.rb
167
+ - lib/sqlite_runner.rb
168
+ - lib/test_hook.rb
169
+ - lib/view/rows_error.html.erb
170
+ - lib/view/rows_success.html.erb
171
+ homepage: http://github.com/leandrojdl/mumuki-sqlite-runner
172
+ licenses:
173
+ - MIT
174
+ metadata: {}
175
+ post_install_message:
176
+ rdoc_options: []
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ requirements: []
190
+ rubyforge_project:
191
+ rubygems_version: 2.5.1
192
+ signing_key:
193
+ specification_version: 4
194
+ summary: SQLite Runner for Mumuki
195
+ test_files: []