mls 1.6.0 → 1.7.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: '05855a1a8a8ff7514aa0d09798c336972c6443cc'
4
- data.tar.gz: 689bc19414f3f6516d74bb8f8721d6019fb86060
2
+ SHA256:
3
+ metadata.gz: 61267aa211891a0f0f6d22339b6997745fa532cdb4d50390b5b8645cfb887ffe
4
+ data.tar.gz: 1df8ded07de72295b7ea2852323cb51838398c77a2a782dffa0c6b4ff03c2c0b
5
5
  SHA512:
6
- metadata.gz: e675d1684d26d1fc2a5c64fe995bce74f185090a161844091bc24b5bfc7d6f36d723d2a6f57700ee41589dedb58d77044ab29a752ed75c3783f714fa2bdc827b
7
- data.tar.gz: a94d1a6219a065cbc0c436e167f06a333c90662aec2160d5cf62e3a4dd1bed96100d2296e364d1ce71c9fd1b9cb6488aaaced1e66cdd0f9e60bf683534fad5ae
6
+ metadata.gz: 169618f8b6f8aec727b99d2a4a893cdcd292ae925095f57df1431d28f72d1fde6a6a3891b65505abd935de9268a5c8b639b1f5c72393c7bff72ee4391c790593
7
+ data.tar.gz: 983cc885eb785faa6dc33e4dce9246b368b9519ab4d451aca20708e0fc47f282401d9238ed5fbc8b33ef253b8715df88200247768b1b72882d17c819a8b143d7
data/bin/mls CHANGED
@@ -3,7 +3,7 @@ require File.expand_path('../../lib/mls/cli', __FILE__)
3
3
 
4
4
  case ARGV.shift.downcase.strip
5
5
  when "documents:backup"
6
- MLS::CLI.parse_args(ARGV).inspect
6
+ MLS::CLI.parse_args(ARGV)
7
7
  directory = ARGV.shift
8
8
 
9
9
  if directory.nil? || !File.directory?(directory)
@@ -12,6 +12,27 @@ when "documents:backup"
12
12
  end
13
13
 
14
14
  MLS::CLI::Documents.backup(directory)
15
+ when "documents:migrate"
16
+ MLS::CLI.parse_args(ARGV)
17
+ directory = ARGV.shift
18
+
19
+ if directory.nil? || !File.directory?(directory)
20
+ puts "Usage: mls documents:backup dir [options...]"
21
+ exit 1
22
+ end
23
+
24
+ b2 = MLS::CLI::Storage::B2.new(MLS::CLI.options[:b2])
25
+ Document.where(excludes: 'b2/sha256').find_each do |document|
26
+ if document.sha256 && File.exists?(File.join(directory, MLS::CLI::Documents.partition(document.sha256)))
27
+ b2.upload_file(documents.sha256, File.open(File.join(directory, MLS::CLI::Documents.partition(document.sha256))), {
28
+ filename: document.filename,
29
+ sha1: document.sha1,
30
+ content_type: document.content_type,
31
+ size: document.size
32
+ })
33
+ end
34
+ end
35
+ when "documents:check"
15
36
  else
16
37
  puts "Usage: mls documents:backup dir [options...]"
17
38
  end
@@ -32,7 +32,19 @@ module MLS::CLI
32
32
  end
33
33
  end
34
34
  when 'b2'
35
-
35
+ MLS::CLI.options[:b2] = {
36
+ account_id: URI.unescape(url.user),
37
+ application_key: URI.unescape(url.password),
38
+ bucket: URI.unescape(url.host)
39
+ prefix: url.path&.empty? ? url.path : nil
40
+ }
41
+ url.query.split('&').each do |qp|
42
+ key, value = qp.split('=').map { |d| URI.unescape(d) }
43
+ case key
44
+ when 'partition'
45
+ MLS::CLI.options[:b2][:partition] = value.to_i
46
+ end
47
+ end
36
48
  when 'mls'
37
49
  arg = arg.sub('mls://', 'https://')
38
50
  MLS::CLI.options[:mls] = arg
@@ -33,7 +33,7 @@ module MLS::CLI::Documents
33
33
 
34
34
  query = if File.exist?(last_sync_file)
35
35
  from_timestamp = Time.iso8601(File.read(last_sync_file))
36
- Document.filter(created_at: {gte: from_timestamp})
36
+ Document.where(Document.arel_table['created_at'].gteq(from_timestamp))
37
37
  else
38
38
  Document.all
39
39
  end
@@ -51,15 +51,16 @@ module MLS::CLI::Documents
51
51
  raise 'unkown storage engine'
52
52
  end
53
53
 
54
- puts "Downloading #{document.id}"
54
+ print "Downloading #{document.id.to_s.ljust(7)} "
55
55
  storage_engine.copy_to_tempfile(document.send(key)) do |file|
56
56
  digests = calculate_digest(file)
57
+ puts partition(document.sha256)
57
58
 
58
59
  raise 'MD5 does not match' if digests[:md5] != document.md5
59
60
  document.update!(digests.merge({provider: ['s3/hash_key']}))
60
61
 
61
62
  FileUtils.mkdir_p(File.dirname(File.join(dir, partition(document.sha256))))
62
- FileUtils.mv(file.path, File.join(dir, partition(document.sha256)), verbose: true)
63
+ FileUtils.mv(file.path, File.join(dir, partition(document.sha256)))
63
64
  end
64
65
 
65
66
  File.write(last_sync_file, document.created_at.iso8601(6))
@@ -1 +1,2 @@
1
- require File.expand_path('../storage/s3', __FILE__)
1
+ require File.expand_path('../storage/s3', __FILE__)
2
+ require File.expand_path('../storage/b2', __FILE__)
@@ -0,0 +1,87 @@
1
+ require 'b2'
2
+
3
+ module MLS::CLI::Storage
4
+ class B2
5
+
6
+ def initialize(configs = {})
7
+ @configs = configs
8
+ @configs[:prefix] ||= ""
9
+
10
+ @client = B2::Bucket.new({
11
+ 'bucketId' => configs[:bucket_id]
12
+ }, B2.new({
13
+ account_id: configs[:account_id],
14
+ application_key: configs[:application_key]
15
+ }))
16
+ end
17
+
18
+ def local?
19
+ false
20
+ end
21
+
22
+ # def url(key)
23
+ # [host, destination(key)].join('/')
24
+ # end
25
+
26
+ # def host
27
+ # h = @configs[:bucket_host_alias] || "https://s3.amazonaws.com/#{@configs[:bucket]}"
28
+ # h.delete_suffix('/')
29
+ # end
30
+
31
+ def destination(key)
32
+ "#{@configs[:prefix]}#{partition(key)}".gsub(/^\//, '')
33
+ end
34
+
35
+ def exists?(key)
36
+ @client.has_key?(destination(key))
37
+ end
38
+
39
+ def write(key, file, meta_info)
40
+ file = file.tempfile if file.is_a?(ActionDispatch::Http::UploadedFile)
41
+ file = File.open(file) if file.is_a?(String)
42
+
43
+ @client.upload_file(key, file, mime_type: meta_info[:content_type], sha1: meta_info[:sha1], content_disposition: "inline; filename=\"#{meta_info[:filename]}\"")
44
+ end
45
+
46
+ def download(key, to=nil, &block)
47
+ @client.download(destination(key), to, &block)
48
+ end
49
+
50
+ def delete(key)
51
+ @client.delete!(key)
52
+ end
53
+
54
+ def copy_to_tempfile(key)
55
+ tmpfile = Tempfile.new([File.basename(key), File.extname(key)], binmode: true)
56
+ download(key, tmpfile.path)
57
+ if block_given?
58
+ begin
59
+ yield(tmpfile)
60
+ ensure
61
+ tmpfile.close!
62
+ end
63
+ else
64
+ tmpfile
65
+ end
66
+ end
67
+
68
+ def partition(value)
69
+ return value unless @configs[:partition]
70
+ split = value.scan(/.{1,4}/)
71
+ split.shift(@configs[:partition_depth] || 3).join("/") + split.join("")
72
+ end
73
+
74
+ def sha1(key)
75
+ @client.file(destination(key)).sha1
76
+ end
77
+
78
+ def last_modified(key)
79
+ @client.file(destination(key)).uploaded_at
80
+ end
81
+
82
+ def mime_type(key)
83
+ @client.file(destination(key)).mime_type
84
+ end
85
+
86
+ end
87
+ end
@@ -21,6 +21,7 @@ class Account < MLS::Model
21
21
 
22
22
  has_many :searches
23
23
  has_many :suggestions, foreign_key: "suggested_by_id"
24
+ has_many :broadcasts, foreign_key: :sender_id
24
25
 
25
26
  has_many :credit_cards
26
27
 
@@ -0,0 +1,11 @@
1
+ class Broadcast < MLS::Model
2
+
3
+ belongs_to :sender, class_name: 'Account'
4
+
5
+ has_many :emails
6
+
7
+ has_and_belongs_to_many :regions
8
+ has_and_belongs_to_many :listings
9
+ has_and_belongs_to_many :attachments, class_name: 'Document'
10
+
11
+ end
@@ -1,6 +1,7 @@
1
1
  class Document < MLS::Model
2
2
 
3
3
  has_many :image_orderings, foreign_key: :image_id
4
+ has_many :reviews
4
5
 
5
6
  def self.create(file)
6
7
  if doc = find_matching(file)
@@ -18,7 +19,7 @@ class Document < MLS::Model
18
19
  end
19
20
 
20
21
  def url(style=:original)
21
- MLS.config['document_host'] + '/' + path(style)
22
+ MLS.config['document_host'].gsub(/\/$/, '') + '/' + path(style)
22
23
  end
23
24
 
24
25
  def path(style=:original)
@@ -168,7 +168,7 @@ class Listing < MLS::Model
168
168
  "Leased"
169
169
  elsif self.archived
170
170
  "Deleted"
171
- elsif self.touched_at < 90.days.ago
171
+ elsif self.touched_at < 180.days.ago
172
172
  "Expired"
173
173
  else
174
174
  "Active"
@@ -33,7 +33,7 @@ class Property < MLS::Model
33
33
  accepts_nested_attributes_for :image_orderings, :addresses
34
34
 
35
35
  def contacts
36
- @contact ||= listings.eager_load(:accounts => [:email_addresses, :phones, :organization]).filter(leased_at: nil, authorized: true, type: ['Lease', 'Sublease'], :touched_at => {:gte => 90.days.ago})
36
+ @contact ||= listings.eager_load(:accounts => [:email_addresses, :phones, :organization]).filter(leased_at: nil, authorized: true, type: ['Lease', 'Sublease'], :touched_at => {:gte => 180.days.ago})
37
37
  .order(size: :desc).first.try(:contacts)
38
38
  end
39
39
 
@@ -128,7 +128,7 @@ class Property < MLS::Model
128
128
  region = neighborhood_region
129
129
  region ||= city_region
130
130
  region ||= market
131
- region ||= regions.where(depth: true).sort_by(&:depth).reverse.first
131
+ region ||= regions.filter(depth: true).select{ |r| r.type != "Zip Code Tabulation Area" }.sort_by(&:depth).reverse.first
132
132
  region
133
133
  end
134
134
 
@@ -15,6 +15,7 @@ class Region < MLS::Model
15
15
  has_one :geometry, as: :subject
16
16
  has_many :stats, as: :subject
17
17
 
18
+ has_and_belongs_to_many :tasks
18
19
  has_and_belongs_to_many :organizations
19
20
  has_and_belongs_to_many :parents, :join_table => 'regions_regions', :class_name => 'Region', :foreign_key => 'child_id', :association_foreign_key => 'parent_id'
20
21
  has_and_belongs_to_many :children, :join_table => 'regions_regions', :class_name => 'Region', :foreign_key => 'parent_id', :association_foreign_key => 'child_id'
@@ -0,0 +1,18 @@
1
+ class Review < MLS::Model
2
+
3
+ belongs_to :document
4
+ belongs_to :account
5
+
6
+ def approved?
7
+ approval == true
8
+ end
9
+
10
+ def rejected?
11
+ approval == false
12
+ end
13
+
14
+ def pending?
15
+ approval == nil
16
+ end
17
+
18
+ end
@@ -11,6 +11,8 @@ class Task < MLS::Model
11
11
  has_many :reviews, -> { where(:type => "review") }, :class_name => "Task", :as => :subject, :inverse_of => :subject
12
12
  has_many :fixes, -> { where(:type => "fix") }, :class_name => "Task", :as => :subject, :inverse_of => :subject
13
13
 
14
+ has_and_belongs_to_many :regions
15
+
14
16
  def for_source?
15
17
  subject_type == "Source"
16
18
  end
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "mls"
6
- s.version = '1.6.0'
6
+ s.version = '1.7.0'
7
7
  s.authors = ["Jon Bracy", "James R. Bracy"]
8
8
  s.email = ["jon@42floors.com", "james@42floors.com"]
9
9
  s.homepage = "http://mls.42floors.com"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mls
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Bracy
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-01-07 00:00:00.000000000 Z
12
+ date: 2018-07-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -253,6 +253,7 @@ files:
253
253
  - lib/mls/cli.rb
254
254
  - lib/mls/cli/documents.rb
255
255
  - lib/mls/cli/storage.rb
256
+ - lib/mls/cli/storage/b2.rb
256
257
  - lib/mls/cli/storage/s3.rb
257
258
  - lib/mls/comment.rb
258
259
  - lib/mls/models/account.rb
@@ -260,6 +261,7 @@ files:
260
261
  - lib/mls/models/action.rb
261
262
  - lib/mls/models/address.rb
262
263
  - lib/mls/models/api_key.rb
264
+ - lib/mls/models/broadcast.rb
263
265
  - lib/mls/models/coworking_space.rb
264
266
  - lib/mls/models/credit_card.rb
265
267
  - lib/mls/models/datum.rb
@@ -286,6 +288,7 @@ files:
286
288
  - lib/mls/models/recommendation.rb
287
289
  - lib/mls/models/reference.rb
288
290
  - lib/mls/models/region.rb
291
+ - lib/mls/models/review.rb
289
292
  - lib/mls/models/search.rb
290
293
  - lib/mls/models/service.rb
291
294
  - lib/mls/models/session.rb
@@ -331,7 +334,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
331
334
  version: '0'
332
335
  requirements: []
333
336
  rubyforge_project: mls
334
- rubygems_version: 2.6.11
337
+ rubygems_version: 2.7.4
335
338
  signing_key:
336
339
  specification_version: 4
337
340
  summary: 42Floors MLS Client