add picard-casm statistics to factory job summary
[arvados.git] / app / models / orvos_base.rb
1 class OrvosBase < ActiveRecord::Base
2   self.abstract_class = true
3   attr_accessor :attribute_sortkey
4
5   def self.uuid_infix_object_kind
6     @@uuid_infix_object_kind ||= {
7       '4zz18' => 'orvos#collection',
8       'tpzed' => 'orvos#user',
9       'ozdt8' => 'orvos#api_client',
10       '57u5n' => 'orvos#log',
11       'j58dm' => 'orvos#specimen',
12       'mxsvm' => 'orvos#pipeline',
13       'uo14g' => 'orvos#pipeline_invocation',
14       'ldvyl' => 'orvos#project'
15     }
16   end
17
18   def initialize(*args)
19     super(*args)
20     @attribute_sortkey ||= {
21       'id' => nil,
22       'uuid' => '000',
23       'owner' => '001',
24       'created_at' => '002',
25       'modified_at' => '003',
26       'modified_by_user' => '004',
27       'modified_by_client' => '005',
28       'tail_kind' => '100',
29       'tail_uuid' => '100',
30       'head_kind' => '101',
31       'head_uuid' => '101',
32       'info' => 'zzz-000',
33       'updated_at' => 'zzz-999'
34     }
35   end
36
37   def self.columns
38     return @columns unless @columns.nil?
39     @columns = []
40     return @columns if $orvos_api_client.orvos_schema[self.to_s.to_sym].nil?
41     $orvos_api_client.orvos_schema[self.to_s.to_sym].each do |coldef|
42       k = coldef[:name].to_sym
43       if coldef[:type] == coldef[:type].downcase
44         @columns << column(k, coldef[:type].to_sym)
45       else
46         @columns << column(k, :text)
47         serialize k, coldef[:type].constantize
48       end
49       attr_accessible k
50     end
51     attr_reader :etag
52     attr_reader :kind
53     @columns
54   end
55   def self.column(name, sql_type = nil, default = nil, null = true)
56     ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
57   end
58   def self.find(uuid)
59     if uuid.class != String or uuid.length < 27 then
60       raise 'argument to find() must be a uuid string. Acceptable formats: warehouse locator or string with format xxxxx-xxxxx-xxxxxxxxxxxxxxx'
61     end
62     new.private_reload(uuid)
63   end
64   def self.where(*args)
65     OrvosResourceList.new(self).where(*args)
66   end
67   def self.limit(*args)
68     OrvosResourceList.new(self).limit(*args)
69   end
70   def self.eager(*args)
71     OrvosResourceList.new(self).eager(*args)
72   end
73   def self.all(*args)
74     OrvosResourceList.new(self).all(*args)
75   end
76   def save
77     obdata = {}
78     self.class.columns.each do |col|
79       obdata[col.name.to_sym] = self.send(col.name.to_sym)
80     end
81     obdata.delete :id
82     postdata = { self.class.to_s.underscore => obdata }
83     if etag
84       postdata['_method'] = 'PUT'
85       obdata.delete :uuid
86       resp = $orvos_api_client.api(self.class, '/' + uuid, postdata)
87     else
88       resp = $orvos_api_client.api(self.class, '', postdata)
89     end
90     return false if !resp[:etag] || !resp[:uuid]
91
92     # set read-only non-database attributes
93     @etag = resp[:etag]
94     @kind = resp[:kind]
95
96     # these attrs can be modified by "save" -- we should update our copies
97     %w(uuid owner created_at
98        modified_at modified_by_user modified_by_client
99       ).each do |attr|
100       self.send(attr + '=', resp[attr.to_sym])
101     end
102
103     self
104   end
105   def save!
106     self.save or raise Exception.new("Save failed")
107   end
108   def links(*args)
109     o = {}
110     o.merge!(args.pop) if args[-1].is_a? Hash
111     o[:link_class] ||= args.shift
112     o[:name] ||= args.shift
113     o[:head_kind] ||= args.shift
114     o[:tail_kind] = self.kind
115     o[:tail_uuid] = self.uuid
116     if all_links
117       return all_links.select do |m|
118         ok = true
119         o.each do |k,v|
120           if !v.nil?
121             test_v = m.send(k)
122             if (v.respond_to?(:uuid) ? v.uuid : v.to_s) != (test_v.respond_to?(:uuid) ? test_v.uuid : test_v.to_s)
123               ok = false
124             end
125           end
126         end
127         ok
128       end
129     end
130     @links = $orvos_api_client.api Link, '', { _method: 'GET', where: o, eager: true }
131     @links = $orvos_api_client.unpack_api_response(@links)
132   end
133   def all_links
134     return @all_links if @all_links
135     res = $orvos_api_client.api Link, '', {
136       _method: 'GET',
137       where: {
138         tail_kind: self.kind,
139         tail_uuid: self.uuid
140       },
141       eager: true
142     }
143     @all_links = $orvos_api_client.unpack_api_response(res)
144   end
145   def reload
146     private_reload(self.uuid)
147   end
148   def private_reload(uuid_or_hash)
149     raise "No such object" if !uuid_or_hash
150     if uuid_or_hash.is_a? Hash
151       hash = uuid_or_hash
152     else
153       hash = $orvos_api_client.api(self.class, '/' + uuid_or_hash)
154     end
155     hash.each do |k,v|
156       if self.respond_to?(k.to_s + '=')
157         self.send(k.to_s + '=', v)
158       else
159         # When OrvosApiClient#schema starts telling us what to expect
160         # in API responses (not just the server side database
161         # columns), this sort of awfulness can be avoided:
162         self.instance_variable_set('@' + k.to_s, v)
163         if !self.respond_to? k
164           singleton = class << self; self end
165           singleton.send :define_method, k, lambda { instance_variable_get('@' + k.to_s) }
166         end
167       end
168     end
169     @all_links = nil
170     self
171   end
172   def dup
173     super.forget_uuid!
174   end
175
176   def attributes_for_display
177     self.attributes.reject { |k,v|
178       attribute_sortkey.has_key?(k) and !attribute_sortkey[k]
179     }.sort_by { |k,v|
180       attribute_sortkey[k] or k
181     }
182   end
183
184   def self.resource_class_for_uuid(uuid, opts={})
185     if uuid.is_a? OrvosBase
186       return uuid.class
187     end
188     unless uuid.is_a? String
189       return nil
190     end
191     if opts[:class].is_a? Class
192       return opts[:class]
193     end
194     if uuid.match /^[0-9a-f]{32}(\+[^,]+)*(,[0-9a-f]{32}(\+[^,]+)*)*$/
195       return Collection
196     end
197     resource_class = nil
198     uuid.match /^[0-9a-z]{5}-([0-9a-z]{5})-[0-9a-z]{15}$/ do |re|
199       resource_class ||= $orvos_api_client.
200         kind_class(self.uuid_infix_object_kind[re[1]])
201     end
202     if opts[:referring_object] and
203         opts[:referring_attr] and
204         opts[:referring_attr].match /_uuid$/
205       resource_class ||= $orvos_api_client.
206         kind_class(opts[:referring_object].
207                    attributes[opts[:referring_attr].
208                               sub(/_uuid$/, '_kind')])
209     end
210     resource_class
211   end
212
213   protected
214
215   def forget_uuid!
216     self.uuid = nil
217     @etag = nil
218     self
219   end
220 end