kosher 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,4 +1,7 @@
1
1
  pkg/*
2
2
  *.gem
3
3
  .bundle
4
+ .rvmrc
5
+ .rspec
4
6
  Gemfile.lock
7
+ credentials.yml
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
- source "http://rubygems.org"
1
+ source 'http://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in kosher.gemspec
4
3
  gemspec
data/LICENSE CHANGED
@@ -1,22 +1,13 @@
1
- (The MIT License)
2
-
3
- Copyright (c) 2010 Paper Cavalier
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- 'Software'), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  Kosher
2
2
  ======
3
3
 
4
- ![Walter Benjamin](https://github.com/papercavalier/kosher/raw/master/walter_benjamin.jpg)
4
+ Kosher wraps Amazon in a loving embrace.
5
5
 
6
- description = Kosher.new "A withdrawn library copy"
7
- description.kosher?
8
- => false
6
+ ![Walter Benjamin](https://github.com/papercavalier/kosher/raw/master/walter_benjamin.jpg)
data/kosher.gemspec CHANGED
@@ -1,23 +1,31 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "kosher/version"
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'kosher/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
- s.name = "kosher"
6
+ s.name = 'kosher'
7
7
  s.version = Kosher::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
- s.authors = ["Paper Cavalier"]
10
- s.email = ["code@papercvalier.com"]
11
- s.homepage = ""
12
- s.summary = %q{Kosher validates descriptions}
13
- s.description = %q{Have you ever wondered if you should buy a book?}
9
+ s.authors = ['Paper Cavalier']
10
+ s.email = 'code@papercavalier.com'
11
+ s.homepage = 'https://rubygems.org/gems/kosher'
12
+ s.summary = %q{Wraps Amazon in a loving embrace.}
13
+ s.description = %q{Kosher wraps Amazon in a loving embrace.}
14
14
 
15
- s.rubyforge_project = "kosher"
15
+ s.rubyforge_project = 'kosher'
16
16
 
17
17
  s.files = `git ls-files`.split("\n")
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
- s.require_paths = ["lib"]
20
+ s.require_paths = ['lib']
21
21
 
22
- s.add_development_dependency("rspec", ["~> 2.4.0"])
22
+ s.add_dependency('sucker', '~> 1.3.1')
23
+ s.add_dependency('throttler', '~> 0.2.4')
24
+ s.add_development_dependency('fabrication', '~> 0.9.5')
25
+ s.add_development_dependency('rspec', '~> 2.5.0')
26
+ if RUBY_VERSION.include? '1.9'
27
+ s.add_development_dependency('ruby-debug19', '~> 0.11.6')
28
+ end
29
+ s.add_development_dependency('vcr', '~> 1.7.0')
30
+ s.add_development_dependency('webmock', '~> 1.6.2')
23
31
  end
@@ -0,0 +1,19 @@
1
+ module Kosher
2
+ class Algorithm
3
+ def initialize(response)
4
+ @response = response
5
+ end
6
+
7
+ def items
8
+ @response.map('Item') do |item|
9
+ Item.build(item)
10
+ end
11
+ end
12
+
13
+ def errors
14
+ @response.errors.map do |error|
15
+ error['Message'].scan(/[0-9A-Z]{10}/).first rescue nil
16
+ end.compact
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ module Kosher
2
+ class Condition < Struct.new(:in_words)
3
+ def to_i
4
+ case in_words
5
+ when 'new'
6
+ 1
7
+ when 'mint'
8
+ 2
9
+ when 'verygood'
10
+ 3
11
+ when 'good'
12
+ 4
13
+ when 'acceptable'
14
+ 5
15
+ else
16
+ 6
17
+ end
18
+ end
19
+
20
+ def kosher?
21
+ to_i <= 4
22
+ end
23
+
24
+ def new?
25
+ in_words == 'new'
26
+ end
27
+
28
+ def used?
29
+ !new?
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,59 @@
1
+ module Kosher
2
+ class Description < String
3
+ DAMAGED = "\\b(?:missing|torn|broken|split|discard|withdrawn|rent|stain|school|damaged|water)"
4
+ EXLIB = "(?:e?x|discarded|retired|former|has|have)[\\s._-]*lib"
5
+ MARKED = "(highlight|hilit|underlin)"
6
+ MISSING_VOL = "(vols?|volume) only"
7
+ REVIEW_COPY = "\\b(?:uncorrected|advanced?\\sreview|arc)\\b"
8
+
9
+ def kosher?
10
+ !(present? && bad?)
11
+ end
12
+
13
+ private
14
+
15
+ def bad?
16
+ damaged? ||
17
+ ex_library? ||
18
+ marked? ||
19
+ missing_volume? ||
20
+ review_copy?
21
+ end
22
+
23
+ def damaged?
24
+ matches(DAMAGED)
25
+ end
26
+
27
+ def does_not_match(value)
28
+ !match(Regexp.new(value, true))
29
+ end
30
+
31
+ def ex_library?
32
+ matches(EXLIB) && does_not_match(negation_of(EXLIB))
33
+ end
34
+
35
+ def marked?
36
+ matches(MARKED) && does_not_match(negation_of(MARKED))
37
+ end
38
+
39
+ def matches(value)
40
+ !does_not_match(value)
41
+ end
42
+
43
+ def missing_volume?
44
+ matches(MISSING_VOL)
45
+ end
46
+
47
+ def negation_of(value)
48
+ "(?:no|not an?)\\s+#{value}"
49
+ end
50
+
51
+ def present?
52
+ self != ''
53
+ end
54
+
55
+ def review_copy?
56
+ matches(REVIEW_COPY)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,26 @@
1
+ module Kosher
2
+ class Item < Struct.new(:asin, :offers)
3
+
4
+ def self.build(doc)
5
+ asin = doc['ASIN']
6
+ offers = build_offers(doc['Offers']['Offer'])
7
+
8
+ new(asin, offers)
9
+ end
10
+
11
+ private
12
+
13
+ def self.build_offers(offers)
14
+ [offers].flatten.compact.map do |offer|
15
+
16
+ # Senify Yen because Ruby Money says so
17
+ price = offer['OfferListing']['Price']
18
+ if price['CurrencyCode'] == 'JPY'
19
+ price['Amount'] = price['Amount'].to_i * 100
20
+ end
21
+
22
+ Offer.build(offer)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ module Kosher
2
+ class Offer < Struct.new(
3
+ :seller,
4
+ :condition,
5
+ :description,
6
+ :ships_in,
7
+ :ships_free,
8
+ :cents)
9
+
10
+ def self.build(doc)
11
+ offer = new
12
+ offer.seller = Seller.build(doc['Merchant'])
13
+
14
+ attributes = doc['OfferAttributes']
15
+ offer.condition = Condition.new(attributes['SubCondition'])
16
+ offer.description = Description.new(attributes['ConditionNote'].to_s)
17
+
18
+ listing = doc['OfferListing']
19
+ offer.ships_in = listing['AvailabilityAttributes']['MaximumHours'].to_i
20
+ offer.ships_free = listing['IsEligibleForSuperSaverShipping'] == '1'
21
+ offer.cents = listing['Price']['Amount'].to_i
22
+
23
+ offer
24
+ end
25
+
26
+ def kosher?
27
+ condition.kosher? && seller.kosher? && description.kosher? && ships_now?
28
+ end
29
+
30
+ def ships_now?
31
+ ships_in.to_i <= 48
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ module Kosher
2
+ class Request < Sucker::Request
3
+ def initialize(args={})
4
+ super
5
+ add_parameters
6
+ end
7
+
8
+ def batchify(asins)
9
+ self.<<({ 'ItemLookup.1.ItemId' => asins[0, 10] })
10
+ self.<<({ 'ItemLookup.2.ItemId' => asins[10, 10] }) if asins.size > 10
11
+ end
12
+
13
+ def get
14
+ Algorithm.new(super)
15
+ end
16
+
17
+ private
18
+
19
+ def add_parameters
20
+ self.<<({
21
+ 'Operation' => 'ItemLookup',
22
+ 'ItemLookup.Shared.IdType' => 'ASIN',
23
+ 'ItemLookup.Shared.Condition' => 'All',
24
+ 'ItemLookup.Shared.MerchantId' => 'All',
25
+ 'ItemLookup.Shared.ResponseGroup' => ['OfferFull', 'SalesRank'] })
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ module Kosher
2
+ class Seller < Struct.new(:merchant_id, :average_rating)
3
+ class << self
4
+ attr_accessor :blacklist
5
+
6
+ def build(doc)
7
+ merchant_id = doc['MerchantId']
8
+ average_rating = doc['AverageFeedbackRating'].to_f
9
+
10
+ new(merchant_id, average_rating)
11
+ end
12
+ end
13
+
14
+ def blacklist
15
+ self.class.blacklist
16
+ end
17
+
18
+ def blacklisted?
19
+ blacklist.include? merchant_id rescue false
20
+ end
21
+
22
+ def kosher?
23
+ return false if blacklisted?
24
+
25
+ average_rating == 0.0 || average_rating > 4.7
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Kosher
2
- VERSION = "0.0.3"
2
+ VERSION = '0.1.0'
3
3
  end
data/lib/kosher.rb CHANGED
@@ -1,9 +1,14 @@
1
- require "kosher/string"
2
- require "kosher/regexps"
1
+ require 'sucker'
2
+ require 'kosher/algorithm'
3
+ require 'kosher/condition'
4
+ require 'kosher/description'
5
+ require 'kosher/item'
6
+ require 'kosher/offer'
7
+ require 'kosher/request'
8
+ require 'kosher/seller'
3
9
 
4
10
  module Kosher
5
- def self.new(val)
6
- String.new(val)
11
+ def self.new(args={})
12
+ Request.new(args)
7
13
  end
8
14
  end
9
-
@@ -0,0 +1,7 @@
1
+ Fabricator(:kosher_condition, :class_name => 'kosher/condition') do
2
+ in_words "new"
3
+ end
4
+
5
+ Fabricator(:unkosher_condition, :class_name => 'kosher/condition') do
6
+ in_words "acceptable"
7
+ end
@@ -0,0 +1,8 @@
1
+ Fabricator(:offer, :class_name => 'kosher/offer') do
2
+ seller { Fabricate(:good_seller) }
3
+ description Kosher::Description.new('')
4
+ condition { Fabricate(:kosher_condition) }
5
+ ships_free false
6
+ ships_in 48
7
+ cents 100
8
+ end
@@ -0,0 +1,15 @@
1
+ Fabricator(:seller, :class_name => 'kosher/seller') do
2
+ merchant_id Faker::Amazon.merchant_id
3
+ end
4
+
5
+ Fabricator(:new_seller, :from => :seller) do
6
+ average_rating 0.0
7
+ end
8
+
9
+ Fabricator(:good_seller, :from => :seller) do
10
+ average_rating 5.0
11
+ end
12
+
13
+ Fabricator(:bad_seller, :from => :seller) do
14
+ average_rating 4.5
15
+ end
@@ -0,0 +1,112 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :get
5
+ uri: http://ecs.amazonaws.com:80/onca/xml?AWSAccessKeyId=0ZVSQ33MDFPQS8H2PM02&AssociateTag=papecava-20&ItemLookup.1.ItemId=0143105825&ItemLookup.Shared.Condition=All&ItemLookup.Shared.IdType=ASIN&ItemLookup.Shared.MerchantId=All&ItemLookup.Shared.ResponseGroup=OfferFull,SalesRank&Operation=ItemLookup&Service=AWSECommerceService&Signature=x3dZXO/H7AvZ7x/bt6P1iTAx13QatyVbMyc1v9yKtzc=&Timestamp=2011-03-02T19:25:56Z&Version=2010-11-01
6
+ body:
7
+ headers:
8
+ accept-encoding:
9
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
10
+ response: !ruby/struct:VCR::Response
11
+ status: !ruby/struct:VCR::ResponseStatus
12
+ code: 200
13
+ message: OK
14
+ headers:
15
+ date:
16
+ - Wed, 02 Mar 2011 19:25:56 GMT
17
+ server:
18
+ - Server
19
+ content-type:
20
+ - text/xml;charset=UTF-8
21
+ vary:
22
+ - Accept-Encoding,User-Agent
23
+ content-encoding:
24
+ - gzip
25
+ content-length:
26
+ - "3620"
27
+ nncoection:
28
+ - close
29
+ body: !binary |
30
+ H4sIAAAAAAAAA+1ceZPiuJL/Kp7enY237w1g+fYsw4QxNxh8ccb+Y2yBDb7w
31
+ AZhP/2SgOKprqquGnunX0VURFe1MSZlpKfOXaUnV5d/3nottYRQ7gf/bJ1DE
32
+ P2G/V8rtBHq9IFinoQrjMPBjiKF+fvzbJztJwl9LpR2cxzDaOiaMi4ZnHAK/
33
+ aAZeSRhrdTHwPBiZUDu1lwgc4AUACjj4VCkPQhgZCdKlwk0K46RSbum63IKG
34
+ hUxAxPEB6xse/O3TEGkQltBPPmEjw00RR03nGRJSOnXLH24Hn0W2rQrP0IAl
35
+ Wb5gQt4qUIDiCgbJEQWL53lIWThpzWG5dO1fFqJl6iFN8fXxbMTF4IsR17nJ
36
+ TXnq/tnA8+tfhr0wNa+Ov6oparYRQasooaG24SODr0Jd961CQDEnbsbigCIB
37
+ TnME/U47nlyiGQVoDp7EDRYLGDVS1/1FM1wYq4a/fn1+nKVvJGl0naE9ac0m
38
+ g1KLFbYzdl+aJ4wMHF3YA1Ixkmw0lzITbPmsmxzM395pshj4lnO3iu+YubOM
39
+ tqVn4c16au3+qxKEOA5Mx0igbiwvo0IjhKaxNQoE/urg0SkiL+PugugVnWNN
40
+ MFFMxl2Y3S71bKQpJCnVGrKicS1ClnDiVTm646HIMLzw1gCknSzghA74Xwn6
41
+ V5qZ3Yso3QTRObTkKMiNcfxlLrCCF3GKIQGLX34uQfisZ7n0OVDkK3IVjegY
42
+ meZYFT1KUTA/UXfQde55Wf0KWvVy6UqWa9B1EPhlEkzswKpothOWS8+Y5dPC
43
+ V/IFR3pORPkajCehN3T5GAqysYQVgF7kQpRPAVi5Bh4Sd2KV72KqcomlfH5u
44
+ G571u0TaC/22Dtw92XBDnVu0IEoqBS2de06cO1oN+elTv2NbeWREznEBjsOO
45
+ 73jPOhn/bKpLd8uF/ApN2t0LHxnli+EVwBIMYl8Zp9nTUs8zoqxS7gU7JK6P
46
+ rI8QYiKBXpAibwMESSFhJ6IsplEEfTMTAwtWhloNrfEtp9wIIs9IEmidhPw3
47
+ IIr58Gfscum5shON8pD192m/0XZiiIHrQjNx5i58ZgV9jKA/bQaN4hH/Qzs+
48
+ V6sHieGiyakQXLl0IU7s3OoKf2YfiRP/RkruiJ/xTr1UuEijuRPbaBx+7nXL
49
+ O0fRxSmOVHwefCbIJ6M+b8zdNa5Qt+0n1knQNZjvw5rA6+MqDVR+WpUH9xGe
50
+ o2SlUCgsI2gkcxQCFjTcuFw68stN1/BNeAySp3Jpt7stk5ZhyYZuWIohmomo
51
+ ZAceLNqJ5/5+Yvz2XPWNQOQUpnECLzFf9+hpvXNcu9JlLUERfXxs9FB4Xah8
52
+ eZ8ECAjlkMwGhNbcMNcqYvvLClVkkVO92HSa0ydmhSRwnjjP6oV5najz9ApJ
53
+ EjnzNMnn+wq9uY/cQTHCoivlOX6CrL5lXcf2gwQB0jwOXCTUzbBqZPgWhnwR
54
+ +x+Usf4Pa/uYlgTmuogBHP8ZI/FCzcgwKfAh6mvk/JoTIQfEFlHgYUEaYTuU
55
+ 4O0gjWERy1NAjM0zbKjJGpLwL8xzXBdpxcw0TtBCRTGWV77QKiA98xSlLJS3
56
+ MPSPCTHAc0wRaxlhmGHipbsTYwOk5L8A1gwMt4jpyP+xRgQhpqVhiMD2Zh6O
57
+ 73Z299uJOzJ6TnxahlsKuWNNMfSVXqfEFlB+JhotYLVY0kBP0y30eBE9jBS9
58
+ pja1Br5x4mxoM03ZrCsLVuQV8YCrI5+PmhZY+IxDM6IZcO2QFS1K0OyA+Zmo
59
+ eqq2I70mNUCl9u6AatcqTs7beic0VxK30yGfqF7q6lSTV3k0k2ua9uo/k7Xz
60
+ W1zNLNf3uV8sIXqeAiCRHMXxHC6RFEXhPIr7m/by3wG4dzo0Y5sjzVkHzzB/
61
+ Xh9fzEd/pu5Oj5x/B/gJirETg0IGPuehADUc15g7rpNkKGJSw0XuHh891PEx
62
+ UCCuDmgZWZyH7c2Au+G3znTLP1YzfrC7H3uucRzf8VKvhUIkrhDIwDtGWTL2
63
+ V4pC8HvHuBd4q15JETjkBlI8QpoLhQq6uuss87SApg6FBgJ8NA1RHpFh7vZ4
64
+ XuW93gXJQGXTAkUstKqZcETc07jP2ff++UR+5IRvlxMQhP9xSvDzxo+M8LaM
65
+ kLU9MZaqDB7PLTDuoAwgiixZ51q8rivAbFlkuHC8JeLbgZa560V9Ya9qq90s
66
+ G4Le0DcP9Dgc1A+oHeE/M+IPq465JFq2MBzElOLXE64fiF5sHjLO6a8EEm9M
67
+ W+NwPj0MdiptjzlxhMa5/Kw2hY1FpMYrZxKYb8kJgGZYFuckjiZIDnzkhB8w
68
+ J3DfW04A3XaDmPICkJVp/YWcgFYIgVeeEb5iNnim9KFs0O+8NxvQb8wGgKZx
69
+ /Bt9IVRVoV/D+vXxo2DqTs1VGFvRSK4jQKwGtjzO3Pai22kknUa3JjM4HDCR
70
+ Xd0Qbt2KKOguiXgLhYTp0rSbit10xkZ1aNBBgi+lSbKKt6my9byeN2pMFGFU
71
+ X5GO3JxChJidxZar1xGKmgNLm8mTRLUQBlc3Mb9bz+bWpL6Hi0MavAVIcZSH
72
+ cyClWcDxNPU6kPLkQ0CaD38XkOLsI0Caj34nkIIfD0hBHngfQPoBpA+W1V8N
73
+ RnGtni0tealpm8GmKltrMhKWK4SoToscrkJyz+9nPZ0+xLi9HvAwZlbeJoxb
74
+ RLXVGaUTtpvU+BGvbC1VGLN9Ymwtxfpy0pG12liudtnG2s/2q6nQRngJSBCM
75
+ uwDqg3biakux0+1M5nLAdeoLfcSAWeLOqfhN1SjF4iSDSyQAOM/xHyD6AaL/
76
+ +SBKCwNlqvV6A0JsvACiiQ0jz0lgwYOWY3xFHL3X+xiOiu/FUe6NOMrwgAbf
77
+ FkeLGIlj+f6DhiyMF4aZd8GaqREhAyAsYkrqmGusjYjIP7624WKCg0LVcX96
78
+ FISTQCM8BJGjeRZ0TY46yLvNIuD3ondQSUJCRWijoc+YbczydTUc0wtu2lat
79
+ znzpdib5VgDBrtfOtt6IJpnT7zbUOncQk1Y6p/czGmrox1CmazLwB5QgJXY1
80
+ ikiXhKugmdJODfdWOk/htsF2N1W+tuDTNQuQwpQLx1Wxz2kCwuS3wDLFAYZl
81
+ gMQwBPpcZF6FZZJ+4KMdkEWaeR8sc9QDmxJckaLeC8v5udcPt0nw3aFyq1+v
82
+ z3hxJMik8vK2cZxb9RdsG99rfgiW8/sPf82uMUXhxLfZNb5sFf+CoSha5JvA
83
+ lw6I5UIjhhgKn2CHUQVA3ccNtggizDpfFzlvLl93la+Y/gs2QD0w8AuO4/nv
84
+ ZzvHxUdxneAGhqivTNCB0YBZQY/cLGmxsenF6iSYbTZ+GLmJQHuQRoC7gmNy
85
+ ILFZbWonW6cTyRtrW4vm6mHSpJRGYFNG1/K6CcJ7ZTGYqIdMiTcCGldr254g
86
+ DYzxaV+4i2BXkA8aomZhjuPSuKroam1Fis15zV2QeisdT1nKF+teg2SWb0R3
87
+ nCZxggQSzVM8mVfFr6I7+xi6s+9Fd/IhdCc/0P3L6M7z/HdXdGvacEDgtUFX
88
+ arx0LFgNgq6PICGyv2LBfa/zIWTXxL8K2QmaobhvguyFUqHd1+tqX9Dbg77Q
89
+ w7RWW5bb/Wbh/wvHZ+103Edj7vltY2yO0N7CUDme5YeAMyfE8gnB8hRh3Ljt
90
+ T9g/ZAHT+1i7jw1UTBP/F/tn4Z9Y01kkqHo38i5HxiDKbyZj58uNSHLbO351
91
+ 5YeUP2EFbAyxJUxOyvLUjyVBTmAjlE5OnwF5P5os8o/mB85dHDrOatsZdu3h
92
+ tsFaGb+FSs2Ze6vqcLtfT/iBzO53+Ylg6m6mLaU5PiQS6Y3sHrOseWBm7Qa1
93
+ g6auD0YAJu7Sby0HC81WNts+O8X3VH7e12JWFLWnM+9gSFxDETdbFkxyiQvi
94
+ MMZd0Hfm9GwwIzt9ShbekgkYChAch0sMTbAcyb6eCXj6oUyQD39XJsDpRzIB
95
+ Tr87E7A/YJ3/3V0PGUjNUZMiSXZGCC/kgePe9SSM0IJ8xYsh90r/5h3sN++8
96
+ MDz3LSv8+hgL8iKcwHGEs07iwvh6L6T4h6V7sahHiIFe6JgFnu6GY0i0s3BO
97
+ k4OKeD85Y3eEwXyj5lG8Jq1pW22MOyq7meU3+VhblubcSqWysWBKQbDf1vTR
98
+ Opzx2aaZBcJM0VbdmjirStOG7a9mQIn1qGk2Q0mHY8XskY4/GXaAi5s7Fe+1
99
+ 2czus67cGLLBlNnLvm3ao7nVn2l8vN6abtCfgb5qd7X87NHtZoCejzqSpkl+
100
+ nXxrFU/SJEUxtMSRDM8yX6ji+cf2aPj37tHgD+3R4NQHdn8Zu7+7q32gyqj9
101
+ XlOY9joU+wJ2d+cwRohpfsUK/l7jLXC/DKl4EX8j2n6bs0IU4/mrITA0n/hF
102
+ zA8w21naaHHt3FIsiDDPiNbxw3se47E10XFvTrmj0Ao5u22IZh1B1qAGuGqz
103
+ ufZbWlWwLbMBlK0RKQaXTvLrzHZaXXuGUtXc7Qa0fA+Y9tDy4YRAbcSSdXpB
104
+ Xe5MpuphARr2xDJVX2p4drTdin590fc9fLffeL2DHm7xjFJTNZw0PQpBdJIm
105
+ wy8jIy3hDAMYipI4nGMZiv4CMvKPISP/XmQEDyEjeDcyMj/goeLHkeLHkeLX
106
+ uePWc9bwm58p7uR+YxkudDwZCNTUS2Xd2/T4YGOOhvv+kJX5uNaZHxouMWe5
107
+ nkI4fCy3+i11ZerN7mAFMlpZHAg+UBWaW9gLoClNSW17rfaQweVFjFBZW/Or
108
+ RtKojV0vQEB7oLVVPNLDQ8zJKbnZ6sp+Np7hbVLoN+c11CGQJK7VsFt1XXn7
109
+ iSLJ5Bc9cBonyNevHVMPnShS7z5RZB85UWT/zIki9XGi+B8ByqWnP2Usnf6Y
110
+ tnT+E+jS5/89Q+XfeGWzi8lBAAA=
111
+
112
+ http_version: "1.1"