Skip to content
This repository was archived by the owner on Oct 14, 2020. It is now read-only.

Commit 25e5609

Browse files
committed
- added CsvImporter for SQL/Ruby
- fixed timeout issue with services - fixed C printing issues
1 parent c7c7084 commit 25e5609

File tree

9 files changed

+187
-25
lines changed

9 files changed

+187
-25
lines changed

docker/ruby.docker

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ RUN gem install stuff-classifier --no-ri --no-rdoc
130130
RUN gem install symbolic --no-ri --no-rdoc
131131
RUN gem install unit --no-ri --no-rdoc
132132

133+
RUN gem install chronic --no-ri --no-rdoc
133134

134135
# partner packages
135136
RUN gem install ably --no-ri --no-rdoc

documentation/output_format.md

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,61 @@ statement, the 2nd completes the nested DESCRIBE and the 3rd completes the top l
5353
#### <COMPLETEDIN::> Details
5454

5555
The value of COMPLETEDIN should be time spent executing the related statement, in milliseconds. It is not required
56-
to support time. `<COMPLETEDIN::>` is valid on its own, and in that case it is only used to terminate the current statement.
56+
to support time. `<COMPLETEDIN::>` is valid on its own, and in that case it is only used to terminate the current statement.
57+
58+
### Advanced Formatting
59+
60+
So far the basic formatting needed to be supported by a language has been discussed. The following
61+
details more advanced features.
62+
63+
#### Advanced Format Schema
64+
The basic `<[TAG]::>[VALUE]` tag format described previously can be expanded to support modes and labels.
65+
This is done using the following convention: `<[TAG]:[MODE]:[LABEL]>[VALUE]`. Modes and labels are optional.
66+
67+
For example. To write a log message with a custom container label. You can do this: `<LOG::My Label>My log message`.
68+
In this example, we provided a tag (required) and a label, and skipped using a mode.
69+
70+
#### What are modes?
71+
A mode is used to output richer content. For example there is a TABLE mode which parses the value as JSON and renders a
72+
table out of it.
73+
74+
#### Advanced Tags
75+
76+
##### LOG
77+
Normally each unformatted line is considered a "LOG" message and is written to STDOUT. Visually this
78+
output will be presented withint a Log container. Multiple log lines are grouped together automatically.
79+
80+
In order to access richer log output content (via modes), you will need to explicitely use a LOG tag.
81+
82+
##### OUT
83+
Same as LOG, however it does not wrap the output within a container UI.
84+
85+
##### TAB
86+
Should be used directly after a LOG tag. Will add a 2nd tab to the log container. Otherwise treated the
87+
same as a LOG tag. You can add multiple tabs to a LOG container.
88+
89+
```
90+
<LOG::Tab 1>This is content displayed within the first tab.<:LF:>This is a 2nd line
91+
<TAB::Tab 2>This is content displayed within the 2nd tab.
92+
```
93+
94+
#### Advanced Modes
95+
96+
##### TABLE
97+
##### CHART
98+
##### DIFF
99+
##### MARKDOWN
100+
##### JSON
101+
##### HIGHLIGHT
102+
##### SCRIPT
103+
104+
In this mode, the tag VALUE will be considered a URL and a script will automatically be
105+
loaded on the page. A specific file will only be loaded once.
106+
107+
```
108+
<LOG:SCRIPT:>https://raw.githubusercontent.com/name/repo/file.js
109+
```
110+
57111

58112
### Why the custom format?
59113

frameworks/c/criterion.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,17 @@ ReportHook(PRE_ALL)(struct criterion_test_set *tests) {
1111

1212
// before the test is run
1313
ReportHook(PRE_SUITE)(struct criterion_suite_set *set) {
14-
printf("<DESCRIBE::>%s\n", set->suite.name);
14+
printf("\n<DESCRIBE::>%s\n", set->suite.name);
1515
}
1616

1717
// before the test is run
1818
ReportHook(PRE_TEST)(struct criterion_test *test) {
19-
printf("<IT::>%s\n",test->name);
19+
printf("\n<IT::>%s\n",test->name);
2020
}
2121

2222
// when an assertion is hit
2323
ReportHook(ASSERT)(struct criterion_assert_stats *stats) {
24+
printf("\n"); // break any lines incase printf was used
2425
if (stats->passed) {
2526
puts("<PASSED::>Test Passed");
2627
} else {
@@ -39,19 +40,19 @@ ReportHook(ASSERT)(struct criterion_assert_stats *stats) {
3940

4041
// when a test crashes unexpectedly
4142
ReportHook(TEST_CRASH)(struct criterion_test_stats *stats) {
42-
puts("<FAILED::>Test Crashed");
43+
puts("\n<FAILED::>Test Crashed");
4344
}
4445

4546
// after a test ends
4647
ReportHook(POST_TEST)(struct criterion_test_stats *stats) {
4748
if (stats->timed_out) {
48-
puts("<FAILED::>Test Timedout");
49+
puts("\n<FAILED::>Test Timed Out");
4950
}
50-
printf("<COMPLETEDIN::>%f\n",stats->elapsed_time*1000);
51+
printf("\n<COMPLETEDIN::>%f\n",stats->elapsed_time*1000);
5152
}
5253

5354
// after the test is run
5455
ReportHook(POST_SUITE)(struct criterion_suite_stats *stats) {
55-
puts("<COMPLETEDIN::>");
56+
puts("\n<COMPLETEDIN::>");
5657
}
5758

frameworks/ruby/sql.rb

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,34 @@
22
require 'sequel'
33
require "faker"
44
require 'active_support/core_ext/object/blank'
5+
require 'hashie'
6+
require_relative 'sql/csv_importer'
57

68
$sql = File.read('/home/codewarrior/solution.txt')
79
$sql_cleaned = $sql.gsub(/(\/\*([\s\S]*?)\*\/|--.*)/, "")
810
$sql_commands = $sql_cleaned.split(/; *$/).select(&:present?)
911

10-
def run_sql(limit: 100, print: true)
11-
results = $sql_commands.map do |cmd|
12-
result = cmd.downcase.start_with?("insert") ? DB.prepare(cmd) : DB[cmd]
13-
result.to_a.tap do |results|
14-
if results.any? or $sql_commands.length == 1
15-
Display.table(results.take(limit), label: "SQL Results") if print
16-
if results.count > limit
17-
puts "Truncated #{results.count} results to show only top 100"
18-
end
12+
def run_sql(limit: 100, cmds: $sql_commands, print: true)
13+
results = cmds.map do |cmd|
14+
(cmd.downcase.start_with?("insert") ? DB.prepare(cmd) : DB[cmd]).tap do |dataset|
15+
if dataset.count > 0 or $sql_commands.length == 1
16+
label = "SQL Results"
17+
label += " (Top #{limit} of #{dataset.count})" if dataset.count > limit
18+
19+
Display.table(dataset.limit(limit).to_a, label: label) if print
1920
end
2021
end
21-
end.select {|r| r.length > 0 }
22+
end
23+
24+
results.select! {|r| r.count > 0 }
2225

23-
$sql_results = results.length > 1 ? results : results.first
26+
if results.length > 1
27+
$sql_multi = true
28+
$sql_results = results
29+
else
30+
$sql_multi = false
31+
$sql_results = results.first
32+
end
2433

2534
rescue Sequel::DatabaseError => ex
2635
msg = ex.message.gsub("SQLite3::SQLException: ", "");
@@ -29,3 +38,19 @@ def run_sql(limit: 100, print: true)
2938
end
3039

3140
alias :run_query :run_sql
41+
42+
# useful helper for finding a specific record and wrapping it in a Hashie::Mash
43+
def find_record(table, id)
44+
result = DB[table].where(id: id).first
45+
result ? Hashie::Mash.new(result) : nil
46+
end
47+
48+
# loops through each sql result and returns the row as a Hashie::Mash. If multiple results were returned,
49+
# the last result set will be used
50+
def each_result(&block)
51+
results = $sql_multi ? $sql_results.last : $sql_results
52+
results.each do |result|
53+
block.call(Hashie::Mash.new(result))
54+
end
55+
end
56+

frameworks/ruby/sql/csv_importer.rb

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
require 'csv'
2+
require 'chronic'
3+
4+
# data importer utility
5+
class CsvImporter
6+
attr_reader :fields, :csv, :table, :limit, :random
7+
8+
def initialize(file, table, fields: {}, limit: 500, random: false)
9+
@csv = CSV.read(file)
10+
@table = table
11+
@fields = fields
12+
@limit = limit
13+
@random = random
14+
end
15+
16+
def create_schema
17+
importer = self
18+
DB.create_table @table do
19+
importer.csv.first.each do |field|
20+
if importer.fields[field]
21+
column field, importer.fields[field]
22+
else
23+
String field
24+
end
25+
end
26+
end
27+
end
28+
29+
def convert_value(field, value)
30+
case @fields[field]
31+
when DateTime
32+
Chronic.parse(value)
33+
else
34+
value
35+
end
36+
end
37+
38+
def import(skip_schema: false)
39+
create_schema unless skip_schema
40+
41+
puts "<STATUS::>Importing #{[limit, @csv.count].min} records..."
42+
43+
fields = @csv.first
44+
dataset = DB[@table]
45+
46+
# remove the columns row
47+
rows = @csv.to_a.drop(1)
48+
# randomize the rows if random is enabled
49+
rows = rows.to_a.sample(limit) if random
50+
51+
rows.each.with_index do |line, row|
52+
return if row > limit
53+
54+
data = {}
55+
56+
line.each.with_index do |value, col|
57+
data[fields[col]] = convert_value(fields[col], value)
58+
end
59+
60+
dataset.insert(data)
61+
end
62+
end
63+
64+
def self.import_sales_data(random: false, limit: 300)
65+
importer = CsvImporter.new("/runner/sample_data/sales.csv", :sales, random: random, limit: limit, fields: {
66+
'latitude' => Float,
67+
'longitude' => Float
68+
})
69+
70+
# TODO: figure out how to fix datetime fields for SQLite
71+
unless defined?(Sequel::SQLite)
72+
importer.fields.merge(
73+
'transation_date' => DateTime,
74+
'account_created' => DateTime,
75+
'last_login' => DateTime
76+
)
77+
end
78+
importer.import
79+
end
80+
end

lib/services.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const startService = {
1717
spawn("/usr/lib/postgresql/9.6/bin/pg_ctl", ['-D', '/home/codewarrior/pg', 'stop'])
1818
});
1919

20-
return spawnAndWait(opts, '/usr/lib/postgresql/9.6/bin/pg_ctl', ['-D', '/home/codewarrior/pg', 'start'], 'autovacuum launcher started')
20+
return spawnAndWait(opts, '/usr/lib/postgresql/9.6/bin/pg_ctl', ['-D', '/home/codewarrior/pg', 'start'], 'autovacuum launcher started', 200 )
2121
},
2222
mariadb: function(opts) {
2323
// TODO
@@ -30,7 +30,7 @@ const startService = {
3030
}
3131
}
3232

33-
function spawnAndWait(opts, cmd, args, text, timeout) {
33+
function spawnAndWait(opts, cmd, args, text, delay) {
3434

3535
return new Promise(function(resolve, reject) {
3636

@@ -41,7 +41,7 @@ function spawnAndWait(opts, cmd, args, text, timeout) {
4141
// console.log(str); // for debugging only
4242

4343
if (str && str.indexOf(text) > 0){
44-
resolve();
44+
setTimeout(resolve, delay || 0);
4545
}
4646
}
4747

@@ -51,7 +51,7 @@ function spawnAndWait(opts, cmd, args, text, timeout) {
5151

5252
s.on('exit', resolve);
5353

54-
setTimeout(resolve, timeout || 2000);
54+
setTimeout(resolve, 2000);
5555
});
5656
}
5757

sample_data/medals.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Year,City,Sport,Discipline,NOC,Event,Event gender,Medal
1+
Year,City,Sport,Discipline,NOC,Event,Gender,Medal
22
1924,Chamonix,Skating,Figure skating,AUT,individual,M,Silver
33
1924,Chamonix,Skating,Figure skating,AUT,individual,W,Gold
44
1924,Chamonix,Skating,Figure skating,AUT,pairs,X,Gold

sample_data/sales.csv

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

test/runners/c_spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ describe('.run', function() {
101101
done();
102102
});
103103
});
104+
104105
it('should support multiple asserts', function(done) {
105106
runner.run({
106107
language: 'c',
@@ -196,7 +197,7 @@ describe('.run', function() {
196197
}
197198
`
198199
}, function(buffer) {
199-
expect(buffer.stdout).to.contain('<FAILED::>Test Timedout');
200+
expect(buffer.stdout).to.contain('<FAILED::>Test Timed Out');
200201
done();
201202
});
202203
});

0 commit comments

Comments
 (0)