From d0a3160db82b1ddcd0c664472a46834e11229f09 Mon Sep 17 00:00:00 2001 From: graph1589 Date: Wed, 26 Jan 2022 19:10:22 +0300 Subject: [PATCH 1/2] configurated active storage --- Dockerfile | 3 +- Gemfile | 3 ++ Gemfile.lock | 21 +++++++++++ ...te_active_storage_tables.active_storage.rb | 36 +++++++++++++++++++ db/schema.rb | 32 ++++++++++++++++- 5 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20220126155632_create_active_storage_tables.active_storage.rb diff --git a/Dockerfile b/Dockerfile index d24dd00..316b500 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,8 @@ ARG PACKAGES="vim openssl-dev postgresql-dev build-base curl nodejs yarn less tz RUN apk update \ && apk upgrade \ - && apk add --update --no-cache $PACKAGES + && apk add --update --no-cache $PACKAGES \ + && apk --update add imagemagick RUN gem install bundler:2.1.4 diff --git a/Gemfile b/Gemfile index 3b9bf4d..493ad50 100644 --- a/Gemfile +++ b/Gemfile @@ -77,3 +77,6 @@ gem 'sidekiq' gem 'sidekiq-failures' gem 'sidekiq-throttled' gem 'sidekiq-unique-jobs' +gem 'mini_magick' +gem 'virtus' +gem 'file_validators' diff --git a/Gemfile.lock b/Gemfile.lock index f35b45e..5b05d97 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -77,6 +77,10 @@ GEM addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) ast (2.4.2) + axiom-types (0.1.1) + descendants_tracker (~> 0.0.4) + ice_nine (~> 0.11.0) + thread_safe (~> 0.3, >= 0.3.1) bcrypt (3.1.16) bindex (0.8.1) bootsnap (1.9.1) @@ -101,6 +105,8 @@ GEM case_transform (0.2) activesupport childprocess (4.1.0) + coercible (1.0.0) + descendants_tracker (~> 0.0.1) concurrent-ruby (1.1.9) connection_pool (2.2.5) coveralls (0.7.1) @@ -110,6 +116,8 @@ GEM term-ansicolor thor crass (1.0.6) + descendants_tracker (0.0.4) + thread_safe (~> 0.3, >= 0.3.1) docile (1.4.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) @@ -120,6 +128,9 @@ GEM factory_bot (~> 6.2.0) railties (>= 5.0.0) ffi (1.15.4) + file_validators (3.0.0) + activemodel (>= 3.2) + mime-types (>= 1.0) globalid (0.5.2) activesupport (>= 5.0) http-accept (1.7.0) @@ -127,6 +138,7 @@ GEM domain_name (~> 0.5) i18n (1.8.11) concurrent-ruby (~> 1.0) + ice_nine (0.11.2) jbuilder (2.11.2) activesupport (>= 5.0.0) js-routes (2.1.2) @@ -167,6 +179,7 @@ GEM mime-types (3.4.1) mime-types-data (~> 3.2015) mime-types-data (3.2021.1115) + mini_magick (4.11.0) mini_mime (1.1.2) mini_portile2 (2.6.1) minitest (5.14.4) @@ -316,6 +329,7 @@ GEM term-ansicolor (1.7.1) tins (~> 1.0) thor (1.1.0) + thread_safe (0.3.6) tilt (2.0.10) tins (1.29.1) sync @@ -326,6 +340,10 @@ GEM unf_ext (0.0.8) unicode-display_width (2.1.0) uniform_notifier (1.14.2) + virtus (2.0.0) + axiom-types (~> 0.1) + coercible (~> 1.0) + descendants_tracker (~> 0.0, >= 0.0.3) web-console (4.1.0) actionview (>= 6.0.0) activemodel (>= 6.0.0) @@ -361,12 +379,14 @@ DEPENDENCIES capybara (>= 3.26) coveralls factory_bot_rails + file_validators jbuilder (~> 2.7) js-routes kaminari letter_opener letter_opener_web listen (~> 3.3) + mini_magick newrelic_rpm pg (~> 1.1) puma (~> 5.0) @@ -389,6 +409,7 @@ DEPENDENCIES state_machines state_machines-activerecord tzinfo-data + virtus web-console (>= 4.1.0) webdrivers webpacker (~> 5.0) diff --git a/db/migrate/20220126155632_create_active_storage_tables.active_storage.rb b/db/migrate/20220126155632_create_active_storage_tables.active_storage.rb new file mode 100644 index 0000000..8779826 --- /dev/null +++ b/db/migrate/20220126155632_create_active_storage_tables.active_storage.rb @@ -0,0 +1,36 @@ +# This migration comes from active_storage (originally 20170806125915) +class CreateActiveStorageTables < ActiveRecord::Migration[5.2] + def change + create_table :active_storage_blobs do |t| + t.string :key, null: false + t.string :filename, null: false + t.string :content_type + t.text :metadata + t.string :service_name, null: false + t.bigint :byte_size, null: false + t.string :checksum, null: false + t.datetime :created_at, null: false + + t.index [ :key ], unique: true + end + + create_table :active_storage_attachments do |t| + t.string :name, null: false + t.references :record, null: false, polymorphic: true, index: false + t.references :blob, null: false + + t.datetime :created_at, null: false + + t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + + create_table :active_storage_variant_records do |t| + t.belongs_to :blob, null: false, index: false + t.string :variation_digest, null: false + + t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 8fbaa72..0cb3b5a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,11 +10,39 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_01_16_111008) do +ActiveRecord::Schema.define(version: 2022_01_26_155632) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "active_storage_attachments", force: :cascade do |t| + t.string "name", null: false + t.string "record_type", null: false + t.bigint "record_id", null: false + t.bigint "blob_id", null: false + t.datetime "created_at", null: false + t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" + t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true + end + + create_table "active_storage_blobs", force: :cascade do |t| + t.string "key", null: false + t.string "filename", null: false + t.string "content_type" + t.text "metadata" + t.string "service_name", null: false + t.bigint "byte_size", null: false + t.string "checksum", null: false + t.datetime "created_at", null: false + t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true + end + + create_table "active_storage_variant_records", force: :cascade do |t| + t.bigint "blob_id", null: false + t.string "variation_digest", null: false + t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true + end + create_table "tasks", force: :cascade do |t| t.string "name" t.text "description" @@ -39,4 +67,6 @@ t.datetime "reset_sent_at" end + add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" + add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" end From 2d84dd23a4fd37c850c15e12bf980cf8510b05ea Mon Sep 17 00:00:00 2001 From: graph1589 Date: Wed, 26 Jan 2022 22:37:28 +0300 Subject: [PATCH 2/2] made active storage work --- Dockerfile | 5 +- app/controllers/api/v1/tasks_controller.rb | 31 ++++++++++- app/forms/task_attach_image_form.rb | 31 +++++++++++ app/models/task.rb | 2 + app/serializers/task_serializer.rb | 6 +- app/services/attachments_service.rb | 7 +++ app/services/image_processing_service.rb | 8 +++ config/environments/development.rb | 1 + config/environments/test.rb | 2 +- config/routes.rb | 7 ++- .../api/v1/tasks_controller_test.rb | 52 ++++++++++++++++++ test/fixtures/files/image.jpg | Bin 0 -> 29793 bytes 12 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 app/forms/task_attach_image_form.rb create mode 100644 app/services/attachments_service.rb create mode 100644 app/services/image_processing_service.rb create mode 100644 test/fixtures/files/image.jpg diff --git a/Dockerfile b/Dockerfile index 316b500..cef9c84 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,11 @@ FROM ruby:2.7.1-alpine ARG RAILS_ROOT=/task_manager -ARG PACKAGES="vim openssl-dev postgresql-dev build-base curl nodejs yarn less tzdata git postgresql-client bash screen" +ARG PACKAGES="vim openssl-dev postgresql-dev build-base curl nodejs yarn less tzdata git postgresql-client bash screen imagemagick" RUN apk update \ && apk upgrade \ - && apk add --update --no-cache $PACKAGES \ - && apk --update add imagemagick + && apk add --update --no-cache $PACKAGES RUN gem install bundler:2.1.4 diff --git a/app/controllers/api/v1/tasks_controller.rb b/app/controllers/api/v1/tasks_controller.rb index 8d1048e..7d3c68d 100644 --- a/app/controllers/api/v1/tasks_controller.rb +++ b/app/controllers/api/v1/tasks_controller.rb @@ -1,10 +1,11 @@ class Api::V1::TasksController < Api::V1::ApplicationController def index - tasks = Task.all. + tasks = Task. ransack(ransack_params). result. page(page). - per(per_page) + per(per_page). + with_attached_image respond_with(tasks, each_serializer: TaskSerializer, root: 'items', meta: build_meta(tasks)) end @@ -45,8 +46,34 @@ def destroy respond_with(task) end + def attach_image + task = Task.find(params[:id]) + task_attach_image_form = TaskAttachImageForm.new(attachment_params) + + if task_attach_image_form.invalid? + respond_with(task_attach_image_form) + return + end + + image = task_attach_image_form.processed_image + task.image.attach(image) + + respond_with(task, serializer: TaskSerializer) + end + + def remove_image + task = Task.find(params[:id]) + task.image.purge + + respond_with(task, serializer: TaskSerializer) + end + private + def attachment_params + params.require(:attachment).permit(:image, :crop_width, :crop_height, :crop_x, :crop_y) + end + def task_params params.require(:task).permit(:name, :description, :author_id, :assignee_id, :expired_at, :state_event) end diff --git a/app/forms/task_attach_image_form.rb b/app/forms/task_attach_image_form.rb new file mode 100644 index 0000000..9455231 --- /dev/null +++ b/app/forms/task_attach_image_form.rb @@ -0,0 +1,31 @@ +class TaskAttachImageForm + include ActiveModel::Validations + include Virtus.model + + attribute :image, ActionDispatch::Http::UploadedFile + attribute :crop_width, Integer + attribute :crop_height, Integer + attribute :crop_x, Integer + attribute :crop_y, Integer + + with_options numericality: { only_integer: true, greater_than_or_equal_to: 0 } do + validates :crop_width, if: -> { crop_width.present? } + validates :crop_height, if: -> { crop_height.present? } + validates :crop_x, if: -> { crop_x.present? } + validates :crop_y, if: -> { crop_y.present? } + end + + validates :image, presence: true, + file_size: { less_than_or_equal_to: 2.megabytes }, + file_content_type: { allow: ['image/jpeg', 'image/png'] } + + def processed_image + ImageProcessingService.crop!(image.path, crop_width, crop_height, crop_x, crop_y) if cropping? + + image + end + + def cropping? + [crop_width, crop_height, crop_x, crop_y].all?(&:present?) + end +end diff --git a/app/models/task.rb b/app/models/task.rb index ece0286..8274f7e 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -2,6 +2,8 @@ class Task < ApplicationRecord belongs_to :author, class_name: 'User' belongs_to :assignee, class_name: 'User', optional: true + has_one_attached :image + validates :name, presence: true validates :description, presence: true validates :author, presence: true diff --git a/app/serializers/task_serializer.rb b/app/serializers/task_serializer.rb index 93f52c5..dae040d 100644 --- a/app/serializers/task_serializer.rb +++ b/app/serializers/task_serializer.rb @@ -1,8 +1,12 @@ class TaskSerializer < ApplicationSerializer - attributes :id, :name, :description, :state, :expired_at, :transitions + attributes :id, :name, :description, :state, :expired_at, :transitions, :image_url belongs_to :author belongs_to :assignee + def image_url + object.image.attached? ? AttachmentsService.file_url(object.image) : nil + end + def transitions object.state_transitions.map do |transition| { diff --git a/app/services/attachments_service.rb b/app/services/attachments_service.rb new file mode 100644 index 0000000..115492c --- /dev/null +++ b/app/services/attachments_service.rb @@ -0,0 +1,7 @@ +module AttachmentsService + class << self + def file_url(file) + Rails.application.routes.url_helpers.rails_blob_url(file) + end + end +end diff --git a/app/services/image_processing_service.rb b/app/services/image_processing_service.rb new file mode 100644 index 0000000..05d2d47 --- /dev/null +++ b/app/services/image_processing_service.rb @@ -0,0 +1,8 @@ +module ImageProcessingService + class << self + def crop!(path_to_image, crop_width, crop_height, crop_x, crop_y) + image = MiniMagick::Image.new(path_to_image) + image.crop("#{crop_width}x#{crop_height}+#{crop_x}+#{crop_y}") + end + end +end diff --git a/config/environments/development.rb b/config/environments/development.rb index b2033dd..9b0ad98 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,6 +1,7 @@ require "active_support/core_ext/integer/time" Rails.application.configure do + routes.default_url_options[:host] = 'localhost:3000' # Settings specified here will take precedence over those in config/application.rb. # In the development environment your application's code is reloaded any time diff --git a/config/environments/test.rb b/config/environments/test.rb index aec0a53..410d2f4 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -6,8 +6,8 @@ # and recreated between test runs. Don't rely on the data there! Rails.application.configure do + routes.default_url_options[:host] = 'localhost:3000' # Settings specified here will take precedence over those in config/application.rb. - config.cache_classes = true # Do not eager load code on boot. This avoids loading your whole application diff --git a/config/routes.rb b/config/routes.rb index 299849a..80f0e48 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -10,7 +10,12 @@ namespace :api do namespace :v1, defaults: {format: :json} do - resources :tasks, only: [:index, :show, :create, :update, :destroy] + resources :tasks, only: [:index, :show, :create, :update, :destroy] do + member do + put 'attach_image' + put 'remove_image' + end + end resources :users, only: [:index, :show] end end diff --git a/test/controllers/api/v1/tasks_controller_test.rb b/test/controllers/api/v1/tasks_controller_test.rb index 19cf4f7..7432b43 100644 --- a/test/controllers/api/v1/tasks_controller_test.rb +++ b/test/controllers/api/v1/tasks_controller_test.rb @@ -58,4 +58,56 @@ class Api::V1::TasksControllerTest < ActionController::TestCase assert !Task.where(id: task.id).exists? end + + test 'should put attach_image' do + author = create(:user) + task = create(:task, author: author) + + image = file_fixture('image.jpg') + attachment_params = { + image: fixture_file_upload(image, 'image/jpeg'), + crop_x: 190, + crop_y: 100, + crop_width: 300, + crop_height: 300, + } + + put :attach_image, params: { id: task.id, attachment: attachment_params, format: :json } + assert_response :success + + task.reload + assert task.image.attached? + end + + test 'should put remove_image' do + author = create(:user) + task = create(:task, author: author) + + image = file_fixture('image.jpg') + attachment_params = { + image: fixture_file_upload(image, 'image/jpeg'), + crop_x: 190, + crop_y: 100, + crop_width: 300, + crop_height: 300, + } + + put :attach_image, params: { id: task.id, attachment: attachment_params, format: :json } + + put :remove_image, params: { id: task.id, format: :json } + assert_response :success + + task.reload + refute task.image.attached? + end + + def after_teardown + super + + remove_uploaded_files + end + + def remove_uploaded_files + FileUtils.rm_rf(ActiveStorage::Blob.service.root) + end end diff --git a/test/fixtures/files/image.jpg b/test/fixtures/files/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..88e92b1e9e30da4fc56fc57d2e85a4c5883ee2d2 GIT binary patch literal 29793 zcmbrlWmuKL7BD)S?gr^bN>C7xmQcDoBot|o?r_tggmibO(hVYrAl)t9B^`p_i|3s0 z{<+U{f85zJvu4%IS~F{EZ~spJT>^M=QnFG21_J;FeZb#25C~A9go=WShK7oP34L&J zu&{7&N$~KY4+%Xn88P&ao|=)G68hs~d-#xzPn3t7M^xd(3k4%RBX93Q*#BPze>(v_ z8X_F}z~})2J`52b_O}aAK^;Uugh2`XFQK3!A)_H8z|jBB0<{0&ccK9}+GHzLzGzJv z+UsTc4=a!69yNPF^H#6eeH}50wSy4Te`)4F#8Az6XE6fm$)ERCSlP1j6V%p54|%cs zD%!rZ`b$7X=W$;FgLpdU%baCg2&yClb>Bi=sXb05ssa!FL z0FynDfePcUGa5MJm~Gqq9UOqBCSwQ(D5LbC3R=JZ51;V55twOBKx4{rhBg|0* z72(|-P%g4ZnFSDcn_QP5VTx`jry*{KLj*)?5df&9?I0NAX&Qv!QOSaG*pp8HJfKmA z&^XGaVgS(U{X-v`c)|hG!o6aC>lctXNy8rq2Iy~-0Brx)b7ROtBQ!Vwwyy6TZx=-$ zl2j{aMfSKn2YfNC)Hg2GLqC7q{kLtKr9hK$g-!AVS**PZ__ zc-|=BrH};}YAFdHoC4TPHRK><@t=_5r)6fHi%^TeT^)@&4ZxM7o&io&+>p&Z1G)spPz@n_>g%a7z$vSRLo*;#3l3nPt8XU{mmnH< zosqleY={AD9FiR!gfI?g-jKEc0ozIr1rvw`cE-4XW#}?GMJfhxTEB=vDjbj?x9?A6 zr0HRizW)IL?u=PK5u;MU6#%Y*P#yOD$!7rGRbX)-r@mX~QSAVLVwu08f=0$8~>jM$XUs|V9Lh5iJ;&pNg>H9GO>GPe{ zY~Oe_P_^B%fCY(D^gevfV~HZVs%-!VjOi_qLMLoK2>%z@GI-H(%()Q|L4)M`{c}Dd zreswp)D-nsh>7Lk9fbbFl4ygysZ9Hv7Qlp0H%XJM-@I>t15E1@hy`Oe8V*qNlV7!( z)8v+MXY0TL;-uSEZw6h_$2V~Bz(^hrMPVBk00h0nuJgT;#eBoJ?f~?rt~kPZzLXgQ zg!8Qq$nl6h5IA*QK8Cfl=6JJI@A;0w}(9LaMgqq40#MI4%K@s_1kv!m?Cd(WR~d zEvr}IIYq&Tw%Nu13kv}Ph?Q`E-hVV+{pjO|hoiS$L3fbwlWBIVb{rFj@oNl8XkD!+ zgG17WI7a}Gpp2Q0G%j6*EWvr6Ts_JIeaW}}r4^-H4FJ_r0qPyCuq&jpbx`@mA>sx{ z3Gh{I!>s24GjeyG1tqodM*!8`2GYh>p$2u^JJH3;_E}>)`7H`5$m0^NnEwomZ=dme zZOIlI)%^+}nk>>n%UC5L9H8vD&{snds39la4uH3YJ9!bLpvM$y^50a)ZGNb14k-q( zRsEhCd|cZ9hB*14a2On*6WuLCG=Nf2AMv6#3*aO-iQO+@vAahA#@|wZPebius}I`| z1K9QNHsMgUn{22d3*eUW{k{fBt`!LHd4QFbI}S8Lo@eFJkN~2z_*e!M+tS%>E#6#r zV!~&@<{0)Vd0R{E8Y0h}iQIDm%iklJhv{mdDRW9L{l5b`B z89C&smkVO`Y_QD(A3G-QM(^*Mdk9YllYCGGAxTs=$8!O<85eV{X#mDLvQ$D7NC`?a zCN8jrxqpg!IM(BSzt&~^^drwvI18NIIdtCWYn$u9A<4>yHxFBFW*?H>q9+5uFt3<% z(RWHw5to@#xsa8+#k|4y@OgLGTn9j?zw;^4<-c$3NSOD`ufm4r=x-ocKuFq!De#ZMn@Y=5g!NdFO&8vF>i3WXl#MiIoPwsf} zWjGrE=HvY19xlx#fOzqAjDqS!?=m8BlIGOv?L9N4H7b-NOm9E_(x-U|jRZ zevcj;!XrkNE0eNRS=W{1c?`I=LT(u)X!6geX4 z&gTCDc}Fp*1_bD#aRW#)8G=a- z=YAO!>ayLRN%wrP^5s2fPrcnVyCsU^g`*nuha_V9J>GwUK&YtEApSeY^u72w?L8+F zMOs#-Ut3LufII7=5wv>MqBMtg%v`vGH(IhI0?hf8G1_|fD|JWF(0$NM{ zmvFqY#1Nl0yma~r1W~iZ{)YxQ?{eX=IU$M98+as{+VKC2r<{-lh@YAA4f>544LbfW z8aQb(0vx~3`5CXy6v6GmEi}UaO^E3%a3F5qUT}@OS+<5)v1k833Lw3F)u;zYKnz-F=Noi>rzH_yMHh_N#H^Qt?w`|J#5B&rH#FN^uuYR|N!rGg}xa)+d5~ggw z{D%#NBt)`c{Ewf4G#s|Br`%-Odu;TXMtegAh`3R-Ze0AEp@2NJ%vuY)DEQX*0}HcMe2ghhWFwp(pnT)ne88NdKcl)5x7ze zA|vELD-jT=X!KD&WaUSokm{6w_ZPx|hpzlk4qMjmh6*~(@5f(%!4aN<@mV!!1aOPH z{({qcHlFCDo*(xBlwkYpseAByh>L-%92MgGm)`!!t1?pg2YPIudKy6Iuz%@x=D~9sCsl$e{($_Lu+%L>&+S(mMe1 zlY4;K9y+Jp1L%eTs6&U`8J9sRd_Xk}z=l20wLr35LTrL|$qvBr8LU%67=U9A(B}bu z)_n_XWx!1g5Rcz0Gy|A6_sYn{VB`Q%2B5MmAs+*b9YCT7Kt&cr#DJFLkr(-}CmXHnr20f7E(}RO&R!NXnvP+;zBM41_39A`e!UpgZU{i%eqyeLSd8 z--p5_d)O~e0lN_~$RZS9AoLRN%)7-H+qXWxCm-*=A$qW`t8abWK^=c-F!Kbn05SkU zWk^!L7kI{gPiTZ2Jb^qyB8f$Da+QTEt%T~TfN*thn6z?iuV)*dF-nwOUx<$!{BbnY za};IiIE%tAbSJ-kql_!FXlyD-9V{VJ>Q9>jqNa1;t*wSh-4eMNLKB?qXdK3Ak92OC z1bGmH9!4X?4-jtaogqUZD*MT96sj$QF1yRt7OLGAK%xTzid;eVp{{c^2E)krcUG=I z37&~AX3t1vg$`HsBxqnhG7!d15TE=MF%X(@tJ;DvFpRWyVu1yx9q8BzdA5UiTp*>k zNm5!2A{l7uq4=U$rEo&7hxTat^i}~4SVlrANLwHGf(F%m>CmHtY}+~sSV9x^37{O% ze+x%-h9ooLU_+)(2FTq(wWmh_zxR8ukYIlWzYA;=4^}=suV~|0gRpbg?J=K zD^H}hP8Bq5a3nYfU*e<4xLJ%MG4v{DL2c*I4rtV2u0arhy%2v%FwJ>y4M6clC}Aps zrV*6TG&_{yx~$XoGsU3JBv)@1q_)~WI$^4j?xd>QJ+=G{0O`@&(B`S=2i~Wx6}Sxi z$TzL%a3HK8=IOQF!@w{$6LdVqWZaNaHfj$u&B4=4n|({X^P2*(lw37=v8 z#(-*S80#~@bOsoZ|MjyF!o{E-ASy+01!;T>)3=S?5op;4nrVPn0b~rg0pKycgwWCf zs}Mu3K6?a^oED<6#I%zDpEKwsyG6-@rEcyX<{I5g@HxY%ulXHspNmmmrBkFmTS_Bvx+Sul`(=UP&A2T0acVk8EOr<*dr6^)U# zoB;yflnSOZzoWFE(<3or$0dMtrY=UlM!AJ!21kP86D5^c^;`Wc+7Fnf3ezC>_7*^V z6-X70_5+|WsU-nuB!H9{kY*x)g3>w~@NWTPll82B_)T&0{Vn1t;UxeoF$SCBlpi>g z9NSc&xyXU0ZdeS*F`;T&jF-p)V83dQJ*=PubRq5SSuFu*7n}yr+<_7#2}}7mIkD9d zBg0w$;TF`8RpL8&kGVpjDv7?pP`saF7|yH!sYKHXbQN^gAHkY}CAzUiSBVjiL8}OM zJuKw|kE^hFzKkv{0>3dpY>N^@)@AZp2^T0C20dbs(X+s@SrA9tTnuev5Q-!hXx#y@ zzTJ{6=!#%2AZL*1xwV?M3AqsuphDrO1H~;O3=std8yOn~85!ChVbHFK3{db<3Fx>~ z9SG^U#l%115z#)8P*HpB=ogdyh2c%r*AZfpr+kv?8k)vV{;{2C4;WuKSM%_i1jf1K zi6_ z(cIp7XrQIvaLPJ?KQV2&nXKicRE}yTD|?|wwcm>OV~O2K4J9uzF7J%xla;Ub;YS6; z!JdTl$QsnvmN|9qU-E89b0pxFp{zTYTcVEw`)EY8-;|? zmw&~MsF|+62=aMv^z*!2wr}Fde}~_LCFzHtz6Wq&+9aBbDnrxf=z%iW4=@>3=V)j+bj?uh$V~b7;}B28^Ci zI(v>;y?hs;V-oBqQaFsl{N#7=?=tuA)xH(wq~+Wt5f9YM6WUb0LX}E2j~B~G!WG$_ zs+3RZC#)~L*rvmH%>&GjZK=^ILSKe4u6UJxaW62K?eh2?_LUh`f6Jk|{NXUORFOmk z_tWIPwrK~{es6WXqJm_ZB4rgM>B!Y2TUJs(^9+ZJG?$4@{@Qp}XV}4e8-O z=JTfG$6B-D8qe^pTNC8=u)bSG&^w751`2byRkqHx(6NR*LH!G)-9k6Dl}o$hKg_sU zz;B0(xo3OU{Dz!nlpao$)zv-yBwes+nF_@VeLl*a4#%vszLU!Gv~{2 zAGK6P>!5Uj(5>Ti9c3-Pvh~P)NsJdLnQ0rt{12}utR}S%ZKa8%J@E9VJ65kJZT^H2 z80ZhR)W6b-=dfZ^T~y|3sx@kwviihRDWLRS%HwK#rEkkdE+77b3*gOg~6zwm6D?V-!dhJObu6`W@#8Wp9aquO(JLhG?^5S%e%?`(y+HNB}lC7R`D7}-2!lzD&YZglQu+k<$Tf4>czF2B-UGLAM zt>SFz57<7Id`>J=3j;1wx8kgqnD5=Sr_TPgTm4!jKE3r}`_bofEmbmmYs=%yS}kDQ zHgKFT?KJwDVe7RS_kxG?@{*X7qgKFgzKBG1IPNgY`L7#wi^g}&<~Y=;A{72492D${ zbc?SVv94`OuN|s&r5iavs_UCy#?<)GnH}a2J7LXzRlZ}6NyVIU2?N^Of$1?RH#Yk_ z%~*E?txB?plvsf#mW|4d_$VorSe0}MTSGuYRRFY z0ZZSd%v)AUWn$^&OI#%f1y^dz|BVyRk6MN2F6)wln&fKm6S|S8aPGBuQX;0;S)zYI z3v-G(TP?XpUaud=cWP<^9MY@Ekqy3x1pT#6G2P&cbX)vW4BOkY#meF0%%5(5L2F_{ z>RL@S663+!nZ0tOM6Q9Nx?ee+qMvOJR4c2Sb}OUA=xAPGig;E&y*#Sz^KY3KJgOee zYjg0ppnNX!(ujs+O~MUTx1@8+lh$o=w5|IxmlN$_l&q0hLiS|T^v6U6F1uECCD_U; z^X0I&YQp5#<|hwEQ^G~|E6?c<RlT&)cAYIEPoo`X;JpU({?&UL8PP=_KZ>9Q<5^od38pcjy>S=9N9hN z+?Q<;_m*4xM*5tYo^#c$#MUM=eVhcAryG^;@UCk4~G_?;m{?BQD_znPMA0MMQ3mQ_9an|d>mKH?$J|4QnRB-Zn{qb`(1jHA9B%xSP|*P<}!WEA(xiW6w0@u*%y5tNjtJf=MA)F_!Sm z@Qa6iY6;4N1IIAi9JQ&|SF_1_tY3@Sx||=K+I^;l zi*HQ5`K{Po>&k{-v)t{{7DA!T`fxnFjJ%bb=kgtL)iD=^)OaSpC}S$OFiTWl^pd0^ zUw%XGaNI}5O4GGi_g&>7H@rwm>^s{pMNYNlEOK%A#$eP|qqsz`4^PHYF!-0iCQ-^4 z<6uMn+F|ch1T-Wko5Wv|62<2q>+StGjHx1oiXL|*o3;uRoB5G>9=YmLSx5_)j`s&8 z=sjnQ&AXCLSGDrU=StTmIwr0j7&zASYTFyd7F7G4j11nmGz?}{;%l*e+U9ADNBzCD zTBB7dN*;V{t6tPwJkF#PQ)b{Z!QXBqw&~IrY0Q^y|8 z*=2@@yk9VueAb9cR1YWUen-y4;rX#UXnG#AYIVZiC1BNpj4hE})5`w!iXu1=$4xDT zu7~YtA{(JkFCizNQkFkJ@@<6J_gfpnRT@ErYAsz{i|gQ@%$!-%a* z%io9}Bo)iMi+fPN0V; zGby<hL#-Qa!%nHtU(Hns~ja{=u=X^TMuQP9@rPjBNn$xE?Hu9vzN6Xn4s=J8fBHsj> zD-&hVO}jeTL`*fi`rmR*yjgp|pV{P=xt{Z7j(4kOoIMOXP5Y*&=A;-mM+ir-RhGnO zy=7-!ca>$uTT-UzeC!oP$hwN)_eJYi!I?b6EoAwx#f&{pmscWkn|*33@vJY9@(5(d zkxS2bHA^+gc6!#=I4paz8GlO`IhB5UOia(M|GXg#9(Fa_gJv9vryep=lTW@-wp62P!=p4 zUeVNdsvFH2C3*su!^=`XxqfeWd+73BwSG}^=GN#h=-ZsL!jU@&Y)`g3RdughA(!t7szZE!y~uo$r2p=@JJav)im8B^8Rw?H1Nw(C{*6 z&Fs20*h`i4cD3UcluG@0X|X&<>~3*NP%lcyXF^+s`)!f9bU2dmw{Eondh<6Nl423V z$Ty<7Z{JF7F=gL6n*t?pjLe4@Z3S-WBPzd(yGKDxQB#Y?YPpi-G@sF*J^I^~;tl4fY7 zN4iAvrTJ{Fy`2G30B1^7kV#5is57MqCM%mzTut)xQ43j}P z#ac|hn2FFR8aQbw=;PviX<)#aU#I@Z8l058@}2VUL;L!|oZVw@Ecd*Iw13%8-h^sX z_E1^s43VRzpi8emZy@j4H%NB8GD-Hr9sn79E=RaiBzIk^hG~S7v7`q!+`^EzA zR;KgU!b0z@IAPKB+v9Zvw5%8BHk=}ZNce8hBlr)+)Ta8^W$uF5ycD^7qNQ9Ils=E7 zEZfCl&v9ORtlE#)mSJFUQSc9$hd@!EdSN|SfY%%8a?^{KaC z=mqim1DAFHCh2OPlW=k`=e!l`RV2f-ePQ}~oiw7WG1utncx)q@@J2dKomtf)`O`02 z%Z1f)QFFYc<@i|*=<__8__vw8r-`ijntg?y5TT#e(~B}#&h!YH`{l2o4%KFJkC00^Xr1JpzsvK*NqNN} zU&=hr;~rB6N)&I8??gUyI266JWk_DL#dsj_;&jkC(k+C&l&nH0ZK-G>X^9je=;?8Tm)D+;ohF={ zrAx$Y-))!o4rzKg>P2MD%&bC}q_Gyx1#>VvkiWM@)t~fHMhS<(VVk4{KW@O# zy@%+$6AJ3-qDoQk+`m9ovQ+wMui8^i-bYtf-haVykyA^7uZZ@0@2IQ{2f2`0jm!Sm z`DkQ|pGLi&k@I<95ugo^>0s~f4F-lO+UTs6{FI2UrISR!$#8@*LR;MUCB5L&aW4B^ zA>nV$rN;YGzV^e#II;T_vc{jwg);(7D@Ck~;$(L>JVf7CJF!^Kkv^VddUePWre;^} z%zm2bAj6QlaOqR8_0gZdNSMB!`3rY027!amY3>`Bq636M!%K92x{&yB4#&e*Ni|crKRq+N*mQA`XMl4VT+CT@9ah}6Nm zpK>naeCbwNB(^nmvv}zM3-wLAQOJv6Bkz^!?1B(ht6H;G)v_O)KfUnK`)w>Fp5gRS z>RZbVSH&}~5aA%R^|gma1_QZgfYf&DiR^IZ^8Zj%K7DT zRdE_QvR&znr!ii54Uw?&rI>fmr^x0@_c>T|-2l$SZi z9~MfM8~FHpI>?*D&8omgZLXwy<1^A}ku!ynxqSq7ChNrGX;L5W!Oiad(g_K3n+@6d zcm$6P{WnK$DTGTFo(O`&xdRpBqO+w>Lsxi@!|$jJZw%4D8_3S%u-Ju0X`6*lvbik4 z8QsXZN@GmbskGqyX1*9yVFRdiErJpb)aAm9NyG05y)?gzwtZJCSr2`ro~lhQKIAm4 zJnL2B|2gm?_jfJ5WspeXm#_RAl_P@t-it#;Po@R&Iz(#;`;YZ{uzgweDV7s_6f-;$ z(O}L^!bi`Mu4kApihHg-Y8UNlES${KF?F=e)-0&+R#K*rv0F=4X^b3P&69|CoGzE5 zk0fr1e=8fqrAQnEA~{6mLN^9z>1r5yWvD*<=I?JA`;(wSCNuMOOZ0B1#6GZHK<487 zVc)C5hL@*wXy*i|Pk%HEGac!*KZ8x&jAnSmyMMj;psGc#>k@)mM>wEsX(v1}}bAX=}^$Ij$FO*>u?jDA8f z#(%+ta?j_RSL`_T>sf_m zI{rAQP33p!l}bO?N*y+mF;VNRcc$kZ4z-1UzC9X1NJpdQ3G=ua{c)o?Kx(SoHpaTn zUz2CYjL9=gZi}5|9LLCr4c9y3Hq4U)mU0reu~BabDTNb8Mk1N*53;3RHGaxvj@vB~ zTg(lL3CyOWH3l87jHROTeY3UR9wo#nHKlL-_NG=yT}nQB^UzD2_z*ZyAMDhKzRW$j zD`j~T+nn;2@s9f-?AH6_5wU~dnQI9y0U^4z=R}V-PqSjsBm)5tZ9ENpj{{wgC*xA>~Mv`l9<~1JR<~cKeKK^FQU%ixS{p3EJT{1^<*T`Qs~8Q`lokhCBzwn9f7v{eaxt@w#F z&7C(IXJptTLlW%E-HqBw2NbrRo=)*XCdk@x*pev zuszMirIiimvgLuTO7kam*D7T`(qB9++Fe-=dz2{Id{r|Q`5?ntF=R1hX#=wSg_1*u z!jE0EW_Q#-RB3XVgiZ!}jN5wW*gNUQpoFOUoqp;+ruJt!Gd(3Uc7e9^m>N2R4c39m zb#tNbw_`diC;Ckvr`fgRwl4sOvKtY}V9ho~)SH9Ra+Sk79l{OMTm<8t874kJ)t*C|~Ntc|>PO-bCEsYIS%N6Al|*j51PwR`6rVls2|o zo9O!H+4*+4ykjMLmBSbl;_cV73m$T$zWEWVxFtV7mv6liHnh)_*n(&6<=NCkV*U79 zJ~vy%oFfv~9v9zH`5u^m(yHdHsXeX8YRd|AYAMkY-8S4-%v+h~3Ef&uXS>L-mQ*78 zF1-F;W+p^liOIkf2@h=)yW)`{22rw(L%N~ysjB5L{m{oXONIRJiRI9;sO8EHn!&0S$f@iH1k=D)Jh=6q7Nto>M_ZWCCvlM{iXvJxpX? z3rDTYz0G3;D8K@^KI~O6BWg>RrS>9_WjqOYu9%KQ7k1v#Hal87XI$reDPkGV(d>1;415ubYG<~&QnZ(xy3d<5i>?GpWc41XsC=A~6UVThO_mRk z4^J8)L$h%y+E?A?YuP*Eo#{PJc(4yI#Y5xY*{-=<;hDjTl+$wZ-WF_3kR|7scb7h? zY-`JpJ1IV)2ts+)BY;1xUpns>R+Eg=8+~$6`hdzoUUwFHOxUO~iQs5*D$(h!-zp+ItE$=6!33q|>?jA?)*^HHxC{tu7X0-ls@JM^X5pV*JK@v8G0B z*@kolV8-vpR=QEVOe5@ySG~VrpV?5bXJE~ep$`1z|S`Jh^sepxF1rlugkE~uf zXX-HOIe$b&iY@G{U5oNhjz(#tZ(~S)g(76QKAbv7D)6UMd$1~<=UXGMnt)Ou_wxuz zYFlwFp2?n=d|Y3VP=%ut2Hy3CxU*FC9ZLsR%N2xc;`%1Fn7DYBC6sEbpcn1Owp(`F zZnb!EF`gWp@-cZWH-Yi|Im)luSXF43q@ryVV@$=uuN&oUne?x{*3fr8mUUa&+?36Q zI?zF{`$ZSJFA@S}0x4A3`*lRE31quwD~7O@y`A&Sqn5||i$?n8c)1^M(DN)(hl5RB zbJ=I&)RyIh;~uqhZX}GnYh~=SGYQ2zxu%&8jJl@?K1j(R?h^*9s_SR;}^uasxP6-7X*K zHng}T!-OKjV|a@&g;YW(>&1l#3r0%@n=tgq!seFsF8Q&346GLCcn-bp3gW9t2YCUn zOOw>-&-eAxA7HtADDv(XF{l5s65M^JRgPX`E~qoT)?80*P)N;Ev47-?{NMhU%6 zS*ysYaWT#o^9-b&T*oM(Q4sLpbn98sck2vZz4VX6z7_6a#=CiUJ)$}s@}a#?)=0Q7 zeuPJ^uTV<*lB|?n2~qN$wVFT)NgfBL>{nUZh?SAI^a`$vO8Gds)>WTVni=}!dYWm7 zp3^1mvrcK@1@1naDA}zZ*Rvyff>ROiG4#gABSHLyH>FYhQdHfL@a_R?4~a(Zs7K_B z<)iuxjU4_j%cXO=l*g4ae!2xe`~=F*4U|^U#TgxXFCHgdPOTM_rVc1()y{xM+H_|EXNT2-H3%P_>Koiv| z!-&XHMSyNovL+TMk5;H}x1Rn__mQQ735z@4-G4G*Hc;_ye;;onizm(fnLGraV2 z)Y&yIjE9W*gIu!p)jxkQQpnWg{MqM@NRJRjAV!&)vdNpLNyrK-)C}mQ9;V{URw85_ zV95|fd2MBcA37N#2;r==KP$G$}7tUbc`D74x9SNd(`n zwe93+%_E{>HV}lE<&Rw%ES>7!$bA^c*J>4<@1UGz%ZK%Y;8g^t(8rbl|LB?}>8EmW zK}@o$NR;9vO%H0Z%c6(gRV91R^mjJXbYJc4Ktpsb``SDM3;#s@0imYY$ z_8X1zg!WTi=PO64&Lf)NX>a4)NG;T}pN9D4zMG4`mTViTx9z*7`(-g#x9%H#SYzw7 z@iTh9AVV)9xKB>5=2MiE$U(kN1QJnnA=Q@cbHkt7mAVd{G;aJ63VhDYJ_TB4vfXsA z$c5u>%~L*q8>O~Q(ASS_$cm$5Ajt7Vu5xks|W)y-wURE zHOJA}?X7}ic68QRBve@a z_ifPPoFa2)jfOUfa_(hZviFVlVVKJ!fdXE2{X-L~60SMd0}@@5V6h zve=;AtP(@A!Ot0;=Aq3_FOsnC?+@~8XLoV2w7FSCM?)s7SYt%q|)(^dJLzUMOPK ze?{x7Eh#e=e4~9>ZdG&qA&O*fOTwc{Ex(vmPL^>AjLM22OldRLrWD&=7dVqPqg}Zi zD8DRn^Nke`W>C+`&vTcXtW7TwWv0awC+E=aT^}l0DYUe(9J|uOMe>Zznk|lA^)ul7 z(%6W8SEH95Gq;UN!20zR7d?ca-8H~6ZuTSbhC=WS#`Lb0_{GK1{TIvR;3YdY z-7;ifg5f#7x`^+CteDnvwRM(hlZa^7? z>{V8mW|!bEw91v?7L!h)RQeQ_XQ@qjQwIItf;WYlg=#sR8rv+Ud6#nALIk|A)>H8U zXVe57jBJl=U;g^}c&{ohpQTui5n3L?geMsV)T!n1O&U&0#)C&Sa(76YuP7^(2V4tw zXrHbfFY+}fQXecV#Y=_~U6Q{Q>aoAgpf5JWKxW2ma&cEVeS>&NSLKFad%OMiu;8R~ zmoF@iJ?zQSz*Yg_7rXrSLNmkH=P|pS*(!ET&wGh;1OpRk{@7D-*NoIO{RjnfOonyr=Y);9s zub)%tQ`zLM{xXq^#a|PlTCdr`Vbvp2%Wn8!&|je#k<@J(OV>VB2kh<0BZ@BkBlOGz z3kPhM1Z2l+4rND$s$irPLdZcoWQ7*hpQ)wde)psr#JeBtP&veFWufBP>tO|V#(1w% z_#7J?PN_UUrns@a=yhv15QCmJ?e^<`F)iM@ApQk(?RCGr6TQyHasLG0&HX>jHq9fh z`lJ=jN@BBb>qMK?*~U|%d1X1OyT75#ne)D=zWSQ{iVvP z{z(-(ce$+$w~C7|_pM<7X}$}qor1O;>W_LW7y4v*b3Z@y(5-FkEM}@3Z|Ow#8}(+Q zB0Ni_1k-W(q#xw7mhJRIZGRHeJTA0WOPS>L#x?Q@M`ia6%90nGyQv6rx+S?1fB2l4 zG3mpRQl)yO)GV0mEQx~&4t|WjvXm}k)QeBl-*+>uNcSi%PSEEUNaIH*Y0_10tgj3( z!xnqWVYJTZo!V9ry@lF+9XG6f9cR`Jy2*jPPo2K zu*7~z4xOH14z+b5Mx-_g~uTI;071pd z0u8NYS7h{l{Nmnp=Kh7ad=5FaIJq#!7zf6T*nX22@vMV5JZ=U?F|%?*P2+!3e#y?{ z^MsJe9%a&HI@ZjlM{jQ;s+Lwwj&u8Ow(97m##pDL@>D#nniMoL8vlr#t-d5??H__4 zjK9VM_dEE_XT%wB_VaVM9683XeBuVxQ5~bl)lQZB(`9PQeB+Z0qQNRb-45ayvNGS0 z_DK@;ayu=W*>Z{FaM9o8)6ZzN@|5v^U;O@6~~9`X%gyDJqz9ElRGw)V$%^ytsTam`eF|<&n&}ZOR4eR@g^Qw4bk^3{#2eP`Q?@ zMQ#psEAX7Y+&A?UOpGY;z$>i{FP`8(jG`OX(=2hLK3C`BwitDi4xV+_aLJ{>6Q})% z>me~7#3Z9C=hua2Py0c|G24CHBjSfnTE~&$`gNieiR!_G5f_TImFRoMzu-Z!XM`Wp z8=>aiSnFo56ty+p4#jUs3Kk9*kKEr(b*^f^`#9V!o~EZOj1*^SIVb(`PvPtvA$gZ6 zBD^^ZWI44+I3Ve5d%3EP$SdDDOOu?f_+oNSG-JYzVEua#+5HdN6l(tcpbgYswjY-s za575NS#Arb%Xtm9UB?x_K=PLz1~u}P9uJpi$1MjDi^b#3&St6Q9@nH{WYGogyNu&H z%GL;nXljPMo20M3$z%N$X`203X`dS*Un!Y(51FH2xXQZ{U~0idW`<0 zH(s-88Pl>;tI3Tv_ku4LI=w8skz}`=99OHGjEQHA^v89uKbB~^6-migdb8T?2K~Gb z6{BXqYo|IHtFgO^kUl5gnLSAS?t@lQVkQ%BQoNj|LU~A-#H(+hwOsha+g+Bp)O|%x zZbZTv#JV?xVah{F<8Idxzp^F}nmyx*kEJ;*kR=_<)qX6D#t{{j#NzEqob;&etN!pY zC{w^F)BfHgZgEpx{_Nq@BsG(UcDl?_($_;r4z^5DvQCX>GeVTMGM5+O#HTG9+;Lqg z`P|QG52@O6ODC*wv{_D`k{4GMy}L4roXC}n;D~!pb}Bg9$S&a@80Z9GdFK+K!{ z3x2t=ANR|ApwH;LOh=x;#kzRmS2oScoTrC8)1pZfB$=n+g8EHfu79^iA=b$4%AUg5 z)@9A-#Vn`fuJvCKRw`jVjOnzan7hW8@=?gF?xwxTlH-ru{sYDb98sOTY|@9+3|nTi z9G#6{-XlwdzIV@fu)S(A{Q3o{bofsO6ASc9x6NOc!3G`^fqb(>wM9Gs?pSw*SBXu> z3dO=2WNa+qBjaO;F&W?GmsB4(va&}J`@ga|TFW9RbfD9nY@OkGZB1PI8CkTF>=pa# z40tCQzS)) zo3|lnl3ytLt$#OBYG0dyBe`ZHk^!kHE>JK^P>aKJ%c)|(*M(y$t_|6}ZZlyvn3Ydq zN~#4rRsc0F=21wB5tB({<!Dfs8Lwew{a?wZxx3#R`|Z!o`Q*aQvtprO7gauI5C# z2y>v zV&pcBw#?cTE{VKjy)ZuY3U<9A6DBTWx%N2M0=}TEL^-uqR(3gb$|?2<;;||xcLRyf z`X96X8qb1<%$Yr2#5(6SeA`k#-JJLk5vfMJo-=H>KA28XM_wr|XWtcKfc?l$?uf6s z{(y;)?YQLgLT56!)$IFhHq!}9r*Zc?+Ln5=2#?~&>Q)Yt5s&4Fm+RVsvQF`BvewQk z9j*L@GZR7&lBzns;zSi2bWipgY_>Rj;zGB2^zxX>J6ek`n|^4G1bPqCDV1>WL8r5* zoha8ne(n8f?suA`+*d-wwydQJ8BwSsLAV-1lk_k2f08| z7;#6h?6puWAD0m|a@<_lr2nIk1*!b>??2Lnd9J&ocdaQkO=ezSJ7-3<2u2xckSW0soJ(Pm7 zqSgfSZP}B6tP|yZq^ge@*DenU7v2swS=VFuaanhd(D=Do>Lb<3{t5|wOuU%4D92M` zp!o6JkQ{9|kHtz)St6)%qPKW{cD3fp+ciFXfZdTLIcY<3%vDyGo+$Dx_r77 zyqSK|H$i%eM@^sfI7JK#10$lcGF4D!G~1kaN3_E(M38zyXQQcAI+`?XLQMIHMbkO` z^b)^Tbv%~z(20f#>eY}9Z+5>^kmYFk89-EyiszA_V;<(#{XMqikygrOMs@06e}NflKh6WID$o+klUJ$2WMjHPApF>6=0W!k22GwSr@x9 zj*NKIG^4A-HoLTE8OD6fRVm#gJ4rx4S?;pQTG0ZK+W!FSz4TBl?35tpjxx3j7G%D0 zUleRML?)BU#YV|O6Uj&&)=Y25+zI9c*FV5YU#XXR7hpjEgQJ=M0BGOD27mQNK7#v< zhFz!+u~DtbVGg*5`(2Fjj%Eo8S+-j)bOvFwUxdOMbA}B{2p9#0>RrFHg+#k_>d;BV zu~E+(okxgo&C8}=8@~NIPzZQ&;>hV~+g7O63MEoqGN8d~OlJxlN7goj+%nSMaV?-? zr5pvI!fqQO3DU0#TTQMF_mg$%AUIaI4=8Z$Z29Q(i$;>frW$>R65~!FEw!geQcjRZ zFqI{kk)M+Cvsz`fIJF*xq+(606&GriS<~F*NPTW$uqfgOtGq5VEAVhTMS@>%QO{3N zv}q-$+I1Qh+QNW4MXsZ9B~@dWMsi9D+Z_c%Nbc{gNp*YARgxU}J{Y#%9Vl;zEQZk7 z;-?PvlN_T6!I~vm>>ALRkE_7g+PRHro4)f>aq@60ZaDtxON^ zOQ;F{i)D%STDUbNFAQCEgg3l87jNvwz9sHYo(cZ|cf?t6$>6+HlUt{=jY?akPfkHnJ}d^XR+j|2s?qyJ=Nf! z_d17Dm<|eNGmB5829Eoa+^H&_8l~3UQsU}rNyMqIGWMp;y4$Ux#+*vfuoRUff)oyr zK^(^|cgefUdlbZ10^rj@VUjg1ej59WyLl>?a;A!fInb9tDNA<69bY!*8(9 z3BtF*k{6saNH`FBETIEhIYEPpH4x{|DO8&KXH(w+-ioH%m^*qcr9T>C1;#RS@ z0czDIWfrL}n=v3i9n8hysPuw7#9RA2(~K*O{KY3s+3u|#gJ3t}JNtB4y|-e0DPRYR zbK^LC2a(7&l^OVwUyS=|ZRT84ZXsIIv>XUFqBFwO8$AdGUDd?Oe3~e31yeov140+A z#(vaV1wsu*D^j6JL+rRQO3-l~?rhkQBE2R{$!VsNRJEw!5(TgI`hN4x4YCy61{8P} z`c{0k_uHaLroHBnucDh%)tv$wvBwr0_ounbM4Z@EXx>}Sx$FQwzW)FMqdJ`_5}`?P zhmomhI2&Djw5J3|ugCuYx@A927N=C{l{lhA8I4G6_)VOBM`7=4T2PXu5C9mGbYX0! z<-#lkKT5k#CHZ<%?9rvRe|NQZefOk1|_$?h_LL2kW!X^>tR zv7TLcZ9_vik6n_GkN_9~pbKRo@4Fd8s-hA&MXp0pkQ1$LAqi^NVv5}c?$T)}z=)Ah z+KBL%;j7ksN&TTNc%@zzE0Np5ijcwYG=}YV+v7-cU`j`KK)m&duK~) z^pk73mMSdtp^CG8qW71jOsLv4sM6R&@tRRf}&0_Y{Xa3Y^^- z9YTI4)3Tj%p<|6xhgXoo6u*9}RdV70S}9bG&dFEwBG=<6x0bbxSyd+r4=JQ^8v|wu z?y_@uq21fp4s_f^a+S2|z{H>|a;<#w!$gHmahepNE^Da43kf-l{PIn*33XLiy-wDp z0;Vavbd%x9Unj)5L%PECT?(Z&CTT`s|z z{B`t}mQ#-WEltY(@P-0jax}6309n7S8Mcd;S5h#3fR9NJk?tbHE)>3+JfCegb1qN- z;!$5a-AJ^YxzrRt!qDTkcDU(x;1Oy0S-z6NN+q=z9)?;U73W%^SE|&PojQDp2cv{> zOT%zH!t0|pl7rJgdv4SSP zk>+ti-*u|LtR;Unc6MB+vEgJm<~s9~RgN3`D_BAnv#B7HxLM~^^Q&=M)S^#wPNdZ; zj}~A-eoKI2QqyH*4W|oLDD_HAj@PI{dNa6U5aJ3+8%EJ+(chIJnCv#E9d*RE+7qox zS-=6j+Th(LE|u4TOF%(*4iZy?!+m;Gd`*;!R7zd+tTh!Gt-9!Zr()hnTD(C*Z-b=6 zP8!jj?VRg0jK-Tsl8`#PoTxY#eJUeQ5eyIhFnQfcS&$^3`eb$J?rx3Sm3eEiDU6)Z zvQ+DyLuutI)H)3=g8`^C=;>*s0g3RIe5Ia~Y`)KkYmTZG)f>O_)c`Y;(V$8J=Q<%{{YxWKLC;d7?Yq{l@xIBBMn*YH}D3W5%BRj(#mZqx{?6or}ch~jY+ zhLU!ys@ERr)^d}2-hOk=76QYy310@$=Qf27EC`X}MP&^z!(q0YNWdj27yvv^l`qG- z&;7sq2U<>{3kYT-t4po2me$!y zb%iIfaqc}{`Bl>f3UeAp3X>&6h{`Q?F2yG7QR7nSt!A>;l`QhzS=P0kr^Ojk{Clkb z0Neinu#SIo*#-G;VWlWa2k{(!t6Ye5U}F?gko)!OsTzj8dWx& z+SsRKipsP&{u_2C**?j2a{0+ipcu5o937ak?r3;wwc5Khd$UVsolA@wWg1j`Hx<&g ztEBj+DyS*HRa51qf(i*k2||e zHQKH`TTXupKvo)X0Cd|Z5h!Bg)D2Chh-MiKDMOBzu)(~}!%Vp9utEv6YH|^Roxw+E zK(rXhiwZP$+d>f2NkBpX2~Go01I1Ze{CcSR4E+Jm?s@+J4Jd4Md@V{wWiASKM{nWX zUoXbG=la)vJ>Av1bFVy!k)gcO9c7Lb2QdL!K*JGg)z+`j)AZYc4>)K5>CTJYW*&w5`=wK)W#8e40S+BYW6QyNZ}2~p}6N>YJ>QUKU3 z9j+PnTJqME^_)CcmIlHvyrbo?!T$i;&+c;c&QI>h_u3c&Bw#fLP(9t%>rqwcuM_&l zK(;Sywo-@$AqXyN!(l>E6YP! zm%XEhE(1#UsXYGZxrIUGc}$;i7Jy2i#3xy0A4j^pODnt;m7QvxAfH;#caaAIZ!JMR zCZ11IiS8=beur6WGNVsr@rD4Xafan<7O!MUg_Su*o?Abde@OPltCvFYP>JpOdx&rI z$>X+vi9Pyg!S7J+GE)f5zqCa7Ev?5?e|Ca=SC=^1wOTZuha))O^3{*^^8gBn0Afy| zWl}20XNFZeP;bjEC?~JKUDtRzylslkN%f@Kj;!iT1OEW?wmOrCbyrfCR;Z~*NtEe3 z%8z%EhjTB2)18KV7gO&_yQL*6NlJhqj0iTa5e_@mJJ4LITv^OY7n^KZx3eky>*hE>QNoj>q_5sNz|pNjl}nPDSLyy^wjh?htPkPPkE`)X;o%;DufnfC!;+e zl5gAD3(88|xaqedcO$XdQ>R$aNsY zx=zhl!E>c4W6*DiJXctzF;%6_fcWA_X-Fq#I*#ydQ>M$M)FnZd*+a}YP_etDd5=eX zyZJS}6}-m;Ue%6&EdD6!PP1#hl8d!T2zE*g`2)guaR%gBDM}I&l!T-V00RIAahC>x zqtadJOtPP89s1$Q?VGt7&p*TkcP}iq%c~mFf=8>oTqzx`eCRH1mV|qJPWfHQ&C{*t zxa1JS-7hhNX4Tw_*CJ=ROOV{w{DbmtT6H>oIr7BVj+c5sTZLE#r%ZCBLqo6 z_b7CWSF&w-=&D&<6-i`jdN?i*Sk^2-Ng#{});p?Q*Ss}iJ3^HuW0 zZ)UEoIk@TRX`hr`s5chuP_q{j0#xw)yT2&r@J_E+cb2d|^=q_PW%iD=IFPaLwPPc< zVrWv~IP@2GB=C*(D%EU_UdUxeCjm7dFQ7@xUC zrEP836K66vbJVO-W~Xza$;jQ$3db4B^A|;!$yLW^0%?nI?OV=$L|PTryKrzZXwaHa zIu;s9S6S~_ZdMm+oQ<^3td87pG&d{x_Gcuw z9kZqj?)q=9`bf1l>)qN?f}ql3vazYafLHAuK0nKX@S{>uwqZR;@7W`jz}}M2L-Cv* zwDG%}^ydY1D{99KV-)*G4)c@geYbMxPGiZ88vao&{>o*^ctoJ1J6o@uHw5?Vi7U0Q zbrpMLhW`MVGdrB6Ihk^*+D0TYI^9os$h^)P=(^A5COD5#0e`ZYby?xA@-Wni2dtyN zQ{?uf_;Pg`Pgi^?I0fGD~nLNU=IpoK=+)FP7$w*ZFEVB>s>>H{-4=Qx&ATJztH~x zS?~7|?i%P(RkAQguu6N3uKp?`{cB$ZcblaPJ*WzX@Bo&JaV z&wsdg{^-*#Nn`SS`HjwHgM|t;- zV@)ATac5Fek%=bRL&_!7uTv3ArOq;652eCV+JXQ;Ju2@YkU<9nYBc`q_IDpW^y#&JH%yZv+iV(#SpEd=2NZ8_qA&c(Po#xey2T~V zO!hr$1LJ1jVc)^Jg?c0(*cTjawC62BBWXBU+VvftT-N^phmZ7Z_3)*mLJ;$tfCJxW zZ?d&D2oE}!XK#I7e2xjfML%twuht#qO>lZRC+Oj4Wy*K(vK?QfqI(yjxA!_H0@A<4 zJP1$CPfEFkL3#6}tNx8bOX!-AIbH!O=ac`I!U1l5tTKr7>BuZ>X}7KTfEl&&N>sCl8c?st!7mK#8edP;M7L}xsQb7k{7KJ4;;zy93PBxWpC%?5KLnjuVS`W=p zPr}SzTFW>N+A9O0U@Fn?v*Z3EA{XaEi1!?A8un4$(Bj-^{N(+Z*@Ar?s^hp!&Pj$(miKy%|SK3(@GWnWaiffk_~ZL})gPc&hQR z0!eWr!E=l)4*E1eOt{4-%WUKBHi=ZH%>%XMAxFu_1Jrc*q>u(A;03BTt_)mIz~S8U z91uNronEQU#4YlK9|0Uu-$@p}nMHZ$9jDRV%xDsqB&U?WLtwEbM3QkP(55*N^tz48 z8%3hbVpDnTFCC+#9{pLB+Y?p?CB7i_YqcJiCaqG91%bzPU}Mmc(xbRq3^pLcj}e66 z+byLD9%0$8Qdz80Wx;gd9YzUPlA)z4^&3({&}Yi0$WCL4Sp_GdXwXM`xsnT2jtimY zl%H7Rb6`jyf{J0)Z4{QpMpa8Cr{+p)425bntehv1`E%x0nj>B zb_-Q&(`qUyD&X1ZZ71WmzvULAA`RD)SR^U+rIMl0mFw;rb;gz!+R9b58!>ofpaAP> zbIv%X(d&@jP)2KsQPeYW>cbl^V@lm|?GIezg zDiNqlZwYcyp(k5y?B08vq+N#!h0OEH5P-wi)#vk$W&q&m{V(4mM>@fy~YB&kFIkV!VH)ZM8}TdL53p|tEz zFsajGRUp3(TY2p|Fe9XFBViV(P_J+{{_F2o=HveWHq2zI@~n*a zA-BE4ege^QK=IQmu937h);{t@p=7q~$H2YY5>L-wXzE55r_rf2(S#@rG3P=6QQmc) z#EN_wUx$j*o^>P>*J)Qaj`k(AxXJm7#C0muu2n^VRT;!Sot%YP<{n|f+s!StjcHII z_bXZ(N|v<%k*u3lY9?c#CqroD`TX|D{l-(OX0KCg2F41E!n`euS9Cxif-whJwePxe zXcAniQxTmBXBS!g(Ld8@br{mq#9j;OLC|}}(4oB?s&s@DsY%UgCs+qsxz*OIDlEjt zTE0c)Vd$*r2gG$?rBdev>ntzf&oYx;Vx3Avbhd@clZb4n1Hq(gU75TxQGj){ug*B8 z$h>j9CR`h^HaChNLr(P%w$9!)eo251)f{ZM=Tsg;e0Nr*0EW^r#GQaz=VaY6)ZFUI z5~nq{7g$f7ec@~;MBJcap!S@)g^$AQE?z|$HZg;FgCAI*G-mlMl39n+O~Y& zca;a7a`ifu>TWwpM&dKOAQcb*#G6pEbzZ6zIQxC49QbaIIoyj~`=v&pxBeBJ=ZNDz zy(>Rp?{|*mQLCX&tp$l)54WuPW5Ts|;ob^4Qp*NI1Ml(*#k~f_dii;y24RJHo zy$b+y%d19PuL;j52GD*;9|>D`4-!d^#HLis&80{>!L?fJN{UqMP+n(ehM%{cUdxjP z@$hb!OA=d7YFz_36zov3$rV>WMNTee+!bgUB7lWsHm2*pHM9o(=Q`A@u|lL^VL)vQ zACx?0`%1+3`n}M*)pDgufPm@zt9(W8B|7alBr%jpg;jAI66yl|;EN+9HA3bKsm2J} z5~6K&?wkv|9mFuDI`g=2uPZ?~bOF#g(h#>6lpzI5IsgL_O{n)xM5XED0jx8R(y|hi zBm{r}7yvv?QM>f%9UnR8&d ziH!2vI}aT;m33;&Vo-!Uov1%i!p|`K3e9M{&h`|lxM-l zC_;}*XEWR^{mErOmL@GeWsL8J7Jlee+R}jAqoXxgeJm{fDx}6dBsYNDnn>#&HA+;X z0#ZrXEX5M6{{Z0OJH10U-mEOe&Z#-2*BNYY&NgD@!$z0oIN{ik5-lEuNsWeNG!i;h z$3FW20Kg0fjoynH@q@A3U!;()jh?%7M~0YzCu&qv?iP0DQ zv+3f$rHz_priKl+lnsdhe!;lJcSp)H+Px%$Wzp#i8elQJBoBp`b|{Pg04av$s9ASD zlC4U8JC;7MY`c>XrxZ+aJHR$_<0(2*Py}Pr-Ia2t_%hi#I&+1Ub85pJ7Qx)3^@}R% zRz{|fcPv>~YPOwlkEv-tU5x^;G5-Jp>D)1~qt%D`2?ujz*wbn$I2cj}_SIt!VonA# zqk0XM9rWvEV|qxjX}~ckP#e3)1n=h8)nvzztxT-$Nj6L=s^rCZ?m;#b%96D~kasq# z3N2iuAGz`{^R`8kK&%s@6Q1E@-J0UR$&tNi`)rG)Sy$wcH>_DVHmG&G9DfqN%Od4V z@a42O0&JU&qZQOgsTLP1o-oE+>f8csi=jtI(q3NmeU@(RQBkTGJJh3OQfY1Bn+7=k z08N`|rp5?uDck_@AOFMvHW2^<00RXE1O)^F0|5X40003300R*OFflSw5E39EKtTl) zagicIP*Q;uLsMZDGjgG^!T;I-2mt~C0Y3n`V?hLy-(^m)9Ke{TsV0zS2r~?CHnCHz zBUjg@n0byMNWj3EtO5^6T|^friA@OUUp2raK$))%L~ky!zPiVlvE~T#1bK@a>#T1L zL{w`P8o`*bW**QBw%BCwThj3iFtZQ&Gboq`bn7m5?&{ynTYx#X6Ytk zK8TrzNcpeTNq~Sw2HFiEqUi=$)(SAaT*IR#f(faL4pW3V9TiykxfASa> z%Ru@4z=3a^F$!RKf$uN=;CO<4hCg2~>}dUbzpNFH_w>fIij7Dja#EkqUK&} z=FD#<24Vt&W67KL#K6JM@FM+Hn9)Hd1(MK4NO^9Vl!#mhq$W{wFE#PR-YB3O9$HLV zFsxc687-(d8`%;XbJMWI#JOoLXrh}LLkba56J+jlTp(d{FB{{DyHQpU^FZV=ho0+9 zB2@L6$nr5ZSeS)OFe*qmi?9hG2$LvEmKWY8&slu2I!Eee{{T@c(ini;j5R23rT4yU z#_qeQPP--_iUA;kB2(|6Xtt*8>ixp*#+%c@`$b}7ghJ3sB$KdWAzrfXrQmO$HQe4s zMuTBBBz)+PQ=+)V#OTF}gukiMiroqV2L~Z%DWQ z0I7fFcj5UT)-8Ca=W1pUo$iw;zH?SLQoF?A%ovzz-kWTTL}RIs5O4Lbsk`v@=P`W@ zT9&8|hiYaJm>Ze7i5scjbdAeP((bWd_;bce&Yy=f{G&3I7*kx zF~f)>7$A--Eb%f}2QbDR6~!1w=@;%XE>cUtL%2mG!kUWQt^)REYq!i}&UsS-U&z)a zT4hWbsB|$=6K!&kay!TRiIW;*7GuwdMBdy;?2H*tIFoFa;08OU0R&dDGSezp=G8g~ zGg@s4{0NnS>81|%b4M^@Zg0iVW*8VGo0LMv6;1*XcSXYH7wnYgFuGw;NS>rqqe&#= z2_y`Cpa3pqj&*(mch|8Nn2%07gBb zZ0^vOWn4{600tLIq`(v%5ta-TXdsM7834md(_vLOh%%N8li0yl7>*zbg_L`k2%`ZD zD_8&vpb5Z0m#Em_d*6>->k>KgX}~A846p zl$?W-U?>vdRvukBhyt*7s_u$F5y~nd2tPsd zIsX7xGY=p()a)V~mm#)Lt9o#QLs(J>>*g8?Y)0&E7}W7i9QMf{2|PfMVOSoy&&9P| zjz$3ofRG`cyoMZecFV{v1i~?Z>U+gT(&{_FH61%81{K;s{I$U~F+lJZ+fq3`Yr2aS&zbV1h9ldyR|J5DXc581mXY zwt+8G0YKn<+;cOXgAsU9Oc{C@^4bEJ0-by2JacnwT*HNMA4+`3j&RGdGcT_UyuV6p z$Bt+u3J&qQ+89M`;mGjI?mm)cAjN?E!w^`J$1gNmtt}&RiVbTPYeP>-l>k#t7Mdj~ zatQ(lh{)04Tde;8arS|0v!C1$CrUU>Uw8(+abTjPfe&o6ug_mjaNZ)HEV6w~A-qCy zz$k{bS{oUM@yZLkV&wXmvr7*&R{_rAUvnNJ)tZZ<$A>q?F?9g&y6-D4q7NcR03={Y zg|tsxCNwR&&^v?bA$&;u(#PP!TjVx1Usy@9-z$UD&9=#x$Q|RLiI(ZjmWn^9W@a^J zC)b8dmz>+H`8q_aB1#6F+gpXArB0PGI$@bL-%}QOzEtFH-Te$UCx+6yvzz8{{YDCvQG>Ir#!|}{{YS#Q-+(%u%+a=k1e;9 z(k%1D>3i552NxYXPY|MyENU$o=rW&0)bh9MNnVkWL z@abnGk?YPL!98%HhLbrnOJUWjvt?3!1TBux-z7a^JUE>5J%lBWLVUG4m;#+sOP1M? z7;7Dz{@5Os5PUjG!I(kt-6s2pJ{>o+tQ!rZKz}f7c9Hvk_9OK3{{V@-pvjssmj1>H zIQa}wv^+FA7~Oxgz?C%ojqL(N^ZQTiU|DSk_RI?vr2K$>L=O+1aVj8qb9b0Mpm=P) z?#33wXCEm(rWVg7aF)K%1uwtH2m=8D000000RjU65d<(X5+O1` zQ3VhaAVF~=QehM`LPJnffsqw*p^~xx+5iXv0|5a)0LoR`HlV6B9mQ1YD8#g4Va$L{ z2$=mSLbMS_rEIT3PMas`%EC7j<|+=Nnl6TjBVJHO#)L{W-#`K7adkYbTmvA0brjKC zPc4s1*l(2AhW#tTdhna5uL-(}@SeOk>02$()sIR5pF=`6G$9_e6b%MT3o#s|TmvYm zI*Qm%m9o7BSaR}@iz-?YCT0Q_Jz9TP3fGR3+8lf}@xdN zcSPS(E3#w@h-5#Ff9UU&UN-iuO|G4Hg#r9&r&BDeQZx;iT%x{KW+(+Lw9vry%7QW7tL_>AldQ{js;yfjXQC1|{#-PB>DFls`>S`#GBnqQ< zm{gh%G?Ao;RfWN+@H0k9QMSKLXo3xhH5L+iPUW!bX<$fhqkmIV4dC+8Hlr>U+(kjb z{N)B=>qL{1@&q4RX9DD!ciK4)Y^U2jBnNTVDOjlnu+9i0lah5&xQ}{y$veNZM3bq> zmA6}vSxDA{1EI-|MvZ|KxY&-Ng$z^&@zThRqXYc;Mz!KEO7Rz^c#G1R!5VoUc#~HY z*yzTJO+G3|^Z-#L>T;dJ(IlJlLRK2@p(>gL9SF)dl&Q8{*YM4%0!(}Q@^PiyHTMqbBlQ^wyCclLxh$cDjWQL= zVxW9lQh)%qoSZ_*-tmLHYeh)Zc>pItNgcPHeB)D=(F3OHF|hQj9`KBPD2sQ@uFjwh z)}Sl^S-9Vf=4KvBioQ(LHJL%ah(HL0%@W~ zZKHlwI9J;}w4tXVJ8Z^HBlwDUTBF0LH{N+<;9qR^)6V9~O$oY+3=W4PMGUqyv8s(Z zc=!f+)6V<{yh_7e^dwW}Z9AJo%S3k(=4ljBm5nb8chr z`c!-aI#Kcdl^+1}k_B9KtB#|~Fwp*z0XhRnETDPi;U4uAVeh|6WC-cM%E`7ZUuS-z zj1qLC6rU-gj5Slw*qRW?eJEkis|pY&sV0O`kHbJH&JgUh5Y-%3~zYrcR3LFIP{E^Edmh4i3&?n^x)?5GtY?I9F~ z>O8BaQ$~%d(_zoyG(iT$a&g2I0(HomH)aHH-Ci7@DW!^EYE(AfYh=}k?{={I)G^!Q zKgWBiQdnv0K!fC6GZ{k*5mF&Ww<0$YK!yN}*;uL@P?5*229b@0BsMo~=gRQJG4!AS(0RxQQAQa0Pyh=I#&umyJml_O z+^V}ShK3=%MG4UBaY4}N!}TXSKUXiW*EABu({`U?9`RiTJp zhJ^9;HR8wfSBW3YD{VeuS6-sGb@N