diff --git a/README.md b/README.md
index 606d8c34..c22a0322 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,12 @@ npm install
docker-compose run --rm web npm install
```
-You may need to log into github to pull the circulation-history image. To do that, get a [Personal Access Token](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token) with permission to "read:packages". On the command line log in with your github username and then Personal Access Token as the password.
+You may need to log into github to pull the circulation-history image. To do that, get a [Personal Access Token](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token) with permission to "read:packages". On the command line:
+```
+docker login docker.pkg.github.com
+```
+
+Log in with your github username and then Personal Access Token as the password.
start containers
diff --git a/css/_banner.scss b/css/_banner.scss
new file mode 100644
index 00000000..c625396d
--- /dev/null
+++ b/css/_banner.scss
@@ -0,0 +1,12 @@
+.banner {
+ background: var(--color-teal-200);
+ padding: var(--space-x-small) 0;
+ * {
+ color: var(--color-neutral-400);
+ }
+ m-icon {
+ position: relative;
+ margin-right: var(--space-xx-small);
+ top: var(--space-xxx-small);
+ }
+}
diff --git a/css/_patron.scss b/css/_patron.scss
new file mode 100644
index 00000000..27f1676e
--- /dev/null
+++ b/css/_patron.scss
@@ -0,0 +1,34 @@
+form[method="post"] {
+ m-icon {
+ position: relative;
+ margin-right: var(--space-xx-small);
+ top: var(--space-xxx-small);
+ }
+
+ #text-notifications:not(:checked) ~ .sms-number-container {
+ display: none;
+ }
+
+ #sms-number:invalid + #sms-number-description {
+ color: var(--color-pink-500);
+ }
+
+ .retain-history-option {
+ input[type="radio"] {
+ &:checked + label m-icon[name="radio-button-unchecked"] {
+ display: none;
+ }
+ &:not(:checked) + label m-icon[name="radio-button-checked"] {
+ display: none;
+ }
+ &:focus + label m-icon {
+ outline: 0;
+ box-shadow: 0 0 0 2px var(--color-maize-400),0 0 0 3px var(--color-neutral-400);
+ border-radius: 2px;
+ }
+ }
+ m-icon {
+ color: var(--color-teal-500);
+ }
+ }
+}
diff --git a/css/index.scss b/css/index.scss
index 3bf380af..0dde13fe 100644
--- a/css/index.scss
+++ b/css/index.scss
@@ -1,5 +1,5 @@
@import "defaults", "base", "utilities", "focus", "site-navigation",
"horizontal-navigation", "site-footer", "overview-cards", "loans",
"empty-state-container", "buttons", "pagination", "messages",
- "text-notifications", "tags", "renew", "dropdown",
- "maybe-design-system/controls";
+ "text-notifications", "tags", "banner", "renew", "dropdown",
+ "patron", "maybe-design-system/controls";
diff --git a/css/maybe-design-system/_controls.scss b/css/maybe-design-system/_controls.scss
index a4e1c5f8..1c45e8a9 100644
--- a/css/maybe-design-system/_controls.scss
+++ b/css/maybe-design-system/_controls.scss
@@ -1,12 +1,18 @@
-input[type="text"] {
+input[type="text"],
+input[type="tel"] {
font-size: 1rem;
font-family: var(--font-base-family);
border: solid 1px rgba(0, 0, 0, 0.3);
- box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.1);
border-radius: 4px;
padding: var(--space-small);
margin: 0;
width: 100%;
+ &:invalid {
+ border-color: var(--color-pink-500);
+ }
+ &:not(:focus) {
+ box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.1);
+ }
}
select {
@@ -49,3 +55,51 @@ option {
select::-ms-expand {
display: none;
}
+
+.toggle-switch {
+ & + .toggle-switch-label {
+ .toggle {
+ background-color: var(--color-neutral-200);
+ border: 1px solid var(--color-neutral-200);
+ border-radius: var(--space-medium);
+ display: inline-block;
+ height: 1.5em;
+ margin-right: var(--space-x-small);
+ position: relative;
+ top: var(--space-x-small);
+ transition: none 0.2s ease-in-out;
+ transition-property: background-color, border-color;
+ width: 3em;
+ &:before {
+ content: '';
+ background-color: white;
+ border-radius: var(--space-medium);
+ display: block;
+ height: 100%;
+ left: 0;
+ position: absolute;
+ transition: left 0.2s ease-in-out;
+ width: 50%;
+ }
+ }
+ }
+ &:checked + .toggle-switch-label {
+ .toggle {
+ background-color: var(--color-teal-400);
+ border-color: var(--color-teal-400);
+ &:before {
+ left: 50%;
+ }
+ }
+ }
+ &:focus + .toggle-switch-label {
+ .toggle {
+ outline: 0;
+ box-shadow: 0 0 0 2px var(--color-maize-400),0 0 0 3px var(--color-neutral-400);
+ }
+ }
+}
+
+.input-description {
+ font-size: var(--space-medium);
+}
diff --git a/docker-compose.yml b/docker-compose.yml
index a81d655c..cb30314c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -43,7 +43,6 @@ services:
circulation-history-db:
image: niquerio/circulation_history_dev_db:latest
-
sass:
build: .
volumes:
@@ -64,3 +63,4 @@ services:
volumes:
gem_cache:
+ circ_history_db:
diff --git a/lib/circ_history_client.rb b/lib/circ_history_client.rb
index d89da68e..405a6b2d 100644
--- a/lib/circ_history_client.rb
+++ b/lib/circ_history_client.rb
@@ -23,7 +23,7 @@ def download_csv
end
def set_retain_history(retain_history)
- self.class.put("users/#{@uniqname}", query: {retain_history: retain_history})
+ self.class.put("/users/#{@uniqname}", query: {retain_history: retain_history})
end
end
diff --git a/models/patron.rb b/models/patron.rb
index cf1a7055..031c87a5 100644
--- a/models/patron.rb
+++ b/models/patron.rb
@@ -1,20 +1,33 @@
require 'telephone_number'
class Patron
- def initialize(uniqname:, parsed_response:)
- @uniqname = uniqname
- @parsed_response = parsed_response
+ def initialize(alma_data:, circ_history_data: nil)
+ @alma_data = alma_data
+ @circ_history_data = circ_history_data
end
- def self.for(uniqname:, client: AlmaRestClient.client)
+ def self.for(uniqname:, alma_client: AlmaRestClient.client, circ_history_client: CircHistoryClient.new(uniqname))
url = "/users/#{uniqname}?user_id_type=all_unique&view=full&expand=none"
- response = client.get(url)
- if response.code == 200
- Patron.new(uniqname: uniqname, parsed_response: response.parsed_response)
+ alma_response = alma_client.get(url)
+ circ_history_response = circ_history_client.user_info
+ if alma_response.code == 200 && circ_history_response.code == 200
+ Patron.new(alma_data: alma_response.parsed_response, circ_history_data: circ_history_response.parsed_response)
else
#should be something else
- AlmaError.new(response)
+ AlmaError.new(alma_response)
end
end
+ def self.set_retain_history(uniqname:, retain_history:, circ_history_client: CircHistoryClient.new(uniqname))
+ circ_history_client.set_retain_history(retain_history)
+ end
+ def confirmed_history_setting?
+ @circ_history_data["confirmed"]
+ end
+ def retain_history?
+ @circ_history_data["retain_history"]
+ end
+ def get_record_count(history=CircHistoryClient.new(uniqname))
+ history.loans["total_record_count"]
+ end
def update_sms(sms, client=AlmaRestClient.client, phone=TelephoneNumber.parse(sms, :US))
return Error.new(message: "Phone number #{sms} is invalid") unless phone.valid? || sms.empty?
@@ -24,30 +37,30 @@ def update_sms(sms, client=AlmaRestClient.client, phone=TelephoneNumber.parse(sm
end
def uniqname
- @parsed_response["primary_id"]&.downcase
+ @alma_data["primary_id"]&.downcase
end
def sms_number
- @parsed_response.dig("contact_info","phone")&.find(-> {{}}){|x| x["preferred_sms"]}&.dig("phone_number")
+ @alma_data.dig("contact_info","phone")&.find(-> {{}}){|x| x["preferred_sms"]}&.dig("phone_number")
end
def sms_number?
!!sms_number
end
def full_name
- @parsed_response["full_name"]
+ @alma_data["full_name"]
end
def user_group
- @parsed_response.dig("user_group","desc")
+ @alma_data.dig("user_group","desc")
end
def can_book?
['Faculty','Graduate','Staff'].any?{|x| user_group.match(x)}
end
def addresses
- @parsed_response.dig("contact_info","address")&.map{|x| Address.new(x)}
+ @alma_data.dig("contact_info","address")&.map{|x| Address.new(x)}
end
private
def patron_with_internal_sms(sms_number)
- updated_patron = JSON.parse(@parsed_response.to_json)
+ updated_patron = JSON.parse(@alma_data.to_json)
phones = updated_patron["contact_info"].delete("phone")
my_phones = phones.map{ |x| Phone.new(x)}
diff --git a/models/session_patron.rb b/models/session_patron.rb
index ea3196fb..0f9511e0 100644
--- a/models/session_patron.rb
+++ b/models/session_patron.rb
@@ -1,15 +1,14 @@
class SessionPatron
- extend Forwardable
- def_delegators :@alma_patron, :uniqname, :full_name, :can_book?
def initialize(uniqname)
- @alma_patron = Patron.for(uniqname: uniqname)
+ @patron = Patron.for(uniqname: uniqname)
end
def to_h
{
- uniqname: @alma_patron.uniqname,
- full_name: @alma_patron.full_name,
- can_book: @alma_patron.can_book?
+ uniqname: @patron.uniqname,
+ full_name: @patron.full_name,
+ can_book: @patron.can_book?,
+ confirmed_history_setting: @patron.confirmed_history_setting?
}
end
end
diff --git a/my_account.rb b/my_account.rb
index 0cb07daf..6082e63c 100644
--- a/my_account.rb
+++ b/my_account.rb
@@ -95,10 +95,10 @@
# :nocov:
get '/' do
- session[:uniqname] = 'mlibrary.acct.testing1@gmail.com' if !session[:uniqname]
- session[:full_name] = 'Julian Tutor' if session[:uniqname] == 'mlibrary.acct.testing1@gmail.com'
- session[:can_book] = true if session[:uniqname] == 'mlibrary.acct.testing1@gmail.com'
-
+ if !session[:uniqname]
+ patron = SessionPatron.new('mlibrary.acct.testing1@gmail.com')
+ patron.to_h.each{|k,v| session[k] = v}
+ end
test_users = [
{
label: 'Graduate student (few)',
@@ -241,10 +241,20 @@
redirect 'https://apps.lib.umich.edu/my-account/favorites'
end
-get '/settings' do
- #session[:uniqname] = 'tutor' #need to get this from cosign?
- patron = Patron.for(uniqname: session[:uniqname])
- erb :patron, :locals => {patron: patron}
+namespace '/settings' do
+ get '' do
+ patron = Patron.for(uniqname: session[:uniqname])
+ erb :patron, :locals => {patron: patron}
+ end
+ post '/history' do
+ response = Patron.set_retain_history(uniqname: session[:uniqname], retain_history: params[:retain_history])
+ if response.code == 200
+ flash[:success] = "Success: History Setting Successfully Changed"
+ else
+ flash[:error] = "Error: #{response.message}"
+ end
+ redirect "/settings"
+ end
end
#TODO set up renew loan to handle renew in place with top part message???
post '/renew-loan' do
@@ -262,12 +272,12 @@
post '/sms' do
patron = Patron.for(uniqname: session[:uniqname])
- response = patron.update_sms(params["phone-number"])
+ response = patron.update_sms(params["text-notifications"] == "on" ? params["sms-number"] : "")
if response.code == 200
- if params["phone-number"] == ''
- flash[:success] = "Success: SMS Successfully Removed"
- else
+ if params["text-notifications"] == "on"
flash[:success] = "Success: SMS Successfully Updated"
+ else
+ flash[:success] = "Success: SMS Successfully Removed"
end
else
flash[:error] = "Error: #{response.message}"
diff --git a/spec/fixtures/circ_history_user.json b/spec/fixtures/circ_history_user.json
new file mode 100644
index 00000000..6c3b5771
--- /dev/null
+++ b/spec/fixtures/circ_history_user.json
@@ -0,0 +1,6 @@
+{
+ "uniqname": "tutor",
+ "retain_history": true,
+ "confirmed": true,
+ "updated_at": "2021-05-20T11:47:48.199-04:00"
+}
diff --git a/spec/models/patron_spec.rb b/spec/models/patron_spec.rb
index 4b523cdf..683fb13b 100644
--- a/spec/models/patron_spec.rb
+++ b/spec/models/patron_spec.rb
@@ -5,6 +5,7 @@
context "found uniqname" do
before(:each) do
@alma_response = JSON.parse(File.read('./spec/fixtures/mrio_user_alma.json'))
+ @circ_history_response = JSON.parse(File.read('./spec/fixtures/circ_history_user.json'))
@patron_url = "users/mrio?user_id_type=all_unique&view=full&expand=none"
end
subject do
@@ -12,6 +13,10 @@
url: @patron_url,
body: @alma_response.to_json
)
+ stub_circ_history_get_request(
+ url: 'users/mrio',
+ output: @circ_history_response.to_json
+ )
Patron.for(uniqname: 'mrio')
end
context "#update_sms(sms_number)" do
@@ -96,6 +101,24 @@
expect(subject.can_book?).to eq(false)
end
end
+ context "#retain_history?" do
+ it "returns correct true boolean" do
+ expect(subject.retain_history?).to eq(true)
+ end
+ it "returns correct false boolean" do
+ @circ_history_response["retain_history"] = false
+ expect(subject.retain_history?).to eq(false)
+ end
+ end
+ context "#confiremd_history_setting?" do
+ it "returns correct true boolean" do
+ expect(subject.confirmed_history_setting?).to eq(true)
+ end
+ it "returns correct false boolean" do
+ @circ_history_response["confirmed"] = false
+ expect(subject.confirmed_history_setting?).to eq(false)
+ end
+ end
context "#sms_number" do
it "returns sms number if preferred_sms is set" do
expect(subject.sms_number).to eq('734-123-4567')
@@ -146,6 +169,9 @@
url: @patron_url,
body: @alma_response
)
+ stub_circ_history_get_request(
+ url: 'users/mrioaaa',
+ )
end
subject do
Patron.for(uniqname: 'mrioaaa')
@@ -157,5 +183,11 @@
end
end
end
-
+end
+describe Patron, ".set_retain_history" do
+ it "updates the circ history setting" do
+ stub = stub_circ_history_put_request(url: 'users/tutor', query: {retain_history: false})
+ Patron.set_retain_history(uniqname: 'tutor', retain_history: 'false')
+ expect(stub).to have_been_requested
+ end
end
diff --git a/spec/requests_spec.rb b/spec/requests_spec.rb
index 84a0c9fa..11813005 100644
--- a/spec/requests_spec.rb
+++ b/spec/requests_spec.rb
@@ -214,10 +214,25 @@
context "get /settings" do
it "contains 'Settings'" do
stub_alma_get_request(url: "users/tutor?expand=none&user_id_type=all_unique&view=full")
+ stub_circ_history_get_request(url: "users/tutor")
get "/settings"
expect(last_response.body).to include("Settings")
end
end
+ context "post /settings/history" do
+ before(:each) do
+ @patron_json = File.read("./spec/fixtures/mrio_user_alma.json")
+ stub_alma_get_request(url: "users/tutor?expand=none&user_id_type=all_unique&view=full", body: @patron_json)
+ stub_circ_history_get_request(url: "users/tutor")
+ stub_circ_history_put_request(url: "users/tutor", query: {retain_history: true})
+ end
+ it "handles retain history" do
+
+ post "/settings/history", {'retain_history' => 'true'}
+ follow_redirect!
+ expect(last_response.body).to include("History Setting Successfully Changed")
+ end
+ end
#ToDO
#context "post /renew-loan" do
#before(:each) do
@@ -252,21 +267,22 @@
before(:each) do
@patron_json = File.read("./spec/fixtures/mrio_user_alma.json")
stub_alma_get_request(url: "users/tutor?expand=none&user_id_type=all_unique&view=full", body: @patron_json)
+ stub_circ_history_get_request(url: 'users/tutor')
end
it "handles good phone number update" do
- phone_number = '(734) 555-5555'
+ sms_number = '(734) 555-5555'
new_phone_patron = JSON.parse(@patron_json)
- new_phone_patron["contact_info"]["phone"][1]["phone_number"] = phone_number
+ new_phone_patron["contact_info"]["phone"][1]["phone_number"] = sms_number
stub_alma_put_request(url: "users/mrio", input: new_phone_patron.to_json, output: new_phone_patron.to_json)
- post "/sms", {'phone-number' => phone_number}
+ post "/sms", {'text-notifications' => 'on', 'sms-number' => sms_number}
follow_redirect!
expect(last_response.body).to include("SMS Successfully Updated")
end
it "handles bad phone number update" do
- post "/sms", {'phone-number' => 'aaa'}
+ post "/sms", {'text-notifications' => 'on', 'sms-number' => 'aaa'}
follow_redirect!
expect(last_response.body).to include("is invalid")
end
@@ -274,7 +290,7 @@
new_phone_patron = JSON.parse(@patron_json)
new_phone_patron["contact_info"]["phone"].delete_at(1)
stub_alma_put_request(url: "users/mrio", input: new_phone_patron.to_json, output: new_phone_patron.to_json)
- post "/sms", {'phone-number' => ''}
+ post "/sms", {'text-notifications' => 'off', 'sms-number' => ''}
follow_redirect!
expect(last_response.body).to include("SMS Successfully Removed")
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 53b7f5ca..d3d70387 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -116,16 +116,18 @@ def app() Sinatra::Application end
Kernel.srand config.seed
=end
end
-def stub_circ_history_get_request(url:, output: "{}",status: 200, query: {})
- stub_request(:get, "#{ENV["CIRCULATION_HISTORY_URL"]}/v1/#{url}").with(
- headers: {
- accept: 'application/json',
- #ApiKey: ENV['ILLIAD_API_KEY'],
- 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
- 'User-Agent'=>'Ruby'
- },
- query: query,
- ).to_return(body: output, status: status, headers: {content_type: 'application/json'})
+[:get, :put].each do |name|
+ define_method("stub_circ_history_#{name}_request") do |url:, output: "{}",status: 200, query: {}|
+ stub_request(name, "#{ENV["CIRCULATION_HISTORY_URL"]}/v1/#{url}").with(
+ headers: {
+ accept: 'application/json',
+ #ApiKey: ENV['ILLIAD_API_KEY'],
+ 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
+ 'User-Agent'=>'Ruby'
+ },
+ query: query,
+ ).to_return(body: output, status: status, headers: {content_type: 'application/json'})
+ end
end
def stub_updater(params)
query = Authenticator.params_with_signature(params: params)
diff --git a/spec/views/layout.erb_spec.rb b/spec/views/layout.erb_spec.rb
index 9f8cda40..de8626bd 100644
--- a/spec/views/layout.erb_spec.rb
+++ b/spec/views/layout.erb_spec.rb
@@ -2,7 +2,8 @@
describe "flash messages" do
include Rack::Test::Methods
before(:each) do
- stub_alma_get_request(url: "users/tutor?expand=none&user_id_type=all_unique&view=full", body: '{}')
+ stub_alma_get_request(url: "users/mlibrary.acct.testing1@gmail.com?expand=none&user_id_type=all_unique&view=full", body: File.read('./spec/fixtures/mrio_user_alma.json'))
+ stub_circ_history_get_request(url: "users/mlibrary.acct.testing1@gmail.com", output: File.read('./spec/fixtures/circ_history_user.json'))
end
it "displays appropriate flash message" do
#post "/session_switcher", {flash: {success: "it was successful"}}
diff --git a/views/banner.erb b/views/banner.erb
new file mode 100644
index 00000000..e7534da4
--- /dev/null
+++ b/views/banner.erb
@@ -0,0 +1,7 @@
+<% if session[:confirmed_history_setting] == false %>
+
+
+
You have not yet specified how whether you would like us to record your checkout history. You can update your preferences on the Settings page.
+
+
+<% end %>
diff --git a/views/layout.erb b/views/layout.erb
index 30e9e10d..f405645e 100644
--- a/views/layout.erb
+++ b/views/layout.erb
@@ -1,3 +1,4 @@
+
@@ -23,7 +24,7 @@
<%= erb :user_drop_down %>
-
+ <%= erb :banner %>
<%= erb :navigation %>
diff --git a/views/patron.erb b/views/patron.erb
index 48bcf1df..f683ca37 100644
--- a/views/patron.erb
+++ b/views/patron.erb
@@ -2,40 +2,122 @@
-
To update you name and address, visit Wolverine Access.
+
You can update your name and address at Wolverine Access.
+
+
To change document delivery preferences, visit your interlibrary loan account.
Full name |
<%= patron.full_name %> |
-
- Address |
-
- <% patron.addresses do |address| %>
- <%= address %>
- <% end %>
- |
-
Email |
<%= patron.uniqname %>@umich.edu |
-