From 360d42e74b8ff29ec50dcc59215e9e9927690ec2 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Fri, 5 May 2023 17:10:36 +0100 Subject: [PATCH 01/41] Project setup --- Gemfile | 5 +++++ Gemfile.lock | 26 ++++++++++++++++++++++++++ app.rb | 8 ++++++++ config.ru | 3 +++ spec/integration/app_spec.rb | 0 spec/spec_helper.rb | 6 ++++-- 6 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 app.rb create mode 100644 config.ru create mode 100644 spec/integration/app_spec.rb diff --git a/Gemfile b/Gemfile index b1a320395a..866f834e6c 100644 --- a/Gemfile +++ b/Gemfile @@ -11,3 +11,8 @@ end group :development, :test do gem 'rubocop', '1.20' end + +gem "sinatra", "~> 3.0" +gem "sinatra-contrib", "~> 3.0" +gem "webrick", "~> 1.8" +gem "rack-test", "~> 2.1" diff --git a/Gemfile.lock b/Gemfile.lock index 66064703c7..58a913dabc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,9 +5,17 @@ GEM ast (2.4.2) diff-lcs (1.4.4) docile (1.4.0) + multi_json (1.15.0) + mustermann (3.0.0) + ruby2_keywords (~> 0.0.1) parallel (1.20.1) parser (3.0.2.0) ast (~> 2.4.1) + rack (2.2.7) + rack-protection (3.0.6) + rack + rack-test (2.1.0) + rack (>= 1.3) rainbow (3.0.0) regexp_parser (2.1.1) rexml (3.2.5) @@ -36,6 +44,7 @@ GEM rubocop-ast (1.11.0) parser (>= 3.0.1.1) ruby-progressbar (1.11.0) + ruby2_keywords (0.0.5) simplecov (0.21.2) docile (~> 1.1) simplecov-html (~> 0.11) @@ -46,18 +55,35 @@ GEM terminal-table simplecov-html (0.12.3) simplecov_json_formatter (0.1.3) + sinatra (3.0.6) + mustermann (~> 3.0) + rack (~> 2.2, >= 2.2.4) + rack-protection (= 3.0.6) + tilt (~> 2.0) + sinatra-contrib (3.0.6) + multi_json + mustermann (~> 3.0) + rack-protection (= 3.0.6) + sinatra (= 3.0.6) + tilt (~> 2.0) terminal-table (3.0.1) unicode-display_width (>= 1.1.1, < 3) + tilt (2.1.0) unicode-display_width (2.0.0) + webrick (1.8.1) PLATFORMS ruby DEPENDENCIES + rack-test (~> 2.1) rspec rubocop (= 1.20) simplecov simplecov-console + sinatra (~> 3.0) + sinatra-contrib (~> 3.0) + webrick (~> 1.8) RUBY VERSION ruby 3.0.2p107 diff --git a/app.rb b/app.rb new file mode 100644 index 0000000000..1dcd37d299 --- /dev/null +++ b/app.rb @@ -0,0 +1,8 @@ +require 'sinatra/base' +require 'sinatra/reloader' + +class Application < Sinatra::Base + configure :development do + register Sinatra::Reloader + end +end \ No newline at end of file diff --git a/config.ru b/config.ru new file mode 100644 index 0000000000..4b0efb1a26 --- /dev/null +++ b/config.ru @@ -0,0 +1,3 @@ +require './app' + +run Application \ No newline at end of file diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 252747d899..a59a8be08f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,7 +6,9 @@ # Want a nice code coverage website? Uncomment this next line! # SimpleCov::Formatter::HTMLFormatter ]) -SimpleCov.start +SimpleCov.start do + add_filter "spec/" +end RSpec.configure do |config| config.after(:suite) do @@ -14,4 +16,4 @@ puts "\e[33mHave you considered running rubocop? It will help you improve your code!\e[0m" puts "\e[33mTry it now! Just run: rubocop\e[0m" end -end +end \ No newline at end of file From d14ec56c13d2e3d98c04b53b4014677202685c38 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 11:19:40 +0100 Subject: [PATCH 02/41] setting up the chitter database --- design/two_table_design_recipe.md | 121 ++++++++++++++++++++++++++++++ spec/seeds/accounts_seeds.sql | 15 ++++ spec/seeds/posts_seeds.sql | 19 +++++ 3 files changed, 155 insertions(+) create mode 100644 design/two_table_design_recipe.md create mode 100644 spec/seeds/accounts_seeds.sql create mode 100644 spec/seeds/posts_seeds.sql diff --git a/design/two_table_design_recipe.md b/design/two_table_design_recipe.md new file mode 100644 index 0000000000..cbb2f520ea --- /dev/null +++ b/design/two_table_design_recipe.md @@ -0,0 +1,121 @@ +# Two Tables Design Recipe Template + +## 1. Extract nouns from the user stories or specification + +``` +STRAIGHT UP + +As a Maker +So that I can let people know what I am doing +I want to post a message (peep) to chitter + +As a maker +So that I can see what others are saying +I want to see all peeps in reverse chronological order + +As a Maker +So that I can better appreciate the context of a peep +I want to see the time at which it was made + +As a Maker +So that I can post messages on Chitter as me +I want to sign up for Chitter + +HARDER + +As a Maker +So that only I can post messages on Chitter as me +I want to log in to Chitter + +As a Maker +So that I can avoid others posting messages on Chitter as me +I want to log out of Chitter + +ADVANCED + +As a Maker +So that I can stay constantly tapped in to the shouty box of Chitter +I want to receive an email if I am tagged in a Peep +``` + +``` +Nouns: + +posts, time(and date), content, account, account_id, email, password, username +``` + +## 2. Infer the Table Name and Columns + +| Record | Properties | +| --------------------- | ------------------ | +| posts | time(& date), contents, account_id +| accounts | email_address, password, username + +1. Name of the first table (always plural): `posts` + + Column names: `time`, `contents`, `account_id` + +2. Name of the second table (always plural): `account` + + Column names: `email_address`, `username`, `password` + +## 3. Decide the column types. + +``` + +Table: posts +id: SERIAL +time: timestamp +contents: text +account_id: int + +Table: accounts +id: SERIAL +email_address: text +username: text +password: text +``` + +## 4. Decide on The Tables Relationship + +``` + +1. Can one account have many posts? YES +2. Can one album have many accounts? NO + +-> Therefore, +-> An account HAS MANY posts +-> A post BELONGS TO an account + +-> Therefore, the foreign key is on the posts table. +``` + +## 4. Write the SQL. + +```sql +CREATE TABLE accounts ( + id SERIAL PRIMARY KEY, + email_address text, + username text, + password text +); + +CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + time timestamp, + contents text, + + account_id int, + constraint fk_account foreign key(account_id) + references accounts(id) + on delete cascade +); + +``` + +## 5. Create the tables. + +```bash +psql -h 127.0.0.1 chitter_database < spec/seeds/accounts_seeds.sql +psql -h 127.0.0.1 chitter_database < spec/seeds/posts_seeds.sql +``` \ No newline at end of file diff --git a/spec/seeds/accounts_seeds.sql b/spec/seeds/accounts_seeds.sql new file mode 100644 index 0000000000..89eb59f73d --- /dev/null +++ b/spec/seeds/accounts_seeds.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS accounts; + +CREATE TABLE accounts ( + id SERIAL PRIMARY KEY, + email_address text, + username text, + password text +); + +TRUNCATE TABLE accounts RESTART IDENTITY; + +INSERT INTO accounts ("email_address", "username", "password") VALUES +('alice@test.com', 'alice1', 'test123'), +('chris@test.com', 'chris1', 'test321'), +('test@test.com', 'test1', 'test987') \ No newline at end of file diff --git a/spec/seeds/posts_seeds.sql b/spec/seeds/posts_seeds.sql new file mode 100644 index 0000000000..a7962cab09 --- /dev/null +++ b/spec/seeds/posts_seeds.sql @@ -0,0 +1,19 @@ +DROP TABLE IF EXISTS posts; + +CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + time timestamp, + contents text, + + account_id int, + constraint fk_account foreign key(account_id) + references accounts(id) + on delete cascade +); + +TRUNCATE TABLE posts RESTART IDENTITY; + +INSERT INTO posts ("time", "contents", "account_id") VALUES +('2023-05-09 11:09:00', 'hello, this is the first peep!', 1), +('2023-05-09 11:10:30', 'hello, this is the second peep!', 2), +('2023-05-09 11:12:00', 'hello, this is the third peep!', 3) \ No newline at end of file From e67e34feefe95df5c9cd03a14c38b93ac0ebe854 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 11:40:25 +0100 Subject: [PATCH 03/41] organising project READMEs --- README.md | 122 ++++++-------------------------------------- challenge_brief.md | 123 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 106 deletions(-) create mode 100644 challenge_brief.md diff --git a/README.md b/README.md index 465eda879b..7241690dbd 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,33 @@ Chitter Challenge ================= -* Feel free to use Google, your notes, books, etc. but work on your own -* If you refer to the solution of another coach or student, please put a link to that in your README -* If you have a partial solution, **still check in a partial solution** -* You must submit a pull request to this repo with your code by 10am Monday morning +Write a small Twitter clone that will allow the users to post messages to a public stream. -Challenge: -------- +## How to use -As usual please start by forking this repo. - -We are going to write a small Twitter clone that will allow the users to post messages to a public stream. - -Features: -------- - -``` -STRAIGHT UP - -As a Maker -So that I can let people know what I am doing -I want to post a message (peep) to chitter - -As a maker -So that I can see what others are saying -I want to see all peeps in reverse chronological order - -As a Maker -So that I can better appreciate the context of a peep -I want to see the time at which it was made - -As a Maker -So that I can post messages on Chitter as me -I want to sign up for Chitter - -HARDER - -As a Maker -So that only I can post messages on Chitter as me -I want to log in to Chitter - -As a Maker -So that I can avoid others posting messages on Chitter as me -I want to log out of Chitter - -ADVANCED - -As a Maker -So that I can stay constantly tapped in to the shouty box of Chitter -I want to receive an email if I am tagged in a Peep +```ruby +rackup +http://localhost:('Port in use')/ ``` -Technical Approach: ------ +## Initial plan -In the last two weeks, you integrated a database using the `pg` gem and Repository classes. You also implemented small web applications using Sinatra, RSpec, HTML and ERB views to make dynamic webpages. You can continue to use this approach when building Chitter Challenge. +![initial plan](/chitter-challenge/initial_plan.png "Initial Plan") -You can refer to the [guidance on Modelling and Planning a web application](https://github.com/makersacademy/web-applications/blob/main/pills/modelling_and_planning_web_application.md), to help you in planning the different web pages you will need to implement this challenge. If you'd like to deploy your app to Heroku so other people can use it, [you can follow this guidance](https://github.com/makersacademy/web-applications/blob/main/html_challenges/07_deploying.md). +## Approach -If you'd like more technical challenge now, try using an [Object Relational Mapper](https://en.wikipedia.org/wiki/Object-relational_mapping) as the database interface, instead of implementing your own Repository classes. +1. Set up the Chitter database with test accounts and test posts so that the homepage has some information prepopulated -Some useful resources: -**Ruby Object Mapper** -- [ROM](https://rom-rb.org/) +2. Create the homepage using GET request so that the test posts are shown -**ActiveRecord** -- [ActiveRecord ORM](https://guides.rubyonrails.org/active_record_basics.html) -- [Sinatra & ActiveRecord setup](https://learn.co/lessons/sinatra-activerecord-setup) +3. Create a sign up page so that details are added to the database and user is registered -Notes on functionality: ------- +4. Create a form to add new posts using POST request -* You don't have to be logged in to see the peeps. -* Makers sign up to chitter with their email, password, name and a username (e.g. samm@makersacademy.com, password123, Sam Morgan, sjmog). -* The username and email are unique. -* Peeps (posts to chitter) have the name of the maker and their user handle. -* Your README should indicate the technologies used, and give instructions on how to install and run the tests. +5. Once form is submitted, return to homepage with list of posts -Bonus: ------ +6. Add a log in and log out page, which checks the database to match log in details -If you have time you can implement the following: - -* In order to start a conversation as a maker I want to reply to a peep from another maker. - -And/Or: - -* Work on the CSS to make it look good. - -Good luck and let the chitter begin! - -Code Review ------------ - -In code review we'll be hoping to see: - -* All tests passing -* High [Test coverage](https://github.com/makersacademy/course/blob/main/pills/test_coverage.md) (>95% is good) -* The code is elegant: every class has a clear responsibility, methods are short etc. - -Reviewers will potentially be using this [code review rubric](docs/review.md). Referring to this rubric in advance may make the challenge somewhat easier. You should be the judge of how much challenge you want at this moment. - -Notes on test coverage ----------------------- - -Please ensure you have the following **AT THE TOP** of your spec_helper.rb in order to have test coverage stats generated -on your pull request: - -```ruby -require 'simplecov' -require 'simplecov-console' - -SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ - SimpleCov::Formatter::Console, - # Want a nice code coverage website? Uncomment this next line! - # SimpleCov::Formatter::HTMLFormatter -]) -SimpleCov.start -``` +7. Send an email when someone has been mentioned in a post -You can see your test coverage when you run your tests. If you want this in a graphical form, uncomment the `HTMLFormatter` line and see what happens! +## Changes to plan diff --git a/challenge_brief.md b/challenge_brief.md new file mode 100644 index 0000000000..465eda879b --- /dev/null +++ b/challenge_brief.md @@ -0,0 +1,123 @@ +Chitter Challenge +================= + +* Feel free to use Google, your notes, books, etc. but work on your own +* If you refer to the solution of another coach or student, please put a link to that in your README +* If you have a partial solution, **still check in a partial solution** +* You must submit a pull request to this repo with your code by 10am Monday morning + +Challenge: +------- + +As usual please start by forking this repo. + +We are going to write a small Twitter clone that will allow the users to post messages to a public stream. + +Features: +------- + +``` +STRAIGHT UP + +As a Maker +So that I can let people know what I am doing +I want to post a message (peep) to chitter + +As a maker +So that I can see what others are saying +I want to see all peeps in reverse chronological order + +As a Maker +So that I can better appreciate the context of a peep +I want to see the time at which it was made + +As a Maker +So that I can post messages on Chitter as me +I want to sign up for Chitter + +HARDER + +As a Maker +So that only I can post messages on Chitter as me +I want to log in to Chitter + +As a Maker +So that I can avoid others posting messages on Chitter as me +I want to log out of Chitter + +ADVANCED + +As a Maker +So that I can stay constantly tapped in to the shouty box of Chitter +I want to receive an email if I am tagged in a Peep +``` + +Technical Approach: +----- + +In the last two weeks, you integrated a database using the `pg` gem and Repository classes. You also implemented small web applications using Sinatra, RSpec, HTML and ERB views to make dynamic webpages. You can continue to use this approach when building Chitter Challenge. + +You can refer to the [guidance on Modelling and Planning a web application](https://github.com/makersacademy/web-applications/blob/main/pills/modelling_and_planning_web_application.md), to help you in planning the different web pages you will need to implement this challenge. If you'd like to deploy your app to Heroku so other people can use it, [you can follow this guidance](https://github.com/makersacademy/web-applications/blob/main/html_challenges/07_deploying.md). + +If you'd like more technical challenge now, try using an [Object Relational Mapper](https://en.wikipedia.org/wiki/Object-relational_mapping) as the database interface, instead of implementing your own Repository classes. + +Some useful resources: +**Ruby Object Mapper** +- [ROM](https://rom-rb.org/) + +**ActiveRecord** +- [ActiveRecord ORM](https://guides.rubyonrails.org/active_record_basics.html) +- [Sinatra & ActiveRecord setup](https://learn.co/lessons/sinatra-activerecord-setup) + +Notes on functionality: +------ + +* You don't have to be logged in to see the peeps. +* Makers sign up to chitter with their email, password, name and a username (e.g. samm@makersacademy.com, password123, Sam Morgan, sjmog). +* The username and email are unique. +* Peeps (posts to chitter) have the name of the maker and their user handle. +* Your README should indicate the technologies used, and give instructions on how to install and run the tests. + +Bonus: +----- + +If you have time you can implement the following: + +* In order to start a conversation as a maker I want to reply to a peep from another maker. + +And/Or: + +* Work on the CSS to make it look good. + +Good luck and let the chitter begin! + +Code Review +----------- + +In code review we'll be hoping to see: + +* All tests passing +* High [Test coverage](https://github.com/makersacademy/course/blob/main/pills/test_coverage.md) (>95% is good) +* The code is elegant: every class has a clear responsibility, methods are short etc. + +Reviewers will potentially be using this [code review rubric](docs/review.md). Referring to this rubric in advance may make the challenge somewhat easier. You should be the judge of how much challenge you want at this moment. + +Notes on test coverage +---------------------- + +Please ensure you have the following **AT THE TOP** of your spec_helper.rb in order to have test coverage stats generated +on your pull request: + +```ruby +require 'simplecov' +require 'simplecov-console' + +SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ + SimpleCov::Formatter::Console, + # Want a nice code coverage website? Uncomment this next line! + # SimpleCov::Formatter::HTMLFormatter +]) +SimpleCov.start +``` + +You can see your test coverage when you run your tests. If you want this in a graphical form, uncomment the `HTMLFormatter` line and see what happens! From 953dc11beee7d786ece36a1cb5bdaeaa4d512af4 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 11:52:03 +0100 Subject: [PATCH 04/41] amending initial approach and creating database connection file --- README.md | 16 ++++++++------- design/two_table_design_recipe.md | 5 +++++ initial_plan.png | Bin 0 -> 203526 bytes lib/database_connection.rb | 32 ++++++++++++++++++++++++++++++ spec/integration/app_spec.rb | 10 ++++++++++ 5 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 initial_plan.png create mode 100644 lib/database_connection.rb diff --git a/README.md b/README.md index 7241690dbd..ee703cecb1 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,20 @@ http://localhost:('Port in use')/ ## Approach -1. Set up the Chitter database with test accounts and test posts so that the homepage has some information prepopulated +1. Set up the Chitter database with test accounts and test peeps so that the homepage has some information prepopulated -2. Create the homepage using GET request so that the test posts are shown +2. Create AccountRepository, Account & PeepRepository, Peep classes so that new instances of each can be made -3. Create a sign up page so that details are added to the database and user is registered +3. Create the homepage using GET request so that the test peeps are shown -4. Create a form to add new posts using POST request +4. Create a sign up page so that details are added to the database and user is registered -5. Once form is submitted, return to homepage with list of posts +5. Create a form to add new peeps using POST request -6. Add a log in and log out page, which checks the database to match log in details +6. Once form is submitted, return to homepage with list of peeps -7. Send an email when someone has been mentioned in a post +7. Add a log in and log out page, which checks the database to match log in details + +8. Send an email when someone has been mentioned in a post ## Changes to plan diff --git a/design/two_table_design_recipe.md b/design/two_table_design_recipe.md index cbb2f520ea..e381dec04b 100644 --- a/design/two_table_design_recipe.md +++ b/design/two_table_design_recipe.md @@ -118,4 +118,9 @@ CREATE TABLE posts ( ```bash psql -h 127.0.0.1 chitter_database < spec/seeds/accounts_seeds.sql psql -h 127.0.0.1 chitter_database < spec/seeds/posts_seeds.sql + +psql -h 127.0.0.1 chitter_test < spec/seeds/accounts_seeds.sql +psql -h 127.0.0.1 chitter_test < spec/seeds/posts_seeds.sql + + ``` \ No newline at end of file diff --git a/initial_plan.png b/initial_plan.png new file mode 100644 index 0000000000000000000000000000000000000000..50114b1a3f5ec13d8e641f736694cd509d7ac874 GIT binary patch literal 203526 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�J%;CwVlBJ3d4O-DWx&tt5K#`04lz@&u8WmZ&v#+50Sheac)x6; z!H8_*a2wC97gU_sXYqDAJx!@opZ5hfJvtFHkegtQO(oI{NezueTw;}iW5{a(0?T#KaR9*lyoPLqiFDi-gE75 zRGC?^M(C$Dp4JPZOZ8L*;vxL&NJ&<%J_NbUm_F}<=OCV?JA7O2!kwWTk)lKAEZW&v zy>(f0&yPLp!CXVe2C&|dwbUAPgW{tUz+m(}Nry(36ooggx-1Nq$^9&J{V+uDwl>EZ z;GOd>?EVh}R$a8|bqFK>j2J1$zW2KjrH~jJQ3s-JeX1VX$iOwWO25FaOcpaf4TTb&g6c(LR|Ju$HWRU?GtMdkhtq^+Ixq{WG ziDzl>qz=g9ix&Mvr=X~_KHJ9-;Cn~p(_Wmz$nqss zj*n*_aWXw~QKV!{G*2FVMZYIkiWH^c&QK_%4*EkxI}V^yQW(vNFY$cWZ`k=RJ)aq> zDA|F8S;IqecWBso=)8md(S8pNqRffAG<7S0PqopPPB8t0~a!A1}~}>LQUkF zsF|*g@6!cE!)zpE$vOhz<>3Vtzz1iI-lN8^`0v8<<+uyRC~~i==9ui=@lA;(tN$Um zmsUc}$w`v93;_p0SByT=w3Gq*NKCM63mj?q=`9oXkZ0H1E?@yFB!b*}D~9bl39r(H zUaTY+kP$%fTe@^U)xxWQLZ9zY?;)_ffIy%pgv9V&N$bvaJ*h{UKMq0zya)0bErubd zFBsPc;QBmg^4-^M&h>me&(@nPPkwILg*3D=m|lc9N#wx|!HE$3Sy$aEH^*g$2BI%F zs@#f}gzM>=R(Mu*^KU&Sl2_h0c6r_}(6Cr(vs~nG^O@MfjW4%95)VO}s>y<5SM+uV z3+A3`<&VGFSBT3aVW0gZeT2|HzlhHEe023k*E-Fu}uvsM1t zU%T?M1Luu>HXV_j>Do&3@lsmT`KC~YHFONCz~FND(k`Ns{74f`*Lk<4&p4epcJ=hY zpqSF)K_@00HoPvf-IE%#>TrOAQX{ZX^(HJHLNC4Qb)^5|N5Em92QULI%rSvV+9l(9 zv*sukt1Rttlvs~_f1H}+qXu;i0$v}N$9`+a4VL&jq#_Q}TRTzlKP|&tC!b-9;i%z! zQC@wDy|qfQSjF8?YfYk%<;jc6jl=1^(}dxNzJ51b(tg1)^3?c^{=MGWJW9;xkzbQ7 z!4NTeANl8^oI7Wb1LI2&9fH0)< z*SgC9qXn5v4Z+DTQv5Pm^_WL!`Ok|sI*Ad9qQ$_M$!U$(V z%%35))riD}As9&aF7Hk*k@`x#L(3(8vPTrWMGDCUskqPf`I{XjDR+0nQ@57)$<{Yg zJVuzjKP=z)6NG$EHQJ`1FF%VOc`}s?IT1G}w?Dz)6b!!bE%fK`j&y4ke^x>Jt%&d{ z?i|cv+w$pC9qKAjPq4k{|3mpQ%Te7fv6Erp=GuvN(&@dA+ESkSbC6!MrY3(FZdX^x ze#hi~-Ni$e%N?OHQwmBT)h9YEbI6$Z-&8=>ucG1MpA~~;^@#u7d&Ox8ooC% z7uo@vSZn7#Stkp77CHP(KK`gj5aPSxYCYvChr_(m3m;n%)_d(O$2Zh9pYSg`&+kV| z$0C%i@V=7V=cAC6&`II3GPyKYOILR6v&>fFi(>RoE%*0!A4D>^R%E+;w@#+6j!L%a z%V)nX&8d$6)t)W(^!;p#R|S_IO21nE_9!d%%pKW_*Eh!9L_F|bIj+1UO;yy73awyz z7~scO`SSr$(VZ${B2~pS4Kj4Qfn`8`5LGj^EYjH|+q~tA49B;(EKJXAJX_2!eh%@u z$@Hq5q=P>Hg;)$StIwiM%RrA$Zn@uH`mo zzmtE3?IxA=t?d$xl(kQWskpMnZ@iI?wi2B54HCEC7gFU639(|CdbBfE<4;!5wnnh8 z$C^BPf9PU~(u+64=e}JLx#D)M`>1Oh!^YSstwX7-b!fz{p0GmNLjxnO7bx1^Lu`+u zSvl|tvY}x)7$e36)0i#Mu?D|VHR$wC*|~j?z)-}pqwlsy5c2!s0IT>jWqsuSa_|i( zW9+lx{ep(v$@T8$C@R4>iPWU@_sV%stzP}cFD7^K>urZv z(r1;$*@hwEFRE;wzF$>&KJ##o%4O$IFw&nSfJ#eN^8-xZIO4qcTrwx))U#{2nd%Dd zI!Rd>13-#ROXDThPh3>4UpKK)(8YiH^95Qiv3a7Vby~;fl~hg>7rsF3sM&{P$kLzI zBa@kBiHIaHP7@n@biC-5rAyinKxeNyW>U|8+6?%ZU@7dJ^5chFr>|S@2Uq29jX~SBfZiD zWA6?Xf>d`R+@(6O(6DD|tS&mdXgL9_P)B-;XTy&n{N> z(9;rk?@2_H*|yRU*^P2p{h++>1051d53MYL@sM&9|Dvr*k*EVVKD238iq?%ljuZeA zL3kXuq0bgw!LLCE-eJOX-`oe=l4I|j%A%sjJUVK8$U8U#2COz@N$9G%VLn-Y#oU3h z0YtUZQC7WW!9Q#Izo95!PW}Mj^VFNxcj6V_(~kImemYqqK&^Zj->#!Rl8{_R%JGHm zh>|~oAUiJOjZt>No|?HUYjYJ~+BX93aOGo*zu8eYe+ujcxJ0d9j<p(s+QUZ3gCwjBPYL90iz+c^37 z-VvD#d1Z92UJ#aHh@%!Sfqa6~^v1p#IXj5va z91!E6mN%_!Q+gcu0}tu?-eKa{3^{%J$ok^B{aYgvpAF{}sI-oezxVCwJik)@@h5-Q z_Pbxw${0+Xw1HlY%XO`_s=CVSWgkz6Q`t4WNxs$P9iski!BaisL*n}P(My}l-BhNF z8SLsyhF_FEE0M`&TozLL&LG;QeULl+bc4WYICh$80Mhmgw{dz0Nlk?d1l)yS5A#59 zS>TN;#j0+JjWx|DzBHz#4vkemzrOk8GauvIF>8a?u*W{L{AekALhxz6 zHTjS&4-Hd5pw0Q#M_04O$7ng#_SAU!5!_IFJT6JRz~oXdalm&`wh^P#tjgxl6R2fC z*x#UZ(t*)IhMR-$l1S$`Bm%g#n(gasCfKklF#pk9{&W)w{?i>_wEuULSSWb?RDN&1 zUkGblP9cWbK{`MzkZ=S{)DP_iGfCVpMT|5brO!*|zE$A*>8L=@piStW_G8e2d^h{( zsq=2}CL+5S$tA%;rtrj>@i)AQjoRSh8$E&Wt#W^fEwfE8p~pxFz8J$nzhj|Cm*@KbG0`}I%#ab7TB$bz}~PorT6?V7Dg}>1>5pKl_8%Yk3P|Ng`2y2 zRV~}51EwPknpTQfMl|;D$!4f+wH(c9SI+!Xag|oZGKPuq! ze(`bU&(ZJ5AtvqRf7-MouqFL$k2qUkKU4U;I4whxahCyAe%_7l?9{mp*XT|Uuk`PH zy6;6M86|h+BgESqAN@MX5q6NrJ@wFAVF0P> z*SKykV*r(C>JZHg4}4lq!>L|hDX(u`s#h=5FS{q3vyU`E5Ab?)eJ`{j%iMX;?&ERt zYN!E$!%C zz${UHK4b(^1XUvsCgp2Y2?6Dx~YR&v*9G1r~nW=6eu!=#cj=mP|xpFg?E$c#7uY zE8i5cOeo&+sgng1-lzS~!z`gh=VYJChw=A+JuW-Ch!aL@yEvRYP7=v?s$IBLR4T2` zx@71ScjY|iq(alzS-@7G{))jT4=f4r*S;LpDSBv+OfO?BaX;@Pfj8+h8Nc~)nH@+O zv3o5&dv>kz`MzBJ6xr-M?d(N)^^|Wq0%xv4npF||l1N)MONJpSfRMJndZvr~$3+M7 z=)SV=K>LEn?rSe|CZBtde)}v}28A%)MqSd{mu}{n#lLf)Xq{U`N_vkniJc$Vr9Pt> z2314!6i3E&0<@t~h+|tht2rg_h^6Z{pD8!Ja_iJMC>^5uMXKxYf!kK!s8;NPZb1^` z+|T>t`_N0}@RsXntzXL__;a+%5TT>+IAI)1I-mL!#pILtWcfty9P!=`F-zpsMICHv#f{&;+$nw)y9 zVj#~|hmhl`{|Sz! zOi~hthwAB;uQuG?^f=9>L!A@B!41mzf~xfs5O71w$X*IC6(|b^yWoa`(lk&94rU~Z))>r`}j)~d?xa+9l&*tfBpLb2T^ zy>a~n!(YVW=sJI@S@$%bRjRc`*Y z{T5#Ko9S21%#1^Chw=B)i*$?q{h2%MN_>u7acnfc@0YftEng?@sV1?TQlDs`Z8;VT z5YjoN=|b72VkY}fESEnQrDU%eb#9pHR^nY{dGp}&hKR|Xn#;j4>s}oC((AL(jc3lx zH{%m-WPhtBa#(EorQ3Q7pxg$zRr%&3)=eYzX$B;J`5a;5pZB0MY~M9#EL@De&VXO_ zRfavIhM87A4msHo)L1-inssH~SR}U?07M59MWl~@!f-LU)XDDh=I z^CXn+N$t0J=xRN)C4|H~y#4^kM`jhuTyEABY48ODlT2JWU;=-TT`A$wUDIgFoCyv)GhkDZomrA0oN_1PJju15 z_Gjt#OA*Rfv?AX-mAQCiE_iAtY(UGIvVDI@Uav)U?spWvG0uOsu&$orF;O~3;!@DH zT05v$Ub>N;M2NfSm5gIFWVHm|K!o7oNLcNt!8SUae? zE_GJ1{CvuGEzzK(;$hYPQA@w1HL;Z^!$b@7qRRk{uMod}8vR|Wb}`Y2cT0isGvQdI zU>61I@gOG}i#d${ppHMtxNs|suAjPK-Sdu;X;be+ll(qy2QmL}Hw7_u5yvTZHv*$R zJ`p!o`A`ZRmLC6oUB)UJQ&3iMp4Nwf$fW?I(!-^hexouKh{ME4|&vd%(dIogN}Q?W9kpmi?@N^1so zAE=$?i+aq&i#0Jf@E3!fX7ITjQO*n&q6BD7KFt1WqQ!M{Go$PGSaDf<24iRx@yP_8 zfHT?gn>pB?@ssH98aL|$C~P7UBj~NaCizx8U$aKW7o|o*H>)gt(A*2FkaF)hb~d5Z%b6c zp!HJg+N1Rs4%6FR+n(xbQ`Czl;!~+u3>&_ruM-em;*g9rS01nsd({;rsSd@Ad?hu; zVGA7n(R%o{mo>CzK4n9_zc+4)@EF6)`klpn#^H}Uym9xf(3n84FYN@VSUh(Yd)7zS zaNpvkY9&0jdLNQYXj_OBX{N8;_-eqYyP&zBV7VUx0?`uJQR(Wi4b}O^O)ver0zH4o zCFHlM6-AOxS+mC}UMV2sM6*%-loGNv3te};|Mr_lEM0L5eT37X6PKqJof zY!-KC<92eTz4 z3>@DqyVaw{Ms7|LK0Y}*@+x2meUH{B49&*7o@`mc(^kVs*0-kImF)xdSStbJjY_$R?QGOF|MKcT`|DLryKR=#msu1tKAWD1m zj$|Fuaf#>4GT^3lAyJ)QO-3yv?cK(IVqBRIUWAXV1Nl)qLhyoTcd_V##Eq5ehM1-u z@z)=0;^N;=-metR?ybJ2UxTsbZMn8z70=qWE4D-+tsnVg|6B(LR3HTxAbU|mn}4D3 z^+oV=LN zK!@OV9`4>iQFRd#mObO_bhPg>m{|(NNz{G84o3E^Ne(PmUGz!dFBzdi1(dC(2*b*; zY6K3Gtwm7MSt0DaI6U-PHQok_ee(64hq_c?a;6?s9~apS_0vG#IX$a9SuGEwh+}oS zgG0-Irpx{{_(*<50A+#U1Aew zVfYU6yyd2sS4*x#Rk#%->n@?Tat;1~F@!-j+Y2wxL;&}`@|FXz|UZg-yx)$DOvo#ah;>8?v~4?uEGU{Sq% zP?gaL`Q$lhL0AHq@^_3{>gr<=tgDS+Nn0ZQ-?jxkk9Ppvl&d%;-JoJ3+7hW-S=fQH zkCU^e%B&JWmn&+vHjm6za60P*(#PlV%-HNU3B-67`^pRFXX};}M3g9#v+JEnG`jpo zy(3GS`2RgUtEdE@219lk;8Q;4c|z4_c@dGxg9y>^`CyJ|^A>4HPrQmYyUkxeoS?H^MXg%F(bY{`&t>{R z!-jj{a0o-oJ#BFSjs|wcWST1137wr@HybbEvg&&DRV&v@a=XP-exUKJdLPmj8KtANsjY-skr2Hg?{Ak;CGB}3>A4Lssopy6TnquHE_R_F#t zyuJ#hDcZp&ga$1Up+!W*LkuJo8a2jmBiNWiXu+fh8<{WoA`!@#+f!+(T!lS5vU^m2 z7f2s}-OEI%gv(BUV55b0?ek~rEE|j4BC|jJ$tQYmA0l&fXB4h*lgipA2{pf|H=h?; z#IS6fa9N#*wCi2_Ue%!?)f3R(^ZyYtZ(I}#jj0oiUSWSu&@6TI$J>b6PMDpQ1w;FO zNLAdb@&uyKg9(-U^H~J#e8-#6q5I1%c^9_U<}H!#U@>-SeE~~9(vm~+!LZZ?B&+pU z{o{pH_}fQtCMPAJroY&6s*S}-ap&yo=pyX`vzDLaiXm>LTQ9x(8V7iVGIg4qs@+4n zcrCu&*;|?OnEPlNnAKHpTKqw4)#__ri}i9SeOA}^A6m&;jCzvJzvqrzBg*ea zfzp=H;n2372I`Pna;mrPh=jLs={{HqI@kKvBviaR4szhx_LFyBCz)~2l!9xPGdS!^ ze1Z?qYbCkgo>0S}Y~6;!YnfVWfMf54Hi3vLe9|QX6WyHSkftVa*P-t2WYgyIFJe?r z_1Ul8)jLu1ce{m(SDDF59Bsv{n{ubePu$2%DVx7P8DkS)XUkOnuKKvh$zrSN$mH$5 z=t<^6vpS|?w|VkncG`P+%#rtRw#Z+}bE~;n8_%d>PMv6k^?NLL^8d@X&tUtC$_$Ti zc7##B+Bf#^8w}|&-Gf#LHir`%L6gqGa3rU5yCD}B&btHlU>`+-P(W|q{?4T2d{#m0fyPHFID30?%V*A1z>L6fUXy6mhuEGf zqpx08Elr`2Y-1`3HslL+H1&7LDVTR#EohOnek&^;(>D<>QrgUttEN(O-Jf{x&vhf!9Q@H& zH?~LihoK};pH}#cnpInZ4_rmfak(%3mXxA0D0xiW#vXI@BjGhTlb}=!BTZmiH zM5_E;6MZYsb-iQ>ucG;o*~JL<)y90T{V(w9z~U<<<5u%Zp}G$jCUri$CeMXy^!I-m z$+_1Qp&QI&b#?tzUc%K&#=+4-^V^%Y+AW7K_Ntf|7U>2@Po(|myQZOUp4NYGg*1n} zETF*E-Xlhl^zpyA#&74o**?cT`p6$C%>e`LjHkQjrV=nn74e9EkS&w4_8c{V=Z z&3*T5K@g>tPmyI$Kh$Jc{~UcseIW^}y3M!-)4~Ra1E9LD(5xl84`NK#8$Z7xcb+0e z<;%beIQ-NZq{ZzGm$b7UTlWCGr1zF0uy-rICo4a9;e^!yLvNF?XGObKP<3tb?AIQr zjb*+9jmmtp{<^gvBdrDVsuk}&H>URc%6sDT8xA$}hsJxDk4SiC>J@7?#qriGXzGbL zJP{Jhsj>EIP@_F{M{8_-*`OeLmA+r4qcHN<*b1H}2Q*GhrmD!&DBojUDxVW{#I2(@-5i7IClAhIVgu#ZpCSleNqj}x; zMyrY!ia4w{e`;6_u&R_a&<5K%S`BY#aLWuD&u$ej#}#CCuU*vYiH)s%b;mp{@Wt9< zT)3q=5vcdk>#&=ekfZaBzO{`y%hvrwHI<3kSzfkWjI}khWp`7_{f;if;$D-)0~f{NA&#b))ZrffdZS zhm8D;5bGL#V(?YMAE_GUF{tca8+1@Di8rqE>+(1D@ov#zt%-g(7?fzF!ZTqQu*})IgAavpAhIiyOBwv_FoymMuTPdmBHC97>~dLZ9Y=V=scJ= zsGejIyFg|QW!5cm7kdDJE>;08Uadeo@=kRqOV68b42C-y zWkJiO*D*Hv5NPd%^6UAh@=SRT$u4{XwGm-MB`TpZ`i45bZ>(cIpT~KiXL zoXW>Z2ka-mZ|l8okGL1|HS6IE+vWmSlZNYV(M;G3{tkXdoe)_(AATYX*3Uw4TB*`o zWLwS0XuLJGcr17mVjm~}YYdEn6#yf_!LLw6!GLA@S!gF~5itV80j!}OKC}A{-0Ev! zPgeZ694hwfQ}2@3_P_}AndUvm!<^v;%_8)3cnJfDQ9{yo(Cw$ofWy_!KojIy2!_+alC}Yj7k13(g@0zdQ@Ix7?I;&Js zW)OHV*XaDNrjWlNVr!L9c49b3S?2Yh^A30g*WyY;EEB6Y4%k7r5Gsg^?@g;$z^upPpF&Kg9Gk9m_N&kuOK-l6L9ME`XP?O?lW8wzAXIIe^0j)z%D{;5{j(!Z zRr}(ZMbZWKZ@9r5u-;hluw3n>954nK#WFK~GqUgXm9?1N(7fmYoA^fd{0nl4--uhz z2k9ko=Sj5)vk5joc^MAzs7JuREDW0Enku*&`eypwyS{z?&5GaU5cT(GX*Xb^lKOtshXFGY2q!C+2+!KEBoohQ0$LE!-)`RBqP2A+fqV z(yCQrC246_*ZG{ca^|bR+Nz|#PO?O#UK~xuKy)N?0b^@ybgPI3Z@C z5A(OR)1J*#`}L126p)-gU{h+su|xI3E#Qxh~TIvHycZIp(bN21ZCs4_GGT1L|wPax@# zYDpaKS^;HMk9bD@UR8FA!%GX(L;vF;qV(W{z2ow;th_&44kgdU9Ff;a4nItJa$?Eg zXM{i(dvTu`eGCjQn1_LF3s0c4y9R_s32NKh)e-2SvQaYPOaLS;ABhI!#)Gr525oYO zj;VF0FV6-Qs!D9u6F%K?hY`Gahse&?@`Oh>z6x+<3$W^vij-!uzG~6kBV)`udH0**5J?SNyF^T?ew{ zf*&~H3P_1aNfcW@0JDt&vr*LGPk9pJM~M7*{hx>wb_f3SjoHMF&K}>4Wx~sQJW`+v z(0_B#{({r_s;?gs!f>4L?=ol)8fTZD%C(>INaIOUAZxPw8ofQHKTz`@J8Zs|AL8Y{gL_jRYT8=+~dG?{2Uk8QbG%ZZ*Sz_{o@^$C~Y9JGBY zLgs`c`HhPJt>$W0X!cSql`1Eg-Cxe|@E8#yG<>Bwf6K7$o_ydq5(yk60dsly`H4Et zFqNpOs%g$x$`#^&)GVIQT{K*{CJfWE_26?w?bF``e04YUU3X4|Xu2LKB4gUFqjjKv zkPfZ$piP22^o_m&X39z_lGpLu7zBfd&b~ncXM6O`)C62ObGqKdcC-PWZXlI zF?g4Zht1RJ{`0L5z=)@L9uvqCCW##07cC*n;~j7*^+E3sV`#ln_>@(?oFvDvX1GPL zGfT>ryuLmHZsq>sA*X>I;jsQgJa5&N!m9;BJxC9UKM`btTl5UvqTNqZ4Sg{R?8=?( zv&{XDoi^k$fqlJ%H}P94S1o;|}lKP{%!#&&s-Ds2}Ss`>c)H6gJV0We=}4x{pE71$Lz zuQhJ17e8w?wNbtOKlF%N?oy~1w|Zcyl^RN&l(dW4jQ+5xrUeHmKUvUeXIQUDlJMZd zw9V~ukj`9xLYg=?kgRD}J3;u^jZp{^6a!LG8(0Ccizs>$wyzPTjHH%uC@a%WI|L!4svidW4=-{hVZf`5-4-wT*=%l*DNBl2CiT9n|x-QlRE z7sdVJc(XSemFv8Nxmv&ooC5BUbDo=a{YIEULfLgPLWF0h^sekbN9kjZe86FFTIk?C zWQ0I2aH$;vRIVZK^#S9M1l3}S37}F7OY36LsuUPmvCEeV`^59`KfCkk5ExB=n~((&iqSfJc4St zDOW!5x1mwLSP^#)$;mEjh!&m1u;8x=1-0GmxCWcvWaH%iiv_@5^2U3rM}WUi#LxBB z%S0;hUqkUd2S`A*K&m{t9!10Swf01uVB3i}F$^^dqo9H9Vpr68C3r=f3|TS3DfsAH z{N%EylvstJ&bu8t|I(HK&WMICwuwbe!N-<~&U8fVe50=9vOWu6QxOK8h0FLIMfJRR z40*=qCw$z`D`?v2JSu~jYSmrukK#LjL5c6*wP8+s13mG>%1{I!D1Q>jn6c1UqDpNr zsNNoabg~RRqN&Ef^r!jqiiND;yOVatoabNX<5M=Uk&SpdDWbWsM+mxjt~OTtiQ--pBMj;D z9H^;bJcdP`WUtMtS0ELM`^T0)$pv!Nm-uEJp?VEUt2Nq|Yb5IWJZ0*fj#AA-yjNQzp@#O*e4u0%`t+4V%yNwcA#WRBRIZH_T zbgp3i9vrA4Tq0p5-q(tSQ6`OSus(ck4);jo;m4D_txW%NBw*s-%s>ig=P2Rh90%I{ z78kSibW zHV!9PB1EVKA{>(N7G%a}MzevA=U!7>c{kdI`unxu*uy8Fd=y!QJ#RwW-49utul?CS zKZ!VPYFHOrpuuvW`Ohvr=uRZvC!>5b)$Dk+z?X?& z&VAlHp+0T=#v-U8PZ&l&S>u@y33Ye|Smb5AooSjffhNKA?RHPi?iIMY!F8 zuY|)9s+46{Te`-W>07N-h)lshEx3CIY$O?ZHZe^P`H5?Q+5I*)y6Cf)vJrq4j9vfm2%47CEgVCU!5UH9aG z-<)(BNFXhmb&uEwhsLlSHjXks$aN!s#ONY=ij0R5IoPvQHx28W2hWonFh(q_ zoYG5+sP(I(zlGit478PjfHo#n5)Z#uvy0x6+=r1qBFbbB!#4N7#|h_z3fNRjRe@h% zu>|Sdh;ZT`SB`#+tT&7n)?4{jos$54ixb3?8()<1FGu1Mkj>nAd>*!fK}X^#J@>N| znLY*!K&6FqaJ&UL@QIIJ^X|od(ba68W{3gW(eAu6u zYJz@b;EfenkyidEFY8kY_vgfm$VNmNzDI*HG>R?zZCkFux|2mFR_J`*5@}MiW#sCP zY3iMLe(wyaUe7f7UmAGhXn^cGqjqQDH)>X};NKb)+4)-%7yP82FA%VQRMoUX4g$dq zl*f?s>i?q+?72Y)nW$wTzxc(Y)wtg34dkvJSn@of=2TCa5LG1(6bJq3WC3XJD zL{1Yb;3}Q#Rse2;FeB!Lc6Uw5i+j8#TE-i;gl@li7Y<1iEdNg}KJYKN#XubR6$BAu z0EfK?D^8?Wzwii;fQX=r@G-Y-K&S_qezVGkG~7AN<2Wu5#TNtpp$cy+Vv{NDpm zy9d0RM2=4i1WzM@8p#;a*nj@`T_f3e)@yPmTd6Rg3HSbjMb_5yae`tUQi8hU*=JU z*nu}rh+s>#^obdx`tJvEwUR*Uz2hXha5e40O}x_M(sttiC$N9GAH=T`IC<1Rj11m} zkJ274#DjW=zk+%i6qzwaTiHKa2-(AiA<%Z;M1};mCkeFudo>=s5=#pUC{s)MZ@K`3 zj)T`s`8_8C5Eee%3MAgm?0*)F=R*JjNQ3uM=?{H=6X_?&!b1_WLU2`g*lT~i-aA3A z`;X0*!U{YbLi6$8`vKN|3!Y#-*Lm+4@HN8qVuv)u{~lS`5jZlj1a(C7{XPZw78Gnl zV8cX#956M81gOXV^A(!xz{v@8%Kla;&NS(`X+DGp_iTGzbEJ)by9k!kD z@4+FI{Z!>mi!HY3{y@VWW3=UdH#EJh1|xLvjJD$?V@PLm(YM85pSmBI1IQDn!+`2O z31vtBt_s;YN(mOBJuYhrix30eJnC7&Gc;X`@!aGWN1p(#2 zV?J$2e!)gzI?NDd;JIm9Bk1dN3;Pq$47ct^c&Up1tUa_lP%5a~4T(g-kO{Tlq{xSR zskt>2a+G*J_tBanTu(hHmlIXu{%$}PHgGz`?bTLRZ8M4fiU7XutAs-a)@lR^35Dmn z23W!uKPtCgE)w@W&)Y{gV`&g|qF8|U5oZsw=p?Y9=!z)b)PPoC16KnZOk@4Wp7g;2 z4G!CN-tU~q%&qPRn8So_ivQBqxG&l8$gs5A;-ay5<+=%-dX|pOhr-hnMa!$%;ntWR z*Tb#0sTbo^^wV1-vT|BodNxEv()5=k<15wj%C~+w?yK=3XXEV!61{s-xbP^D<|pbH zTTZOd$V4zx{IOId0XKca44{RV{!w2T1yF7Rq}*h?vbe;=Cl91|zt(v!eyfyNe^3vN zK&AX}Y^Z!vJfa+8uNW7TiCwWk@?)z7fdZU6Q3tMjZ73XzCj)gqsAIGyZ*7*$!avfA zbwDe_Bjd4UQarfgn1mqA1+Ez*1s4GUF3g0a?TRS??UXTgO{ z1sWw#{5^=fa1h?)Hxr{534Ps4bNpu>$J1soB3NY1rxNe?GP^+?BW`u5OUBh7(h-yC zXD(Vvyp0U2Ou5; z#btUR{W`sggh@~uSP6xdVKIv4S8aC=_)M8lPFbQI5gPG}NAoW`$mt??8i>zJt&gq; z%auKrc1P>vHXDuI%~E*9fdp@8fsQMY*4XhAn0>opf+K-@WP_m6BI)@ng3Xw!SK5_q))Gr%<0al8FTYCl(~LD^WQQTB zf2zX}A{_Z3r_bf%t08K13=B9bFx-ffRhj>YQo;qG<;-DxL7CFU<-LA`Fe0j#bc8rlddw!x*9bkA5P; z{q+H{A8Ej6W5*S9k(J$p`=R6OnD(DFpu>SUQF7;+s_Pfs(~gYKExlkr{RSwNM%YU( z&{?N6iL8}llrX?L+A)u?$xQnT*~6^sA1_TEyjn&L?5_t8RU&^AGoTdSR&VRQvA`ub zSm|02;|eIGW{K|;2qiLT+P)}Jo3Yp*oYJ>rpoAw&+qqyNSUsP{_w`6p;vR0%Ds%Gw zpT*&7k=w4C&TmXa$|sTO(JV`;*~v2sDF2oT zPw&q=KsX5bTFmJXSd`tMhx0YtO?BS@q<~2{cu_9kFpIahA;g0Jm@3}5HTbeVdi6ud z3#olJO&SKCh)l#t$>xHMSO7Lmc8Z3DCfyVS6@Qae0mv%z@TJA|vD?fum(D9inw|L! zERT`J-#ywD!w*N$5W|<`a;WV#m;ptwh&2DmRzv_@DHMdco?->~IyImCxxHM$B5FQ& z7n1>~iZpg*p+ip(oZql=Z_kjk?iU^TLJ-CN00J#^&z~kC3fX%faYo>>Fa@`sl$J*d z*()3{8|-3`#%3aj>AocmX#sf|3-a3)LP2-EbC^C!VULTg2w8T(P@S6mnrMx1+Fc}K z7n_%q`g>>CpP2|tKkNcVBJ}4rY(yMi^ex_m_*8C_=rZQph*WwWJO872D@W&#*q7J1 zJ2HI#_`3bn!7A_&|LSI#Ie_I4h*gi651!{-wwa&!{7`~_wRwQ5QuH=eE*KlX1&`E5 zB1g?Ya{}=}Q3hC@ur!qytBZj8Zps3>k8Ud*`F)%?>;`=D9dlilTLK=m3%#c8#>b_o zB8TS_Kw?fKqI5}q=TyQr90MZPZKRxx2x0DXAZ}KSh}-=#rx(9ZlZetoL{|{ev_Kwn zP&aaxm0T+kyX35%hYl9^5^ozj5%lZ{^0gy~7h`tX9I|Oxh-gKKknUnSE+LOXj1dV( zma;TiKcvRGoii(UnvWi6Jcy|Tgn5f3>$x}nS{9T#qCnzp3;{7g(yZPS0cVPG{OTGULtmne6k3wV0-Ylhp7BnO=LIdO*+rSXX#H z3B*8b$M5DMzlr?~$-R8hHtVDkpX*jW-V&8~@W?HWQ67>j-cfO^w-K+(6N$PsDYPMX z6C|Op5k3yS_(FHud)W2eOG&p%#Q$J!sGLF^Oyu!*h|$z={zvu;RDf%8sxj!*1Yl{@ zkvQH1Z2^w8c;!7ZSSvafmuSWwGBO<%bwy`?*p-CykO-dO@1%%N077W1TB?dBdv_$= zB|(7xj-)5w#A+gOmt&yT@}{J5hYl$F4E_D0Lad}&WedIdE6mdh@VJn}3V=wg+{R%S z=*n`1yrSrSdW67lqTGU$h>BzWy*;X*1P6&}aJ7)@=ym8H7h&U4LO%AYHP^JJ8AOL} zSE4vsAbJyZ5WNA953$k4wrE#pPyw<(Cdc5orF;;hWc7(SM*4i|J%F;vswcom)<07u^~ptBL1@838nJQxBJ;efh~M9T08 z2&oieMW^)7#~gpWBE$<`v;VP66L# zEZmJDTvby4iEYC(Wr$8II=y-jp*yCLkb%8AuTH1kUY6Cb<2x6q-F)?X|H1D60+OrC zPv9rz`aNubSEEtKurs&0l%)M{@=rq`Ne1y#f0f|jA>VtN@EG1-Bd*XZeZem6hw`<^ zC#|9`?;R6Yk~Dt5RYnIMd7sSOfM*AFS6ToVMTefzmj~kl%uO1NcuQnsWnfr0XLCUP z3L=uK*nuBch$wbNUKjq+3^x$-T>aclM_*3d4xldPpcbO=Lm=${j02y8Iu){B^R^qn z^bc`So;iL-VpGWV5f%)hA?z0WyWWD$XC^`<8*CsJ<1o4C;sc)o8;0bv+zb*gTu>%2 zN5;XCzZW_6@!O(y^7pD>)TEVn4Vdy+G3y=bDdB4{nt7?QR4n=w;HpP&M zO>pvYLa2b=QAN@%_=tdEGYHaA2g=~`0XK6)z}E^MI}h$5<`d$ME^w!Vs9^plkb~a1gnhxEXN9w+WurW~WmIpD z5DPg3#P5hR9li6*()+yauvp9m#|#+Me0vT0FAV^$<0hc9^PfGVtk~O{HED&p0tL|2 zE%U)Pzz{6m=ZmhlFS$S${z0b42oQL8t)!+1s*Iw{_$1Z`9g3l#xfjyO^Q|4q4bW0; z00A*vgg+pFXkkD?X2Kwhm}wWl+1_T*gtkfhMf5hM@ATimWRY$-WNkg$ig{tAc*4@Z zKfITQ5oQB~4j@C6VR}Fjj1s6QhEdF1QaO=EfH}^JtH54j4Ic)1OIW!9#Uw=wPar!w>J29n<4B^Y}Q!en}N? zjPGrJ@#=>;TUU*4F~fXzD4iK^^w7ueg}l8rv;}oC>VsOjNrQNRf;i6ge)Y%_h6BnN zbS3^}Q?t7kwD2modaKbl@eJ|Vrp89&)tv?B8V5rJjz9l^gg31%0^F!*f|?Ec?@#5+ zYpt7jN}AKehl8%HUiAp(Lr#{2%NZI-_C^A7(RlKOHds<-cMR_%UG0bLLx^R^1Gf@P zs6Ij;g(9c-L>#>!G#ZJXzE$L6KXDE3tmV$fKEg#4ce*-FSumpM$9kHbv5?`9uU_40`@)GkbR^%UI&w^d`r-JQ5UWpWE%Nwr>5s&;}t zZZH%4*fvZyFu{KgaA*GTTGV*6M1ME#o)z9}N3mfuGt|?OJSVd;h!#+|(8}pNQ z61Q3$k&Bfdbe*4_<}}rTa?e4v6M=$pNL3O4O}9T~Bh}^6BTVueSxG&)0E0T`eO2?H2WYBk-`NKlUe4~|)-r+M-TYQ9bN<{o-> z#T&W8BV$NiVf=cTS^-$*HjmqspU`4Ybtm4v)>M*lI_t`fM!`K@Voz#3z<)0xnryut z>UFQ2qq`=!t)Ud@mI?qs8wLQOLE|!AV2B}>=tP&UYu7)7^aQmdAa7nV55S0>n?5xO zC3yHAsuq%3zi-gDpe`SZdlmww0=&<=1nG|+6H_Q%h%Bq3Qyi2oacV-XWx5Y`?{_jFB^3At;}z4{9^qQ&6B3N0S*WEEb7c?i;UJZ-4&rcM`##R z$6H+SGqxq1!JP6qE%7zuG9B@088EQiS~UFg#7Z9oq3i-NGMhch@e#U-m+&Qp0JCr)%5%yfd@M7_8+Gvppf;kxpeNQMjNde zLfq~fx5MARX5+2~!0sJ8NZVK1=g~o}l?ONFNa$7rPc+bl@756nl6w&?k1;KAejw{- z+Wbxq&no!u0A6v42`pT6eqQ!8DnbQDM!~z|K9irTzPKE(e++8={ur(2QrMD{uj&HAqweb+s+WKJ_ zC2E(G;^!DzxvE|>tRM3;my()4RSnn^(HjiiG`ud*KIrP@&NQpU6^^1jkRJydfXr6Xi`on>) zGX!SKCm7n|)iykBc8j2cN;$9+a3M)l66)I4Jr5`K*s#DExN>A@^|@eIbZX2$s=*Q0lbx;tl-%1YswU$ zL`lIodEh#GZaJj3Af#C#UZ5N5Go16D?|Hkd{R`N%wFmsLFTm_=K*-@`&8PHk4p0Lo zYP0-#X&d5rE;qt9*>Mtw>q}E4tft{1#&4$ZRWM8lnIERjFoSS_SsLYyyQ*SY>q#V$HM zqY8U%XPS2^1^EOxup93^+#7C=GqLQKZ3c?yIfuSi_lU9U>i3%<)Ro%E55G2HrtE_C zsI;?!w86@3XZy6?NR{nXTXjd+AKN-h72owu0G3W{|_+>nx8%|n&0sK^yglSHRGx7y_fWM5x_VOj)q>q0>uRGb8NX?eMNHN(OvY^dxM-UrgI>2Z3-s7v{i8-xUUO z0gBK!cb}9dN+=<(aU0@A@vD5;M~IRbLPp@q1tCMf?+JDGLVlFvZ#8{1-Vbo9<0O~=#rXK*yg*c&c1 z65pwI9{gln>6oxxH=vUxBk$&&QS=KTuKTUR%uD8?D#yW(0cUwDxdVmYN6K_wCi|uh zYx$%`4{1*_T!*0X=Bp`EULvsf#8R75r@Y?n>qho03W*PcMd+$IlU2}92`Ff1?5?)? z+(U~#Rc5QXNRK9QX?GvK(M*hmrKkrE^d~Q^x6dM~@8-!H^yJv!H{$((&mcv7-&|X3 zmeKW>`f^TgBfb9NZkxhO+uwQo^Ex6dL7LozxQ$N=lf>5RssRS|-@B6+n+iV%tv2qM z*WzinwOa$F%?m%tFrUJppxz3&3I(#g-+}bzSx%`hAEwpv#0>qDko5h4oO%bqU@c*=zfB0&hAh zphH^pI!m_^Pd6l$03$rGMYUBKPJe0?@`Uk|G;*6-{m))4f2n>#90ZKvqJtT(Kex*- zNLuw#(t0&)OCg7wS#W*{+!O`D;Z4`3U#&$TVFOWrxyUB7Rbt?p~*N#x9 z{rj#EMsJewX8;(_ZqGbXsIw8N{^BD01uULlgMR9KX7A4=#C#c&98H%GdV|Gcew+4- zlg&ZhMbEFOzI3Q|926L4VhlKho$&>jxIgxa15Z(kS5oDgPeQ(>Q>zi%=8HcKb24sS zVs{Irl#-+#b?WsOG9orss)PpxWXf_oWqau-uQXP*r52#I6_>%5el^Z@d91eFMBYHX zMl(U@R}(1(ee>a-lVNe3!MNYz>+%(0M)cEbaG2a(&_UoHt?TgpJ3kM}zt>+zt42k` z$WF&*%lokwR7XA`){5YPbRh5e)`<(@P72xyZCeG$mv?7fkG4HHvX7Ym(Vqk5;JD6( z^4kyfC69|SzgD9{d1%||d%>wJn|;8fFR`dse(W?^D8i)_F^JbNR;us^RU)a~4Ro;vH5F{)J!WC%9X*W)CH2RSveg9I_y6Orh=an-7DX zucjrxUbL#}R)TCMu2zN-y{3G;V^Y-Ev$XqFlNd7$)-rYRVE2d)0I#2Oa)$ zS#2?m1J{i%C>&2M4i>!lvLo_h76c$AFwJ)yp+y&%R2LOkd~g19u=*OD%4-`Kytuzo z0u*FHMfez579A$79x2P8sblLid#_LXm}lc>_)Pp`QJt<1L-=Ax@zg71*G7GnWnySF zcIeuH^%2snB=E*Po10^V$NLdpDR%dLlji^=F#aBZ+LARxowVB)&k48Lm#87_)1vlS z0-UbP{?^nbTvZvvqY57~|*-R1YpYE-n7Nn?4tk28ov0C%BU z>am%;O@LcEmWLn@SZ27Pfyjrt$NQ88%|{aWqZ<>M+BH(%l18DPL2~KAzaH*ai;&%u zdm|e?%`++;kh|9qtaJ11b2Iq}-V5}CM!=IBOv^i*p3P7&A^lB3$7#KzjS85BJJ%7I zNtf%`n#PQFWbK2NM$N1mqnLpK(#PV+c_y7GdR?I6A6oW@;&*zR;IQ;N?l)*zf{Z2Q~@&ueM%b%ONqe0NKdr3IA2IbY?KKsXS{|N0jFCA#oQt>m!hv zte7Km&qAn=LYCF_j%x<93`XZn0-$M)@5*Fp89d*`KQMf%i`8hB3b%`X_W7Y=({X!Q zr1T3;CEhb%Z#u1Fy`7$VDZC0AC8AcH%2T6QOO%|(IL9@DnBN6>C zWF83Y*IO>asM681P`Dq5k!p*Gg}*C!=U>slKswIH#}wuQ^{4fXEcyK6Ic4L+bpU-bI+10oxd5jwEA1)j6_b!I5q4LDSxOUe564epHmA~h5QTCYTgWb+NZ|I3e0QcEw?pF+(YbA7|Y>$w-*iU1tGSyJpgxQuOr?Zdbbj#T)krrs!20; zWB?ay3uf#8kDm{FU4aZoie2xB&!$7SrWQ1n+|A><81om54!w#F+@9iS`C?q(w|8@q z=K#TXfARswEj6m9rx6Sc+M=6)M6F>gOovC)BuUdevh~M zxMk51jf_gI>N@vj1iZtXU~FdvME-gkaT=LGUH}y42vfh-?`+Li z0;iFZn}ywG-k;xVf@_ESuv(=MfN~|sWNz;ji^HGH`>@@dVJTgfyrEP9;{*%m9~~Mb zVuMx4fu3!14mtP{sn%6ZB)2#K7^_?T;KE{Ic_BzXidgeOtJ_rdo(bf)GG1fy51=%9 zQa|Ykn<70AnGYog<3t&oie@zmjOosC;b+958bSMOCR+_*(5KjQVc-K;Us|LG{1_9S z_#n1F+r? zK@2?Jb7144rP96v+-!Su1slVUl8u)yhBU#g=&TG+@ zLao9&*C&8Dp5jfzCq}e@o<1_9<<>(E@7$hjI9|WRoYr-IBDc-IGYYC`XYdwajIyY? zoxTjoTkN(F7ydN;wF^^9jkpXOea3|$@Xp-Uk=c2q`K)%^QKP{X($@?1aQd4y5@8)C zKQ>82+Qi&~;YCue3gTx%%Eo5FAn1oB>$jczM`qQbTZGF)fNj^*KC(Gk`CJK(8xamlf=^gpA5? zT_eb-k;Le{GJwz%VPI)uIzMk%D#dj@&m!B&_fO_vN87D7Y()8+wfWz)6eYmHv(OW^ zSSLIvwcN4`ErPnLEl_J;e8S1$p)nWOdl__c&&~XPWUc&0FtE|8TZ>AU!{W@jF&g^@ z+waMd4mSxK!i9PzB;vNMmMCHOO7@dUYegLtot^`C;0Ba?98qaOAxiFx&(rZD8~0uj zDZ+^nRnGsc@R6&|ZyPVww}Vg}YmeV#G@nEcr6RL00cLN%=o^|b%6VN6AE5+HJF-hR<)C9N3Q?b@+01-h3g| zCUsz%Chf2rh5qJP+skCY$HexZCL%o-ctqckL8pDeap{Xbi$@WpV^a)}d$fP*za8Z! z@246ZPcw)&_~QkhwzYnj@uO2)+x6-XjkpuH1qzFNZ((Ur&EUP_-VyCr-X?Yo)qm^J zu&pp=pezL$?dD~6`Nx*$muD`)$RLhHTVF?#n`JtKWd-l4QEh;z!H^6OMZ2;Lo2w?^ zlwy8mE-*ax{esZ_#X&9w`WJYjStcVsq_pTg7d3mN!}+uf11IbBlCl{_oasLj=Tm?D z)es*z^s-s83`f_~3Aa;1ygD~kHJD{x_RTeLk9jx^sYUhV;db>3oiT~Nx)xs?$uYK? z0F*w2jO7_a@RJ>x$|J&p6JIr`{P~u3>TazItP`lXZd;sf=)0vZgFg*ss=e0JcbgnR^~sr z$Wl2kK*DF#;ndD~e9N;dYwv~?gTY_62x0DiyMlIO)$NkjI7j$*%YJtx%Khv05fVW# zarn?Kb!4pjeCc_WqkU`y+^qPY;*yj&Y%&~5rTEvDT=WQVT!Fi9!RdRkF6cd@;RrOB zOlI5d=+FXMqu_L5*7*F?wW^iz`csV;|6U_5n)lKu^maVl=d!=Vc@p3N=lmEYzh^(( zjg-`E=bY!|hn{6qIC-$51-|No;lobz@__EOuF>xxH?Kyl(iqsg!6nJS;_la%4#B%n z8uos}n7`0%Fz#ju*=g4z+feFt63munzZgw4|RY^@wX63 zXr+^3qL@uY;?4X3Zb`lg=Vs3)uSPuWa{bC{>8)D+%qh3uk#B>tzAk^-jwai`nH}u< zz**zdXLJ8$8yNmmeep3{Lfpr}XL&DU^B{(GV}H$E2g9^T;v?lvAER28MBV~CWLxXl z4?z+7T}v&VvFDGP5P)Vtb=$u&ZI5K00{E+T0{A>Tm-7Ml%exY`S;J3^MLhYyp{i~y z#hc=#gt!|>-?w38OpTWI$#G13z1+XV+jGffJ?_fjQVoD~M>&;KUX^M`fS+1Wt zN)zrxWVzF?Txl|U{SM>8Y73EwZK#9daW(Ug1H1!f5b^{QIyk& z$9}(nJoiw;BB^p$cqS6m^oT|b&oQ2i3Wv=TxqE=h!qZlMoh5;I9Q^}`ilHwM;0<|Z zy(|y19FGsd4rmk%@NA$!@(R(qYt-QD<{aLy_a$T7PkiUuc)syHPo(|0cUE(77P|S~ z;@=x=^x)N&2;PEu)MtRs9Oe8rZ{PY1Y_|P<=zn@~f-k>&Di|n_Nz`8pkby?B)$)*g z8eqa^wT*md76%}&y4l9=p;7ySktqeb*rjrUjm-Nys}^T_u97Z5*FwB2L+^HOUy+%t zuCzqp@7)nW#jQDb!amIh!tgJ(E@#xN{gN%e>8VR)j+y+1#d{vXyknKjQ-XI5Qd z%;sTKv5#r9WP<4fkE5jm&_fj;T1HmBCjF7De|uctVe2G(QQguYz~x5${CmB8}?T;cdl`SU2rXQ`(v9=;*k79?@7F64YA$l52BOpTWS9OPRRZ(ZB1Z zr1FO%<}WeqcrDF}T*tNU7;c1l`kr95MrY-i{Z0wj)P0 zt6&TkGDqbLHJ%_(qc{r;#JaV_=dz5y_e@26;j-QyFHfqH{s?6#cOJr!6!9q6Ll{zr zR2(u4^hc)#9>s~XR>nrb?LV{d*-c+Y!fr#Oq#-4EbBy&EPzAXtJCmDRH$7tUdfV=Q zE6;{w$r(d2&Q5?+dgLJoNJlv!9r2f5Gy$((kLX#KlE#v3IZdX!RL`}e*SACzr2a%T zKw=8L`jtk`mKHb>S{ox=2sxc3-H`x?j-`Gs8x%Bry6*9_y68C z{z!O}zakZtOHlI5SS3G7p+kRH5`E(Zzva7T51h|k%`|}MwgDslqAuKM4V}_0v;Ct3 zG#KMZ5Z)2bmm*m-`Eo#Dn*Z-}gK+EcM9;_rFYbuSia-6YjwIvO%7Qn(cA%1B1ufX3M6;QXjW);i81xwO@;kfV>wEwTU^;d^fC!6!TqIRu z2f{%4d5`ZS+z;6;p>1Av%{bOiya|xrFHi-Ik)J$yvdZ#(#K5)hYDD=6&bf^Q!*?gs zP}-`BPyjq|a{j015+U;K;fQq0h=r}qj=ws$@cGoaY7b3k&ZGzZeHlmtA#7ZMd9Y;# zI2XoX2XxTSZ#?ZELa!E9l6^G-SPF~=$4g- z+!(!Kq#KDiCp@4?dVm2DfyxTS(Vh73k0V-qtO-nK7y@=q;@C}H-87<2jm3vtfhFL* z{wrhrR|D5k`>0kSW(nT2Tq;uM7-**0mQMt1=>|)CE$9JuNzg6?XI_|m?Gnkwr z7{xy6Iy^MO!;j!rjv^khdz(MD0U(Qy%hp4`nmsM+Ie!Dj4tm$lY&}^-uoLh>ECaXJ z5$FR*$%0A(@!mV_lT?(m=r=1I^y|_7r8@w0Y=Z;(=c4)*M@VA!RnX>%-tA=B)_A2=EHMvG_{C~? ze}}5sI1^jX;f_V%75L13v;&{OAuu`GZ~n92?7o#J1U7hrJFq<2pAqH~q?75(0Pb%; zRQUZCTn1~BLVb`t`Lu3!&3fPNB6A$DjE2C9#$T#jq)BMl)Z_61B!;u22FAt5_Ue7G^;j!+~l!Xn*>1>>0y4?_9a?TfOUblQtIBE@q3+3vr@s=DFNs z@*fxAwv8BFUb1p<{7d>vN1Qzb&pnDWd9c~}3}wLga2atA`PYp6$gv4TD&B4OxBiNs zMpIzUqF(!XK0E?Q>tFj*ZzArHU=8{jrdix^+5#BM#V_hS)TWEjAn04v^HQ+=as;>! zkjM@9ZH(dU3iSt5uY4)LmFjZ{!+BmqlE9eXzmr)N{8N`3v)OOI+bjfUHCJXsRnIkl zv^mhaT+VBRoj2y}KeI>NM(McOGIC}z=uabu_sGq&7!3nJ)I5Qewr?qX`g&hy?GcDH zMe*PWLTaBmXqn?cEJ$m-PZzF7yyFQoZ2r~dx`c3k($fRoj-Pk84%=g_LO`9;Vg>L? zIqR$;xRApf=U03uBy@XWqo^3q14JC9pBP8S;r5g+04Aa{#jMs?17lJ@QwleH-9AvS z=9kuIF)-7pPB*H22#ZVyaAfnj#yJJ53V_wu}Kfy^JVoXJ9$7ZwV7 zk3sb;%j_}%%`ES?#LP9tZPltB^cK*Dn_Y;lS9-5;|68OCZTfR)7)gwHG9HDQe}DWu z3(VE%>r;rjVVu%WvlNOjchVmTp()qiGdJR!x{A;^^?I<$ab1|k@-9KQ^Mixb3n3F* zgi=wU>-uxjrGDAnIEXLy2kWa8)%^K{??^}cvgGOH%n!7FMR#Bj_6U?yhEqukD{V1n zb>1t!)R=B8HUA{PMI|w)WA;V&^{LpI%}$xm0(QTseRUm7rbQvW%2nNgjA1^iaq-Sm zLY0G=sV=nv*;@>e681fB4oVEJ=YGuv-;+|>p(Sx*0Rb62HxWs)uk@6fq--!qu|_O2BHqdowl)BkMZ^Jo3{A@-F%psM}c=%K9}bt zjxR17<~4FbH!e~y@r|1UJk;4C@6(T-@c;e*S>}a#P3l*6rzvzH2-jV&ndQhEtlR8V zQBD5@6C=K-+zLI9xd4-a$He22&V7Zk?%78vq#v%S-kgST7vHfUp6>*qd*yR>ill>5 zXo})&!7I9V`pDycR!(?u!}#jg9uIBUGH=aM_Cnr*QD&noDaAK{o<) zwYEEDwz>ehlT}iAW=iPT(-qmLo{C)*NCu|NJdc< z=zBi#6-P4~_^(o{=d^q#!cyqZ*eUxpYn=%Nz`DBMfut=P%mCtf7#{v_5I&9EuVB6R z+!Xzr7u^YPog4=&XxoXz%};`!WG@0K&nRTZ-`X)y>kmRYTf2y5-5l)A8O*vQ^ILZDn-UUd7cdDRW z@C90Yw~#F2R^!%C=~;W(7m%d80}AwF$`AWl{@1r(dB6~q`8zRHEWO7Y zBuy@JTR^^db)S*%j33{(F2POnVe!+e$9g_=e=rQrwWIK}Dzgp!(KY1foj#bhc{c|* zDlW4HIH{tX>0J5w$9sO7*;1;D?629g=dArEa_lZoh@a?c>ObD14`t)G37nAfRTNg( zk8idMtudRj5!JhNRqoV4;_lYnYpNZYe$PoZYg1m|`BdGov6&rk)wd_{aO#1p@i zd@Hj6p z(|6_er-ZDl-h6gQ+rRg1;?QsJ;VF5wyiOTWy>$W{J|W48gG-|YZImc_yStCM6ehx+ zhvR1iuFoTm;lyG_y)dTCq{>q`iyqzjsiJTpa4+c3>Ysz4?QX&=5sr6ehy~Bv#jy{P zVk7W-a?oFuQzN4J)fi6X=JMzA|EASaRMEair$S4tasrDc;fx^2;8%-bKQWiI6Vy~c zQR?Guo8xc!u(*3lMAl>9d)#6?A$hsD;0k%_)Js41 zCCui|EF9n)v2TtU)*L&fX)g1CAmc?wSLHK2@~8jT7w#n{gBS-p(ttep@WJtlUMAw8 zjh5TT2pGd}AGG;Co(7cIQ0T^u8uPH`ey`LDDAHuUR%YKAP9q3stSJrr7;{>&1$73W z_T(9_M20`lNf$co<9NUKEvjsZm`zIR$FurDxO>l{ z6`J+2(6!uKNun^s;KJQbF$Atpt2>EAqfRPRVpzL}`e4#$V8q^wth3GepKLlgC5t54 zEdZ&_ER$cS@gd?F8hxWU%)XN<%Zslav`#j_RSGj92oxpagY5-xKJsYQ!3<=1uD_=! z4W6Q*7fRaOzgIuGFp!ton*J%1X{2SU+lqibEx0{dk>CX8f4+KopQ_>04zoTDAij^n zQs|-d;lrhQ%oMrE{jxBBe=+7r*_ZxC3e@ENK5_Y;PV14?1ejB$2 zr7h%>PhnB==AMc^aN?{TYT|O=i(YuR z_;jxhO;iH*{+c$zJO(1xdee1^(j@CfpYG4NagbSh@#FW_1QEL77F!op!D!Wxu?t3G zqzx53=lcE0%3Hy_!B<|M^pFuZv&s3p0nZBF3B{AC`%(redKHdL(h0N2cy$a4P-rBY37nxu<_Ezvx6nhhu z3RWNGu-VlHlgmH5)-9b3eF%I{V&Vay5T6vAVzKTf7RTz=Fg+#1U(b9Z|1&(X<}&B9 zjhNUOcF`wH<`U|1)hl)n7aiLRCD=wsF*m3-jbGLGI@73_PJWCNN|Z^~Hf`PDU!>sf zR#+6Gn^fA`DSxMJ_~o6KJlWT}--1_`pdEAnW7iF(ljf9_T+zvt(c{elF-;+p3PTsq z@>?V(B^9PnoIo3Ijpqlm@L#-7a!hA%^#*F|pks?|N7NsEv8>}xAqSZjrb3y4ZvK8U zk&2lCzI4;?pXy$hx*=KB55E#nO-ZD5(7{sK%uT_zKQ~pxFd%b@DXv-Vp5R5Z6^7{CkwsFPX z3<_lq@4Ml3kjurYJ+yGVVPDwK)xCZEf^SK2A>I`aPz7BNH?1ikn`=*ztAEK9K-7B; z^+31@cwa$-94c@S>Wiw9Hb;Dk>%*T#G&xims%8VI(1m0E z-FrU;kH0_`h}E}CHsVo4oBNUjUDQOE|0g&gu>$oH!$YiMrcK8Bu%;^GSi%jMTd1YL zV^wk`RzMzK`_W@#Oeo%cs)8E?JUH4hvPKv^uYZzk`dCrcSK~IQ9q$oPoE(`aw9LK#@O>F8u?lLHF5@S(J3*sFv~17rI@D_|c<6JZ{@0Q-Z?1VRoc~11rdwDa9HEogxrIqg5 zObJUEf51pg9#b}=ywGZ9Qg=>1Fk$*dzCJ!}b1-*Q22(@t+AMw3OcB=~wx0&o z98I{r7nnh>!(J4tQOe#UlBS;B9sYs27Umg*M6mE<(3ibHy@G8f_f(6=?!AnC+OO(Q zOUcWEoZZ-8qBGggQgyxdav&>FKCd++y3T;|8p@|-@NE&c_I>AhE>{~fAo`a1I@!a~ z%@>s=Z#L4XkmsC)8+p#{P;YilY+Chx|I>KcuvsMTm3!;Q`IG<7{F+CY3?h{=_T`}E zQz#?r-#06^kq?3RQ~c&F@`T2Yup z0lVl6CiB}uS}y|MZ?L}pObvG+kNZ&MkH&f4V*Byi&ITBXmQvsK>4i}a7d=lIVFa%zBIeaxj&6z{Bt$GVc7eLz#uB z{THn&4|oDEryth*A#HAwC9~>zBNd;{p5I;4Z-w*<2ZJCsQVtN(wtb}*k*FQ&iq{tB zY{rP14|@*#B>woxSQnXp_}&+0s_17h%spUj4o1t;_}Fk~O;|LDQ&>bh{{c`hU+mSW zg`6RHj1tK)RdTDN)-mVu&J{9SMLvH1de5@(`AU$WiXD!i@@M4_cXHO`OOL8pJeEd8 z^{vKh1mN5&#;%x=GA8QKs>N-0I$8G#6?}d&YRB)=``nEx$LPnLl7F(RKYD!|P|8j~5Q-L?-5= zoNgezd{->fYo+&t5tHema%b4LOmiI{IQd?r^x}+^^c`F0 z8g@Yz6h$u;D-Z9>)0d2g#FNsLS>E|gNl|6+4c+sylnZgRH<6?1TY@9F`S{t@jP2o%@2?Jsr#NfOuiZvh1g61#{ z6(uZJ?8SITSmridH|QTVh8UcN-oB1*v2o=U7+s#tX)=XfHwYDOq&BNP{qm&J)>vHt zbzll;awQ!4PN3yYQw6osg8jM_-r_MFgD^7Xe}op*kI)0PiAg9ePg zyvh4a%^g;$(~8$azmoRyLF$7)3~?(-A*e~haX_srgsK3__eIHYM@N-qg5k!dzcaWF9|kqqrnZ9~Erh zNjE@w&vk7Ph_bpufjpvECscl0w22Q@78gDc%*g6($)aY)7jY zX%(|UdluOU(znx~QoCe>$~c7%WoMTHoa&^KwHqU-a4u-k%5`*z{iOnpr`2(pqgJKQ#dG2 zh@T}>xJQmfBt;9dcHdk!j}I&N*wHjZGO4{PD>IJ_%MH_sso{JzlQoAqcM4~63l$H_ z4D@_z&avTVPg%pAu1>LqUJkJGFF7B1Qth?-7!V&TV49l`^%CupL(DInyd>0TE0@Mz zxia7%?3+3c*{T>|qk*WP%U`$~wcdUK=dVur9MsII`R8zC*R$n*s9hV4e0yCU!4YlRvfGnjy4e`(=Yj2zgZRs{ie`qI&^s&Z}0< zGZ#h(+m*VY$#+4^+Lu~3poov1goEsIFoC6-$_gihjPdo^1=xYp>hMo@?`$~3YD}EG z(I%F0F_r&>pO35iED|3u1idHUP>%P`e)x>+SeT$JS$21@G;}lWWWXG4TJU^BylR(~ z>9;rUU1AL0s8X{X1rYErj5hLjR3rdJE62{jyBnv^mX*S`VA4AK5HheS_$R&#M4oUBZ?cG%QoQ16 zy_aRnY0c`+*9soh+ad9E@K(AFNlEY{_ub!Tn{__t$ODW%T>-9Gp5j=hPVB1BLT^+5qUTCKi6U`r^5Biy225QG6^x z2JbtSQ%H#YpFCYxaLZfK_CT8{<9R!;h6B z%T#rD{G2SjsOrEwKe)=^_vy$B+_(pCME_Je6j>$>)a~+~R%aFZyUf%BIk=8ob20Vb|vx@SWasB7n-nq^zc(w_hO&JNB6}BKlkS_KK49ORi{8u zI_1B+2A`+B@n-BBKrl{ROSxb`<8iPHjCwU4oo9QOHqODdPee5j4(ehmu5_x-oJQC2 z$D2~XCJ;hOeN~ouBKTgJ+*e!6>Rg7h)S2(PVneqTc(pDGZ{vrKud0#`c1|EGLpp@(L&P>9GNbxD4hFUGE|c zsRzX~?WYgq_vl&9ImZM0lExjHqV3QNwv^RlK;2@dGm%xn?;V;7f9(PHrQ=08>_6Wp zaHm(_C!K+X!$#tTl?UyL9v3ZZ#AU*!g8MrLk9dac44b1q3@M9-6Yw&0qijI*mi}Ph z{0}fGUjanv4pFxQlsPAi2PEY}u993d!I*;O3M8B=vEE^Cl9Q60pU2kqKYo~(C^PmS z7l6}G4VlyY0|sG5vWUL(e)KBXzW(XRC^rSRiu#&!azBCPl`B`Aui-u;pX3cM@(7*i zG36tFiqRv}x+Rod~jd8N?~G;OY?r#o=m<0Z zlqr}573#H^OUKnyjI z#%JCA_0z!=F<`NQF~)shb#gxFu)(+u25o)=Zj0{h`BK{vXgAJ6!t@-v-5)@HF0d;f zBjsxD<&rr?QUG1rj-3H0)vbXm@6h_KJK5y6u7SutP;RUtpZZ9XG?QOY^bByID{!*C z`#J858VXq|hnMVt7Pfx#TNnwY`clf3Q-qhURE4U_`E!N~r_*>{<)}||oQNuU7xdAy zm3zQT<+OH&%F8*Qm9f)^1n3)NR$EVThkrnq5!1XzZX^Bt*|T@!miNBJg8nOarrf53 z@1%NM41yLz`kT%dhSJ*Pa2p1OgMx4kL#yP6>DVFMP93P zUYoi2`dPMHcWFCu%D|E$$Y9Q3w|>lkl4HMn7IHH*AJG~qg}lj~QYk*)^QR#;QId%) z_86nKRv9Xi)rTuI1>D7RlL2${PF8*sZ|?6cUWIG>c{V6qI*C| zn1w!f#ZtXI-dqC|Ju$oAU9lXY_L-=i5vwO3z!~Ye?L1HpUa91(5vy!Qg|f`MqPp`9 zi&MTkkX4~JZ=i6G@2;D|r?!FaAGZ~~GagYjk7@*wHT6yEX^$G)&4a8L=Fbif_Hz;_ zR(rvKN8s(X#8|oDpT($8+^fMjbJKDH<0pX)V}en|kDJKEEW7a>B+HE=<&klr-~ zI0T~sCq*Q=f#wxhbUFeCnik4ehuss!778Ix)p-AvlESoVF~Pe)rairm;$baYLJH}! zji-{N+-rF`DLU_X$`a8NZzB)3(GnSvQ*dPH*)lr=A}KJVd0d6udjBnyXJ-vf-NGWd z6wKp!>5c>67!$rw#QH|PQk+j9~IUE5Kqyzi6><#X)@5SFVQ&J)Fi`h2i|Rh!qumMM?B&yB^geyR zl#1^dXI0UvjfrHs?Aob}-+SDRy5`FNGhRz41)psTr;?$#F-5BT;)Cru%U!BFCyvg7 z;0>}9S1?KEZowS7J{5huT~O#lun=sbl8c!NmB!M=qbSfyZ!3baRANFWzTO%CM6L7= z3@`>pKtpK+^2IF9{T;cXnf0BMV)-{N;;#dpTqld`W~&AUYtMhoVXz{Y>?Yw!Htsb= z+&$mW!t*hep-@VFYVTBez(++85f}gfs7}6Ko~M&o#O-#=P5_J8GRs#53<~*S%G#9~ z?;1Ij0?-Ea`y}IovCj%+%-h!rbh093ghOS7HZgA+VTAa=hQZzD+f(-h63N9|yK4+G zG2zeh+V#TtDW{nHsj`RD4#Xn54Xg^)SMzoT6dE;1$MqTS3RAL zIOVoDt|B=1y^s)Esa=UAwmCRAOxT(_<8EdzI)z8KKi(lnwPtT?@r}~$@yH!xI&AkLcX{gvo$Gd@TbV~J_hdd&^Lq1p6{zp+?O(+gO;Y>GQV%77{ct<(t#E*p zw7=JT{Bzf!K>12eP^U*O?tFpP9gQ(6&CSU1OiCLtk0x8tN%qB;qqgv! z3Zqh#0z8ZSnuriO!;t->oFnB)s)X=Y(6Pu8BHY>)Pw9wH$WxWxjAVyamFxA3pYI(l zVC(fa+%cI0tikOiD&fZQjFXR9y;$8Yg&VM3$y2%@9`SllIysCj&)&{El|{7%GR|K_ z8th!hES}!P?ogG<6B?mOY`4$0T7Q%Mh{xMPpDphBdIg`XW$yug<~9DdM|61*uWrk5 zM-yaxo_9;4huG|?6l3{~ml($qGFt&BV+vFyz2)h1?;z`=A{r?dmj9w?$GbP_z>!+M zA&4BE@P*<+c3kx97$K8$mF>NmmkK7r+JAQ>%T^17V<#S#fE|u{2m$ecl@K}*toiac zbE+@|(=c*5zyqCm`f2*B+#vFPAhim-RWacqQW1(md!Q1ohfrnl_CBP}R+WyS4lBtO zq9x!7RkoJTFy&Jbx;1ZM2iX z%C*-UQ#VKKvEHp44@OTx&3Fx9qq421@3E2QWWMc=Doi|nk24f33yD;$FHFl3_P@&= zluJ90Q2c&^e-C9D+hc;d(pM#%1(UqHW5Hz3QdZ=;^{TjuL#TLs?piKz5aZ&fXTQNZ zaGrp*y019*4A!a-btAlM*M%&ia(%X|Jo<>_flEcY0EK#XSZ)srBkX*?;cH__@Z^Z1 zOZctsYD|mL<|_j^5=CzFR~BqpH>To#aIQGhR8B(EqbV}1ZnlQD++1ghvN+`+VX4pR zggh<%JN=XkckYVTfW&anD#l2)Rv)$q%%?wd-u;_a+pkAiA(j!&LvU0>!eXWuZgOir z3;V3LUDH#UBttZgl!h}#DU2d7i-(@xM&th@>pH-xZo_`(M3LecAt5t+D`Xve%ih_u zWy^L%IVd7KE2FHkE3%U8ktiu8TT+s}zx%JZ_wD<>>pGX~yn3DU{GV|@_x-zn_wNpK zuu0pg5{k-#iXDUTyt4@ht`0o7y%6YY?M+7T>!27ZlY;MXg@cQ5^oq&yfa3hEdYYcc z9E=kPAXX93D~IZrF0ZPiID$`#TReb-x9xYpZe7K|TLDVzQ}0_8M@`;Pu~#7>60M4_ z(eDVD0*?v>Ggqs^pne?D*b@fi)DM`m4xqJJ6v)+?mUB7(EDt(Ih=*he#9O|p>x5^c z!>~5p)|Gd0&hs=x_mOs{9uv34xP?e_?vzKIdS|N?KETV|JpyuQb1HbDSq0-74~G?z z_g@+B?3(i@;GVgS$&X%RCsJGA^@nUw4X_EPfE=%rBdDmmeJW-FbFaUT>}EyvY-7Y_ z`R`(#d5~$oup?#Pj$s#VF8hplw@mUZlGTu!oAVPR!q*P5zYEh;C0Y_psC@M~v>wQS z+`XI9_EWhnoJ=ftDJ?*HMd10h1*3B1;Xzob^U!qn@q_Bx!*?Wzv*LnEjxC@@R1c8T z4YxicZgO_5Tf8Z?vpg`zwM_hKms>9npG`IiE6C>5n{Jduy8=5nH)ilHr5@JH+gHRg zorbVwBHFR@v)HM--4aWxuOctWD~Fgr4e8pb|CsOmJUD)@ckmZs)fa-ZXNozwXarP$ zi7cr7Fnh{D_4bBBfTCVkr^W+bQ#OszK|QKa-l=AZ8sBA|v}EC#ZoX8{{9tGXhBE~p zt|xw699B$>-1^;p>6{Mn_pGGL*MCRQwFDGgOmo8FCUHMTBN}eZS`jIvYS^<}tYl#t zr%jMklu*f))0FaGE}%GlYuL<(0y9=BM54PzHp*PGx#n_I1*=HSsHjGC=>yYTXmt5D zXA4t?1m4~ElendpDnvTMOo+9#IjF*?^8-|5y{7A>^JvxmfG z+wIk{R{sd`Sa6Sj`r{z+($<@=logA+RgDGYOC{``8b3Z#jg=E9>DfL}h4$PaNbCNcn!$-oYn>xQEy-FqMloX100d>DHxJUil7=58}n#UYzn zmBXhLPE5<2>b!^ebP{9-lWkt!+#n4T6!n_bae+`ct+08KVK}$oNNBz7@=3}qlIxr` zktMId%Pd&B&R`q5d#nAyEk&%TV>jW1h~F=Jmv<$p2&D74e%0(u_xmAA*>AzO-cx2s zUL(D6?#nkgw#ETRY)>8?r>h`j=AEO+^<`W*oKfzws;G=2-#UsG9Dmfe1aQN;`kOh4 zo*$v00UJ^$B7JB+4q zT=b|c{$#q#dLjV_98`4uk1uG^hMO+CA2m`Q+OudERWf4hB@7k+ut3(;q!^C26(6h% zI0}eatK|iQrdQx1zYvql5yo&aDX+GQIx-p2?%iWMuANl*@m*)X07rYj->HHQPlFn> zPSU+B{}Mt)zW0{rg7kVz%uh!=IF_v6)u?Z$zH%@9XGKYz?pXgK6F{#l@0z0z-2+ZBCbvm|b+I7Y2)I)5r|+J)!X@JCO774V73&?geDZnte@ z_>cx>j(Z+Idl73`XeymGkc17H&;(dMJ@n&OFKtT9TI_dLr>7-xnrf!(8lSJnPyD*X z-go;NmpkX3`^(c~Us)3{1U8&jFR9}_#_Hx7-bq^%59C^DCEV4R>Dc&W&Su@*9NAxe?0P7oDEi!2+v(77a>2HyCq)Rrs)|R~LQXhNQw-vnVz_d!oliZu0K&dg^`AQ*x zt!-*ZZOo|tV3Mr#bMM>q8C#+0!$0q(ai~C{K$CO^yLsW@^!u@tMqJ97n(MA~ZXaZ) z30FJ|lOGcMZj#{rf4-WCaL^wZw2Q7<;JA^tOw`7}IKhnI3RIW*6A7f$vC(?nmpxk# z2_}gu`AAigXON5e|K?QvBtsp+j~oNv>;xd&&R0UfXKRhlRRTD|mu8mah<43^s*U?R zlg;;~zp@zBZJmVMa@8d>g0?z$h9TbLhi2N0=TPm1$nafh%(cch+A$$nW)bG*@8XY7 zy@_N+5w=K&Np_SI>L2{USh;h2hfG@{#OSOjKck1r^j4pO-gpOjgS*qaU0z9lG3}>D zM>%&}%;Wb~&oa*(6}7(9_HA;+>ZFJibv~9yTeJQWA;1$o!@h3Q={5P|d9s9im%^N% zul2N{Gjk_3?y{ZFkbL<|niVB9HBKn!_%;7-8(T>3JH5KLk8*@9cN+s)L+Vqwza#~I zwd>HIX|&mR8CItNh}bpTp2T=C{DSv-hRKK6wYFBiL0k4-Qug}&ghn5dfvCCT`GU{*3M2Q{`unWl7d%NFKP9)c*C1Mo^AvO`NRy>XALs zsL$_qs3o4e@_YDnapNB+={Ba{p;hAL|(dd{&p+lbvX>*+cnBK%~W2`Nz`W++-Cyq8m^b-abp)DC}3g6E(%ikuSz7j zSDojb{SA`i{1Er5a$J?-CrcaS~Sg*N^ zc20qs8l~gx*y9&J>qk|6&CLm`xMzN<#!_1O0i*D0)z{3MfRB_0_ZS+nC_YiY`))%$ zmhqmf+Un774@e!wu$OkBQJ##1E1f=^|G zi~TL0t$6A^b0{qfl6t;DHvI0pqj%s9vfVo+uk=nb92VwdVhN~hem3>>+04$Gbo{pb zMY86Dr|Nh)?;a^${%X__{}AUN7(7Zk{AOBx<=Nn#mKv*0+66^Ulx~)}lg=E)gV1RK z-w-jb!SNGWA&x;`#9bjmpY3_(`IhaA5qkYGQ9tp`d#ujA98K{LdSjW5_oGcqgu8Ny ze8wLp)E3Ej@_w!Lym|!x<5uxk!!g^ZcY({;CHXWc!$Q2unE0HEW328Dy8m7&n{bQb zWXrgKgYjO{oO_~a0K5LXkRMXRTkj}6ygnax5ym|ADk!X&XQDbD#CFD<+-LDUv*8?t zBNawqeoy19Y?X#k z4oieDOGuzHzl*KuV(2@Qg zx>k|J5=9n#4iS3-mqTDS?grdWtXt6wINa>h_{K6OL{|DX?%7KJ2f2yDSf0U2V#Bj< zi*8V0*6Kjca?7V;^4az|uB~Y?xd5il{OF31Kv{|H;g6j+bWWrg?M(V!gUsr=?l!JfjEdu9f@rh&?5$HWtrvGjZQ?6n?ayyz}8i`J}(Tb(|2=r9GQUxi|ajXe7D zRPmv7N_NwwGtO6szeBl1f<{G+p<%VF;I+ui+%OV6ekqc*qEO9mN+I&0idw@>KHjaD z2kYbxWIYr_(raSIYUrzM*s<5Qb$fiZT7|1YhWI!>@{ikJMhUm0Pt>6x;HtWvgZJ98 z*4sM|akL@RPtN61@>6AuKELp!>e2mYiT~UxGB?dkM5F8<8u7WAt zAU4&s9_-Tc>dGhAxXOD*>X{*8N$~=KvnQk9B%WLlcvL~TBZ4_dxY$l(=P-)ToXhuc z70^5`@ltdjv#7p#bdyvb`)No0dE>BVyp;D3gIH#Id2IM`=Ml9i?-TS7I9QZ-Uj@|h zZJewoJ(qkO<-Yg4uVd^)uKZ&m`Zf^?3|WYb-fH7oUE0}=4dVE<=>QQbOkc^K;gdDz z>{goZ+eK7$YyEB(<3fx~O}i0{M8e6cX#Ue*!WY8%TeE)Vm*=@OD_{>J`Ewk>b>Jkl zKLTJ7$Pox3$e*`fn37PjRPkKjjj=wT|6C%zSy;2maOs0oXb7jWm+GT z5bXVg;*dbbIsSWTMmqK1n0S*m8@b^ADo!7*)OsT8UOacjxf>gvO=LrEs;!wY$$?3B-!_-s;o9_Jfk-kG`x*UwlMxg z|Con`D|xbSk6N$IiO0~CiDkOPKTLI+7k!^}X24MiEA`y{#^r#Lt8;ZzkMFQNveF4+ z6Pf8O_7}I53RbO{i=nee4wic42J6MfsnuS>UNT&LG9I27 z(zcwJZgJL%G-&mS#rvoF(#AgThvFxqzPvo)_sgHJL14@osMr1>C-~>r@BWM4QbDY6=+1-j$y)H z-y@Z`UX6M?H9u`A!}zMUo}ci?9OqVBJzK=XV^Hfl2t4wbG$|#k*i))y@xE5T|o2OBV zTxm+U>$)ZF=dM{MiF#k1pOrweC;u=_lD@H^C{zjHq()}{nD+z-$Y(oeyq=PuG2>H( z8L2is8%M~_I}QStm!+y(!4*4;gAI0NIShwYUmUJ2KKkU~!rI-J11jq+(s*U1kE@BF z!cM6Tomw1O_5Ni%Dv}+Gfq^Tx5ppK5UYnW`tI#foxHNKH=rKneTqA^!ZG$e4N(!s zJ~mKDnfR$P5BMAu9yT(l#hS(VwWO_}mp+XjD6#7nr!fs2Ek zTc=bpNbS~C8-AnPdMP=EA-<&JIGo{l4UX6-d+qpc36H|8YMYT)F9X=8wk5nj^ZFf8 zPujj=Xxis|Qt^f7^CwKB=Y^g6xG+`MBd^nt{xFhp;Ilk?yHR(Ei(RRAr&`GSlbTw* z!87}d9sQgpoi{v-Mr}KXMdBsS?~1b9<_bzb1SS>v>nV-YAX<@@^b|NYQ3NSA)JiG* zS)IBetk0yVEf|q)jR_q=eM+M#+eXQKve}d z95F%#v!u<8m22mUtBB2_O8-tt;ty;ZEZP#u2^7c8)FRte4)MSWY6F14@gt`6>)Wp+ z>f$(4(Ff)3OyHNa5tpsqZxcI^d>&28f?a$q_oCu{@uuTTLK5nN_)mW>B0LR@Ws#+4 z2^nK3GDby-+Dmyj+6$R6nS2AI?B|fEMaD8FhAjgbIt?oD>bN{Nf598XWH-eSRJpbH zaZ4(D^6eiQq^|I(V*EFFqOybV4LPJNQ+fpAY0|n851t9NK+!~Kkl<;@CARPb^*EhiUK_Xb=sP2`(^eb!ura58M_n99 z)RpJlZw!C&oD%M84X42;&z~>%9RhRrfr-e+Gn6KMo|=490rn;b+Dyv6#z|7?Mgz=` zGrI1?tpH|Af@eeHN@+9uWhSmbwwwLF?zsYs^AFpuUJ%%J0*b@qbStmi>03!AsmI*K zVL^(Cb)*0|t`VnjlR_Oi7ejV`g4&wbr8BrGhU65(K3z)%F2Gs5V80p+E&_Rf2Rq!Z z13{Wu%CMLVYaS=q(kC&3XuPQ5&z(Gl$fyw zvSSt@Ga{5(<;M>n`4k!KDv?S^5X@oU;29A;MK3MR8psob5)lg)bI9YQfTPFcNU**^ zB~q3Co(1(oDY{7mt(L&>d9xEAIGC@MO|tG`7JfC`Qd7$;MvDI+ZPnNeQvzx7$Z?tbW*{d&+Fj|elxn{{gcav;edfn5TrdsrU|)7 zGKoA5Xw*?lW%8sU;mGb)WVf9??rUSu&8f$7y!Fg|Gy<7VW5g_$_XbZbf?etgqZZbxWn(+>LVmU>F9fd?#C~BpE zNzIf*z{%Je&p#8xfvQgiEuyA-q!YxMGjJ8c{zNv@&jW9Q;JrBl@f5p>H=j6yk0H7% zk}m4F)MU4v9`_70dxpn0HL<*$g*L|5b|uA`MCOG_i^9>{hV*H18;<@Wf-qfIcmSxSW4!zKJc_XUMBynut{H5clF)GIjxX%>Ncy3(W-+L5zK4j-leQO;C?xs+Y1ts!- z{`11+63xDeaZ8gPr$2t~+({HFN%ZysMza0vcPwHD;;^2n@YL>5k|4yNa={yNeUFcD z(14BF->v%-B8u)l=VjJ1>sm!zmgiCx=^=w0I5z}``w_`*47<>p3|;->LJqy@x!fnx z^f@$7PQzw-H+J9izfZxUubzg>0y%Oe7;80so~qxJ`u_ken6(I)k##qpE>pO_*yAlA zz470Z*b$Z47uv}ysZ6Ni*tI5z?mhgyTYg)!ek~YN8}dFyXSi7x2#!PtGhmeezJ4A- zIWqXOzp-!ik|5+*2t0TK3tl@dtA?3!9HN=)u)YNT1)!?W#tM#h=&v zr~|JQ%x{GCzE3%zk@i7c+-sk|IIarUCB4m(0X%Fbpv(P~HZdPq!15ep=hz?OpFxB+ z5meD$H;ywj1pAr;|3oAQPg0|VEI1Ca0A$ElVZC5D!2EdH2Y$&Zr4yD7!N9rd2odkIE{sxYkRJSPM zdc=hsdD(Pr-1WKvR@FT)ysAw|$oVdHkqCL_c02-fk^Sa&zbgNC5Xc+M^#e+zSK~}5 zAai>i3Qd%RL6)Lx;Jwe)B+=?C&a<@0w6}!Aen_#N%7edPM7Gq+7i4x{p=t!z`iCaH zKXs~17?fCa1Z30WK-EeOdhOXm@4}G-#7t$-76T}k0eRR^bWyq!@OS#6K|nRc^+;rb92xExMZ_r3p4mhkODA~$ zot|N90~RP)=)TI|4@Sy*KxhV7Zie;4BW4xLx5Yl+0n)13P%~e9wgpPE z9iaI$Xe+b*Q^5sDnyxmWOMQ35Ye4HOwg3808+P%{kC#g8PXlzQz2pT27*kNk<9(D@ zjAS*27|nnj-%wi0*J#_{8H#+nz_Z%tkt6#9vC=z2G1Et2|J^*cAEZvbU0UonzG<{>CmPJ$%v+zj*sXa{+-2aHfci0}@e_*mZ#)Vlcu z>X+liwXop}ye?_$L26{krU6Uds{NVE?(_l`7@@rG8sz$wx85kSyM9u{8eE{&&Wf5| zB4juHu{(c;a~6gZffpuJ44C&Ox>0t^pNS>6!g_;5_+jG>fdh^zl_ZwFXLvB~S`((chZ`*_P1+ z;}wqF&@R1($n1RNi{LJSNr-%^X-jT6T%SjtcWoJA)hP3BYSvXL8&1OwvRn1-)+gU@s%=?)V`?x zy>f4x(#_ycDX z_V@3X^+EI^H5W92V+OXlRc)feWoXTiIpRBUyJ1MDnW8zR5qcarAasrz5S1UOt8(gN z^qajOF$n@~B;vB+dceyQ<;QBsX|7N?bFd-2{GGsMJAcSDB9 zLU0Jg)tG?is6Jx_2X+!6aUUfQ{ZQs2UA#z5Mf36tG6tPtnfGs(%ffI9h zFLu^vP;zXOq zbD!^ncEuK6t(-A8tk?1nl2$V&MwVO;uy*RN30>F2 z#eKfL(<+BfkZn_so_^NTXEjGicKWg8`U!M5bg|tjQW|My!>PY-g;n;Z;0vp_k3j;8 z!i^ENn&@rWP4QibumyBhXMUH){JuWRq)~Q8l){rb*M%8x@>XX`2Ndbc(RU{cWck;* z44f{ssHyO;14~;7OaYy1Iu(>Bt_UVzA6eGd`qz0(sn^UE6%S5;73SCSVN@)JbTZE( zQe1fPmM%0>v<{7Z0c~Uxm=1-N8JAhhbvfV4xln3ZKl#?!Y6Xf#J3wtxTq{JT*y2a+ zH-4ZA#89Y0!_R=x5k#et+vZ4^V6g~~t{#sFaw zzWUrElt2VFKX7RZHVtV{6J6C0|9N=NW_F0EvXc-<>ra7}J;dQ-%V@6&lnvb;VoNBF z(7qe$3^VX;8(}CvxOY`AHDh+s8FHwYb})L3och9b%B~#dpefl$G}dR z+XL$3mOmX3u39 zxHg<>3ty6BdJ+O(zJvT94LU05@DD z{XsJFT-GA!bGaw;vzeLRs5Tu1@63_Y{pf!_?H!yG(R(#}>?wI7hISJLBb!JH_Uo;v z)XRvxw*L;)OzRcsFt5;#TLalM0SB2gkcc;D7j8y$Uuq^|B!zF4LldfM9G+L2$nS$r zZ8cm+Q7_>61_z9uSxX7;fc*PmLm4V*4A6n-y&i$jbW6Z!lol#ij7@KXmQxZ^*aLTK z;?DqRLQ>>W&6ykJQ54!;f07C{JtZn8tvYFz21HpqQob>6@S>+7@V;lhA=c|2p#~`g7n?Pf;JsCRRI5RBglY z21#opv>$#`pVc%j3&| zQj-i*K%!nP&!l*Cx-aXLn^`5A#TGG_XWD@DEzth#rP+&e=l&dS)&b{MnY)-A=F?W7 z(*(7u9MCm4|1JAD+d2l}TMVcuZRBj=j)6y3C5M?s!}|$h=&CfKkaC9*DxiscGWRPt z|FLmRR|)31IcG0RvI-CdV|FhNJ?4w15ik>An~xOg=H6Uzf@5)@MbO@y zWvkm6v&ht0zdjz#T}%%9tY_2KLEvOp>FSS9=9Yd_$Niw+xkLLM=8I|2$$@0ktXZex zjD&rFe;m0VavhAn&t2B=_YrzfT7qCA`!Q*7K1k!t#UtniYA|cGQ`MOa0 zkao2%`PM-&sj|9I;APQVbR%)H}`jy3ajxiWkps&JmZz@s1`Ofl3`cI*v)8 zJDM7VP-z{k0hOBnMiV*2y274Nx){tbb(xIeT%LNBZR0FOb?7#>QFCIqnek`T@R@_d zJli$e$oPnqa1NS(;5AGNu2-g|IQNjjaXc^~1))+p$5f9Vs9XQ~xL>%@W`ZW*bripw zYI#LK@tvZYRB|Jp^N^)<>A+RapBK}gA$X!I1Pk5$7PE{%ph#CVTG@A3 zr6K+JRCz!7^V`RQp8(O1-vzr@TH00}Y@AjJtkff?$D@=^V5Aa@>F3i7FEdnu?UQnPw@hKh_O|1m~oH1!`DN zM(N=5i(U7q?`H$__Q)6Q{Q+!|7kDqki)!;_=za{=e#45)Z5b+5=e_y1{NQt`k04OC|*)_5LodT!00D77^v;DDY9t z3AQUSsiqT;);+6XJ?|6K2DA$6ONt7_5CM+Qa_xViMc;@7FSUC!PhCd}$=wvtEs7&| zni}xLWQQ4Gd_M(Id%TL-+4$M9V=wYyee;;DsmM};+Iz^k;EIf+m;`kDzfJRm1&}MF zv1}1G*ge~U?uOzuv@k!1Dd7XYeChXwXgq~na8)|bmsX@ZGc5(3^RT(+6=G+9zi zzF(VnA?)ObEuH{QVJFB#Pk+ufvq+_UHvhTa92&3qR96TztjohP*|{guXF8)dD{oH4 zQ8ExChqdV&0Vj)p&|$Y77?(1-TWT^GPY5F|&+e4?+aVoSZ_*fNZu;y!bO9;B{Q8fC zqPs%|{_bsBA^9Xe^V87sBmtsILhl#E7Eoxx57(eR(!nkaq}e1c=Q-Ad2ca2h6L95i z+*>RjxyUnM$BAeKQZ5M~DlqMJ_xG&@4H0c6Y58Xe#0x8WMJe!yU^xBR?Gpc79Q!UZ zg)(-H9opSxeUWdV=_Zyp6`gnum3XNjm9q>KWAJjRy)JUNMLxheCuxYq)6FE7}Yi5@5kbSi7St72{x4k0&>TR#_cSJ%uo;-6Rw_!Z=TId2t;GQFhHR=Fi z7%~R_d?C8zJ^NY?VdZ8*fY4@9>!pT><(pR6N1o$&KlsSpm`>~}Wcypgnmj?Fqr&T} zo`m~&IMRDiuhLPgW;}{#P!LLBDus?jM6bI11Cvx6!^O!49Bh3dZm1bDBb=J705>87LP4z0tI%UEe$n36y#76L2^G&!v5!%7jK{n zz2lx(>HKKpCv? zZ_1!{QmJP>Z<|Jpc>;$Rye-kT$lU*RHZ-woC&Nz8dgG&)Ezj^J*zB}-LYK;7C>DD? zbg%+wJ;H!Wp2~z;a~`>g`Qggzyv-2!8yP*eD70K#;1TzkH}RW{QyIL}o`#fVxiZ@G zBK}6L*H;z@tql5D`8mgk!uG*~t|G%ENtug(xB$fl(+nk0>hcS~KXcNz5YG>el!<-z zRY1%Gcq;LLVmwGC8NS}p(J?1cY_YkvuwZwnoNNhPRP)G8!Q~6k%reqp)V=BTeT96d zzRbDzG**GSuZV5=!sUUP(SP|?^Oy$+y&s(&e#f1Q2;C}puR`>A|?9)j$Kqw)y?3?h!VD1I>!IoM;W4;`h=t{{$O z*qY%F!fV|ip)rck&XW0*TL=GT94YaZY~=xrecnDR4M zQU+Ej8!F7f3^tQn%O;f;w``}OoQ~b9A@Jgn1cgTkcyf#4arMQwM=m{*;^af+7Lbm8 z?h=!9bKN(+Im9W)DRy=StPcJSp9_of@*(KA*{;9r3>?588m~>x-F7^L_<|_I@hpV;H#dRGHuykEs_+0No zD&;+w(0Ohf0@+D$KIf`I`@3K-?yV7Ygc|i-KA~uZO;}79b&2;6Zo!jt-@|w=e5mf? zAi8*e9-2xe!iBOi-L%1DR8kQ9x!f)yfj?vOu&cPE5ocvHQg%1hv&0aBWpPiU*aXZ$ZI$!z+@Il9w^+E4~?j}S&v`3-gH@Xxk$0&<9V44MbI_4 z>Rm{GDje6F(u!z)EIHIt>}Lp&n`!nkV^HX5H{n)`UzZ4?w~=w6#Rk7H?>f7Zap(Pt z{vTDRKcI~&TR{oMQ8gHFRk!U<JBHruY&p|w=cE0wJg2c2;mrC8<+lCjsX*B) z6DT#x!5Lxa)Vm!nmhSEQ*p~rL5y{olqQPg}z;U!2d=gbaa3pnJQs4*`i$xn-R5vD$vZ6s27cabRoq<0B|m|Xffu;fCU zpk(=vUqrzAtq7`@}MfS&iLgG~ygfOvJ*3@1? zk`elh*sA`&sllB?oESaE5fYi@^({6XC5wJ|i%VRrp z^+T1*FQ{BvkCAijMB?if%|WU~%E@apC3GI$oVba`s#%|*E{3b8Wwr9EI@GrDCpNc*5#@Vq)z zxExlISTliN>0NF=sN73`g?=yoMWAi+>$L(ISLjhRoq9T0ZzGSHUIu^N6Srug&^X(I zXC68~B7lJgNQDr{GxWwu2l%X{9p|TY?d}e1|4?S5fIzlsBs)Y9Ar|f4utP*916SS} zBcfif9)nm1xG?1G-NAq(UsMl4_HDO^+=501!v{d$ze2s(TvrN@F~3~oJ2)mnWfpJcaTk#^l!@p#z&PC+(RUB2?z9Fzls)!E22+N!8AZiphD3r9}Ki^ z9Tmj~w9~ds@H~Eoc&iUA!(QK*7rz3aN<08*9dLyQ^=+&zy(9s&piYiS$P7{;Z_B}R z&S5&pPw^43*jkDL&ca&PQJF*u{89N7cZ`W)Yu?pF#HZup!h`xKS z4a3)*LBE5ro?NI=CJXlN?Ko3@H+d&!x}lsC^3N>5`iXZ<3Q-f$Dk(Y|n;&S~7jOD& zEs2qEhWWjp-X9kf@kK+>o8#vyJ>?f}gNtSC*qffJy|0W~iTL1%SX{V*31syMZTVWi zT^0vmCGVCaRfaG{YHyZoX=es0ov13YtMa9fPabvB-5N~dzrYQcWifd5Iv8?w;lgIsrz5jYPAA}V4IPSjHUnWKf&AAW} zJvaMSIr>;AjEY2grlz<(CWpWobZ6K=0D?Sg9WLMsM8cSkaz?JO-JckkdwO-giv=qe!c%;|G@%TGj|ZVy`Ra>?ZfmL}#N5z1tU#|LV~}il<`=P;ve7f_ z&mFju4E#*(6>T4t&t5pu^X$0`KOf64M`AIh#7p}?^}kcUh9K4rDIQ9H4OkrgEIa0? zqT{4Ezn2%nqdSVGBL9CGWE{b`I;8O}+?Not%fGK(0`Qer2_`Q3dx_sDHikO z|36cRF}_NEU-sX>Wcva$b>WEJ>isr!-&8!X<^)NAKZEP~JF&uqx(9p%=cNPv(*HgK zg_|VGeoS!d^>^L<+&>y3X_mG9Xz1UAa|d?;F8HcT_Ok$X{y9*-xC5QZEF=-bK!!dG zWN5ZKF$XF!HC+GMqWv3i9-j06q`;aE61XkWvw%D`8NodbHWXBBv`har3&=x#p#@Ng zfs*^&UkSq>$WsRiO+~?1bW>U%%JDOz|3AXl7=(p6(RI)1Z+jq!T7cbTj;6=p0WwmA=0NTh*rTU`K1En=MZL_r&j0a1FkhaOK(wWK4X3 z?z^mzF|C5);x6#m$A7GY3`QQ57lSek^#;`Y<^wsveT~_OV+4X-DG)=yxCwSg4Kbd@ z`mX{%Lm*&sT05N+WbBim&(!JZnF&z(g1PvudH3JaS{ow_0J;;Kf5FO(%y+o-Pk)72 zclrCrAy%H4Pr|YrlawyQ75Na|X#$FQvOiM!7A-S4Lw6zYRfmqPnmuPcr!{K;5q|p~ z`kZw=bDz*;NN$(IT#((IWo55Lx}3{hKt-Rt`b@4qczIU0BU!*|@YC6E8f+XgZeMUz zd}an(as4up5dv`~FZtS#rcn(tWuQRi*;Vn3^uH~&?;_J6vwoe)gZRG(`C^Z5Zwdt! z7^$d!D$Knzmres*m&hpX-wyNyO^L%ckAinLD?DekA&C0q!;s&M! zX>G4B5#BLK==iOphma5y0$PFnV)u>7yC{{`u4L7r@#P8kgM^Yz4e>d44rvNc18{!}V z-{LI85+vq2fNQ~Le8D?$5VR;)$lwYnrxLKV0F4`#rz0L3wRS!WpC4_$-+{&*NN&XM zOXy*Ha0kc}1wU1}+znhGib&WT5o_xzzw)!r%S zd>aR@TjA$0Wd%nY02zYLKQgUUcHoDkia^gZ5Vte9BSC$YOff+>oR+$iDO1iOOs=U8 zvc7NNK~g~nPGd<*Q4;UebCDCyBbYC^fxUIIMWE(=9?t$+g~RA9>(n>;R?j; z#SM-aJjBtbfo}8&(0;xC)de1)0WcgPGJvoe9ePy$ULzBc+!bg}f)+Z`#!`71^jKxa zge*|*5N4x=Xm0v3r=0zSJq z(fO}-rF}tD?E0Gu!S9ug;IJdRMXRmMtMFbj%SSk$nA~soC#%uNtcM$PdArM67YJjv; z_$L1Y0Hv&w*M1z8JlNm<9KsV}@3}!QTc?5)ELPzOGB?7cx=)Uuh{7x&SQtAM#Ipuk zv3mKKA567yjx?m5Mkq1OI`YRaDW$##C;Fxb&+IqHfE~bp=Jlaj#dsODBtRRFD%itL zw7dh+w1Q-n+jQ)hsN0V3lFOiBaVX}hJAVj>R5mEqQa@xUeWkGei15P0N1?m_A;0@4 z#y1RjaIP(>#c;FkTVnXxhH=xkDnpVZB?`;LQXm!>B^OQ2H@Mfnst~J?q6_L};IA3< zDc}cAT$wF20lAaJjsn6~L^uwv$)NHeXj5kosO=LPkeE6C4cJfNV#`X)G?=&-hvP_w zLudO^K!$GAjG68tIq6=+B}XMms7LZB2AC&zApy@StOOzKj@NM7&yzU<QU(gFIuE_X+CdSWx8me*`z?t663j7rbgb(4O6L{!^ znG6BcN6`=wcFmq6_=WnlOfTRMWo!Y?LNa?${gVvzI(o3PbgDq5KHFZif27EvrmcxXN4 zmxlP&w6#}XxdNhtej%Xalk=8I;R_uCnaP6!&y~%eP%z#a?A|`P)fcjB_kpbPAcs~O z8=_zLGOn7~C$u{XhYwCfkhFnp10DkXE1WixAO(+P-K@GF^Su`rLEF{^B10F4p;P84 zp87vS-v-MtnfdQ}z?-r&D2^yRh;FSiZ%*C6HVUn#2p#15_ejAepF?P8Z|$I?hUTGh zU`Zi5u0^63L4Px`JvkvEZZvM7{OYc8{@FWO)dVt5-0cX7_ zh*AqhzQgna0Veg}26;PL4vu1jWh5uNSvEElzgnt$CkO;Cr=We@UFZT-AmP3XdaH$P zMg)p2=-uY&-V7cjX!hR*uy9?N>tmG*E66g{mZU4JpkcUV$!qH|)j)ha2U6Gu~1j!IJc=~{b5Q69oB8+i~N&mUr@&mD3t)A2Z zmLuox2{xBQ{GLL=2t4Iqa$^j1mcJZ(GYpd^C_~?ahd8SK{1)X^N)^1hu zEdW_`d*?bK$va6JpQ6>@SD(6oSbtv@*u4v+;hj!h*TdS!6?J7C29M-(|e#)8|Ke^>h)7oTYb+u=y} z%rWR~JP32XcI#c~g8$HSk29m|k#iV9my;FHRca#)VUcon%BzP=sh)&#=j4@O~@US1?E~U9!RcnTUxy; z`pSLop2(khfC>3@9K+-@H1r8N&|~)}=o}};P()CsERZwodQ`O>#M;D(bq4|g`_g>B;mxsz#N1!{ap{0tH)4eNC zI+zbPfWC|nSF7FW%X!l7!hW+2D=zb zSEP61lP41ybV2<0gFVpbmd-1w#L&(A_b;i?{hgw_Z9zx(CO!wq;jpEI1`MlaBTM@` z?DwC|CgKj18^6g^`13KZ&!G6Uo-2QAB_PsdVUk+D_}8=OIKi`ITpRlE=L3#Wa6T2i z?MWVS^Cn%DzXm%tI~m>l((+;e^Et|eqV0>=A?ByG-hw1T_OKC%S z1KN_0zV%%iKWM;k)_v$v>R(SKkHyVi__LxI=UZ6a+u~=>CgtR`Gri^t1=hFl4g{R% zfz#yk0o*4^?~iH=tk`+}@}FhT8-8g2SywEs=?e)F=5R=C_nkkVo*jn19!hFkYp4NT znzNFbckhQL<*y8%=VAVDS6Ms4u1c-h_;*sVHwdFVdu)l_ADa#ok%P8iqPEP&WL;SK z?wL#P^2kEgUv6w&Yux(e63zES6@WLjwGq$r{;zl}I~5ZeZV76L}A z-Wr+b10UPr`C-}h7nBXh_QkLvW=unRr9DKHyJYt^`dsd6omB(kiTBl|jm@FPU}e{S z=oy^E->^Pj1wwh#TZ(T-A@0ls@8m4;6%Oj-H>)!{#n6-kN+NX*zzv0 z<@2BRP5!$}&njmZhRPAxlAFE4m%_(RFXwScbA_B@ev4d(&`&$Daj!o%I3e>_!o$f@ z*1FA=(vW(_jf0Foaqyf$T=la`0%i*{;J#in19BcIWQk9D`i!c`{EA!dz1AS|^hiC( z;CuR-FanEC8$#Y?e_3-7z1*Btk*$4U!18nJ!%#z#!hmf$u+P-$r3p%435r zlOAKm?YOe`DyKub;#&m~6>BzwZ033k$F%jb4OxK6x2L`5zLn5>B&npxF8K9zEq$hK z+dI2(dM9}CTIg!)|Fm`G@lbC6Khvm5#Y}}U#6(e+kR@apG2DAm6e)XTmqM~<29>qC zEv^uA$K^8F(oke;p_{Ttm!u+V_ARpX`^@*g%slS>_1E+`&pDsv{aMcEobw!$PJhom z2&74KanZj;vccJm>hpMk;Y(J4o4mSDw0(fjWTxN$WVh5KIFM=BW^3Mb_Q`0;!XoSH zWOtSuw>LC>3<;J^LO`2^WuHgoyhbfd$#38P9y|Gz?~;P0QtjU$1V88QNYNSEQrEmO z`g~(xt&+*v)C~N z3opiIHG#E4u3ys0&d%s~Mfxz*MLHI~J`Tv;)y~iLi3QgS5RbCnRgC$U9#eSME^ewd zNQ<0urEB0J>n895S)Sv5wJI-4N2i@|cmquK5QdZ_)>hyb}qm_a$TZu`pEP0+P^SP#t%pR5@ zdzgKHTt<9Vyh&7&Igy;ybUQH%moH~uscDIU9gKHMllMn3vRhJg!Y|zy_kn`XkoLFI z#wa@M{x|d<66!sk{@pydSNP-KWn;w!VP|=3=*eS=*52;iZc2YAZ`D4=i>;czH~z(= z9FLpO8aBILXAy&23j4HP>@PU>F`b?sS;NAa`k?Hya>2@p%R}!!u3Sv(g`+%)1?qOenfWNkUVuGre{_ye$7kAWM-x z>&etisGhr15bC^g3Vnr~y?9k;>39Bi9_;(hv`z9J9^6xJYC>xb?+??L zy(*jyss(o0m`m1nTuoni0$KGEThpY%nWuN{gXA5|@n zE-Y3p^~ncmncWkTvyre#;CTVp-K%MRJ=Iv}nFN)EKc>A8l4Se0dHYnJQ53HZed8p0 ztIGC)BboP5uU7mG8HQv=Y>{T{#Uj*;p{id@bk_TSchuHhPw6X z{(e%CPEe2@t^{wF9}pDf>$BmLb@x=_Y(&*i`AsrK&8#{ICMyFWh9OOGhDg$KnxERo z3U|C(GfnLo`Hy>IMYhz=_sNIf75y>)a-cxIeBznrx5B;GF;mx|ini72cCE@NI=Zja zxVRhR7Xa?nW_cd`>7{3?naw3C&rG>`E_A2fUf+RnNtw8g&XVf&SdF}_sGKWLB+VFb za+f3$4MXU$d7&nj=j{Yn)egbO>g%a>QreR-p%?AZHaL`lObcwd(Xc#i(6p`hZeys6 zcL0IQfw?7tvLE#lBi1WYJ;~+x*&jXu+fUlxV5AA>aIE6T;ls?W5MU~W9td#tz;>au zMn~D}d|`unLvRJho2L$`e)tTxr&e}Hm&$GS2%Xh^1QDt|uRY&3Wq;|Hn@jdlSv9Aw z`sgx)DIJYw)sP=~X{$Eiz(du5G;MlQ23_G6erz@4uZicaw=diBRUfzyM%qTe20 zfHQ5m0UzKJ_8SmEMYtS=!y|Jvyw<2)E)b zg;qM{2EfvDX>wtxog;q7S2?} znRl2?aUnJ3^k4;p)G9o602Yd-#tkXLyS^@u1yn69_6giSE&Oy0?hR;x-5wnt6%3Tm zu;ovIY8g6?+WH*hUPs+cn%TyYg_|DDTUVd?r3+FeD;ay(TM$e0x}R6#u`C!4hPkAA{|fyA#&Q*TUJCcbY#YT?qmKl_2rf?P(KHOJ_rEnF0xm zh8U|AnnoDwmuU+m8u=)BZ zZ8~+}54hDrEnMu~3+}yUvG5phz?m$e>b8_e-q0IHGCP zHXljHsBIRaI0kOzYx>H}hR+VoJSFqS=IWQGZlxPT zmfS&nD(wN)pT!=X6erl3}ngP9OhKj+P*-}-RSD$3w>drp3WCv5V&%ko>+MYX9; zV|~RYD&zBB=_J&e|7$a@b$EjtQY~SIJN3QW<7Y}YfD!PL! zTy&$e)Ejn+2*JG!gTa0+y4B7Oa!r0zjbi0d^=<=AQ_5pppvt0~Ihv1;Z@?l)Be$d9 ziWlK2!=SP-cjx1CcCO9XZ^~3ou7f)5zJjGzgzD zayw|->fCG#lOhmX@jeao`{xkcuw%rUuxP-VZpb%>qXaxPTO+>7uI+QRS4<4YNQtIO z0M~lex4x@$T=aJ-Q*U>T0|)4Y2jSMAU#al#wcN?W=UMhDk<2mnY7#KM(NWkqV%5vj z_M`d|vryN{Mi~BEA}C6R8&3>FGxHP4s8v)q#Mqg8NhoKe{jM6KwaRuZvE55g?&_)p916La$~#Av6`!nN z=KY$*0*)f)9+c`cD3ty@h=C({;ZEfv$h4t5WE8tO9iM+MiN!*AL&~>a=*0Y~b zwKwUvwM|T%C%|>J5)u*%mXV}e=+v#9e|#u8fsia?H<6Uv;(eE0Js^#uncle{60J;y zQ4HSqB;QT!KH65UccP zVksM$Z>>O3R_6+8GH)|wX9)=Ok!S>H>@jld6uYsqJ~Y1Kb>VqpjFaACCK5DR6BPzo`uNU!R}3-G zfAC>W0#10mZtip88fOS1Oo0p4CJ01@^X09Y0*ST@WtFUYCPSv&Sk6eMJSYoMI9W%% zNmdiGlr zSjwu)z!DYgpuBUB^M6w_s+&9Ev&oVM)OfC3xguS*tu>!PnJ8o&a_weI9eyNP!HZit z{?pV4>`kVU%AZ=9n91Lze4L%)L+91XKvZkB?>@G;sJi@3Ef-7!vMx;@^FLXoRVlao^ec_!A2 z2xZh`$YCff=UuUpBSeBEakt0cS00siiQBXnF-;VHL%2y2trn;voXgMTJWMhMp2rP# z5l!dzuj#ou5Q-=P?4Up)MRG?OZtY&WeRKKT+={gp8vyV_k|q6v9O*0D&}yLXv0xGgNOgKOC^+JX|kM)9R;q>AMr=&P>d`i02Rv6-EoN z35$S+at^UGr)b$1@y@Bw9l@$yrzsNf2& z|5`%AY=C$g(qVF;iUBR{E;4daY-N#tpzGSABXYY(^pai%-G&8_Qq)@nBv?AS2h*2} zzqEuQ=>3VT;usaJr>Cc1wwpHm(5TSc2%$|b)G0s~8PN?b1I54{sw;h3ADfujaBn34 z;gdDYyaHw<$196D4+rTKj~5L+42_Z8Kc8@f&T^YyAi|&9h=8ZK6Cn`4kFViY)deH)@5SW`L~va$-OeiAtAq zUDAL68mZqK&W*m>jYiN4utNjvh$9i|94))isv(<+#hR(mCkm+rReLxxBa$3wkgcz; z(2_J{tAo~!BG=gF7Ui@>J4eD2u1Gck32b0G9hkl}9HNRKwvuOOxT%+smDXlj0Ugq;t#2;ZP^E7yltGXYq zafqglZPq`3lDSDPt-XNwKm=&~u5x|seh3PoVKhdW3~;=CujB{^aVk6ZK6^q>aF>_a z#(h;K^mo}9PN(p05hPr3oP>))>f?rmTbyWZLCrhZmODN#6ch2hu4=ykq78Ha7DO53 zNAO7#fc3goN5#po`0OJhLP8zcM8(6;@y%&Wyw#odp5g}>4oSc(Y}BF*79+jM2~^)S zPC_&OM=aw-&tkT5yctt_C!Z<$O3grBmAqPR+gC@$H9~@V6fehwh)uhxlNK@Lq?7m^ z7O}VP?szA7=c?H&H$)4TF3L7%lX4e%t&!<$DGF*!Mdyo#pFb|iHji(_Y^@-z8L3Gb z)X{peD;VKe{zBcd5IZ6u>&U5o{7*tA;Eq+D|IE9!M#>1J0na31uujz*Hir>5wMLCB zx3v%2WQ{Q4eDPYzBPny!19fk>X|%TcwIlyOkU)0vPw!^|qW_)_{j1ae&T9W3x@cQL zQo=v&M*!nLJT~osHpee^Y9OTfA0C;zh@^j)AphMcSsdEb9kG`Bx9ku@QdJ=zKT9m- zM8~z#VZnoR9NL(4xFe@_h%Wx=z3B=#v8}s*TZ^Z)BfCNR@?hVgv-kchV9t|Ha6Ki| kFWc6b!@pQF!Oe@+^T()$7 Date: Tue, 9 May 2023 12:03:47 +0100 Subject: [PATCH 05/41] updating documentation to have same terminology - peeps --- README.md | 7 +++++++ design/two_table_design_recipe.md | 20 +++++++++---------- .../{posts_seeds.sql => peeps_seeds.sql} | 8 ++++---- 3 files changed, 21 insertions(+), 14 deletions(-) rename spec/seeds/{posts_seeds.sql => peeps_seeds.sql} (71%) diff --git a/README.md b/README.md index ee703cecb1..1f4011a4dd 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,10 @@ http://localhost:('Port in use')/ 8. Send an email when someone has been mentioned in a post ## Changes to plan + +## Techonologies used + +- Ruby +- RSpec +- PostgreSQL +- Sinatra \ No newline at end of file diff --git a/design/two_table_design_recipe.md b/design/two_table_design_recipe.md index e381dec04b..b7753832cc 100644 --- a/design/two_table_design_recipe.md +++ b/design/two_table_design_recipe.md @@ -41,17 +41,17 @@ I want to receive an email if I am tagged in a Peep ``` Nouns: -posts, time(and date), content, account, account_id, email, password, username +peeps, time(and date), content, account, account_id, email, password, username ``` ## 2. Infer the Table Name and Columns | Record | Properties | | --------------------- | ------------------ | -| posts | time(& date), contents, account_id +| peeps | time(& date), contents, account_id | accounts | email_address, password, username -1. Name of the first table (always plural): `posts` +1. Name of the first table (always plural): `peeps` Column names: `time`, `contents`, `account_id` @@ -63,7 +63,7 @@ posts, time(and date), content, account, account_id, email, password, username ``` -Table: posts +Table: peeps id: SERIAL time: timestamp contents: text @@ -80,14 +80,14 @@ password: text ``` -1. Can one account have many posts? YES +1. Can one account have many peeps? YES 2. Can one album have many accounts? NO -> Therefore, --> An account HAS MANY posts +-> An account HAS MANY peeps -> A post BELONGS TO an account --> Therefore, the foreign key is on the posts table. +-> Therefore, the foreign key is on the peeps table. ``` ## 4. Write the SQL. @@ -100,7 +100,7 @@ CREATE TABLE accounts ( password text ); -CREATE TABLE posts ( +CREATE TABLE peeps ( id SERIAL PRIMARY KEY, time timestamp, contents text, @@ -117,10 +117,10 @@ CREATE TABLE posts ( ```bash psql -h 127.0.0.1 chitter_database < spec/seeds/accounts_seeds.sql -psql -h 127.0.0.1 chitter_database < spec/seeds/posts_seeds.sql +psql -h 127.0.0.1 chitter_database < spec/seeds/peeps_seeds.sql psql -h 127.0.0.1 chitter_test < spec/seeds/accounts_seeds.sql -psql -h 127.0.0.1 chitter_test < spec/seeds/posts_seeds.sql +psql -h 127.0.0.1 chitter_test < spec/seeds/peeps_seeds.sql ``` \ No newline at end of file diff --git a/spec/seeds/posts_seeds.sql b/spec/seeds/peeps_seeds.sql similarity index 71% rename from spec/seeds/posts_seeds.sql rename to spec/seeds/peeps_seeds.sql index a7962cab09..27ead131a0 100644 --- a/spec/seeds/posts_seeds.sql +++ b/spec/seeds/peeps_seeds.sql @@ -1,6 +1,6 @@ -DROP TABLE IF EXISTS posts; +DROP TABLE IF EXISTS peeps; -CREATE TABLE posts ( +CREATE TABLE peeps ( id SERIAL PRIMARY KEY, time timestamp, contents text, @@ -11,9 +11,9 @@ CREATE TABLE posts ( on delete cascade ); -TRUNCATE TABLE posts RESTART IDENTITY; +TRUNCATE TABLE peeps RESTART IDENTITY; -INSERT INTO posts ("time", "contents", "account_id") VALUES +INSERT INTO peeps ("time", "contents", "account_id") VALUES ('2023-05-09 11:09:00', 'hello, this is the first peep!', 1), ('2023-05-09 11:10:30', 'hello, this is the second peep!', 2), ('2023-05-09 11:12:00', 'hello, this is the third peep!', 3) \ No newline at end of file From 604b3a06376e10dadb317152f8f0cc7907940193 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 12:59:45 +0100 Subject: [PATCH 06/41] adding pg gem requirement to Gemfile --- Gemfile | 1 + Gemfile.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index 866f834e6c..f63ad85e63 100644 --- a/Gemfile +++ b/Gemfile @@ -16,3 +16,4 @@ gem "sinatra", "~> 3.0" gem "sinatra-contrib", "~> 3.0" gem "webrick", "~> 1.8" gem "rack-test", "~> 2.1" +gem "pg", "~> 1.3" \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 58a913dabc..884ce79b7b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -11,6 +11,7 @@ GEM parallel (1.20.1) parser (3.0.2.0) ast (~> 2.4.1) + pg (1.5.3) rack (2.2.7) rack-protection (3.0.6) rack @@ -76,6 +77,7 @@ PLATFORMS ruby DEPENDENCIES + pg (~> 1.3) rack-test (~> 2.1) rspec rubocop (= 1.20) From 6595839ac50e554bcf0a62e9465e25352318aaf6 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 13:11:11 +0100 Subject: [PATCH 07/41] updating gemfile to have pg gem --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index f63ad85e63..a70fdb6f3c 100644 --- a/Gemfile +++ b/Gemfile @@ -16,4 +16,4 @@ gem "sinatra", "~> 3.0" gem "sinatra-contrib", "~> 3.0" gem "webrick", "~> 1.8" gem "rack-test", "~> 2.1" -gem "pg", "~> 1.3" \ No newline at end of file +gem "pg", "~> 1.3" From 1767ba41876a661d886f76bf5d19c8d57d03ca08 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 14:21:12 +0100 Subject: [PATCH 08/41] peep repository class - find all peeps tested and passing --- app.rb | 7 +- design/peep_respository_class_recipe.md | 176 ++++++++++++++++++++++++ lib/peep.rb | 3 + lib/peep_repository.rb | 26 ++++ spec/integration/app_spec.rb | 4 +- spec/peep_repository_spec.rb | 27 ++++ 6 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 design/peep_respository_class_recipe.md create mode 100644 lib/peep.rb create mode 100644 lib/peep_repository.rb create mode 100644 spec/peep_repository_spec.rb diff --git a/app.rb b/app.rb index 1dcd37d299..b63f61cc29 100644 --- a/app.rb +++ b/app.rb @@ -1,7 +1,12 @@ require 'sinatra/base' require 'sinatra/reloader' +require_relative 'lib/account_repository' +require_relative 'lib/peep_repository' +require_relative 'lib/database_connection' -class Application < Sinatra::Base +DatabaseConnection.connect + +class Chitter < Sinatra::Base configure :development do register Sinatra::Reloader end diff --git a/design/peep_respository_class_recipe.md b/design/peep_respository_class_recipe.md new file mode 100644 index 0000000000..3d714398b0 --- /dev/null +++ b/design/peep_respository_class_recipe.md @@ -0,0 +1,176 @@ +# Peep Model and Repository Classes Design Recipe + + +## 1. Design and create the Table + +``` +# EXAMPLE + +Table: peeps + +Columns: +id | time | contents | account_id +``` + +## 2. Create Test SQL seeds + +Your tests will depend on data stored in PostgreSQL to run. + +If seed data is provided (or you already created it), you can skip this step. + +```sql + +TRUNCATE TABLE peeps RESTART IDENTITY; + + +INSERT INTO peeps ("time", "contents", "account_id") VALUES +('2023-05-09 11:09:00', 'hello, this is the first peep!', 1), +('2023-05-09 11:10:30', 'hello, this is the second peep!', 2), +('2023-05-09 11:12:00', 'hello, this is the third peep!', 3) +``` + + +## 3. Define the class names + +```ruby +# Table name: peeps + +# Model class +# (in lib/peep.rb) +class Peep +end + +# Repository class +# (in lib/peep_repository.rb) +class PeepRepository +end +``` + +## 4. Implement the Model class + +Define the attributes of your Model class. You can usually map the table columns to the attributes of the class, including primary and foreign keys. + +```ruby +# Table name: peeps + +# Model class +# (in lib/peep.rb) + +class Peep + attr_accessor :id, :time, :contents, :account_id +end +``` + + +## 5. Define the Repository Class interface + +Your Repository class will need to implement methods for each "read" or "write" operation you'd like to run against the database. + +Using comments, define the method signatures (arguments and return value) and what they do - write up the SQL queries that will be used by each method. + +```ruby +# Table name: peeps + +# Repository class +# (in lib/peep_repository.rb) + +class PeepRepository + + # Selecting all records + # No arguments + def all + # Executes the SQL query: + # SELECT id, time, contents, account_id FROM peeps; + + # Returns an array of peep objects. + end + + # Creates a new Peep + # Takes a peep object as an argument + def add(peep) + # Executes the SQL query: + # INSERT INTO peeps (time, contents, account_id) VALUES ($1, $2, $3);' + + # Returns nil, just creates an object. + end +end +``` + +## 6. Write Test Examples + +```ruby + +# 1 +# Get all peeps + +repo = PeepRepository.new + +peeps = repo.all + +peeps.length # => 3 + +peeps.first.id # => 1 +peeps.first.time # => '2023-05-09 11:09:00' +peeps.first.contents # => 'hello, this is the first peep!' +peeps.first.account_id # => 1 + +# 2 +# Get a single peep + +repo = PeepRepository.new + +peep = repo.find(1) + +peep.id # => 1 +peep.time # => '2023-05-09 11:09:00' +peep.contents # => 'hello, this is the first peep!' +peep.account_id # => 1 + + +# 3 +# Add a peep + +repo = PeepRepository.new + +new_peep = Peep.new + +new_peep.time = Time.now +new_peep.contents = "we are adding a new peep!" +new_peep.account_id = 1 + +repo.create(new_peep) + +peeps = repo.all + +peeps.length # => 4 +peeps.last.contents # => "we are adding a new peep!" +``` + +Encode this example as a test. + +## 7. Reload the SQL seeds before each test run + +Running the SQL code present in the seed file will empty the table and re-insert the seed data. + +This is so you get a fresh table contents every time you run the test suite. + +```ruby + +# file: spec/peep_repository_spec.rb + +def reset_peeps_table + seed_sql = File.read('spec/seeds_peeps.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' }) + connection.exec(seed_sql) +end + +describe PeepRepository do + before(:each) do + reset_peeps_table + end + +end +``` + +## 8. Test-drive and implement the Repository class behaviour + diff --git a/lib/peep.rb b/lib/peep.rb new file mode 100644 index 0000000000..848781b7ea --- /dev/null +++ b/lib/peep.rb @@ -0,0 +1,3 @@ +class Peep + attr_accessor :id, :time, :contents, :account_id +end \ No newline at end of file diff --git a/lib/peep_repository.rb b/lib/peep_repository.rb new file mode 100644 index 0000000000..0aa2bea566 --- /dev/null +++ b/lib/peep_repository.rb @@ -0,0 +1,26 @@ +require 'peep' + +class PeepRepository + def all + peeps = [] + + sql = 'SELECT id, time, contents, account_id FROM peeps;' + result_set = DatabaseConnection.exec_params(sql, []) + + result_set.each do |record| + + peep = Peep.new + peep.time = record['time'] + peep.contents = record['contents'] + peep.account_id = record['account_id'].to_i + + + peeps << peep + end + return peeps + end + + def add(peep) + # adds a peep object to the peep table + end +end \ No newline at end of file diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index 2f8f8b5051..9fcd08e940 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -2,9 +2,9 @@ require 'rack/test' require_relative '../../app' -class Application +describe Chitter do include Rack::Test::Methods - let(:app) { Application.new } + let(:app) { Chitter.new } end \ No newline at end of file diff --git a/spec/peep_repository_spec.rb b/spec/peep_repository_spec.rb new file mode 100644 index 0000000000..a463de96e3 --- /dev/null +++ b/spec/peep_repository_spec.rb @@ -0,0 +1,27 @@ +require 'peep' +require 'peep_repository' + +def reset_peeps_table + seed_sql = File.read('spec/seeds/peeps_seeds.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' }) + connection.exec(seed_sql) +end + +RSpec.describe Peep do + + before(:each) do + reset_peeps_table + end + + it 'finds all peeps' do + peep_repo = PeepRepository.new + + peeps = peep_repo.all + + expect(peeps.length).to eq(3) + expect(peeps.first.time).to eq('2023-05-09 11:09:00') + expect(peeps.first.contents).to eq('hello, this is the first peep!') + expect(peeps.first.account_id).to eq(1) + end + +end \ No newline at end of file From 60eaaba58c9f367b8c4f027d41242fcbf7712d4f Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 14:32:35 +0100 Subject: [PATCH 09/41] peep repository - add method tdd'ed --- design/peep_respository_class_recipe.md | 16 ++-------------- lib/peep_repository.rb | 9 +++++++-- spec/peep_repository_spec.rb | 18 +++++++++++++++++- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/design/peep_respository_class_recipe.md b/design/peep_respository_class_recipe.md index 3d714398b0..b8202de6ef 100644 --- a/design/peep_respository_class_recipe.md +++ b/design/peep_respository_class_recipe.md @@ -89,7 +89,7 @@ class PeepRepository # Takes a peep object as an argument def add(peep) # Executes the SQL query: - # INSERT INTO peeps (time, contents, account_id) VALUES ($1, $2, $3);' + # INSERT INTO peeps (time, contents, account_id) VALUES ($1, $2, $3); # Returns nil, just creates an object. end @@ -114,20 +114,8 @@ peeps.first.time # => '2023-05-09 11:09:00' peeps.first.contents # => 'hello, this is the first peep!' peeps.first.account_id # => 1 -# 2 -# Get a single peep - -repo = PeepRepository.new - -peep = repo.find(1) -peep.id # => 1 -peep.time # => '2023-05-09 11:09:00' -peep.contents # => 'hello, this is the first peep!' -peep.account_id # => 1 - - -# 3 +# 2 # Add a peep repo = PeepRepository.new diff --git a/lib/peep_repository.rb b/lib/peep_repository.rb index 0aa2bea566..11d90fc920 100644 --- a/lib/peep_repository.rb +++ b/lib/peep_repository.rb @@ -13,7 +13,7 @@ def all peep.time = record['time'] peep.contents = record['contents'] peep.account_id = record['account_id'].to_i - + peeps << peep end @@ -21,6 +21,11 @@ def all end def add(peep) - # adds a peep object to the peep table + sql = 'INSERT INTO peeps (time, contents, account_id) VALUES ($1, $2, $3);' + sql_params = [peep.time, peep.contents, peep.account_id] + + DatabaseConnection.exec_params(sql, sql_params) + + return peep end end \ No newline at end of file diff --git a/spec/peep_repository_spec.rb b/spec/peep_repository_spec.rb index a463de96e3..0568239682 100644 --- a/spec/peep_repository_spec.rb +++ b/spec/peep_repository_spec.rb @@ -12,7 +12,7 @@ def reset_peeps_table before(:each) do reset_peeps_table end - + it 'finds all peeps' do peep_repo = PeepRepository.new @@ -24,4 +24,20 @@ def reset_peeps_table expect(peeps.first.account_id).to eq(1) end + + it 'adds a peep' do + repo = PeepRepository.new + new_peep = Peep.new + + new_peep.time = Time.now + new_peep.contents = "we are adding a new peep!" + new_peep.account_id = 1 + + repo.add(new_peep) + + peeps = repo.all + + expect(peeps.length).to eq(4) + expect(peeps.last.contents).to eq("we are adding a new peep!") + end end \ No newline at end of file From 508c07114cec81607ec537996d7cd02be7b2a482 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 14:52:36 +0100 Subject: [PATCH 10/41] fixing database connection file to link to correct database --- lib/database_connection.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/database_connection.rb b/lib/database_connection.rb index 9dca03fde1..46136fa423 100644 --- a/lib/database_connection.rb +++ b/lib/database_connection.rb @@ -7,11 +7,10 @@ class DatabaseConnection # PG gem. We connect to 127.0.0.1, and select # the database name given in argument. def self.connect -# to be added in when RENDER is up and running - # if ENV['DATABASE_URL'] != nil - # @connection = PG.connect(ENV['DATABASE_URL']) - # return - # end + if ENV['DATABASE_URL'] != nil + @connection = PG.connect(ENV['DATABASE_URL']) + return + end if ENV['ENV'] == 'test' database_name = 'chitter_test' From 176f99500353ed49dea9d15a40e5a5cc89007a41 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 14:53:59 +0100 Subject: [PATCH 11/41] updating test to check that peeps are sorted reverse chronologically --- lib/peep_repository.rb | 4 ++-- spec/peep_repository_spec.rb | 13 ++++++------- spec/seeds/peeps_seeds.sql | 2 +- spec/spec_helper.rb | 5 +++++ 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/peep_repository.rb b/lib/peep_repository.rb index 11d90fc920..18d5c59280 100644 --- a/lib/peep_repository.rb +++ b/lib/peep_repository.rb @@ -17,7 +17,7 @@ def all peeps << peep end - return peeps + return peeps.reverse end def add(peep) @@ -26,6 +26,6 @@ def add(peep) DatabaseConnection.exec_params(sql, sql_params) - return peep + return nil end end \ No newline at end of file diff --git a/spec/peep_repository_spec.rb b/spec/peep_repository_spec.rb index 0568239682..af193e45b8 100644 --- a/spec/peep_repository_spec.rb +++ b/spec/peep_repository_spec.rb @@ -7,11 +7,11 @@ def reset_peeps_table connection.exec(seed_sql) end -RSpec.describe Peep do - +RSpec.describe PeepRepository do before(:each) do reset_peeps_table end + it 'finds all peeps' do peep_repo = PeepRepository.new @@ -19,12 +19,11 @@ def reset_peeps_table peeps = peep_repo.all expect(peeps.length).to eq(3) - expect(peeps.first.time).to eq('2023-05-09 11:09:00') - expect(peeps.first.contents).to eq('hello, this is the first peep!') - expect(peeps.first.account_id).to eq(1) + expect(peeps.first.time).to eq('2023-05-09 11:12:00') + expect(peeps.first.contents).to eq('hello, this is the third peep!') + expect(peeps.first.account_id).to eq(3) end - it 'adds a peep' do repo = PeepRepository.new new_peep = Peep.new @@ -38,6 +37,6 @@ def reset_peeps_table peeps = repo.all expect(peeps.length).to eq(4) - expect(peeps.last.contents).to eq("we are adding a new peep!") + expect(peeps.first.contents).to eq("we are adding a new peep!") end end \ No newline at end of file diff --git a/spec/seeds/peeps_seeds.sql b/spec/seeds/peeps_seeds.sql index 27ead131a0..fab26be624 100644 --- a/spec/seeds/peeps_seeds.sql +++ b/spec/seeds/peeps_seeds.sql @@ -16,4 +16,4 @@ TRUNCATE TABLE peeps RESTART IDENTITY; INSERT INTO peeps ("time", "contents", "account_id") VALUES ('2023-05-09 11:09:00', 'hello, this is the first peep!', 1), ('2023-05-09 11:10:30', 'hello, this is the second peep!', 2), -('2023-05-09 11:12:00', 'hello, this is the third peep!', 3) \ No newline at end of file +('2023-05-09 11:12:00', 'hello, this is the third peep!', 3); \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a59a8be08f..6b9ae13213 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,10 @@ require 'simplecov' require 'simplecov-console' +require 'database_connection' + +ENV['ENV'] = 'test' + +DatabaseConnection.connect SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::Console, From 234a3b813dbf81155fd087eb4a99142ec8911c5e Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 16:10:25 +0100 Subject: [PATCH 12/41] creating the first sinatra path for / test driven - user/name not yet implemented --- app.rb | 11 ++++++++++- config.ru | 2 +- lib/database_connection.rb | 3 +-- spec/spec_helper.rb | 4 ++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app.rb b/app.rb index b63f61cc29..6f4ea1a3f9 100644 --- a/app.rb +++ b/app.rb @@ -9,5 +9,14 @@ class Chitter < Sinatra::Base configure :development do register Sinatra::Reloader + also_reload 'lib/peep_repository' + also_reload 'lib/account_repository' end -end \ No newline at end of file + + get '/' do + peep_repo = PeepRepository.new + @peeps = peep_repo.all + + return erb(:homepage) + end +end diff --git a/config.ru b/config.ru index 4b0efb1a26..0f109cda04 100644 --- a/config.ru +++ b/config.ru @@ -1,3 +1,3 @@ require './app' -run Application \ No newline at end of file +run Chitter diff --git a/lib/database_connection.rb b/lib/database_connection.rb index 46136fa423..df2c284070 100644 --- a/lib/database_connection.rb +++ b/lib/database_connection.rb @@ -1,7 +1,6 @@ require 'pg' - class DatabaseConnection # This method connects to PostgreSQL using the # PG gem. We connect to 127.0.0.1, and select @@ -28,4 +27,4 @@ def self.exec_params(query, params) end @connection.exec_params(query, params) end -end \ No newline at end of file +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6b9ae13213..04cab217be 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,7 +9,7 @@ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([ SimpleCov::Formatter::Console, # Want a nice code coverage website? Uncomment this next line! - # SimpleCov::Formatter::HTMLFormatter + SimpleCov::Formatter::HTMLFormatter ]) SimpleCov.start do add_filter "spec/" @@ -21,4 +21,4 @@ puts "\e[33mHave you considered running rubocop? It will help you improve your code!\e[0m" puts "\e[33mTry it now! Just run: rubocop\e[0m" end -end \ No newline at end of file +end From 8e30ab123cf1d9d1de31c45fcd9b87e6bbc3ebf1 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 16:12:47 +0100 Subject: [PATCH 13/41] first draft of homepage printing peeps --- design/sinatra_route_recipe.md | 99 ++++++++++++++++++++++++++++++++++ lib/peep.rb | 2 +- lib/peep_repository.rb | 27 +++++----- spec/integration/app_spec.rb | 10 +++- spec/seeds/accounts_seeds.sql | 9 ++-- views/homepage.erb | 16 ++++++ 6 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 design/sinatra_route_recipe.md create mode 100644 views/homepage.erb diff --git a/design/sinatra_route_recipe.md b/design/sinatra_route_recipe.md new file mode 100644 index 0000000000..53aab83e9d --- /dev/null +++ b/design/sinatra_route_recipe.md @@ -0,0 +1,99 @@ +# GET / Route Design Recipe (for the hompage to view list of peeps) + +_Copy this design recipe template to test-drive a Sinatra route._ + +## 1. Design the Route Signature + +You'll need to include: + * the HTTP method + * the path + * any query parameters (passed in the URL) + * or body parameters (passed in the request body) + + method: GET + path: / + no parameters + +## 2. Design the Response + +The route might return different responses, depending on the result. + +For example, a route for a specific blog post (by its ID) might return `200 OK` if the post exists, but `404 Not Found` if the post is not found in the database. + +Your response might return plain text, JSON, or HTML code. + +_Replace the below with your own design. Think of all the different possible responses your route will return._ + +```html + + + + + + +

Post title

+
Post content
+ + +``` + +## 3. Write Examples + +_Replace these with your own design._ + +``` +# Request: + +GET /posts?id=1 + +# Expected response: + +Response for 200 OK +``` + +``` +# Request: + +GET /posts?id=276278 + +# Expected response: + +Response for 404 Not Found +``` + +## 4. Encode as Tests Examples + +```ruby +# EXAMPLE +# file: spec/integration/application_spec.rb + +require "spec_helper" + +describe Application do + include Rack::Test::Methods + + let(:app) { Application.new } + + context "GET /" do + it 'returns 200 OK' do + # Assuming the post with id 1 exists. + response = get('/posts?id=1') + + expect(response.status).to eq(200) + # expect(response.body).to eq(expected_response) + end + + it 'returns 404 Not Found' do + response = get('/posts?id=276278') + + expect(response.status).to eq(404) + # expect(response.body).to eq(expected_response) + end + end +end +``` + +## 5. Implement the Route + +Write the route and web server code to implement the route behaviour. + diff --git a/lib/peep.rb b/lib/peep.rb index 848781b7ea..6e0702c07f 100644 --- a/lib/peep.rb +++ b/lib/peep.rb @@ -1,3 +1,3 @@ class Peep attr_accessor :id, :time, :contents, :account_id -end \ No newline at end of file +end diff --git a/lib/peep_repository.rb b/lib/peep_repository.rb index 18d5c59280..67437d3ac3 100644 --- a/lib/peep_repository.rb +++ b/lib/peep_repository.rb @@ -1,23 +1,15 @@ -require 'peep' +require_relative './peep' class PeepRepository def all peeps = [] - sql = 'SELECT id, time, contents, account_id FROM peeps;' result_set = DatabaseConnection.exec_params(sql, []) result_set.each do |record| - - peep = Peep.new - peep.time = record['time'] - peep.contents = record['contents'] - peep.account_id = record['account_id'].to_i - - - peeps << peep + peeps << record_to_peep(record) end - return peeps.reverse + return peeps.reverse! end def add(peep) @@ -28,4 +20,15 @@ def add(peep) return nil end -end \ No newline at end of file + + private + + def record_to_peep(record) + peep = Peep.new + peep.time = record['time'] + peep.contents = record['contents'] + peep.account_id = record['account_id'].to_i + + return peep + end +end diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index 9fcd08e940..a263889ae4 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -7,4 +7,12 @@ let(:app) { Chitter.new } -end \ No newline at end of file + context 'GET /' do + it 'should return the list of tweets in reverse chronological order' do + response = get('/') + + expect(response.status).to eq(200) + expect(response.body).to include('hello, this is the third peep!') + end + end +end diff --git a/spec/seeds/accounts_seeds.sql b/spec/seeds/accounts_seeds.sql index 89eb59f73d..cbb10dede2 100644 --- a/spec/seeds/accounts_seeds.sql +++ b/spec/seeds/accounts_seeds.sql @@ -4,12 +4,13 @@ CREATE TABLE accounts ( id SERIAL PRIMARY KEY, email_address text, username text, + name text, password text ); TRUNCATE TABLE accounts RESTART IDENTITY; -INSERT INTO accounts ("email_address", "username", "password") VALUES -('alice@test.com', 'alice1', 'test123'), -('chris@test.com', 'chris1', 'test321'), -('test@test.com', 'test1', 'test987') \ No newline at end of file +INSERT INTO accounts ("email_address", "username", "name", "password") VALUES +('alice@test.com', 'alice1', 'Alice Wood', 'test123'), +('chris@test.com', 'chris1', 'Chris Wood', 'test321'), +('kay@test.com', 'kay1', 'Kay Lack', 'test987') \ No newline at end of file diff --git a/views/homepage.erb b/views/homepage.erb new file mode 100644 index 0000000000..85c2070c16 --- /dev/null +++ b/views/homepage.erb @@ -0,0 +1,16 @@ + + + +

Welcome to Chitter!

+

Latest peeps...

+
+ <% @peeps.each do |peep|%> +

+ from: <%= peep.account_id %> says...
+ <%= peep.contents %>
+ time: <%= peep.time %> +

+ <% end %> +
+ + \ No newline at end of file From 0714fef241721bffbf5adcdf99fd391d84b2ce1c Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 16:57:45 +0100 Subject: [PATCH 14/41] added the name and username display for the homepage --- lib/peep.rb | 18 ++++++++++++++++++ spec/peep_repository_spec.rb | 10 +++++++++- views/homepage.erb | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/peep.rb b/lib/peep.rb index 6e0702c07f..3059c87d07 100644 --- a/lib/peep.rb +++ b/lib/peep.rb @@ -1,3 +1,21 @@ class Peep attr_accessor :id, :time, :contents, :account_id + + def find_username + sql = 'SELECT username FROM accounts where id = $1' + sql_params = [account_id] + + result = DatabaseConnection.exec_params(sql, sql_params) + + return result.to_a.first['username'] + end + + def find_name + sql = 'SELECT name FROM accounts where id = $1' + sql_params = [account_id] + + result = DatabaseConnection.exec_params(sql, sql_params) + + return result.to_a.first['name'] + end end diff --git a/spec/peep_repository_spec.rb b/spec/peep_repository_spec.rb index af193e45b8..82f65b8468 100644 --- a/spec/peep_repository_spec.rb +++ b/spec/peep_repository_spec.rb @@ -39,4 +39,12 @@ def reset_peeps_table expect(peeps.length).to eq(4) expect(peeps.first.contents).to eq("we are adding a new peep!") end -end \ No newline at end of file + + it 'returns the username for peep at id 1 (most recent peep)' do + peep_repo = PeepRepository.new + + peeps = peep_repo.all + expect(peeps.first.contents).to eq('hello, this is the third peep!') + expect(peeps.first.find_username).to eq('kay1') + end +end diff --git a/views/homepage.erb b/views/homepage.erb index 85c2070c16..77381bec4c 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -6,7 +6,7 @@
<% @peeps.each do |peep|%>

- from: <%= peep.account_id %> says...
+ <%= peep.find_name%> (<%= peep.find_username %>) says...
<%= peep.contents %>
time: <%= peep.time %>

From 7747e1adc9e2a197b7d2eb88f96c3fdf667fb28a Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 17:04:37 +0100 Subject: [PATCH 15/41] trying to fix the readme initial plan image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f4011a4dd..ba926a7040 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ http://localhost:('Port in use')/ ## Initial plan -![initial plan](/chitter-challenge/initial_plan.png "Initial Plan") +![initial plan](./initial_plan.png "initial plan") ## Approach From 609fd71eda8014cc8e3aca2c92f4a2e627ee8848 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Tue, 9 May 2023 17:53:29 +0100 Subject: [PATCH 16/41] Account model and Repository class TDD'ed --- design/account_repository_recipe.md | 197 +++++++++++++++++++++++ design/two_table_design_recipe.md | 6 +- lib/account.rb | 3 + lib/account_repository.rb | 47 ++++++ spec/account_repository_spec.rb | 62 +++++++ spec/peep_repository_spec.rb | 2 +- spec/seeds/accounts_and_peeps_tables.sql | 36 +++++ spec/seeds/accounts_seeds.sql | 3 +- spec/seeds/peeps_seeds.sql | 5 +- 9 files changed, 353 insertions(+), 8 deletions(-) create mode 100644 design/account_repository_recipe.md create mode 100644 lib/account.rb create mode 100644 lib/account_repository.rb create mode 100644 spec/account_repository_spec.rb create mode 100644 spec/seeds/accounts_and_peeps_tables.sql diff --git a/design/account_repository_recipe.md b/design/account_repository_recipe.md new file mode 100644 index 0000000000..c1766ea5e9 --- /dev/null +++ b/design/account_repository_recipe.md @@ -0,0 +1,197 @@ +# Account Model and Repository Classes Design Recipe + +_Copy this recipe template to design and implement Model and Repository classes for a database table._ + +## 1. Design and create the Table + +``` +# EXAMPLE + +Table: accounts + +Columns: +id | email_address | username | name | password +``` + +## 2. Create Test SQL seeds + +Your tests will depend on data stored in PostgreSQL to run. + +If seed data is provided (or you already created it), you can skip this step. + +```sql + +-- (file: spec/accounts_seeds.sql) + +TRUNCATE TABLE accounts RESTART IDENTITY; -- replace with your own table name. + +INSERT INTO accounts ("email_address", "username", "name", "password") VALUES +('alice@test.com', 'alice1', 'Alice Wood', 'test123'), +('chris@test.com', 'chris1', 'Chris Wood', 'test321'), +('kay@test.com', 'kay1', 'Kay Lack', 'test987') +``` + +## 3. Define the class names + +```ruby +# Table name: accounts + +# Model class +# (in lib/account.rb) +class Account +end + +# Repository class +# (in lib/account_repository.rb) +class AccountRepository +end +``` + +## 4. Implement the Model class + +Define the attributes of your Model class. You can usually map the table columns to the attributes of the class, including primary and foreign keys. + +```ruby +# Table name: accounts + +# Model class +# (in lib/account.rb) + +class Account + attr_accessor :id, :email_address, :username, :name, :password +end + +``` + +## 5. Define the Repository Class interface + +```ruby +# Table name: accounts + +# Repository class +# (in lib/account_repository.rb) + +class AccountRepository + + # Selecting all records + # No arguments + def all + # Executes the SQL query: + # SELECT id, email_address, username, name, password FROM accounts; + + # Returns an array of account objects. + end + + # Gets a single record by its ID + # One argument: the id (number) + def find(id) + # Executes the SQL query: + # SELECT id, email_address, username, name, password FROM accounts WHERE id = $1; + + # Returns a single account object. + end + + def create(account) + # Executes the SQL query: + # INSERT INTO accounts(email_address, username, name, password) VALUES ($1, $2, $3, $4); + + # Returns nil, just creates account. + end + + # def update(account) + # not needed for MVP + # end + + # def delete(account) + # not needed for MVP + # end +end +``` + +## 6. Write Test Examples + +```ruby + +# 1 +# Get all accounts + +repo = AccountRepository.new + +accounts = repo.all + +accounts.length # => 3 + +accounts.first.id # => 1 +accounts.first.email_address # => 'alice@test.com' +accounts.first.username # => 'alice1' +accounts.first.name # => 'Alice Wood' +accounts.first.password # => 'test123' + +# 2 +# Get a single account + +repo = AccountRepository.new + +account = repo.find(2) + +accounts.id # => 1 +accounts.email_address # => 'chris@test.com' +accounts.username # => 'chris1' +accounts.name # => 'Chris Wood' +accounts.password # => 'test321' + +# 3 +# Create a new account + +repo = AccountRepository.new + +account = Account.new + +account.email_address = 'leo@test.com' +account.username ='leo1' +account.name = 'Leo Hetsch' +account.password = 'test456' + +repo.add(account) + +accounts = repo.all + +accounts.length # => 4 +accounts.last.email_address # => 'leo@test.com' +accounts.last.username # => 'leo1' +accounts.last.name # => 'Leo Hetsch' +accounts.last.password # => 'test456' + +``` + +Encode this example as a test. + +## 7. Reload the SQL seeds before each test run + +Running the SQL code present in the seed file will empty the table and re-insert the seed data. + +This is so you get a fresh table contents every time you run the test suite. + +```ruby +# EXAMPLE + +# file: spec/account_repository_spec.rb + +def reset_accounts_table + seed_sql = File.read('spec/seeds_accounts.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' }) + connection.exec(seed_sql) +end + +describe AccountRepository do + before(:each) do + reset_accounts_table + end + + # (your tests will go here). +end +``` + +## 8. Test-drive and implement the Repository class behaviour + +_After each test you write, follow the test-driving process of red, green, refactor to implement the behaviour._ diff --git a/design/two_table_design_recipe.md b/design/two_table_design_recipe.md index b7753832cc..d83e782ab2 100644 --- a/design/two_table_design_recipe.md +++ b/design/two_table_design_recipe.md @@ -116,11 +116,9 @@ CREATE TABLE peeps ( ## 5. Create the tables. ```bash -psql -h 127.0.0.1 chitter_database < spec/seeds/accounts_seeds.sql -psql -h 127.0.0.1 chitter_database < spec/seeds/peeps_seeds.sql +psql -h 127.0.0.1 chitter_database < spec/seeds/accounts_and_peeps_tables.sql -psql -h 127.0.0.1 chitter_test < spec/seeds/accounts_seeds.sql -psql -h 127.0.0.1 chitter_test < spec/seeds/peeps_seeds.sql +psql -h 127.0.0.1 chitter_test < spec/seeds/accounts_and_peeps_tables.sql ``` \ No newline at end of file diff --git a/lib/account.rb b/lib/account.rb new file mode 100644 index 0000000000..a2ee2546a3 --- /dev/null +++ b/lib/account.rb @@ -0,0 +1,3 @@ +class Account + attr_accessor :id, :email_address, :username, :name, :password +end diff --git a/lib/account_repository.rb b/lib/account_repository.rb new file mode 100644 index 0000000000..b469a8e924 --- /dev/null +++ b/lib/account_repository.rb @@ -0,0 +1,47 @@ +require_relative './account' + +class AccountRepository + def all + accounts = [] + sql = 'SELECT id, email_address, username, name, password FROM accounts;' + result_set = DatabaseConnection.exec_params(sql, []) + + result_set.each do |record| + accounts << record_to_account(record) + end + return accounts + end + + def find(id) + sql = 'SELECT id, email_address, username, name, password FROM accounts WHERE id = $1;' + sql_params = [id] + + result_set = DatabaseConnection.exec_params(sql, sql_params) + + record = result_set[0] + + return record_to_account(record) + end + + def add(account) + sql = 'INSERT INTO accounts (email_address, username, name, password) VALUES ($1, $2, $3, $4);' + sql_params = [account.email_address, account.username, account.name, account.password] + + DatabaseConnection.exec_params(sql, sql_params) + + return nil + end + + private + + def record_to_account(record) + account = Account.new + account.id = record['id'].to_i + account.email_address = record['email_address'] + account.username = record['username'] + account.name = record['name'] + account.password = record['password'] + + return account + end +end diff --git a/spec/account_repository_spec.rb b/spec/account_repository_spec.rb new file mode 100644 index 0000000000..1d79feaa40 --- /dev/null +++ b/spec/account_repository_spec.rb @@ -0,0 +1,62 @@ +require 'account' +require 'account_repository' + +def reset_accounts_table + seed_sql = File.read('spec/seeds/accounts_and_peeps_tables.sql') + connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' }) + connection.exec(seed_sql) +end + +RSpec.describe AccountRepository do + + before(:each) do + reset_accounts_table + end + + context 'with a database pre-loaded with accounts' do + it 'returns a list of accounts' do + repo = AccountRepository.new + + accounts = repo.all + + expect(accounts.length).to eq(3) + expect(accounts.first.id).to eq(1) + expect(accounts.first.email_address).to eq('alice@test.com') + expect(accounts.first.username).to eq('alice1') + expect(accounts.first.name).to eq('Alice Wood') + expect(accounts.first.password).to eq('test123') + end + + it 'returns the account details with id 2' do + repo = AccountRepository.new + + account = repo.find(2) + + expect(account.id).to eq(2) + expect(account.email_address).to eq('chris@test.com') + expect(account.username).to eq('chris1') + expect(account.name).to eq('Chris Wood') + expect(account.password).to eq('test321') + end + end + + it 'creates a new account' do + repo = AccountRepository.new + + account = Account.new + account.email_address = 'leo@test.com' + account.username = 'leo1' + account.name = 'Leo Hetsch' + account.password = 'test456' + + repo.add(account) + + accounts = repo.all + + expect(accounts.length).to eq(4) + expect(accounts.last.email_address).to eq('leo@test.com') + expect(accounts.last.username).to eq('leo1') + expect(accounts.last.name).to eq('Leo Hetsch') + expect(accounts.last.password).to eq('test456') + end +end diff --git a/spec/peep_repository_spec.rb b/spec/peep_repository_spec.rb index 82f65b8468..dbedb2a764 100644 --- a/spec/peep_repository_spec.rb +++ b/spec/peep_repository_spec.rb @@ -2,7 +2,7 @@ require 'peep_repository' def reset_peeps_table - seed_sql = File.read('spec/seeds/peeps_seeds.sql') + seed_sql = File.read('spec/seeds/accounts_and_peeps_tables.sql') connection = PG.connect({ host: '127.0.0.1', dbname: 'chitter_test' }) connection.exec(seed_sql) end diff --git a/spec/seeds/accounts_and_peeps_tables.sql b/spec/seeds/accounts_and_peeps_tables.sql new file mode 100644 index 0000000000..f70fcc7d89 --- /dev/null +++ b/spec/seeds/accounts_and_peeps_tables.sql @@ -0,0 +1,36 @@ +DROP TABLE IF EXISTS peeps; +DROP TABLE IF EXISTS accounts; + + +CREATE TABLE accounts ( + id SERIAL PRIMARY KEY, + email_address text, + username text, + name text, + password text +); + +CREATE TABLE peeps ( + id SERIAL PRIMARY KEY, + time timestamp, + contents text, + + account_id int, + constraint fk_account foreign key(account_id) + references accounts(id) + on delete cascade +); + +TRUNCATE TABLE peeps RESTART IDENTITY CASCADE; +TRUNCATE TABLE accounts RESTART IDENTITY CASCADE; + +INSERT INTO accounts ("email_address", "username", "name", "password") VALUES +('alice@test.com', 'alice1', 'Alice Wood', 'test123'), +('chris@test.com', 'chris1', 'Chris Wood', 'test321'), +('kay@test.com', 'kay1', 'Kay Lack', 'test987'); + +INSERT INTO peeps ("time", "contents", "account_id") VALUES +('2023-05-09 11:09:00', 'hello, this is the first peep!', 1), +('2023-05-09 11:10:30', 'hello, this is the second peep!', 2), +('2023-05-09 11:12:00', 'hello, this is the third peep!', 3); + diff --git a/spec/seeds/accounts_seeds.sql b/spec/seeds/accounts_seeds.sql index cbb10dede2..caf66f1f2d 100644 --- a/spec/seeds/accounts_seeds.sql +++ b/spec/seeds/accounts_seeds.sql @@ -13,4 +13,5 @@ TRUNCATE TABLE accounts RESTART IDENTITY; INSERT INTO accounts ("email_address", "username", "name", "password") VALUES ('alice@test.com', 'alice1', 'Alice Wood', 'test123'), ('chris@test.com', 'chris1', 'Chris Wood', 'test321'), -('kay@test.com', 'kay1', 'Kay Lack', 'test987') \ No newline at end of file +('kay@test.com', 'kay1', 'Kay Lack', 'test987'); + diff --git a/spec/seeds/peeps_seeds.sql b/spec/seeds/peeps_seeds.sql index fab26be624..71e931bead 100644 --- a/spec/seeds/peeps_seeds.sql +++ b/spec/seeds/peeps_seeds.sql @@ -1,4 +1,5 @@ -DROP TABLE IF EXISTS peeps; +DROP TABLE IF EXISTS peeps CASCADE; + CREATE TABLE peeps ( id SERIAL PRIMARY KEY, @@ -11,7 +12,7 @@ CREATE TABLE peeps ( on delete cascade ); -TRUNCATE TABLE peeps RESTART IDENTITY; +TRUNCATE TABLE peeps RESTART IDENTITY ; INSERT INTO peeps ("time", "contents", "account_id") VALUES ('2023-05-09 11:09:00', 'hello, this is the first peep!', 1), From a68c6c845fc33fe633bf7c85c67e2a6f7f3d0446 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 10:39:59 +0100 Subject: [PATCH 17/41] updating README.md --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ba926a7040..06d0139551 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,13 @@ Write a small Twitter clone that will allow the users to post messages to a publ ## How to use ```ruby -rackup -http://localhost:('Port in use')/ +$ git clone https://github.com/aliceswood/chitter-challenge.git +$ cd chitter-challenge +$ bundle install +$ createdb chitter_test +$ psql -h 127.0.0.1 chitter_test < spec/seeds/accounts_and_peeps_tables.sql +$ rackup +$ http://localhost:('Port in use')/ ``` ## Initial plan @@ -36,6 +41,7 @@ http://localhost:('Port in use')/ ## Techonologies used +- Excalidraw - Ruby - RSpec - PostgreSQL From a12ddf556e404356796abf5e51483175346c78dd Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 11:42:53 +0100 Subject: [PATCH 18/41] tidying up seed data --- spec/seeds/accounts_seeds.sql | 17 ----------------- spec/seeds/peeps_seeds.sql | 20 -------------------- 2 files changed, 37 deletions(-) delete mode 100644 spec/seeds/accounts_seeds.sql delete mode 100644 spec/seeds/peeps_seeds.sql diff --git a/spec/seeds/accounts_seeds.sql b/spec/seeds/accounts_seeds.sql deleted file mode 100644 index caf66f1f2d..0000000000 --- a/spec/seeds/accounts_seeds.sql +++ /dev/null @@ -1,17 +0,0 @@ -DROP TABLE IF EXISTS accounts; - -CREATE TABLE accounts ( - id SERIAL PRIMARY KEY, - email_address text, - username text, - name text, - password text -); - -TRUNCATE TABLE accounts RESTART IDENTITY; - -INSERT INTO accounts ("email_address", "username", "name", "password") VALUES -('alice@test.com', 'alice1', 'Alice Wood', 'test123'), -('chris@test.com', 'chris1', 'Chris Wood', 'test321'), -('kay@test.com', 'kay1', 'Kay Lack', 'test987'); - diff --git a/spec/seeds/peeps_seeds.sql b/spec/seeds/peeps_seeds.sql deleted file mode 100644 index 71e931bead..0000000000 --- a/spec/seeds/peeps_seeds.sql +++ /dev/null @@ -1,20 +0,0 @@ -DROP TABLE IF EXISTS peeps CASCADE; - - -CREATE TABLE peeps ( - id SERIAL PRIMARY KEY, - time timestamp, - contents text, - - account_id int, - constraint fk_account foreign key(account_id) - references accounts(id) - on delete cascade -); - -TRUNCATE TABLE peeps RESTART IDENTITY ; - -INSERT INTO peeps ("time", "contents", "account_id") VALUES -('2023-05-09 11:09:00', 'hello, this is the first peep!', 1), -('2023-05-09 11:10:30', 'hello, this is the second peep!', 2), -('2023-05-09 11:12:00', 'hello, this is the third peep!', 3); \ No newline at end of file From 914905f962643385214fa74cf22400b4e55e3643 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 11:46:55 +0100 Subject: [PATCH 19/41] refactoring some of the sql queries --- lib/account_repository.rb | 3 +-- lib/peep.rb | 4 ++-- lib/peep_repository.rb | 4 +--- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/account_repository.rb b/lib/account_repository.rb index b469a8e924..8eac46570b 100644 --- a/lib/account_repository.rb +++ b/lib/account_repository.rb @@ -13,11 +13,10 @@ def all end def find(id) - sql = 'SELECT id, email_address, username, name, password FROM accounts WHERE id = $1;' + sql = 'SELECT * FROM accounts WHERE id = $1;' sql_params = [id] result_set = DatabaseConnection.exec_params(sql, sql_params) - record = result_set[0] return record_to_account(record) diff --git a/lib/peep.rb b/lib/peep.rb index 3059c87d07..40dada31a0 100644 --- a/lib/peep.rb +++ b/lib/peep.rb @@ -2,7 +2,7 @@ class Peep attr_accessor :id, :time, :contents, :account_id def find_username - sql = 'SELECT username FROM accounts where id = $1' + sql = 'SELECT username FROM accounts WHERE id = $1' sql_params = [account_id] result = DatabaseConnection.exec_params(sql, sql_params) @@ -11,7 +11,7 @@ def find_username end def find_name - sql = 'SELECT name FROM accounts where id = $1' + sql = 'SELECT name FROM accounts WHERE id = $1' sql_params = [account_id] result = DatabaseConnection.exec_params(sql, sql_params) diff --git a/lib/peep_repository.rb b/lib/peep_repository.rb index 67437d3ac3..01bdcc5926 100644 --- a/lib/peep_repository.rb +++ b/lib/peep_repository.rb @@ -3,7 +3,7 @@ class PeepRepository def all peeps = [] - sql = 'SELECT id, time, contents, account_id FROM peeps;' + sql = 'SELECT * FROM peeps;' result_set = DatabaseConnection.exec_params(sql, []) result_set.each do |record| @@ -17,8 +17,6 @@ def add(peep) sql_params = [peep.time, peep.contents, peep.account_id] DatabaseConnection.exec_params(sql, sql_params) - - return nil end private From e5fe4ad4f0571553e868ee6334270fc79600ad3e Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 11:50:48 +0100 Subject: [PATCH 20/41] you can add a peep to the homepage and it redirects you back --- app.rb | 20 ++++++++++++++++++-- spec/integration/app_spec.rb | 21 +++++++++++++++++++++ views/add_a_peep.erb | 12 ++++++++++++ views/homepage.erb | 2 ++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 views/add_a_peep.erb diff --git a/app.rb b/app.rb index 6f4ea1a3f9..d39ae085af 100644 --- a/app.rb +++ b/app.rb @@ -14,9 +14,25 @@ class Chitter < Sinatra::Base end get '/' do - peep_repo = PeepRepository.new - @peeps = peep_repo.all + repo = PeepRepository.new + @peeps = repo.all return erb(:homepage) end + + get '/add_peep' do + return erb(:add_a_peep) + end + + post '/add_peep' do + repo = PeepRepository.new + peep = Peep.new + peep.time = Time.now + peep.contents = params[:contents] + peep.account_id = '1' + + repo.add(peep) + + redirect '/' + end end diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index a263889ae4..236d16a2d6 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -12,7 +12,28 @@ response = get('/') expect(response.status).to eq(200) + expect(response.body).to include('

Welcome to Chitter!

') + expect(response.body).to include('Alice Wood (alice1) says...
') expect(response.body).to include('hello, this is the third peep!') end end + + context 'GET /add_peep' do + it 'shows a form to be completed to add a tweet to the homepage' do + response = get('/add_peep') + + expect(response.status).to eq(200) + expect(response.body).to include("

What's your peep?

") + expect(response.body).to include('
') + end + end + + context 'POST /add_peep' do + it 'should add a post to the database and return to homepage' do + response = post('/add_peep') + + expect(response.status).to eq(200) + expect(response.body).to include('Your peep has been posted!') + end + end end diff --git a/views/add_a_peep.erb b/views/add_a_peep.erb new file mode 100644 index 0000000000..ca7373d52e --- /dev/null +++ b/views/add_a_peep.erb @@ -0,0 +1,12 @@ + + + +
+

What's your peep?

+ +


+

+
+
+ + \ No newline at end of file diff --git a/views/homepage.erb b/views/homepage.erb index 77381bec4c..f79b119be9 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -2,6 +2,8 @@

Welcome to Chitter!

+

What's on your mind?

+

Latest peeps...

<% @peeps.each do |peep|%> From ad29f4744f3a0b19edc3dd21df0113989b0cb0f3 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 12:08:27 +0100 Subject: [PATCH 21/41] added redirection buttons after peep is submitted --- app.rb | 4 +++- views/posted_peep.erb | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 views/posted_peep.erb diff --git a/app.rb b/app.rb index d39ae085af..bfa05f6a48 100644 --- a/app.rb +++ b/app.rb @@ -16,6 +16,8 @@ class Chitter < Sinatra::Base get '/' do repo = PeepRepository.new @peeps = repo.all + + @accounts = AccountRepository.new return erb(:homepage) end @@ -33,6 +35,6 @@ class Chitter < Sinatra::Base repo.add(peep) - redirect '/' + return erb(:posted_peep) end end diff --git a/views/posted_peep.erb b/views/posted_peep.erb new file mode 100644 index 0000000000..8f6a711e6f --- /dev/null +++ b/views/posted_peep.erb @@ -0,0 +1,9 @@ + + + +

Nice one, your peep has been posted!

+
+
+
+ + \ No newline at end of file From 705a6c6b1d0797dfe652d6cdd225b54518668a31 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 12:56:18 +0100 Subject: [PATCH 22/41] added sign up link to the homepage - still need to make this conditional with the post a peep --- README.md | 19 +++++++++++++------ app.rb | 17 +++++++++++++++++ lib/account.rb | 1 + spec/integration/app_spec.rb | 10 ++++++++++ views/homepage.erb | 2 ++ views/new_account_confirmation.erb | 7 +++++++ views/sign_up.erb | 22 ++++++++++++++++++++++ 7 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 views/new_account_confirmation.erb create mode 100644 views/sign_up.erb diff --git a/README.md b/README.md index 06d0139551..99d3f90991 100644 --- a/README.md +++ b/README.md @@ -27,17 +27,23 @@ $ http://localhost:('Port in use')/ 3. Create the homepage using GET request so that the test peeps are shown -4. Create a sign up page so that details are added to the database and user is registered +4. Create a form to add new peeps using POST request -5. Create a form to add new peeps using POST request +5. Once peep is submitted, return to homepage with list of peeps -6. Once form is submitted, return to homepage with list of peeps +6. Create a sign up page so that details are added to the database and user is registered -7. Add a log in and log out page, which checks the database to match log in details +7. Add a log in and log out page, which checks the database to match log in details, with errors for non unique details 8. Send an email when someone has been mentioned in a post -## Changes to plan +## Upcoming to-dos + +- REGEX test for @ sign in email addresses +- Errors for empty fields in post a peep form +- Hide password when entering +- Better CSS + ## Techonologies used @@ -45,4 +51,5 @@ $ http://localhost:('Port in use')/ - Ruby - RSpec - PostgreSQL -- Sinatra \ No newline at end of file +- Sinatra +- HTML \ No newline at end of file diff --git a/app.rb b/app.rb index bfa05f6a48..b065078529 100644 --- a/app.rb +++ b/app.rb @@ -37,4 +37,21 @@ class Chitter < Sinatra::Base return erb(:posted_peep) end + + get '/sign_up' do + return erb (:sign_up) + end + + post '/sign_up' do + repo = AccountRepository.new + @account = Account.new + @account.email_address = params[:email_address] + @account.name = params[:name] + @account.username = params[:username] + @account.password = params[:password] + + repo.add(@account) + + return erb(:new_account_confirmation) + end end diff --git a/lib/account.rb b/lib/account.rb index a2ee2546a3..6765b868d0 100644 --- a/lib/account.rb +++ b/lib/account.rb @@ -1,3 +1,4 @@ class Account attr_accessor :id, :email_address, :username, :name, :password + end diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index 236d16a2d6..b4c22a3168 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -36,4 +36,14 @@ expect(response.body).to include('Your peep has been posted!') end end + + context 'GET /sign_up' do + it 'shows you the sign up form' do + response = get('/sign_up') + + expect(response.status).to eq(200) + expect(response.body).to include("input type='text' name='username'/>
") + expect(response.body).to include("

Please enter your details below!

") + end + end end diff --git a/views/homepage.erb b/views/homepage.erb index f79b119be9..8b2a4f64b8 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -2,6 +2,8 @@

Welcome to Chitter!

+

Please sign up here:

+

What's on your mind?


Latest peeps...

diff --git a/views/new_account_confirmation.erb b/views/new_account_confirmation.erb new file mode 100644 index 0000000000..d97defe95a --- /dev/null +++ b/views/new_account_confirmation.erb @@ -0,0 +1,7 @@ + + + +

Welcome <%= @account.name%>, lets start peeping!

+
+ + \ No newline at end of file diff --git a/views/sign_up.erb b/views/sign_up.erb new file mode 100644 index 0000000000..59e5656b41 --- /dev/null +++ b/views/sign_up.erb @@ -0,0 +1,22 @@ + + + +

Welcome to Chitter!

+
+

Please enter your details below

+
+


+
+
+
+
+
+
+
+
+

+
+ + + + \ No newline at end of file From 7369a9c60fbd19a0487e83073b45f3588c238d96 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 14:14:53 +0100 Subject: [PATCH 23/41] sign in page and log in page created ahead of session being implemented --- app.rb | 23 +++++++++++++------- spec/integration/app_spec.rb | 34 +++++++++++++++++++++++++++--- views/login.erb | 22 +++++++++++++++++++ views/new_account_confirmation.erb | 4 ++-- 4 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 views/login.erb diff --git a/app.rb b/app.rb index b065078529..a637c58dee 100644 --- a/app.rb +++ b/app.rb @@ -7,6 +7,7 @@ DatabaseConnection.connect class Chitter < Sinatra::Base + enable :sessions configure :development do register Sinatra::Reloader also_reload 'lib/peep_repository' @@ -43,15 +44,21 @@ class Chitter < Sinatra::Base end post '/sign_up' do - repo = AccountRepository.new - @account = Account.new - @account.email_address = params[:email_address] - @account.name = params[:name] - @account.username = params[:username] - @account.password = params[:password] + repo = AccountRepository.new + @account = Account.new + @account.email_address = params[:email_address] + @account.name = params[:name] + @account.username = params[:username] + @account.password = params[:password] - repo.add(@account) + repo.add(@account) - return erb(:new_account_confirmation) + return erb(:new_account_confirmation) end + + get '/login' do + return erb(:login) + end + + post '/login' end diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index b4c22a3168..eed32315c4 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -33,7 +33,7 @@ response = post('/add_peep') expect(response.status).to eq(200) - expect(response.body).to include('Your peep has been posted!') + expect(response.body).to include('

Nice one, your peep has been posted!

') end end @@ -42,8 +42,36 @@ response = get('/sign_up') expect(response.status).to eq(200) - expect(response.body).to include("input type='text' name='username'/>
") - expect(response.body).to include("

Please enter your details below!

") + expect(response.body).to include("

Please enter your details below

") + expect(response.body).to include('
') + end + end + + context 'POST /sign_up' do + it 'adds the account details to the database and redirects to confirmation page' do + response = post('/sign_up') + + expect(response.status).to eq(200) + expect(response.body).to include("

Welcome, your Chitter account has been created!

") + end + end + + context 'GET /login' do + it 'should return the login page' do + response = get('/login') + + expect(response.status).to eq(200) + expect(response.body).to include("

Please enter your login details

") + expect(response.body).to include('

') + end + end + + context 'POST /login' do + it 'should check the details entered against the database' do + response = post('/login') + + expect(response.status).to eq(200) + expect(response.body).to include() end end end diff --git a/views/login.erb b/views/login.erb new file mode 100644 index 0000000000..98eb9b434c --- /dev/null +++ b/views/login.erb @@ -0,0 +1,22 @@ + + + +

Welcome to Chitter!

+
+

Please enter your login details

+
+


+
+
+
+
+
+
+
+
+

+
+ + + + \ No newline at end of file diff --git a/views/new_account_confirmation.erb b/views/new_account_confirmation.erb index d97defe95a..dcd0a22178 100644 --- a/views/new_account_confirmation.erb +++ b/views/new_account_confirmation.erb @@ -1,7 +1,7 @@ -

Welcome <%= @account.name%>, lets start peeping!

-
+

Welcome, your Chitter account has been created!

+
\ No newline at end of file From 5a466f8d37f955a8f1bc18fd91d1210fcfe3f3dc Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 16:12:29 +0100 Subject: [PATCH 24/41] fixed the sorting of peeps to be via time not just id and unique checks for username and email added --- README.md | 5 ++++- app.rb | 32 +++++++++++++++----------------- lib/account.rb | 7 +++++++ lib/peep_repository.rb | 3 ++- spec/integration/app_spec.rb | 22 +++++----------------- views/add_a_peep.erb | 3 ++- 6 files changed, 35 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 99d3f90991..8f6791219b 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ $ cd chitter-challenge $ bundle install $ createdb chitter_test $ psql -h 127.0.0.1 chitter_test < spec/seeds/accounts_and_peeps_tables.sql +$ rspec $ rackup $ http://localhost:('Port in use')/ ``` @@ -39,12 +40,14 @@ $ http://localhost:('Port in use')/ ## Upcoming to-dos +- Refactor +- Update tests for creating a peep to link to the logged in user - when sessions are implemented +- Encrypt passwords stored on DB - REGEX test for @ sign in email addresses - Errors for empty fields in post a peep form - Hide password when entering - Better CSS - ## Techonologies used - Excalidraw diff --git a/app.rb b/app.rb index a637c58dee..9cc634a7c3 100644 --- a/app.rb +++ b/app.rb @@ -7,7 +7,6 @@ DatabaseConnection.connect class Chitter < Sinatra::Base - enable :sessions configure :development do register Sinatra::Reloader also_reload 'lib/peep_repository' @@ -18,7 +17,7 @@ class Chitter < Sinatra::Base repo = PeepRepository.new @peeps = repo.all - @accounts = AccountRepository.new + accounts = AccountRepository.new return erb(:homepage) end @@ -45,20 +44,19 @@ class Chitter < Sinatra::Base post '/sign_up' do repo = AccountRepository.new - @account = Account.new - @account.email_address = params[:email_address] - @account.name = params[:name] - @account.username = params[:username] - @account.password = params[:password] - - repo.add(@account) - - return erb(:new_account_confirmation) - end - - get '/login' do - return erb(:login) + account = Account.new + account.email_address = params[:email_address] + account.name = params[:name] + account.username = params[:username] + account.password = params[:password] + + if account.valid? + p account.valid? + repo.add(account) + return erb(:new_account_confirmation) + else + status 400 + return "This username and/or email address are already in use, please try again!" + end end - - post '/login' end diff --git a/lib/account.rb b/lib/account.rb index 6765b868d0..2bd3a824f5 100644 --- a/lib/account.rb +++ b/lib/account.rb @@ -1,4 +1,11 @@ class Account attr_accessor :id, :email_address, :username, :name, :password + def valid? + sql = "SELECT * FROM accounts WHERE email_address=$1::text OR username=$2::text;" + sql_params = [self.email_address, self.username] + + result_set = DatabaseConnection.exec_params(sql, sql_params) + return result_set.to_a.length == 0 + end end diff --git a/lib/peep_repository.rb b/lib/peep_repository.rb index 01bdcc5926..361dd4ba34 100644 --- a/lib/peep_repository.rb +++ b/lib/peep_repository.rb @@ -9,7 +9,8 @@ def all result_set.each do |record| peeps << record_to_peep(record) end - return peeps.reverse! + + return peeps.sort_by(&:time).reverse end def add(peep) diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index eed32315c4..5d2b5b74fc 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -49,29 +49,17 @@ context 'POST /sign_up' do it 'adds the account details to the database and redirects to confirmation page' do - response = post('/sign_up') + response = post('/sign_up', name: 'Leo Hetsch', email_address: 'leo@test.com', username: 'leo1', password: 'test') expect(response.status).to eq(200) expect(response.body).to include("

Welcome, your Chitter account has been created!

") end - end - context 'GET /login' do - it 'should return the login page' do - response = get('/login') + it 'fails if the username and/or email are already in use' do + response = post('/sign_up', name: 'Alice Wood', email_address: 'alice@test.com', username: 'alice1', password: 'test') - expect(response.status).to eq(200) - expect(response.body).to include("

Please enter your login details

") - expect(response.body).to include('

') - end - end - - context 'POST /login' do - it 'should check the details entered against the database' do - response = post('/login') - - expect(response.status).to eq(200) - expect(response.body).to include() + expect(response.status).to eq(400) + expect(response.body).to include("This username and/or email address are already in use, please try again!") end end end diff --git a/views/add_a_peep.erb b/views/add_a_peep.erb index ca7373d52e..c31bda0f8b 100644 --- a/views/add_a_peep.erb +++ b/views/add_a_peep.erb @@ -5,7 +5,8 @@

What's your peep?


-

+
+

From a7a440fb3c4e237d6b452b9d272e853b817d4536 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 16:19:03 +0100 Subject: [PATCH 25/41] updating readme now that MVP is completed --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8f6791219b..ecce8f928c 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,9 @@ $ http://localhost:('Port in use')/ 5. Once peep is submitted, return to homepage with list of peeps -6. Create a sign up page so that details are added to the database and user is registered +6. Create a sign up page so that details are added to the database and user is registered, with error for non unique details -7. Add a log in and log out page, which checks the database to match log in details, with errors for non unique details +7. Add a log in and log out page, which checks the database to match log in details 8. Send an email when someone has been mentioned in a post From 97890dd5bcbe0cc51d930da949d0d39fb2849554 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 16:48:34 +0100 Subject: [PATCH 26/41] updated seeds and tests to have test emails as @example.com --- design/account_repository_recipe.md | 14 +++++++------- design/peep_respository_class_recipe.md | 4 ++-- spec/account_repository_spec.rb | 8 ++++---- spec/integration/app_spec.rb | 6 +++--- spec/peep_repository_spec.rb | 6 +++--- spec/seeds/accounts_and_peeps_tables.sql | 10 +++++----- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/design/account_repository_recipe.md b/design/account_repository_recipe.md index c1766ea5e9..384ee64e50 100644 --- a/design/account_repository_recipe.md +++ b/design/account_repository_recipe.md @@ -26,9 +26,9 @@ If seed data is provided (or you already created it), you can skip this step. TRUNCATE TABLE accounts RESTART IDENTITY; -- replace with your own table name. INSERT INTO accounts ("email_address", "username", "name", "password") VALUES -('alice@test.com', 'alice1', 'Alice Wood', 'test123'), -('chris@test.com', 'chris1', 'Chris Wood', 'test321'), -('kay@test.com', 'kay1', 'Kay Lack', 'test987') +('alice@example.com', 'alice1', 'Alice Wood', 'test123'), +('chris@example.com', 'chris1', 'Chris Wood', 'test321'), +('will@example.com', 'will1', 'will Davies', 'test987') ``` ## 3. Define the class names @@ -122,7 +122,7 @@ accounts = repo.all accounts.length # => 3 accounts.first.id # => 1 -accounts.first.email_address # => 'alice@test.com' +accounts.first.email_address # => 'alice@example.com' accounts.first.username # => 'alice1' accounts.first.name # => 'Alice Wood' accounts.first.password # => 'test123' @@ -135,7 +135,7 @@ repo = AccountRepository.new account = repo.find(2) accounts.id # => 1 -accounts.email_address # => 'chris@test.com' +accounts.email_address # => 'chris@example.com' accounts.username # => 'chris1' accounts.name # => 'Chris Wood' accounts.password # => 'test321' @@ -147,7 +147,7 @@ repo = AccountRepository.new account = Account.new -account.email_address = 'leo@test.com' +account.email_address = 'leo@example.com' account.username ='leo1' account.name = 'Leo Hetsch' account.password = 'test456' @@ -157,7 +157,7 @@ repo.add(account) accounts = repo.all accounts.length # => 4 -accounts.last.email_address # => 'leo@test.com' +accounts.last.email_address # => 'leo@example.com' accounts.last.username # => 'leo1' accounts.last.name # => 'Leo Hetsch' accounts.last.password # => 'test456' diff --git a/design/peep_respository_class_recipe.md b/design/peep_respository_class_recipe.md index b8202de6ef..ca84546a2b 100644 --- a/design/peep_respository_class_recipe.md +++ b/design/peep_respository_class_recipe.md @@ -25,8 +25,8 @@ TRUNCATE TABLE peeps RESTART IDENTITY; INSERT INTO peeps ("time", "contents", "account_id") VALUES ('2023-05-09 11:09:00', 'hello, this is the first peep!', 1), -('2023-05-09 11:10:30', 'hello, this is the second peep!', 2), -('2023-05-09 11:12:00', 'hello, this is the third peep!', 3) +('2023-05-09 11:10:30', 'Oh wow, hello @alice1! Peep!!', 2), +('2023-05-09 11:12:00', 'I am peeping too! How cool', 3) ``` diff --git a/spec/account_repository_spec.rb b/spec/account_repository_spec.rb index 1d79feaa40..da8613dce1 100644 --- a/spec/account_repository_spec.rb +++ b/spec/account_repository_spec.rb @@ -21,7 +21,7 @@ def reset_accounts_table expect(accounts.length).to eq(3) expect(accounts.first.id).to eq(1) - expect(accounts.first.email_address).to eq('alice@test.com') + expect(accounts.first.email_address).to eq('alice@example.com') expect(accounts.first.username).to eq('alice1') expect(accounts.first.name).to eq('Alice Wood') expect(accounts.first.password).to eq('test123') @@ -33,7 +33,7 @@ def reset_accounts_table account = repo.find(2) expect(account.id).to eq(2) - expect(account.email_address).to eq('chris@test.com') + expect(account.email_address).to eq('chris@example.com') expect(account.username).to eq('chris1') expect(account.name).to eq('Chris Wood') expect(account.password).to eq('test321') @@ -44,7 +44,7 @@ def reset_accounts_table repo = AccountRepository.new account = Account.new - account.email_address = 'leo@test.com' + account.email_address = 'leo@example.com' account.username = 'leo1' account.name = 'Leo Hetsch' account.password = 'test456' @@ -54,7 +54,7 @@ def reset_accounts_table accounts = repo.all expect(accounts.length).to eq(4) - expect(accounts.last.email_address).to eq('leo@test.com') + expect(accounts.last.email_address).to eq('leo@example.com') expect(accounts.last.username).to eq('leo1') expect(accounts.last.name).to eq('Leo Hetsch') expect(accounts.last.password).to eq('test456') diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index 5d2b5b74fc..be8edf5c09 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -14,7 +14,7 @@ expect(response.status).to eq(200) expect(response.body).to include('

Welcome to Chitter!

') expect(response.body).to include('Alice Wood (alice1) says...
') - expect(response.body).to include('hello, this is the third peep!') + expect(response.body).to include('I am peeping too! How cool') end end @@ -49,14 +49,14 @@ context 'POST /sign_up' do it 'adds the account details to the database and redirects to confirmation page' do - response = post('/sign_up', name: 'Leo Hetsch', email_address: 'leo@test.com', username: 'leo1', password: 'test') + response = post('/sign_up', name: 'Leo Hetsch', email_address: 'leo@example.com', username: 'leo1', password: 'test') expect(response.status).to eq(200) expect(response.body).to include("

Welcome, your Chitter account has been created!

") end it 'fails if the username and/or email are already in use' do - response = post('/sign_up', name: 'Alice Wood', email_address: 'alice@test.com', username: 'alice1', password: 'test') + response = post('/sign_up', name: 'Alice Wood', email_address: 'alice@example.com', username: 'alice1', password: 'test') expect(response.status).to eq(400) expect(response.body).to include("This username and/or email address are already in use, please try again!") diff --git a/spec/peep_repository_spec.rb b/spec/peep_repository_spec.rb index dbedb2a764..0d9fe05e31 100644 --- a/spec/peep_repository_spec.rb +++ b/spec/peep_repository_spec.rb @@ -20,7 +20,7 @@ def reset_peeps_table expect(peeps.length).to eq(3) expect(peeps.first.time).to eq('2023-05-09 11:12:00') - expect(peeps.first.contents).to eq('hello, this is the third peep!') + expect(peeps.first.contents).to eq('I am peeping too! How cool') expect(peeps.first.account_id).to eq(3) end @@ -44,7 +44,7 @@ def reset_peeps_table peep_repo = PeepRepository.new peeps = peep_repo.all - expect(peeps.first.contents).to eq('hello, this is the third peep!') - expect(peeps.first.find_username).to eq('kay1') + expect(peeps.first.contents).to eq('I am peeping too! How cool') + expect(peeps.first.find_username).to eq('will1') end end diff --git a/spec/seeds/accounts_and_peeps_tables.sql b/spec/seeds/accounts_and_peeps_tables.sql index f70fcc7d89..840a84243c 100644 --- a/spec/seeds/accounts_and_peeps_tables.sql +++ b/spec/seeds/accounts_and_peeps_tables.sql @@ -25,12 +25,12 @@ TRUNCATE TABLE peeps RESTART IDENTITY CASCADE; TRUNCATE TABLE accounts RESTART IDENTITY CASCADE; INSERT INTO accounts ("email_address", "username", "name", "password") VALUES -('alice@test.com', 'alice1', 'Alice Wood', 'test123'), -('chris@test.com', 'chris1', 'Chris Wood', 'test321'), -('kay@test.com', 'kay1', 'Kay Lack', 'test987'); +('alice@example.com', 'alice1', 'Alice Wood', 'test123'), +('chris@example.com', 'chris1', 'Chris Wood', 'test321'), +('will@example.com', 'will1', 'Will Davies', 'test987'); INSERT INTO peeps ("time", "contents", "account_id") VALUES ('2023-05-09 11:09:00', 'hello, this is the first peep!', 1), -('2023-05-09 11:10:30', 'hello, this is the second peep!', 2), -('2023-05-09 11:12:00', 'hello, this is the third peep!', 3); +('2023-05-09 11:10:30', 'Oh wow, hello @alice1! Peep!', 2), +('2023-05-09 11:12:00', 'I am peeping too! How cool', 3); From 164ed9b65fb9cede4ba9e2d8f23a790d9ea5c96d Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 16:50:02 +0100 Subject: [PATCH 27/41] adding my first box to erb file! Also added placeholders to forms and hid passwords when entering --- README.md | 2 +- views/homepage.erb | 20 ++++++++++++++++---- views/login.erb | 8 ++++---- views/sign_up.erb | 10 +++++----- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ecce8f928c..a6135e0dc6 100644 --- a/README.md +++ b/README.md @@ -40,12 +40,12 @@ $ http://localhost:('Port in use')/ ## Upcoming to-dos +- Add condidtions to the sign in and post a peep - Refactor - Update tests for creating a peep to link to the logged in user - when sessions are implemented - Encrypt passwords stored on DB - REGEX test for @ sign in email addresses - Errors for empty fields in post a peep form -- Hide password when entering - Better CSS ## Techonologies used diff --git a/views/homepage.erb b/views/homepage.erb index 8b2a4f64b8..9e33e78e74 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -1,18 +1,30 @@ -

Welcome to Chitter!

+ + +

Welcome to Chitter!

Please sign up here:

-
+
+

What's on your mind?


+

Latest peeps...

<% @peeps.each do |peep|%>

- <%= peep.find_name%> (<%= peep.find_username %>) says...
+ <%= peep.find_name%> (<%= peep.find_username %>) says...
<%= peep.contents %>
- time: <%= peep.time %> + <%= peep.time %>

<% end %>
diff --git a/views/login.erb b/views/login.erb index 98eb9b434c..862ab22299 100644 --- a/views/login.erb +++ b/views/login.erb @@ -6,13 +6,13 @@

Please enter your login details


-
+

-
+

-
+

-
+

diff --git a/views/sign_up.erb b/views/sign_up.erb index 59e5656b41..56eabed209 100644 --- a/views/sign_up.erb +++ b/views/sign_up.erb @@ -4,15 +4,15 @@

Welcome to Chitter!

Please enter your details below

- +


-
+

-
+

-
+

-
+

From 0fcc8b7dc3904023553379594cde24e32703b873 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Wed, 10 May 2023 17:06:17 +0100 Subject: [PATCH 28/41] some css for the background and font --- views/add_a_peep.erb | 15 ++++++++++++++- views/homepage.erb | 4 ++++ views/sign_up.erb | 16 +++++++++++++++- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/views/add_a_peep.erb b/views/add_a_peep.erb index c31bda0f8b..4ec62b2a7f 100644 --- a/views/add_a_peep.erb +++ b/views/add_a_peep.erb @@ -1,10 +1,23 @@ +

What's your peep?

-


+



diff --git a/views/homepage.erb b/views/homepage.erb index 9e33e78e74..c5beb1b97b 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -8,6 +8,10 @@ padding: 20px; width: 200px; height: 75px; +} +body { + background-color: lightblue; + font-family: verdana; } diff --git a/views/sign_up.erb b/views/sign_up.erb index 56eabed209..2df455bb7d 100644 --- a/views/sign_up.erb +++ b/views/sign_up.erb @@ -1,6 +1,20 @@ - + + +

Welcome to Chitter!

Please enter your details below

From a8523c372d8a5c6124a2dd3f7600b357f4f7e9c9 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Thu, 11 May 2023 10:22:28 +0100 Subject: [PATCH 29/41] updated spec to reflect the css changes in app.rb --- spec/integration/app_spec.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index be8edf5c09..c660b1583b 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -12,8 +12,8 @@ response = get('/') expect(response.status).to eq(200) - expect(response.body).to include('

Welcome to Chitter!

') - expect(response.body).to include('Alice Wood (alice1) says...
') + expect(response.body).to include('

Welcome to Chitter!

') + expect(response.body).to include('Alice Wood (alice1) says...
') expect(response.body).to include('I am peeping too! How cool') end end @@ -43,7 +43,7 @@ expect(response.status).to eq(200) expect(response.body).to include("

Please enter your details below

") - expect(response.body).to include('
') + expect(response.body).to include('
') end end From 3169b1a490a08d5b447bcb941c8368e6f811c112 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Thu, 11 May 2023 11:41:57 +0100 Subject: [PATCH 30/41] updating view files with css --- views/add_a_peep.erb | 2 +- views/homepage.erb | 5 ++++- views/login.erb | 21 +++++++++++++++------ views/new_account_confirmation.erb | 13 +++++++++++++ views/posted_peep.erb | 13 +++++++++++++ views/sign_up.erb | 2 +- 6 files changed, 47 insertions(+), 9 deletions(-) diff --git a/views/add_a_peep.erb b/views/add_a_peep.erb index 4ec62b2a7f..8e6c39c404 100644 --- a/views/add_a_peep.erb +++ b/views/add_a_peep.erb @@ -9,7 +9,7 @@ width: 200px; height: 75px; } -body { + body { background-color: lightblue; font-family: verdana; } diff --git a/views/homepage.erb b/views/homepage.erb index c5beb1b97b..c8a82ab809 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -9,7 +9,7 @@ width: 200px; height: 75px; } -body { + body { background-color: lightblue; font-family: verdana; } @@ -18,6 +18,9 @@ body {

Welcome to Chitter!

Please sign up here:


+ +

Please login here:

+

What's on your mind?


diff --git a/views/login.erb b/views/login.erb index 862ab22299..9610b08f94 100644 --- a/views/login.erb +++ b/views/login.erb @@ -1,20 +1,29 @@ +

Welcome to Chitter!

Please enter your login details

- -


-
-
-
+




-

+

diff --git a/views/new_account_confirmation.erb b/views/new_account_confirmation.erb index dcd0a22178..beded2657a 100644 --- a/views/new_account_confirmation.erb +++ b/views/new_account_confirmation.erb @@ -1,5 +1,18 @@ +

Welcome, your Chitter account has been created!


diff --git a/views/posted_peep.erb b/views/posted_peep.erb index 8f6a711e6f..e121cc9e57 100644 --- a/views/posted_peep.erb +++ b/views/posted_peep.erb @@ -1,5 +1,18 @@ +

Nice one, your peep has been posted!


diff --git a/views/sign_up.erb b/views/sign_up.erb index 2df455bb7d..ef7931490e 100644 --- a/views/sign_up.erb +++ b/views/sign_up.erb @@ -9,7 +9,7 @@ width: 200px; height: 75px; } -body { + body { background-color: lightblue; font-family: verdana; } From 2c3a8a45ab97bcf169f5914384036734d891003d Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Thu, 11 May 2023 11:43:21 +0100 Subject: [PATCH 31/41] successful log in working - errors for incorrect details being worked on --- app.rb | 40 ++++++++++++++++++++++++++++++++---- lib/account.rb | 5 +++-- lib/account_repository.rb | 17 +++++++++++++-- spec/integration/app_spec.rb | 28 ++++++++++++++++++++++++- views/login_fail.erb | 20 ++++++++++++++++++ views/login_success.erb | 20 ++++++++++++++++++ 6 files changed, 121 insertions(+), 9 deletions(-) create mode 100644 views/login_fail.erb create mode 100644 views/login_success.erb diff --git a/app.rb b/app.rb index 9cc634a7c3..7946e51a0a 100644 --- a/app.rb +++ b/app.rb @@ -39,7 +39,7 @@ class Chitter < Sinatra::Base end get '/sign_up' do - return erb (:sign_up) + return erb(:sign_up) end post '/sign_up' do @@ -50,13 +50,45 @@ class Chitter < Sinatra::Base account.username = params[:username] account.password = params[:password] - if account.valid? - p account.valid? + if account.unique? repo.add(account) return erb(:new_account_confirmation) else status 400 - return "This username and/or email address are already in use, please try again!" + return "This username and/or email address is already in use, please try again!" end end + + get '/login' do + return erb(:login) + end + + post '/login' do + accounts = AccountRepository.new + submitted_email = params[:email_address] + submitted_password = params[:password] + + account = accounts.find_by_email_address(submitted_email) + + if submitted_password == account.password && submitted_email == account.email_address && account != nil + return erb(:login_success) + else + status 400 + return erb(:login_fail) + end + end + + + # def sign_in(email_address, submitted_password) + # account = find_by_email_address(email) + + # return nil if account.nil? + + # stored_password = BCrypt::Password.new(account.password) + # if stored_password == submitted_password + # return erb(:login_success) + # else + # return erb(:login_fail) + # end + # end end diff --git a/lib/account.rb b/lib/account.rb index 2bd3a824f5..ba85281592 100644 --- a/lib/account.rb +++ b/lib/account.rb @@ -1,11 +1,12 @@ class Account attr_accessor :id, :email_address, :username, :name, :password - def valid? + def unique? sql = "SELECT * FROM accounts WHERE email_address=$1::text OR username=$2::text;" - sql_params = [self.email_address, self.username] + sql_params = [email_address, username] result_set = DatabaseConnection.exec_params(sql, sql_params) return result_set.to_a.length == 0 end + end diff --git a/lib/account_repository.rb b/lib/account_repository.rb index 8eac46570b..b8f16999be 100644 --- a/lib/account_repository.rb +++ b/lib/account_repository.rb @@ -23,15 +23,28 @@ def find(id) end def add(account) + # encrypted_password = BCrypt::Password.create(account.password) sql = 'INSERT INTO accounts (email_address, username, name, password) VALUES ($1, $2, $3, $4);' - sql_params = [account.email_address, account.username, account.name, account.password] + sql_params = [account.email_address, account.username, account.name, account.password]#encrypted_password] + DatabaseConnection.exec_params(sql, sql_params) return nil end - private + def find_by_email_address(email_address) + sql = 'SELECT * FROM accounts WHERE email_address = $1;' + sql_params = [email_address] + p sql_params + + + result_set = DatabaseConnection.exec_params(sql, sql_params).first + + return record_to_account(result_set) + end + + private def record_to_account(record) account = Account.new diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index c660b1583b..d51b49e428 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -59,7 +59,33 @@ response = post('/sign_up', name: 'Alice Wood', email_address: 'alice@example.com', username: 'alice1', password: 'test') expect(response.status).to eq(400) - expect(response.body).to include("This username and/or email address are already in use, please try again!") + expect(response.body).to include("This username and/or email address is already in use, please try again!") + end + end + + context 'GET /login' do + it 'displays the empty log in form' do + response = get('/login') + + expect(response.status).to eq(200) + expect(response.body).to include('

Please enter your login details

') + expect(response.body).to include('
') + end + end + + context 'POST /login' do + it 'checks the login details against the database and successfully logs in' do + response = post('/login', name: 'Alice Wood', email_address: 'alice@example.com', username: 'alice1', password: 'test123') + + expect(response.status).to eq(200) + expect(response.body).to include('

Welcome back! Click below to see the latest peeps

') + end + + it 'checks the incorrect details against the database and gives login fail message'do + response = post('/login', name: 'Alice Wood', email_address: 'incorrect@example.com', username: 'alice1', password: 'test123') + + expect(response.status).to eq(400) + expect(response.body).to include('

Sorry! Your details did not match, please click below to try again

') end end end diff --git a/views/login_fail.erb b/views/login_fail.erb new file mode 100644 index 0000000000..03e58d7686 --- /dev/null +++ b/views/login_fail.erb @@ -0,0 +1,20 @@ + + + + +

Sorry! Your details did not match, please click below to try again

+
+ + \ No newline at end of file diff --git a/views/login_success.erb b/views/login_success.erb new file mode 100644 index 0000000000..02bb5cd313 --- /dev/null +++ b/views/login_success.erb @@ -0,0 +1,20 @@ + + + + +

Welcome back! Click below to see the latest peeps

+
+ + \ No newline at end of file From 78f04a5a64be52d4e3a8de0fa94044f1f4fd93e5 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Thu, 11 May 2023 12:13:16 +0100 Subject: [PATCH 32/41] safe navigation operator added for account login --- app.rb | 2 +- lib/account_repository.rb | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app.rb b/app.rb index 7946e51a0a..71c8a0fe87 100644 --- a/app.rb +++ b/app.rb @@ -70,7 +70,7 @@ class Chitter < Sinatra::Base account = accounts.find_by_email_address(submitted_email) - if submitted_password == account.password && submitted_email == account.email_address && account != nil + if account && submitted_password == account&.password && submitted_email == account&.email_address return erb(:login_success) else status 400 diff --git a/lib/account_repository.rb b/lib/account_repository.rb index b8f16999be..d8f96756aa 100644 --- a/lib/account_repository.rb +++ b/lib/account_repository.rb @@ -36,12 +36,14 @@ def add(account) def find_by_email_address(email_address) sql = 'SELECT * FROM accounts WHERE email_address = $1;' sql_params = [email_address] - p sql_params - result_set = DatabaseConnection.exec_params(sql, sql_params).first - return record_to_account(result_set) + if result_set.to_a.empty? + return nil + else + return record_to_account(result_set) + end end private From e084e7482c114dc6da79ef4923b60daa06129ce0 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Thu, 11 May 2023 12:46:02 +0100 Subject: [PATCH 33/41] makes the sign up, log in and post conditional on session id --- app.rb | 15 +++------------ spec/integration/app_spec.rb | 1 + views/homepage.erb | 16 +++++++++------- views/login.erb | 4 ++++ views/sign_up.erb | 3 +++ 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/app.rb b/app.rb index 71c8a0fe87..f771ee0d00 100644 --- a/app.rb +++ b/app.rb @@ -7,6 +7,7 @@ DatabaseConnection.connect class Chitter < Sinatra::Base + enable :sessions configure :development do register Sinatra::Reloader also_reload 'lib/peep_repository' @@ -71,6 +72,8 @@ class Chitter < Sinatra::Base account = accounts.find_by_email_address(submitted_email) if account && submitted_password == account&.password && submitted_email == account&.email_address + p session[:account_id] = account.id + return erb(:login_success) else status 400 @@ -79,16 +82,4 @@ class Chitter < Sinatra::Base end - # def sign_in(email_address, submitted_password) - # account = find_by_email_address(email) - - # return nil if account.nil? - - # stored_password = BCrypt::Password.new(account.password) - # if stored_password == submitted_password - # return erb(:login_success) - # else - # return erb(:login_fail) - # end - # end end diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index d51b49e428..c99ae3d7b7 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -88,4 +88,5 @@ expect(response.body).to include('

Sorry! Your details did not match, please click below to try again

') end end + end diff --git a/views/homepage.erb b/views/homepage.erb index c8a82ab809..cfd2ebbfee 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -16,14 +16,16 @@

Welcome to Chitter!

-

Please sign up here:

-
-

Please login here:

-
- -

What's on your mind?

-
+ <% if session[:account_id] == nil %> +

Please sign up here:

+
+

Or please login here:

+
+ <% else %> +

What's on your mind?

+
+ <%end%>

Latest peeps...

diff --git a/views/login.erb b/views/login.erb index 9610b08f94..d9d4c22d6d 100644 --- a/views/login.erb +++ b/views/login.erb @@ -25,6 +25,10 @@

+ +

Haven't got an account?

+
+
diff --git a/views/sign_up.erb b/views/sign_up.erb index ef7931490e..b15d1a2118 100644 --- a/views/sign_up.erb +++ b/views/sign_up.erb @@ -29,6 +29,9 @@

+
+

Already have an account?

+
From 7fa1780175e54280b116a5e0710d6ee7d7ae9bec Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Thu, 11 May 2023 13:27:50 +0100 Subject: [PATCH 34/41] added a log out button and used sessions to implement --- app.rb | 10 +++++++--- spec/integration/app_spec.rb | 11 ++++++++++- views/add_a_peep.erb | 18 +++++++++++++----- views/homepage.erb | 21 +++++++++++++++++---- views/logged_out.erb | 24 ++++++++++++++++++++++++ views/login_success.erb | 5 ++++- views/new_account_confirmation.erb | 4 ++++ views/posted_peep.erb | 4 ++++ 8 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 views/logged_out.erb diff --git a/app.rb b/app.rb index f771ee0d00..0ef8fa98dc 100644 --- a/app.rb +++ b/app.rb @@ -72,8 +72,7 @@ class Chitter < Sinatra::Base account = accounts.find_by_email_address(submitted_email) if account && submitted_password == account&.password && submitted_email == account&.email_address - p session[:account_id] = account.id - + session[:account_id] = account.id return erb(:login_success) else status 400 @@ -81,5 +80,10 @@ class Chitter < Sinatra::Base end end - + post '/log_out' do + p session[:account_id] + session[:account_id] = nil + p session[:account_id] + return erb(:logged_out) + end end diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index c99ae3d7b7..0a9d283250 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -78,7 +78,8 @@ response = post('/login', name: 'Alice Wood', email_address: 'alice@example.com', username: 'alice1', password: 'test123') expect(response.status).to eq(200) - expect(response.body).to include('

Welcome back! Click below to see the latest peeps

') + expect(response.body).to include('

Welcome back!

') + expect(response.body).to include('

Click below to see the latest peeps

') end it 'checks the incorrect details against the database and gives login fail message'do @@ -89,4 +90,12 @@ end end + context 'POST /log_out' do + it ' logs the user out and deletes the session info' do + response = post('/log_out') + + expect(response.status).to eq(200) + expect(response.body).to include('

See ya! You have been logged out

') + end + end end diff --git a/views/add_a_peep.erb b/views/add_a_peep.erb index 8e6c39c404..788678d775 100644 --- a/views/add_a_peep.erb +++ b/views/add_a_peep.erb @@ -17,10 +17,18 @@

What's your peep?

-


-
-

-
- +

+ +
+
+ +

+ + +
+
+
+ +
\ No newline at end of file diff --git a/views/homepage.erb b/views/homepage.erb index cfd2ebbfee..627cc9713c 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -19,12 +19,17 @@ <% if session[:account_id] == nil %>

Please sign up here:

-
+ +

Or please login here:

-
+ <% else %> -

What's on your mind?

-
+

What's on your mind?

+
<%end%>

Latest peeps...

@@ -36,6 +41,14 @@ <%= peep.time %>

<% end %> +
+
+ <% if session[:account_id] != nil %> +
+ +
+
+ <% end %> \ No newline at end of file diff --git a/views/logged_out.erb b/views/logged_out.erb new file mode 100644 index 0000000000..2c55978d8f --- /dev/null +++ b/views/logged_out.erb @@ -0,0 +1,24 @@ + + + + +

See ya! You have been logged out

+
+
+
+
+
+ + \ No newline at end of file diff --git a/views/login_success.erb b/views/login_success.erb index 02bb5cd313..d100007f10 100644 --- a/views/login_success.erb +++ b/views/login_success.erb @@ -14,7 +14,10 @@ } -

Welcome back! Click below to see the latest peeps

+

Welcome back!

+

Click below to see the latest peeps


+
+
\ No newline at end of file diff --git a/views/new_account_confirmation.erb b/views/new_account_confirmation.erb index beded2657a..7db966e63c 100644 --- a/views/new_account_confirmation.erb +++ b/views/new_account_confirmation.erb @@ -16,5 +16,9 @@

Welcome, your Chitter account has been created!


+
+
+
+
\ No newline at end of file diff --git a/views/posted_peep.erb b/views/posted_peep.erb index e121cc9e57..5b15abc4d9 100644 --- a/views/posted_peep.erb +++ b/views/posted_peep.erb @@ -18,5 +18,9 @@


+
+
+
+
\ No newline at end of file From 177087844c651f2844454b56b38d6bce2365e973 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Thu, 11 May 2023 14:49:18 +0100 Subject: [PATCH 35/41] fixed the html to try and improve layout - amended tests accordingly --- app.rb | 4 +- lib/account.rb | 2 +- lib/account_repository.rb | 12 ++--- spec/integration/app_spec.rb | 12 ++--- views/add_a_peep.erb | 36 ++++++++------ views/homepage.erb | 69 ++++++++++++++------------- views/logged_out.erb | 37 +++++++++------ views/login.erb | 56 ++++++++++++---------- views/login_fail.erb | 32 +++++++------ views/login_success.erb | 37 +++++++++------ views/new_account_confirmation.erb | 34 +++++++------ views/posted_peep.erb | 2 +- views/sign_up.erb | 76 +++++++++++++++++------------- 13 files changed, 228 insertions(+), 181 deletions(-) diff --git a/app.rb b/app.rb index 0ef8fa98dc..664b10f852 100644 --- a/app.rb +++ b/app.rb @@ -71,7 +71,9 @@ class Chitter < Sinatra::Base account = accounts.find_by_email_address(submitted_email) - if account && submitted_password == account&.password && submitted_email == account&.email_address + if (account && + submitted_password == account&.password && + submitted_email == account&.email_address) session[:account_id] = account.id return erb(:login_success) else diff --git a/lib/account.rb b/lib/account.rb index ba85281592..7ff7399351 100644 --- a/lib/account.rb +++ b/lib/account.rb @@ -6,7 +6,7 @@ def unique? sql_params = [email_address, username] result_set = DatabaseConnection.exec_params(sql, sql_params) - return result_set.to_a.length == 0 + return result_set.to_a.length.zero? end end diff --git a/lib/account_repository.rb b/lib/account_repository.rb index d8f96756aa..4fb108aaeb 100644 --- a/lib/account_repository.rb +++ b/lib/account_repository.rb @@ -23,10 +23,8 @@ def find(id) end def add(account) - # encrypted_password = BCrypt::Password.create(account.password) sql = 'INSERT INTO accounts (email_address, username, name, password) VALUES ($1, $2, $3, $4);' - sql_params = [account.email_address, account.username, account.name, account.password]#encrypted_password] - + sql_params = [account.email_address, account.username, account.name, account.password] DatabaseConnection.exec_params(sql, sql_params) @@ -39,11 +37,9 @@ def find_by_email_address(email_address) result_set = DatabaseConnection.exec_params(sql, sql_params).first - if result_set.to_a.empty? - return nil - else - return record_to_account(result_set) - end + return nil if result_set.to_a.empty? + + return record_to_account(result_set) end private diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index 0a9d283250..648a7faa4f 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -43,7 +43,7 @@ expect(response.status).to eq(200) expect(response.body).to include("

Please enter your details below

") - expect(response.body).to include('
') + expect(response.body).to include('') end end @@ -69,7 +69,7 @@ expect(response.status).to eq(200) expect(response.body).to include('

Please enter your login details

') - expect(response.body).to include('
') + expect(response.body).to include('') end end @@ -82,11 +82,11 @@ expect(response.body).to include('

Click below to see the latest peeps

') end - it 'checks the incorrect details against the database and gives login fail message'do - response = post('/login', name: 'Alice Wood', email_address: 'incorrect@example.com', username: 'alice1', password: 'test123') + it 'checks the incorrect details against the database and gives login fail message' do + response = post('/login', name: 'Alice Wood', email_address: 'incorrect@example.com', username: 'alice1', password: 'test123') - expect(response.status).to eq(400) - expect(response.body).to include('

Sorry! Your details did not match, please click below to try again

') + expect(response.status).to eq(400) + expect(response.body).to include('

Sorry! Your details did not match, please click below to try again

') end end diff --git a/views/add_a_peep.erb b/views/add_a_peep.erb index 788678d775..d0ea87a3dc 100644 --- a/views/add_a_peep.erb +++ b/views/add_a_peep.erb @@ -1,19 +1,20 @@ - - + + #roundedcorners { + border-radius: 25px; + border: 2px solid #1DA1F2; + padding: 20px; + width: 200px; + height: 75px; + } + body { + background-color: lightblue; + font-family: verdana; + } + + +

What's your peep?

@@ -26,9 +27,14 @@

+

- +
\ No newline at end of file diff --git a/views/homepage.erb b/views/homepage.erb index 627cc9713c..45766d82de 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -1,20 +1,20 @@ - + + + - -

Welcome to Chitter!

<% if session[:account_id] == nil %> @@ -29,26 +29,29 @@ <% else %>

What's on your mind?

-
- <%end%> + +
+ <%end%> -

Latest peeps...

-
- <% @peeps.each do |peep|%> -

- <%= peep.find_name%> (<%= peep.find_username %>) says...
- <%= peep.contents %>
- <%= peep.time %> -

- <% end %> -
-
- <% if session[:account_id] != nil %> -
+
+

Latest peeps...

+ <% @peeps.each do |peep|%> +

+ <%= peep.find_name%> (<%= peep.find_username %>) says...
+ <%= peep.contents %>
+ <%= peep.time %> +

+ <% end %> +
+
+ <% if session[:account_id] != nil %> + - -
- <% end %> + +
+ <% end %>
\ No newline at end of file diff --git a/views/logged_out.erb b/views/logged_out.erb index 2c55978d8f..b5918012c4 100644 --- a/views/logged_out.erb +++ b/views/logged_out.erb @@ -1,24 +1,31 @@ - + + #roundedcorners { + border-radius: 25px; + border: 2px solid #1DA1F2; + padding: 20px; + width: 200px; + height: 75px; + } + body { + background-color: lightblue; + font-family: verdana; + } + +

See ya! You have been logged out

-
+ +



-
+ +
\ No newline at end of file diff --git a/views/login.erb b/views/login.erb index d9d4c22d6d..fefdc07c9c 100644 --- a/views/login.erb +++ b/views/login.erb @@ -1,35 +1,41 @@ - + + #roundedcorners { + border-radius: 25px; + border: 2px solid #1DA1F2; + padding: 20px; + width: 200px; + height: 75px; + } + body { + background-color: lightblue; + font-family: verdana; + } + +

Welcome to Chitter!

Please enter your login details

-
-
-
-
-
-

+ +
+ +
+
+ +
+
+ +
+
+

Haven't got an account?

+ +
- -

Haven't got an account?

-
-
- - + \ No newline at end of file diff --git a/views/login_fail.erb b/views/login_fail.erb index 03e58d7686..dda90196b2 100644 --- a/views/login_fail.erb +++ b/views/login_fail.erb @@ -1,20 +1,24 @@ - + + #roundedcorners { + border-radius: 25px; + border: 2px solid #1DA1F2; + padding: 20px; + width: 200px; + height: 75px; + } + body { + background-color: lightblue; + font-family: verdana; + } + +

Sorry! Your details did not match, please click below to try again

-
+ +
\ No newline at end of file diff --git a/views/login_success.erb b/views/login_success.erb index d100007f10..ec20346ab5 100644 --- a/views/login_success.erb +++ b/views/login_success.erb @@ -1,23 +1,30 @@ - + + #roundedcorners { + border-radius: 25px; + border: 2px solid #1DA1F2; + padding: 20px; + width: 200px; + height: 75px; + } + body { + background-color: lightblue; + font-family: verdana; + } + +

Welcome back!

Click below to see the latest peeps

-
+ +

-
+ +
\ No newline at end of file diff --git a/views/new_account_confirmation.erb b/views/new_account_confirmation.erb index 7db966e63c..77b537a49c 100644 --- a/views/new_account_confirmation.erb +++ b/views/new_account_confirmation.erb @@ -1,24 +1,28 @@ - + + #roundedcorners { + border-radius: 25px; + border: 2px solid #1DA1F2; + padding: 20px; + width: 200px; + height: 75px; + } + body { + background-color: lightblue; + font-family: verdana; + } + +

Welcome, your Chitter account has been created!

-
+ +



-
+
\ No newline at end of file diff --git a/views/posted_peep.erb b/views/posted_peep.erb index 5b15abc4d9..691c8c208e 100644 --- a/views/posted_peep.erb +++ b/views/posted_peep.erb @@ -21,6 +21,6 @@


-
+
\ No newline at end of file diff --git a/views/sign_up.erb b/views/sign_up.erb index b15d1a2118..e54c1d6741 100644 --- a/views/sign_up.erb +++ b/views/sign_up.erb @@ -1,39 +1,51 @@ - - - - + + + +

Welcome to Chitter!

Please enter your details below

-


-
-
-
-
-
-
-
-
-

-
-

Already have an account?

-
-
- - +

+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +

+
+

Already have an account?

+ +
+ + \ No newline at end of file From e4931279c217fd65d69f5f21ae4952ee59ef47bc Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Thu, 11 May 2023 16:19:31 +0100 Subject: [PATCH 36/41] update readme --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index a6135e0dc6..7b29ea7197 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,10 @@ $ http://localhost:('Port in use')/ 7. Add a log in and log out page, which checks the database to match log in details -8. Send an email when someone has been mentioned in a post - ## Upcoming to-dos -- Add condidtions to the sign in and post a peep +- Send an email when someone has been mentioned in a post - Refactor -- Update tests for creating a peep to link to the logged in user - when sessions are implemented - Encrypt passwords stored on DB - REGEX test for @ sign in email addresses - Errors for empty fields in post a peep form From cdf96bea85ed666380974d300567e7f82d69cecc Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Thu, 11 May 2023 17:56:25 +0100 Subject: [PATCH 37/41] added bcrypt for passwords --- Gemfile | 2 ++ Gemfile.lock | 2 ++ app.rb | 32 ++++++++++++----- lib/account_repository.rb | 15 +++++++- spec/account_repository_spec.rb | 10 +++--- spec/integration/app_spec.rb | 45 +++++++++++++++++++++--- spec/seeds/accounts_and_peeps_tables.sql | 6 ++-- spec/spec_helper.rb | 1 + 8 files changed, 91 insertions(+), 22 deletions(-) diff --git a/Gemfile b/Gemfile index a70fdb6f3c..c1f41fea37 100644 --- a/Gemfile +++ b/Gemfile @@ -17,3 +17,5 @@ gem "sinatra-contrib", "~> 3.0" gem "webrick", "~> 1.8" gem "rack-test", "~> 2.1" gem "pg", "~> 1.3" + +gem "bcrypt", "~> 3.1" diff --git a/Gemfile.lock b/Gemfile.lock index 884ce79b7b..156df76bd6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,6 +3,7 @@ GEM specs: ansi (1.5.0) ast (2.4.2) + bcrypt (3.1.18) diff-lcs (1.4.4) docile (1.4.0) multi_json (1.15.0) @@ -77,6 +78,7 @@ PLATFORMS ruby DEPENDENCIES + bcrypt (~> 3.1) pg (~> 1.3) rack-test (~> 2.1) rspec diff --git a/app.rb b/app.rb index 664b10f852..4a34ce7f5e 100644 --- a/app.rb +++ b/app.rb @@ -8,12 +8,15 @@ class Chitter < Sinatra::Base enable :sessions + enable :raise_errors + enable :dump_errors configure :development do register Sinatra::Reloader - also_reload 'lib/peep_repository' - also_reload 'lib/account_repository' + also_reload 'lib/' end +# Homepage + get '/' do repo = PeepRepository.new @peeps = repo.all @@ -23,6 +26,8 @@ class Chitter < Sinatra::Base return erb(:homepage) end + # Peeps + get '/add_peep' do return erb(:add_a_peep) end @@ -39,6 +44,8 @@ class Chitter < Sinatra::Base return erb(:posted_peep) end + # Sign up + get '/sign_up' do return erb(:sign_up) end @@ -46,10 +53,11 @@ class Chitter < Sinatra::Base post '/sign_up' do repo = AccountRepository.new account = Account.new + encrypted_password = BCrypt::Password.create(params[:password]) account.email_address = params[:email_address] account.name = params[:name] account.username = params[:username] - account.password = params[:password] + account.password = encrypted_password if account.unique? repo.add(account) @@ -60,6 +68,8 @@ class Chitter < Sinatra::Base end end + # Log in + get '/login' do return erb(:login) end @@ -68,12 +78,16 @@ class Chitter < Sinatra::Base accounts = AccountRepository.new submitted_email = params[:email_address] submitted_password = params[:password] - account = accounts.find_by_email_address(submitted_email) - if (account && - submitted_password == account&.password && - submitted_email == account&.email_address) + if account == nil + status 400 + return erb(:login_fail) + end + + stored_password = BCrypt::Password.new(account.password) + + if stored_password == submitted_password && session[:account_id] = account.id return erb(:login_success) else @@ -82,10 +96,10 @@ class Chitter < Sinatra::Base end end + # Log out + post '/log_out' do - p session[:account_id] session[:account_id] = nil - p session[:account_id] return erb(:logged_out) end end diff --git a/lib/account_repository.rb b/lib/account_repository.rb index 4fb108aaeb..55cde9cc56 100644 --- a/lib/account_repository.rb +++ b/lib/account_repository.rb @@ -1,4 +1,5 @@ require_relative './account' +require 'bcrypt' class AccountRepository def all @@ -23,14 +24,26 @@ def find(id) end def add(account) + encrypted_password = BCrypt::Password.create(account.password) sql = 'INSERT INTO accounts (email_address, username, name, password) VALUES ($1, $2, $3, $4);' - sql_params = [account.email_address, account.username, account.name, account.password] + sql_params = [account.email_address, account.username, account.name, encrypted_password] DatabaseConnection.exec_params(sql, sql_params) return nil end + # def sign_in(email_address, submitted_password) + # account = find_by_email_address(email_address) + + # return nil if account.nil? + + # stored_password = BCrypt::Password.new(account.password) + # return true if stored_password == submitted_password + + # return false + # end + def find_by_email_address(email_address) sql = 'SELECT * FROM accounts WHERE email_address = $1;' sql_params = [email_address] diff --git a/spec/account_repository_spec.rb b/spec/account_repository_spec.rb index da8613dce1..83ba47abd1 100644 --- a/spec/account_repository_spec.rb +++ b/spec/account_repository_spec.rb @@ -13,7 +13,7 @@ def reset_accounts_table reset_accounts_table end - context 'with a database pre-loaded with accounts' do + context 'with a database pre-loaded with test accounts' do it 'returns a list of accounts' do repo = AccountRepository.new @@ -24,10 +24,10 @@ def reset_accounts_table expect(accounts.first.email_address).to eq('alice@example.com') expect(accounts.first.username).to eq('alice1') expect(accounts.first.name).to eq('Alice Wood') - expect(accounts.first.password).to eq('test123') + expect(BCrypt::Password.new(accounts.first.password)).to eq('test123') end - it 'returns the account details with id 2' do + it 'returns the test account details with id 2' do repo = AccountRepository.new account = repo.find(2) @@ -36,7 +36,7 @@ def reset_accounts_table expect(account.email_address).to eq('chris@example.com') expect(account.username).to eq('chris1') expect(account.name).to eq('Chris Wood') - expect(account.password).to eq('test321') + expect(BCrypt::Password.new(account.password)).to eq('test321') end end @@ -57,6 +57,6 @@ def reset_accounts_table expect(accounts.last.email_address).to eq('leo@example.com') expect(accounts.last.username).to eq('leo1') expect(accounts.last.name).to eq('Leo Hetsch') - expect(accounts.last.password).to eq('test456') + expect(BCrypt::Password.new(accounts.last.password)).to eq('test456') end end diff --git a/spec/integration/app_spec.rb b/spec/integration/app_spec.rb index 648a7faa4f..949a2ff73e 100644 --- a/spec/integration/app_spec.rb +++ b/spec/integration/app_spec.rb @@ -7,6 +7,8 @@ let(:app) { Chitter.new } + # Homepage tests + context 'GET /' do it 'should return the list of tweets in reverse chronological order' do response = get('/') @@ -18,6 +20,8 @@ end end + # Add peep tests + context 'GET /add_peep' do it 'shows a form to be completed to add a tweet to the homepage' do response = get('/add_peep') @@ -37,6 +41,8 @@ end end + # Sign up tests + context 'GET /sign_up' do it 'shows you the sign up form' do response = get('/sign_up') @@ -49,19 +55,29 @@ context 'POST /sign_up' do it 'adds the account details to the database and redirects to confirmation page' do - response = post('/sign_up', name: 'Leo Hetsch', email_address: 'leo@example.com', username: 'leo1', password: 'test') + response = post('/sign_up', + name: 'Leo Hetsch', + email_address: 'leo@example.com', + username: 'leo1', + password: 'test') expect(response.status).to eq(200) expect(response.body).to include("

Welcome, your Chitter account has been created!

") end it 'fails if the username and/or email are already in use' do - response = post('/sign_up', name: 'Alice Wood', email_address: 'alice@example.com', username: 'alice1', password: 'test') + response = post('/sign_up', + name: 'Alice Wood', + email_address: 'alice@example.com', + username: 'alice1', + password: 'test') expect(response.status).to eq(400) expect(response.body).to include("This username and/or email address is already in use, please try again!") end end + + # Log in tests context 'GET /login' do it 'displays the empty log in form' do @@ -75,7 +91,11 @@ context 'POST /login' do it 'checks the login details against the database and successfully logs in' do - response = post('/login', name: 'Alice Wood', email_address: 'alice@example.com', username: 'alice1', password: 'test123') + response = post('/login', + name: 'Alice Wood', + email_address: 'alice@example.com', + username: 'alice1', + password: 'test123') expect(response.status).to eq(200) expect(response.body).to include('

Welcome back!

') @@ -83,13 +103,30 @@ end it 'checks the incorrect details against the database and gives login fail message' do - response = post('/login', name: 'Alice Wood', email_address: 'incorrect@example.com', username: 'alice1', password: 'test123') + response = post('/login', + name: 'Alice Wood', + email_address: 'incorrect@example.com', + username: 'alice1', + password: 'test123') + + expect(response.status).to eq(400) + expect(response.body).to include('

Sorry! Your details did not match, please click below to try again

') + end + + it 'returns the log in fail for incorrect passwords' do + response = post('/login', + name: 'Alice Wood', + email_address: 'alice@example.com', + username: 'alice1', + password: 'incorrect') expect(response.status).to eq(400) expect(response.body).to include('

Sorry! Your details did not match, please click below to try again

') end end +# Log out tests + context 'POST /log_out' do it ' logs the user out and deletes the session info' do response = post('/log_out') diff --git a/spec/seeds/accounts_and_peeps_tables.sql b/spec/seeds/accounts_and_peeps_tables.sql index 840a84243c..5c70fbf352 100644 --- a/spec/seeds/accounts_and_peeps_tables.sql +++ b/spec/seeds/accounts_and_peeps_tables.sql @@ -25,9 +25,9 @@ TRUNCATE TABLE peeps RESTART IDENTITY CASCADE; TRUNCATE TABLE accounts RESTART IDENTITY CASCADE; INSERT INTO accounts ("email_address", "username", "name", "password") VALUES -('alice@example.com', 'alice1', 'Alice Wood', 'test123'), -('chris@example.com', 'chris1', 'Chris Wood', 'test321'), -('will@example.com', 'will1', 'Will Davies', 'test987'); +('alice@example.com', 'alice1', 'Alice Wood', '$2a$12$EG2Oq93fK9rGWA.5WK28Y.VhcpMXOwqyWZfsjANuOoSwa6QGRotHq'), +('chris@example.com', 'chris1', 'Chris Wood', '$2a$12$cCyDdoyGltyOwuMGgmX2zuHgAwApVH6fgcZtclo.m/ZGZqJN4OPHe'), +('will@example.com', 'will1', 'Will Davies', '$2a$12$A18MdlXlP0YgmNSDcjqRM.Oj0fqHqifApsMVo/xr5l/W8torB9VqG'); INSERT INTO peeps ("time", "contents", "account_id") VALUES ('2023-05-09 11:09:00', 'hello, this is the first peep!', 1), diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 04cab217be..a667ed4d2f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,7 @@ require 'simplecov-console' require 'database_connection' +ENV['RACK_ENV'] = 'test' ENV['ENV'] = 'test' DatabaseConnection.connect From fd9738087a41df5d2dbdce25751a021a96c7c278 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Fri, 12 May 2023 17:55:46 +0100 Subject: [PATCH 38/41] fixing sessions bug and updating html --- Gemfile | 1 + Gemfile.lock | 2 ++ app.rb | 7 ++++++- views/add_a_peep.erb | 6 +++--- views/homepage.erb | 7 +++---- views/logged_out.erb | 6 +++--- views/login.erb | 6 +++--- views/login_fail.erb | 7 ++++--- views/login_success.erb | 6 +++--- views/new_account_confirmation.erb | 7 ++++--- views/posted_peep.erb | 2 +- views/sign_up.erb | 6 +++--- 12 files changed, 36 insertions(+), 27 deletions(-) diff --git a/Gemfile b/Gemfile index c1f41fea37..63676c0345 100644 --- a/Gemfile +++ b/Gemfile @@ -19,3 +19,4 @@ gem "rack-test", "~> 2.1" gem "pg", "~> 1.3" gem "bcrypt", "~> 3.1" +gem 'byebug' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 156df76bd6..fe870e0a6a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,7 @@ GEM ansi (1.5.0) ast (2.4.2) bcrypt (3.1.18) + byebug (11.1.3) diff-lcs (1.4.4) docile (1.4.0) multi_json (1.15.0) @@ -79,6 +80,7 @@ PLATFORMS DEPENDENCIES bcrypt (~> 3.1) + byebug pg (~> 1.3) rack-test (~> 2.1) rspec diff --git a/app.rb b/app.rb index 4a34ce7f5e..f1122ac0d4 100644 --- a/app.rb +++ b/app.rb @@ -18,6 +18,9 @@ class Chitter < Sinatra::Base # Homepage get '/' do + p "/" + p session + repo = PeepRepository.new @peeps = repo.all @@ -61,6 +64,8 @@ class Chitter < Sinatra::Base if account.unique? repo.add(account) + new_account = repo.find_by_email_address(account.email_address) + session[:account_id] = new_account.id return erb(:new_account_confirmation) else status 400 @@ -87,7 +92,7 @@ class Chitter < Sinatra::Base stored_password = BCrypt::Password.new(account.password) - if stored_password == submitted_password && + if stored_password == submitted_password session[:account_id] = account.id return erb(:login_success) else diff --git a/views/add_a_peep.erb b/views/add_a_peep.erb index d0ea87a3dc..0efe92712a 100644 --- a/views/add_a_peep.erb +++ b/views/add_a_peep.erb @@ -5,8 +5,8 @@ border-radius: 25px; border: 2px solid #1DA1F2; padding: 20px; - width: 200px; - height: 75px; + width: 260px; + height: 35px; } body { background-color: lightblue; @@ -16,7 +16,7 @@
-

What's your peep?

+

What's your peep?

diff --git a/views/homepage.erb b/views/homepage.erb index 45766d82de..d41c636f8d 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -5,8 +5,8 @@ border-radius: 25px; border: 2px solid #1DA1F2; padding: 20px; - width: 200px; - height: 75px; + width: 365px; + height: 45px; } body { background-color: lightblue; @@ -14,9 +14,8 @@ } +

Welcome to Chitter!

-

Welcome to Chitter!

- <% if session[:account_id] == nil %>

Please sign up here:

diff --git a/views/login.erb b/views/login.erb index fefdc07c9c..5bebaf1e96 100644 --- a/views/login.erb +++ b/views/login.erb @@ -5,8 +5,8 @@ border-radius: 25px; border: 2px solid #1DA1F2; padding: 20px; - width: 200px; - height: 75px; + width: 365px; + height: 45px; } body { background-color: lightblue; @@ -15,7 +15,7 @@ -

Welcome to Chitter!

+

Welcome to Chitter!

Please enter your login details

diff --git a/views/login_fail.erb b/views/login_fail.erb index dda90196b2..dff45dc034 100644 --- a/views/login_fail.erb +++ b/views/login_fail.erb @@ -5,8 +5,8 @@ border-radius: 25px; border: 2px solid #1DA1F2; padding: 20px; - width: 200px; - height: 75px; + width: 600px; + height: 45px; } body { background-color: lightblue; @@ -15,7 +15,8 @@ -

Sorry! Your details did not match, please click below to try again

+

Sorry! Your details did not match

+

Please click below to try again

diff --git a/views/login_success.erb b/views/login_success.erb index ec20346ab5..e9c93dd71c 100644 --- a/views/login_success.erb +++ b/views/login_success.erb @@ -5,8 +5,8 @@ border-radius: 25px; border: 2px solid #1DA1F2; padding: 20px; - width: 200px; - height: 75px; + width: 275px; + height: 40px; } body { background-color: lightblue; @@ -15,7 +15,7 @@ -

Welcome back!

+

Welcome back!

Click below to see the latest peeps

diff --git a/views/posted_peep.erb b/views/posted_peep.erb index 691c8c208e..c881c647c8 100644 --- a/views/posted_peep.erb +++ b/views/posted_peep.erb @@ -14,7 +14,7 @@ } -

Nice one, your peep has been posted!

+

Nice one, your peep has been posted!




diff --git a/views/sign_up.erb b/views/sign_up.erb index e54c1d6741..03b3a0310a 100644 --- a/views/sign_up.erb +++ b/views/sign_up.erb @@ -5,8 +5,8 @@ border-radius: 25px; border: 2px solid #1DA1F2; padding: 20px; - width: 200px; - height: 75px; + width: 365px; + height: 45px; } body { background-color: lightblue; @@ -15,7 +15,7 @@ -

Welcome to Chitter!

+

Welcome to Chitter!

Please enter your details below

From fce620c0b96c88fd37857f404e8ee833d78d422e Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Fri, 12 May 2023 18:07:58 +0100 Subject: [PATCH 39/41] html edit --- views/add_a_peep.erb | 22 +++++++++++----------- views/homepage.erb | 17 ++++++++++------- views/logged_out.erb | 14 +++++++------- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/views/add_a_peep.erb b/views/add_a_peep.erb index 0efe92712a..a5721fd35e 100644 --- a/views/add_a_peep.erb +++ b/views/add_a_peep.erb @@ -1,17 +1,17 @@ diff --git a/views/homepage.erb b/views/homepage.erb index d41c636f8d..5a5cbb4276 100644 --- a/views/homepage.erb +++ b/views/homepage.erb @@ -2,15 +2,18 @@ diff --git a/views/logged_out.erb b/views/logged_out.erb index 9c2913f3d2..181d3537ba 100644 --- a/views/logged_out.erb +++ b/views/logged_out.erb @@ -2,15 +2,15 @@ From 7cf72c3efb92b6372d09aab9e6b2475d4bdfe4e4 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Fri, 12 May 2023 19:11:04 +0100 Subject: [PATCH 40/41] adding reflection to my README --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7b29ea7197..984ad8984e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ $ http://localhost:('Port in use')/ ![initial plan](./initial_plan.png "initial plan") -## Approach +## Initial Approach 1. Set up the Chitter database with test accounts and test peeps so that the homepage has some information prepopulated @@ -36,14 +36,6 @@ $ http://localhost:('Port in use')/ 7. Add a log in and log out page, which checks the database to match log in details -## Upcoming to-dos - -- Send an email when someone has been mentioned in a post -- Refactor -- Encrypt passwords stored on DB -- REGEX test for @ sign in email addresses -- Errors for empty fields in post a peep form -- Better CSS ## Techonologies used @@ -52,4 +44,44 @@ $ http://localhost:('Port in use')/ - RSpec - PostgreSQL - Sinatra -- HTML \ No newline at end of file +- HTML +- BCrypt +- rack +- rubocop + +## Reflection + + This challenge to recreate Twitter was a rather daunting project from first look as it was very easy to compare to the platform and think of all the additional features. + + It was important and useful for me to keep referring back to the user stories to ensure that I was working on the most important and key features to have a MVP. + + I started making my initial plan on Excalidraw and to breakdown the requirements into steps to use as a starting to-do list. + + I found that usig the recipe templates helped me to plan my tables for the database (one for accounts and one for peeps)and create some base seed data to prepopulate the site with and test with. + + I wrote the account model and repository classes and also the peep account and repository classes to achieve the main functionality of the app. + + I found it useful to build up the HTML view files as I completed the http routes along side as sometimes I found myself encountering 500 status errors that would not give much away in the error message. It was useful for me to be able to have a bit more feedback from my web app itself. + + I worked through the ability to add and display peeps before adding the opportunity to sign up, with the functionality to check for unique user details. At this point, the homepage had most of the details but it was not recognising whether a user was signed in. + + I was able to use sessions in Sinatra to register when a user was logged in and who it was, they were then able to post as this user. + + At this point it was a priority for me to encrypt the passwords stored on the database using bcrypt. This took some playing around with to try and understand what was needed and where and it was interesting to use irb to ensure my seed data was also encrypted to ensure my rspec tests were passing also. + + I then tried to implement some basic CSS, this is something that I have never used before so it was nice to see some visual changes to my web app. + + +## Goals going forward + + As mentioned before, this project was a big one and there was always more I was wanting to achieve. + + I would start by adding some better functionality for the sign up page. I would want to add tests to ensure that the user input is in the right format, for example including REGEXs for email addresses and ensuring that forms have no missing data or blank fields. I would also like to work on sanitising user input before storing it to the database. + + Going on from this, I think it would be fun to have a looking into Sinatra flash messages to display error or confirmation messages to the user. + + I would also like to have a go at the stretch goals of implementing user emailing notifications and implementing threads and replies on the homepage. This would make the web app more interactive with other users. + + I would also like to put more time into the CSS styling as I am very aware it is basic at the moment. It would be fun to try implementing navigation bars or a side menu to help with the user direction and use of the site. + + In conclusion I would say that I am happy with the progress I have made on this project and I am glad I got to complete the MVP basic level product needed from the user stories. From e4fe7d0a05bdb3030fdf27b2b4d2bb2e5ecb3321 Mon Sep 17 00:00:00 2001 From: Alice Wood Date: Fri, 12 May 2023 19:27:14 +0100 Subject: [PATCH 41/41] fixing formatting --- README.md | 64 +++++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 984ad8984e..c1f11b9282 100644 --- a/README.md +++ b/README.md @@ -51,37 +51,37 @@ $ http://localhost:('Port in use')/ ## Reflection - This challenge to recreate Twitter was a rather daunting project from first look as it was very easy to compare to the platform and think of all the additional features. - - It was important and useful for me to keep referring back to the user stories to ensure that I was working on the most important and key features to have a MVP. - - I started making my initial plan on Excalidraw and to breakdown the requirements into steps to use as a starting to-do list. - - I found that usig the recipe templates helped me to plan my tables for the database (one for accounts and one for peeps)and create some base seed data to prepopulate the site with and test with. - - I wrote the account model and repository classes and also the peep account and repository classes to achieve the main functionality of the app. - - I found it useful to build up the HTML view files as I completed the http routes along side as sometimes I found myself encountering 500 status errors that would not give much away in the error message. It was useful for me to be able to have a bit more feedback from my web app itself. - - I worked through the ability to add and display peeps before adding the opportunity to sign up, with the functionality to check for unique user details. At this point, the homepage had most of the details but it was not recognising whether a user was signed in. - - I was able to use sessions in Sinatra to register when a user was logged in and who it was, they were then able to post as this user. - - At this point it was a priority for me to encrypt the passwords stored on the database using bcrypt. This took some playing around with to try and understand what was needed and where and it was interesting to use irb to ensure my seed data was also encrypted to ensure my rspec tests were passing also. - - I then tried to implement some basic CSS, this is something that I have never used before so it was nice to see some visual changes to my web app. - +This challenge to recreate Twitter was a rather daunting project from first look as it was very easy to compare to the platform and think of all the additional features. + +It was important and useful for me to keep referring back to the user stories to ensure that I was working on the most important and key features to have a MVP. + +I started making my initial plan on Excalidraw and to breakdown the requirements into steps to use as a starting to-do list. + +I found that usig the recipe templates helped me to plan my tables for the database (one for accounts and one for peeps)and create some base seed data to prepopulate the site with and test with. + +I wrote the account model and repository classes and also the peep account and repository classes to achieve the main functionality of the app. + +I found it useful to build up the HTML view files as I completed the http routes along side as sometimes I found myself encountering 500 status errors that would not give much away in the error message. It was useful for me to be able to have a bit more feedback from my web app itself. + +I worked through the ability to add and display peeps before adding the opportunity to sign up, with the functionality to check for unique user details. At this point, the homepage had most of the details but it was not recognising whether a user was signed in. + +I was able to use sessions in Sinatra to register when a user was logged in and who it was, they were then able to post as this user. + +At this point it was a priority for me to encrypt the passwords stored on the database using bcrypt. This took some playing around with to try and understand what was needed and where and it was interesting to use irb to ensure my seed data was also encrypted to ensure my rspec tests were passing also. + +I then tried to implement some basic CSS, this is something that I have never used before so it was nice to see some visual changes to my web app. + ## Goals going forward - - As mentioned before, this project was a big one and there was always more I was wanting to achieve. - - I would start by adding some better functionality for the sign up page. I would want to add tests to ensure that the user input is in the right format, for example including REGEXs for email addresses and ensuring that forms have no missing data or blank fields. I would also like to work on sanitising user input before storing it to the database. - - Going on from this, I think it would be fun to have a looking into Sinatra flash messages to display error or confirmation messages to the user. - - I would also like to have a go at the stretch goals of implementing user emailing notifications and implementing threads and replies on the homepage. This would make the web app more interactive with other users. - - I would also like to put more time into the CSS styling as I am very aware it is basic at the moment. It would be fun to try implementing navigation bars or a side menu to help with the user direction and use of the site. - - In conclusion I would say that I am happy with the progress I have made on this project and I am glad I got to complete the MVP basic level product needed from the user stories. + +As mentioned before, this project was a big one and there was always more I was wanting to achieve. + +I would start by adding some better functionality for the sign up page. I would want to add tests to ensure that the user input is in the right format, for example including REGEXs for email addresses and ensuring that forms have no missing data or blank fields. I would also like to work on sanitising user input before storing it to the database. + +Going on from this, I think it would be fun to have a looking into Sinatra flash messages to display error or confirmation messages to the user. + +I would also like to have a go at the stretch goals of implementing user emailing notifications and implementing threads and replies on the homepage. This would make the web app more interactive with other users. + +I would also like to put more time into the CSS styling as I am very aware it is basic at the moment. It would be fun to try implementing navigation bars or a side menu to help with the user direction and use of the site. + +In conclusion I would say that I am happy with the progress I have made on this project and I am glad I got to complete the MVP basic level product needed from the user stories.