marty 0.5.36 → 0.5.38
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/models/marty/data_grid.rb +780 -0
- data/app/models/marty/grid_index_boolean.rb +4 -0
- data/app/models/marty/grid_index_int4range.rb +3 -0
- data/app/models/marty/grid_index_integer.rb +3 -0
- data/app/models/marty/grid_index_numrange.rb +3 -0
- data/app/models/marty/grid_index_string.rb +3 -0
- data/app/models/marty/name_validator.rb +15 -0
- data/db/migrate/100_create_marty_data_grids.rb +16 -0
- data/db/migrate/101_create_marty_grid_index_numranges.rb +27 -0
- data/db/migrate/102_create_marty_grid_index_int4ranges.rb +27 -0
- data/db/migrate/103_create_marty_grid_index_integers.rb +27 -0
- data/db/migrate/104_create_marty_grid_index_strings.rb +27 -0
- data/db/migrate/105_create_marty_grid_index_booleans.rb +27 -0
- data/lib/marty/migrations.rb +3 -4
- data/lib/marty/version.rb +1 -1
- data/spec/dummy/app/models/gemini/state.rb +19 -0
- data/spec/dummy/config/application.rb +1 -1
- data/spec/dummy/db/migrate/20160100000038_create_gemini_states.rb +8 -0
- data/spec/dummy/db/seeds.rb +67 -0
- data/spec/dummy/lib/class_list.rb +1 -1
- data/spec/lib/data_exporter_spec.rb +0 -1
- data/spec/lib/data_importer_spec.rb +0 -1
- data/spec/lib/migrations/vw_marty_postings.sql.expected +2 -2
- data/spec/models/data_grid_spec.rb +614 -0
- data/spec/models/srp_data.csv +55 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/spec_setup.rb +23 -0
- metadata +20 -2
@@ -0,0 +1,15 @@
|
|
1
|
+
class Marty::NameValidator < ActiveModel::Validator
|
2
|
+
def validate(entry)
|
3
|
+
raise "need field option" unless options[:field]
|
4
|
+
field = options[:field].to_sym
|
5
|
+
value = entry.send(field)
|
6
|
+
|
7
|
+
return if value.nil?
|
8
|
+
|
9
|
+
# disallow leading, trailing, >1 internal spaces, special chars (|)
|
10
|
+
if value =~ /\A\s|\s\z|\A.*\s\s.*\z|.*\|.*/
|
11
|
+
entry.errors[field] =
|
12
|
+
I18n.t("activerecord.errors.messages.extraneous_spaces")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateMartyDataGrids < McflyMigration
|
2
|
+
include Marty::Migrations
|
3
|
+
|
4
|
+
def change
|
5
|
+
table_name = "marty_data_grids"
|
6
|
+
|
7
|
+
create_table table_name do |t|
|
8
|
+
t.string :name, null: false
|
9
|
+
# 2-dimensional array which holds grid data
|
10
|
+
t.jsonb :data
|
11
|
+
t.jsonb :metadata
|
12
|
+
t.string :data_type, null: true
|
13
|
+
t.boolean :lenient, null: false, default: false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class CreateMartyGridIndexNumranges < ActiveRecord::Migration
|
2
|
+
include Marty::Migrations
|
3
|
+
|
4
|
+
def change
|
5
|
+
table_name = "marty_grid_index_numranges"
|
6
|
+
|
7
|
+
# drop deprecated version
|
8
|
+
execute("DROP TABLE IF EXISTS #{table_name}")
|
9
|
+
|
10
|
+
create_table table_name do |t|
|
11
|
+
t.datetime :created_dt, null: false
|
12
|
+
t.references :data_grid, null: false
|
13
|
+
t.string :attr, null: false
|
14
|
+
t.numrange :key, null: false
|
15
|
+
t.integer :index, null: false
|
16
|
+
end
|
17
|
+
|
18
|
+
# FIXME: not sure if this index is appropriate for our queries.
|
19
|
+
# May need to break it up.
|
20
|
+
add_index table_name,
|
21
|
+
[:created_dt, :data_grid_id, :attr],
|
22
|
+
name: "index_#{table_name}"
|
23
|
+
add_index table_name, :key, using: "GIST"
|
24
|
+
|
25
|
+
add_fk table_name, :data_grids
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class CreateMartyGridIndexInt4ranges < ActiveRecord::Migration
|
2
|
+
include Marty::Migrations
|
3
|
+
|
4
|
+
def change
|
5
|
+
table_name = "marty_grid_index_int4ranges"
|
6
|
+
|
7
|
+
# drop deprecated version
|
8
|
+
execute("DROP TABLE IF EXISTS #{table_name}")
|
9
|
+
|
10
|
+
create_table table_name do |t|
|
11
|
+
t.datetime :created_dt, null: false
|
12
|
+
t.references :data_grid, null: false
|
13
|
+
t.string :attr, null: false
|
14
|
+
t.int4range :key, null: false
|
15
|
+
t.integer :index, null: false
|
16
|
+
end
|
17
|
+
|
18
|
+
# FIXME: not sure if this index is appropriate for our queries.
|
19
|
+
# May need to break it up.
|
20
|
+
add_index table_name,
|
21
|
+
[:created_dt, :data_grid_id, :attr],
|
22
|
+
name: "index_#{table_name}"
|
23
|
+
add_index table_name, :key, using: "GIST"
|
24
|
+
|
25
|
+
add_fk table_name, :data_grids
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class CreateMartyGridIndexIntegers < ActiveRecord::Migration
|
2
|
+
include Marty::Migrations
|
3
|
+
|
4
|
+
def change
|
5
|
+
table_name = "marty_grid_index_integers"
|
6
|
+
|
7
|
+
# drop deprecated version
|
8
|
+
execute("DROP TABLE IF EXISTS #{table_name}")
|
9
|
+
|
10
|
+
create_table table_name do |t|
|
11
|
+
t.datetime :created_dt, null: false
|
12
|
+
t.references :data_grid, null: false
|
13
|
+
t.string :attr, null: false
|
14
|
+
t.integer :key, array: true, null: false
|
15
|
+
t.integer :index, null: false
|
16
|
+
end
|
17
|
+
|
18
|
+
# FIXME: not sure if this index is appropriate for our queries.
|
19
|
+
# May need to break it up.
|
20
|
+
add_index table_name,
|
21
|
+
[:created_dt, :data_grid_id, :attr],
|
22
|
+
name: "index_#{table_name}"
|
23
|
+
add_index table_name, :key, using: "GIN"
|
24
|
+
|
25
|
+
add_fk table_name, :data_grids
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class CreateMartyGridIndexStrings < ActiveRecord::Migration
|
2
|
+
include Marty::Migrations
|
3
|
+
|
4
|
+
def change
|
5
|
+
table_name = "marty_grid_index_strings"
|
6
|
+
|
7
|
+
# drop deprecated version
|
8
|
+
execute("DROP TABLE IF EXISTS #{table_name}")
|
9
|
+
|
10
|
+
create_table table_name do |t|
|
11
|
+
t.datetime :created_dt, null: false
|
12
|
+
t.references :data_grid, null: false
|
13
|
+
t.string :attr, null: false
|
14
|
+
t.text :key, array: true
|
15
|
+
t.integer :index, null: false
|
16
|
+
end
|
17
|
+
|
18
|
+
# FIXME: not sure if this index is appropriate for our queries.
|
19
|
+
# May need to break it up.
|
20
|
+
add_index table_name,
|
21
|
+
[:created_dt, :data_grid_id, :attr],
|
22
|
+
name: "index_#{table_name}"
|
23
|
+
add_index table_name, :key, using: "GIN"
|
24
|
+
|
25
|
+
add_fk table_name, :data_grids
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class CreateMartyGridIndexBooleans < ActiveRecord::Migration
|
2
|
+
include Marty::Migrations
|
3
|
+
|
4
|
+
def change
|
5
|
+
table_name = "marty_grid_index_booleans"
|
6
|
+
|
7
|
+
# drop deprecated version
|
8
|
+
execute("DROP TABLE IF EXISTS #{table_name}")
|
9
|
+
|
10
|
+
create_table table_name do |t|
|
11
|
+
t.datetime :created_dt, null: false
|
12
|
+
t.references :data_grid, null: false
|
13
|
+
t.string :attr, null: false
|
14
|
+
t.boolean :key, null: false
|
15
|
+
t.integer :index, null: false
|
16
|
+
end
|
17
|
+
|
18
|
+
# FIXME: not sure if this index is appropriate for our queries.
|
19
|
+
# May need to break it up.
|
20
|
+
add_index table_name,
|
21
|
+
[:created_dt, :data_grid_id, :attr],
|
22
|
+
name: "index_#{table_name}"
|
23
|
+
add_index table_name, :key
|
24
|
+
|
25
|
+
add_fk table_name, :data_grids
|
26
|
+
end
|
27
|
+
end
|
data/lib/marty/migrations.rb
CHANGED
@@ -73,12 +73,11 @@ module Marty::Migrations
|
|
73
73
|
|
74
74
|
def self.write_view(target_dir, target_view, klass, jsons, excludes, extras)
|
75
75
|
colnames = klass.columns_hash.keys
|
76
|
-
|
77
|
-
excludes += user_id_cols
|
76
|
+
excludes += ["user_id", "o_user_id"]
|
78
77
|
joins = ["join marty_users u on main.user_id = u.id",
|
79
78
|
"left join marty_users ou on main.o_user_id = ou.id"]
|
80
|
-
columns = ["
|
81
|
-
"
|
79
|
+
columns = ["u.login AS user_name",
|
80
|
+
"ou.login AS obsoleted_user"]
|
82
81
|
jointabs = {}
|
83
82
|
colnames.each do |c|
|
84
83
|
if jsons[c]
|
data/lib/marty/version.rb
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
class Gemini::State < ActiveRecord::Base
|
2
|
+
extend Marty::Enum
|
3
|
+
|
4
|
+
self.table_name_prefix = "gemini_"
|
5
|
+
|
6
|
+
validates_presence_of :name, :full_name
|
7
|
+
validates_uniqueness_of :name, :full_name
|
8
|
+
|
9
|
+
delorean_fn :lookup, sig: 1 do
|
10
|
+
|name|
|
11
|
+
self.find_by_name(name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
name
|
16
|
+
end
|
17
|
+
|
18
|
+
# FIXME: prevent deletion/update
|
19
|
+
end
|
data/spec/dummy/db/seeds.rb
CHANGED
@@ -8,3 +8,70 @@ Gemini::MortgageType.create(name: "FHA")
|
|
8
8
|
Gemini::MortgageType.create(name: "VA")
|
9
9
|
Gemini::MortgageType.create(name: "USDA/Rural Housing")
|
10
10
|
Gemini::StreamlineType.seed
|
11
|
+
|
12
|
+
######################################################################
|
13
|
+
|
14
|
+
STATES ||=
|
15
|
+
[
|
16
|
+
[ "Alabama", "AL" ],
|
17
|
+
[ "Alaska", "AK" ],
|
18
|
+
[ "Arizona", "AZ" ],
|
19
|
+
[ "Arkansas", "AR" ],
|
20
|
+
[ "California", "CA" ],
|
21
|
+
[ "Colorado", "CO" ],
|
22
|
+
[ "Connecticut", "CT" ],
|
23
|
+
[ "Delaware", "DE" ],
|
24
|
+
[ "District Of Columbia", "DC" ],
|
25
|
+
[ "Florida", "FL" ],
|
26
|
+
[ "Georgia", "GA" ],
|
27
|
+
[ "Hawaii", "HI" ],
|
28
|
+
[ "Idaho", "ID" ],
|
29
|
+
[ "Illinois", "IL" ],
|
30
|
+
[ "Indiana", "IN" ],
|
31
|
+
[ "Iowa", "IA" ],
|
32
|
+
[ "Kansas", "KS" ],
|
33
|
+
[ "Kentucky", "KY" ],
|
34
|
+
[ "Louisiana", "LA" ],
|
35
|
+
[ "Maine", "ME" ],
|
36
|
+
[ "Maryland", "MD" ],
|
37
|
+
[ "Massachusetts", "MA" ],
|
38
|
+
[ "Michigan", "MI" ],
|
39
|
+
[ "Minnesota", "MN" ],
|
40
|
+
[ "Mississippi", "MS" ],
|
41
|
+
[ "Missouri", "MO" ],
|
42
|
+
[ "Montana", "MT" ],
|
43
|
+
[ "Nebraska", "NE" ],
|
44
|
+
[ "Nevada", "NV" ],
|
45
|
+
[ "New Hampshire", "NH" ],
|
46
|
+
[ "New Jersey", "NJ" ],
|
47
|
+
[ "New Mexico", "NM" ],
|
48
|
+
[ "New York", "NY" ],
|
49
|
+
[ "North Carolina", "NC" ],
|
50
|
+
[ "North Dakota", "ND" ],
|
51
|
+
[ "Ohio", "OH" ],
|
52
|
+
[ "Oklahoma", "OK" ],
|
53
|
+
[ "Oregon", "OR" ],
|
54
|
+
[ "Pennsylvania", "PA" ],
|
55
|
+
[ "Rhode Island", "RI" ],
|
56
|
+
[ "South Carolina", "SC" ],
|
57
|
+
[ "South Dakota", "SD" ],
|
58
|
+
[ "Tennessee", "TN" ],
|
59
|
+
[ "Texas", "TX" ],
|
60
|
+
[ "Utah", "UT" ],
|
61
|
+
[ "Vermont", "VT" ],
|
62
|
+
[ "Virginia", "VA" ],
|
63
|
+
[ "Washington", "WA" ],
|
64
|
+
[ "West Virginia", "WV" ],
|
65
|
+
[ "Wisconsin", "WI" ],
|
66
|
+
[ "Wyoming", "WY" ],
|
67
|
+
|
68
|
+
# US Territories (FIXME: incomplete)
|
69
|
+
[ "American Samoa", "AS" ],
|
70
|
+
[ "Guam", "GU" ],
|
71
|
+
[ "Puerto Rico", "PR" ],
|
72
|
+
[ "Virgin Islands", "VI" ],
|
73
|
+
]
|
74
|
+
|
75
|
+
STATES.each { |s| Gemini::State.create(full_name: s[0], name: s[1]) }
|
76
|
+
|
77
|
+
######################################################################
|
@@ -7,8 +7,8 @@ $$ language plpgsql;
|
|
7
7
|
drop view if exists vw_marty_postings;
|
8
8
|
create or replace view vw_marty_postings as
|
9
9
|
select
|
10
|
-
|
11
|
-
|
10
|
+
u.login AS user_name,
|
11
|
+
ou.login AS obsoleted_user,
|
12
12
|
main.id,
|
13
13
|
main.group_id,
|
14
14
|
main.created_dt,
|
@@ -0,0 +1,614 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Marty::DataGridSpec
|
4
|
+
describe DataGrid do
|
5
|
+
|
6
|
+
G1 =<<EOS
|
7
|
+
state\tstring\tv\t\t
|
8
|
+
ltv\tnumrange\tv\t\t
|
9
|
+
fico\tnumrange\th\t\t
|
10
|
+
|
11
|
+
\t\t>=600<700\t>=700<750\t>=750
|
12
|
+
CA\t<=80\t1.1\t2.2\t3.3
|
13
|
+
TX|HI\t>80<=105\t4.4\t5.5\t6.6
|
14
|
+
NM\t<=80\t1.2\t2.3\t3.4
|
15
|
+
MA\t>80<=105\t4.5\t5.6\t
|
16
|
+
\t<=80\t11\t22\t33
|
17
|
+
EOS
|
18
|
+
|
19
|
+
G2 =<<EOS
|
20
|
+
units\tinteger\tv\t\t
|
21
|
+
ltv\tnumrange\tv\t\t
|
22
|
+
cltv\tnumrange\th\t\t
|
23
|
+
fico\tnumrange\th\t\t
|
24
|
+
|
25
|
+
\t\t>=100<110\t>=110<120\t>=120
|
26
|
+
\t\t>=600<700\t>=700<750\t>=750
|
27
|
+
1|2\t<=80\t1.1\t2.2\t3.3
|
28
|
+
1|2\t>80<=105\t4.4\t5.5\t6.6
|
29
|
+
3|4\t<=80\t1.2\t2.3\t3.4
|
30
|
+
3|4\t>80<=105\t4.5\t5.6\t6.7
|
31
|
+
EOS
|
32
|
+
|
33
|
+
G3 = File.open(File.expand_path("../srp_data.csv", __FILE__)).read
|
34
|
+
|
35
|
+
G4 =<<EOS
|
36
|
+
lenient
|
37
|
+
hb_indicator\tboolean\tv
|
38
|
+
cltv\tnumrange\th
|
39
|
+
|
40
|
+
\t<=60\t>60<=70\t>70<=75\t>75<=80\t>80<=85\t>85<=90\t>90<=95\t>95<=97
|
41
|
+
true\t-0.750\t-0.750\t-0.750\t-1.500\t-1.500\t-1.500\t\t
|
42
|
+
EOS
|
43
|
+
|
44
|
+
G5 =<<EOS
|
45
|
+
ltv\tnumrange\tv\t\t
|
46
|
+
|
47
|
+
<=115\t-0.375
|
48
|
+
>115<=135\t-0.750
|
49
|
+
EOS
|
50
|
+
|
51
|
+
G6 =<<EOS
|
52
|
+
ltv\tnumrange\th
|
53
|
+
|
54
|
+
<=115\t>115<=135
|
55
|
+
-0.375\t-0.750
|
56
|
+
EOS
|
57
|
+
|
58
|
+
G7 =<<EOS
|
59
|
+
string
|
60
|
+
hb_indicator\tboolean\tv
|
61
|
+
cltv\tnumrange\th
|
62
|
+
|
63
|
+
\t<=60\t>60<=70\t>70<=75\t>75<=80\t>80<=85\t>85<=90\t>90<=95\t>95<=97
|
64
|
+
true\tThis\tis\ta\ttest\tof\tstring type\t\t
|
65
|
+
EOS
|
66
|
+
|
67
|
+
G8 =<<EOS
|
68
|
+
Marty::DataGrid
|
69
|
+
ltv\tnumrange\tv\t\t
|
70
|
+
|
71
|
+
<=115\tG1
|
72
|
+
>115<=135\tG2
|
73
|
+
>135<=140\tG3
|
74
|
+
EOS
|
75
|
+
|
76
|
+
G9 =<<EOS
|
77
|
+
state\tstring\tv
|
78
|
+
ltv\tnumrange\tv
|
79
|
+
|
80
|
+
CA|TX\t>80\t123
|
81
|
+
\t>80\t456
|
82
|
+
EOS
|
83
|
+
|
84
|
+
Ga =<<EOS
|
85
|
+
dg\tMarty::DataGrid\tv\t\t
|
86
|
+
|
87
|
+
G1|G2\t7
|
88
|
+
G3\t8
|
89
|
+
EOS
|
90
|
+
|
91
|
+
Gb =<<EOS
|
92
|
+
property_state\tGemini::State\tv\t\t
|
93
|
+
|
94
|
+
CA|TX\t70
|
95
|
+
GA\t80
|
96
|
+
MN\t90
|
97
|
+
EOS
|
98
|
+
|
99
|
+
Gc =<<EOS
|
100
|
+
Marty::DataGrid
|
101
|
+
property_state\tGemini::State\tv\t\t
|
102
|
+
|
103
|
+
CA|TX\tGb
|
104
|
+
EOS
|
105
|
+
|
106
|
+
Gd =<<EOS
|
107
|
+
hb_indicator\tboolean\tv
|
108
|
+
|
109
|
+
true\t456
|
110
|
+
false\t123
|
111
|
+
EOS
|
112
|
+
|
113
|
+
before(:each) do
|
114
|
+
#Mcfly.whodunnit = Marty::User.find_by_login('marty')
|
115
|
+
marty_whodunnit
|
116
|
+
end
|
117
|
+
|
118
|
+
def lookup_grid_helper(pt, gridname, params, follow=false)
|
119
|
+
dg=Marty::DataGrid.lookup(pt, gridname)
|
120
|
+
res=dg.lookup_grid_distinct_entry(pt, params, nil, follow)
|
121
|
+
[res["result"], res["name"]]
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "validations" do
|
125
|
+
it "a basic data grid should load ok" do
|
126
|
+
dg_from_import("G1", G1)
|
127
|
+
dg_from_import("G2", G2)
|
128
|
+
dg_from_import("G3", G3)
|
129
|
+
dg_from_import("G8", G8)
|
130
|
+
dg_from_import("Ga", Ga)
|
131
|
+
|
132
|
+
expect(Marty::DataGrid.lookup('infinity', "G1").name).to eq "G1"
|
133
|
+
expect(Marty::DataGrid.lookup('infinity', "G2").name).to eq "G2"
|
134
|
+
expect(Marty::DataGrid.lookup('infinity', "G3").name).to eq "G3"
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should not allow dup attr names" do
|
138
|
+
g_bad = G1.sub(/fico/, "ltv")
|
139
|
+
|
140
|
+
expect {
|
141
|
+
dg_from_import("G2", g_bad)
|
142
|
+
}.to raise_error(ActiveRecord::RecordInvalid)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should not allow dup grid names" do
|
146
|
+
dg_from_import("G1", G1)
|
147
|
+
|
148
|
+
expect {
|
149
|
+
dg_from_import("G1", G2)
|
150
|
+
}.to raise_error(ActiveRecord::RecordInvalid)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should not allow extra attr rows" do
|
154
|
+
g_bad = "x\tnumrange\th\t\t\n" + G1
|
155
|
+
|
156
|
+
expect {
|
157
|
+
dg_from_import("G2", g_bad)
|
158
|
+
}.to raise_error(RuntimeError)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should not allow dup row/col key combos" do
|
162
|
+
g_bad = G1 + G1.split("\n").last + "\n"
|
163
|
+
expect {
|
164
|
+
dg_from_import("G2", g_bad)
|
165
|
+
}.to raise_error(ActiveRecord::RecordInvalid)
|
166
|
+
|
167
|
+
g_bad = G2 + G2.split("\n").last + "\n"
|
168
|
+
expect {
|
169
|
+
dg_from_import("G2", g_bad)
|
170
|
+
}.to raise_error(ActiveRecord::RecordInvalid)
|
171
|
+
end
|
172
|
+
|
173
|
+
it "NULL keys are only allowed on string fields" do
|
174
|
+
g_bad = G2.sub(/1\|2/, "")
|
175
|
+
|
176
|
+
expect {
|
177
|
+
dg_from_import("G2", g_bad)
|
178
|
+
}.to raise_error(ActiveRecord::RecordInvalid)
|
179
|
+
|
180
|
+
g_bad = G2.sub(/<=80/, "")
|
181
|
+
|
182
|
+
expect {
|
183
|
+
dg_from_import("G2", g_bad)
|
184
|
+
}.to raise_error(ActiveRecord::RecordInvalid)
|
185
|
+
end
|
186
|
+
|
187
|
+
it "Unknown keys for typed grids should raise error" do
|
188
|
+
g_bad = G8.sub(/G3/, "XXXXX")
|
189
|
+
|
190
|
+
expect {
|
191
|
+
dg_from_import("G8", g_bad)
|
192
|
+
}.to raise_error(RuntimeError)
|
193
|
+
|
194
|
+
g_bad = G8.sub(/DataGrid/, "Division")
|
195
|
+
|
196
|
+
expect {
|
197
|
+
dg_from_import("G8", g_bad)
|
198
|
+
}.to raise_error(RuntimeError)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "Unknown keys for grid headers should raise error" do
|
202
|
+
g_bad = Ga.sub(/G3/, "XXXXX")
|
203
|
+
|
204
|
+
expect {
|
205
|
+
dg_from_import("Ga", g_bad)
|
206
|
+
}.to raise_error(RuntimeError)
|
207
|
+
|
208
|
+
g_bad = Ga.sub(/DataGrid/, "Division")
|
209
|
+
|
210
|
+
expect {
|
211
|
+
dg_from_import("Ga", g_bad)
|
212
|
+
}.to raise_error(RuntimeError)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "lookup" do
|
217
|
+
before(:each) do
|
218
|
+
["G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8", "Ga", "Gb",
|
219
|
+
"Gc", "Gd"].each { |g|
|
220
|
+
dg_from_import(g, "Marty::DataGridSpec::#{g}".constantize)
|
221
|
+
}
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should handle boolean lookups" do
|
225
|
+
res = [true, false].map { |hb_indicator|
|
226
|
+
lookup_grid_helper('infinity',
|
227
|
+
"Gd",
|
228
|
+
{"hb_indicator" => hb_indicator,
|
229
|
+
},
|
230
|
+
)
|
231
|
+
}
|
232
|
+
expect(res).to eq [[456.0, "Gd"], [123.0, "Gd"]]
|
233
|
+
end
|
234
|
+
|
235
|
+
it "should handle basic lookups" do
|
236
|
+
res = lookup_grid_helper('infinity',
|
237
|
+
"G3",
|
238
|
+
{"amount" => 160300,
|
239
|
+
"state" => "HI",
|
240
|
+
},
|
241
|
+
)
|
242
|
+
expect(res).to eq [1.655,"G3"]
|
243
|
+
|
244
|
+
[3,4].each {
|
245
|
+
|units|
|
246
|
+
res = lookup_grid_helper('infinity',
|
247
|
+
"G2",
|
248
|
+
{"fico" => 720,
|
249
|
+
"units" => units,
|
250
|
+
"ltv" => 100,
|
251
|
+
"cltv" => 110.1,
|
252
|
+
},
|
253
|
+
)
|
254
|
+
expect(res).to eq [5.6,"G2"]
|
255
|
+
}
|
256
|
+
|
257
|
+
dg = Marty::DataGrid.lookup('infinity', "G1")
|
258
|
+
|
259
|
+
h = {
|
260
|
+
"fico" => 600,
|
261
|
+
"state" => "RI",
|
262
|
+
"ltv" => 10,
|
263
|
+
}
|
264
|
+
|
265
|
+
res = lookup_grid_helper('infinity', "G1", h)
|
266
|
+
expect(res).to eq [11,"G1"]
|
267
|
+
|
268
|
+
dg.update_from_import("G1", G1.sub(/11/, "111"))
|
269
|
+
|
270
|
+
res = lookup_grid_helper('infinity', "G1", h)
|
271
|
+
expect(res).to eq [111,"G1"]
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should result in error when there are multiple cell hits" do
|
275
|
+
expect {
|
276
|
+
lookup_grid_helper('infinity',
|
277
|
+
"G2",
|
278
|
+
{"fico" => 720,
|
279
|
+
"ltv" => 100,
|
280
|
+
"cltv" => 110.1,
|
281
|
+
},
|
282
|
+
)
|
283
|
+
}.to raise_error(RuntimeError)
|
284
|
+
end
|
285
|
+
|
286
|
+
it "should return nil when matching data grid cell is nil" do
|
287
|
+
res = lookup_grid_helper('infinity',
|
288
|
+
"G1",
|
289
|
+
{"fico" => 800,
|
290
|
+
"state" => "MA",
|
291
|
+
"ltv" => 81,
|
292
|
+
},
|
293
|
+
)
|
294
|
+
expect(res).to eq [nil,"G1"]
|
295
|
+
end
|
296
|
+
|
297
|
+
it "should handle string wildcards" do
|
298
|
+
res = lookup_grid_helper('infinity',
|
299
|
+
"G1",
|
300
|
+
{"fico" => 720,
|
301
|
+
"state" => "GU",
|
302
|
+
"ltv" => 80,
|
303
|
+
},
|
304
|
+
)
|
305
|
+
expect(res).to eq [22,"G1"]
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should handle matches which also have a wildcard match" do
|
309
|
+
dg_from_import("G9", G9)
|
310
|
+
|
311
|
+
res = lookup_grid_helper('infinity',
|
312
|
+
"G9",
|
313
|
+
{"state" => "CA", "ltv" => 81},
|
314
|
+
)
|
315
|
+
expect(res).to eq [123,"G9"]
|
316
|
+
|
317
|
+
res = lookup_grid_helper('infinity',
|
318
|
+
"G9",
|
319
|
+
{"state" => "GU", "ltv" => 81},
|
320
|
+
)
|
321
|
+
expect(res).to eq [456,"G9"]
|
322
|
+
end
|
323
|
+
|
324
|
+
it "should handle nil attr values to match wildcard" do
|
325
|
+
dg_from_import("G9", G9)
|
326
|
+
|
327
|
+
res = lookup_grid_helper('infinity',
|
328
|
+
"G9",
|
329
|
+
{"state" => nil, "ltv" => 81},
|
330
|
+
)
|
331
|
+
expect(res).to eq [456,"G9"]
|
332
|
+
|
333
|
+
expect {
|
334
|
+
res = lookup_grid_helper('infinity',
|
335
|
+
"G9",
|
336
|
+
{"state" => "CA", "ltv" => nil},
|
337
|
+
)
|
338
|
+
}.to raise_error(RuntimeError)
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should handle boolean keys" do
|
342
|
+
res = lookup_grid_helper('infinity',
|
343
|
+
"G4",
|
344
|
+
{"hb_indicator" => true,
|
345
|
+
"cltv" => 80,
|
346
|
+
},
|
347
|
+
)
|
348
|
+
expect(res).to eq [-1.5,"G4"]
|
349
|
+
|
350
|
+
res = lookup_grid_helper('infinity',
|
351
|
+
"G4",
|
352
|
+
{"hb_indicator" => false,
|
353
|
+
"cltv" => 80,
|
354
|
+
},
|
355
|
+
)
|
356
|
+
expect(res).to eq [nil,"G4"]
|
357
|
+
end
|
358
|
+
|
359
|
+
it "should handle vertical-only grids" do
|
360
|
+
res = lookup_grid_helper('infinity',
|
361
|
+
"G5",
|
362
|
+
{"ltv" => 80},
|
363
|
+
)
|
364
|
+
expect(res).to eq [-0.375,"G5"]
|
365
|
+
end
|
366
|
+
|
367
|
+
it "should handle horiz-only grids" do
|
368
|
+
res = lookup_grid_helper('infinity',
|
369
|
+
"G6",
|
370
|
+
{"ltv" => 80, "conforming" => true},
|
371
|
+
)
|
372
|
+
expect(res).to eq [-0.375,"G6"]
|
373
|
+
end
|
374
|
+
|
375
|
+
it "should handle string typed data grids" do
|
376
|
+
expect(Marty::DataGrid.lookup('infinity', "G7").data_type).to eq "string"
|
377
|
+
|
378
|
+
res = lookup_grid_helper('infinity',
|
379
|
+
"G7",
|
380
|
+
{"hb_indicator" => true,
|
381
|
+
"cltv" => 80,
|
382
|
+
},
|
383
|
+
)
|
384
|
+
expect(res).to eq ["test","G7"]
|
385
|
+
end
|
386
|
+
|
387
|
+
it "should handle DataGrid typed data grids" do
|
388
|
+
expect(Marty::DataGrid.lookup('infinity', "G8").data_type).to eq "Marty::DataGrid"
|
389
|
+
g1 = Marty::DataGrid.lookup('infinity', "G1")
|
390
|
+
|
391
|
+
res = lookup_grid_helper('infinity',
|
392
|
+
"G8",
|
393
|
+
{"ltv" => 80,
|
394
|
+
},
|
395
|
+
)
|
396
|
+
expect(res).to eq [g1,"G8"]
|
397
|
+
end
|
398
|
+
|
399
|
+
it "should handle multi DataGrid lookups" do
|
400
|
+
expect(Marty::DataGrid.lookup('infinity', "G8").data_type).to eq "Marty::DataGrid"
|
401
|
+
g1 = Marty::DataGrid.lookup('infinity', "G1")
|
402
|
+
|
403
|
+
h = {
|
404
|
+
"fico" => 600,
|
405
|
+
"state" => "RI",
|
406
|
+
"ltv" => 10,
|
407
|
+
}
|
408
|
+
|
409
|
+
g1_res = lookup_grid_helper('infinity', "G1", h)
|
410
|
+
expect(g1_res).to eq [11,"G1"]
|
411
|
+
|
412
|
+
res = lookup_grid_helper('infinity',
|
413
|
+
"G8",
|
414
|
+
h,true
|
415
|
+
)
|
416
|
+
expect(g1_res).to eq res
|
417
|
+
end
|
418
|
+
|
419
|
+
it "should handle DataGrid typed data grids" do
|
420
|
+
g1 = Marty::DataGrid.lookup('infinity', "G1")
|
421
|
+
|
422
|
+
res = lookup_grid_helper('infinity',
|
423
|
+
"Ga",
|
424
|
+
{"dg" => g1,
|
425
|
+
},
|
426
|
+
)
|
427
|
+
expect(res).to eq [7,"Ga"]
|
428
|
+
|
429
|
+
# should be able to lookup bu name as well
|
430
|
+
res = lookup_grid_helper('infinity',
|
431
|
+
"Ga",
|
432
|
+
{"dg" => "G2",
|
433
|
+
},
|
434
|
+
)
|
435
|
+
expect(res).to eq [7,"Ga"]
|
436
|
+
end
|
437
|
+
|
438
|
+
it "should handle DataGrid typed data grids -- non mcfly" do
|
439
|
+
ca = Gemini::State.find_by_name("CA")
|
440
|
+
|
441
|
+
res = lookup_grid_helper('infinity',
|
442
|
+
"Gb",
|
443
|
+
{"property_state" => ca,
|
444
|
+
},
|
445
|
+
)
|
446
|
+
expect(res).to eq [70,"Gb"]
|
447
|
+
|
448
|
+
# should be able to lookup bu name as well
|
449
|
+
res = lookup_grid_helper('infinity',
|
450
|
+
"Gb",
|
451
|
+
{"property_state" => "CA",
|
452
|
+
},
|
453
|
+
)
|
454
|
+
expect(res).to eq [70,"Gb"]
|
455
|
+
end
|
456
|
+
|
457
|
+
it "should return grid data and metadata simple" do
|
458
|
+
pt = 'infinity'
|
459
|
+
expected_data = [[1.1, 2.2, 3.3], [4.4, 5.5, 6.6], [1.2, 2.3, 3.4],
|
460
|
+
[4.5, 5.6, 6.7]]
|
461
|
+
expected_metadata = [{"dir"=>"v",
|
462
|
+
"attr"=>"units",
|
463
|
+
"keys"=>[[1, 2], [1, 2], [3, 4], [3, 4]],
|
464
|
+
"type"=>"integer"},
|
465
|
+
{"dir"=>"v",
|
466
|
+
"attr"=>"ltv",
|
467
|
+
"keys"=>["[,80]", "(80,105]", "[,80]", "(80,105]"],
|
468
|
+
"type"=>"numrange"},
|
469
|
+
{"dir"=>"h",
|
470
|
+
"attr"=>"cltv",
|
471
|
+
"keys"=>["[100,110)", "[110,120)", "[120,]"],
|
472
|
+
"type"=>"numrange"},
|
473
|
+
{"dir"=>"h",
|
474
|
+
"attr"=>"fico",
|
475
|
+
"keys"=>["[600,700)", "[700,750)", "[750,]"],
|
476
|
+
"type"=>"numrange"}]
|
477
|
+
|
478
|
+
dg = Marty::DataGrid.lookup(pt, 'G2')
|
479
|
+
res = dg.lookup_grid_distinct_entry(pt, {}, nil, true, true)
|
480
|
+
expect(res["data"]).to eq (expected_data)
|
481
|
+
expect(res["metadata"]).to eq (expected_metadata)
|
482
|
+
end
|
483
|
+
|
484
|
+
it "should return grid data and metadata multi (following)" do
|
485
|
+
pt = 'infinity'
|
486
|
+
expected_data = [[1.1, 2.2, 3.3],[4.4, 5.5, 6.6],[1.2, 2.3, 3.4],
|
487
|
+
[4.5, 5.6, nil],[11.0, 22.0, 33.0]]
|
488
|
+
expected_metadata = [{"dir"=>"v",
|
489
|
+
"attr"=>"state",
|
490
|
+
"keys"=>[["CA"], ["HI", "TX"], ["NM"], ["MA"], nil],
|
491
|
+
"type"=>"string"},
|
492
|
+
{"dir"=>"v",
|
493
|
+
"attr"=>"ltv",
|
494
|
+
"keys"=>["[,80]", "(80,105]", "[,80]", "(80,105]",
|
495
|
+
"[,80]"],
|
496
|
+
"type"=>"numrange"},
|
497
|
+
{"dir"=>"h",
|
498
|
+
"attr"=>"fico",
|
499
|
+
"keys"=>["[600,700)", "[700,750)", "[750,]"],
|
500
|
+
"type"=>"numrange"}]
|
501
|
+
dg = Marty::DataGrid.lookup(pt, 'G8')
|
502
|
+
res = dg.lookup_grid_distinct_entry(pt, { "ltv" => 10,
|
503
|
+
"state" => "RI" }, nil, true,
|
504
|
+
true)
|
505
|
+
expect(res["data"]).to eq (expected_data)
|
506
|
+
expect(res["metadata"]).to eq (expected_metadata)
|
507
|
+
end
|
508
|
+
|
509
|
+
it "should return grid data and metadata multi (not following)" do
|
510
|
+
pt = 'infinity'
|
511
|
+
expected_data = [["G1"], ["G2"], ["G3"]]
|
512
|
+
expected_metadata = [{"dir"=>"v",
|
513
|
+
"attr"=>"ltv",
|
514
|
+
"keys"=>["[,115]", "(115,135]", "(135,140]"],
|
515
|
+
"type"=>"numrange"}]
|
516
|
+
dg = Marty::DataGrid.lookup(pt, 'G8')
|
517
|
+
res = dg.lookup_grid_distinct_entry(pt, { "ltv" => 10,
|
518
|
+
"state" => "RI" }, nil, false,
|
519
|
+
true)
|
520
|
+
expect(res["data"]).to eq (expected_data)
|
521
|
+
expect(res["metadata"]).to eq (expected_metadata)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
describe "updates" do
|
526
|
+
it "should be possible to modify a grid referenced from a multi-grid" do
|
527
|
+
dgb = dg_from_import("Gb", Gb, '1/1/2014')
|
528
|
+
dgc = dg_from_import("Gc", Gc, '2/2/2014')
|
529
|
+
|
530
|
+
dgb.update_from_import("Gb", Gb.sub(/70/, "333"), '1/1/2015')
|
531
|
+
dgb.update_from_import("Gb", Gb.sub(/70/, "444"), '1/1/2016')
|
532
|
+
|
533
|
+
res = dgc.lookup_grid_distinct_entry('2/2/2014',
|
534
|
+
{"property_state" => "CA"})
|
535
|
+
|
536
|
+
expect(res["result"]).to eq(70)
|
537
|
+
|
538
|
+
res = dgc.lookup_grid_distinct_entry('2/2/2015',
|
539
|
+
{"property_state" => "CA"})
|
540
|
+
|
541
|
+
expect(res["result"]).to eq(333)
|
542
|
+
|
543
|
+
res = dgc.lookup_grid_distinct_entry('2/2/2016',
|
544
|
+
{"property_state" => "CA"})
|
545
|
+
|
546
|
+
expect(res["result"]).to eq(444)
|
547
|
+
end
|
548
|
+
|
549
|
+
it "should not create a new version if no change has been made" do
|
550
|
+
dg = dg_from_import("G4", G1)
|
551
|
+
dg.update_from_import("G4", G1)
|
552
|
+
expect(Marty::DataGrid.unscoped.where(group_id: dg.group_id).count).to eq 1
|
553
|
+
end
|
554
|
+
|
555
|
+
it "should be able to export and import back grids" do
|
556
|
+
[G1, G2, G3, G4, G5, G6, G7, G8, G9, Ga, Gb].each_with_index do
|
557
|
+
|grid, i|
|
558
|
+
dg = dg_from_import("G#{i}", grid)
|
559
|
+
g1 = dg.export
|
560
|
+
dg = dg_from_import("Gx#{i}", g1)
|
561
|
+
g2 = dg.export
|
562
|
+
expect(g1).to eq g2
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
it "should be able to externally export/import grids" do
|
567
|
+
load_scripts(nil, Date.today)
|
568
|
+
|
569
|
+
dg = dg_from_import("G1", G1)
|
570
|
+
|
571
|
+
p = posting("BASE", DateTime.tomorrow, '?')
|
572
|
+
|
573
|
+
engine = Marty::ScriptSet.new.get_engine("DataReport")
|
574
|
+
res = engine.evaluate("TableReport",
|
575
|
+
"result",
|
576
|
+
{
|
577
|
+
"pt_name" => p.name,
|
578
|
+
"class_name" => "Marty::DataGrid",
|
579
|
+
},
|
580
|
+
)
|
581
|
+
|
582
|
+
# FIXME: really hacky removing "" (data_grid) -- This is a bug
|
583
|
+
# in TableReport/CSV generation.
|
584
|
+
res.gsub!(/\"\"/, '')
|
585
|
+
sum = do_import_summary(Marty::DataGrid,
|
586
|
+
res,
|
587
|
+
'infinity',
|
588
|
+
nil,
|
589
|
+
nil,
|
590
|
+
",",
|
591
|
+
)
|
592
|
+
|
593
|
+
expect(sum).to eq({same: 1})
|
594
|
+
|
595
|
+
res11 = res.sub(/G1/, "G11")
|
596
|
+
|
597
|
+
sum = do_import_summary(Marty::DataGrid,
|
598
|
+
res11,
|
599
|
+
'infinity',
|
600
|
+
nil,
|
601
|
+
nil,
|
602
|
+
",",
|
603
|
+
)
|
604
|
+
|
605
|
+
expect(sum).to eq({create: 1})
|
606
|
+
|
607
|
+
g1 = Marty::DataGrid.lookup('infinity', "G1")
|
608
|
+
g11 = Marty::DataGrid.lookup('infinity', "G11")
|
609
|
+
|
610
|
+
expect(g1.export).to eq g11.export
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|
614
|
+
end
|