updox 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7eeab31e87fabd58b70a27bcf1a2012ba06ac93421d23861cc246d12bcf3614f
4
- data.tar.gz: c4969565b9a34f4f87afacf4bd1e26165e04e17bc931457dfebc9108acb037b7
3
+ metadata.gz: 0bbcd6275257fa4aec9f5d2ee479d2e40cc35c6c1554b0d6daa32edf9dec4cd5
4
+ data.tar.gz: e3358374b773851ce3958441b264a7983f7944ebfc93dfeb9b8e2a6036a6457a
5
5
  SHA512:
6
- metadata.gz: a32b3b74cc2f7afd897418274d8a041a02602c0fabf783ea2a6bcf172750cdbaa2af5873a8a7ac48e0e4ec047a5bcc26a50a8db8a759ceb662587cea54a19fd1
7
- data.tar.gz: 451421900c7777190b72db3c95a8124a3294c0b0c945ac28cb08b263015b7222c5d276252e80c9cbbab637c13273fa909aa91bbb6bee69ffa6297a0f546cca93
6
+ metadata.gz: e848a98e0ecefbd2578478a1ce22b69b5c41c1e3f4e56aecf4a7a864aec2cb4f42c6ae133a3649603c62b2b4a4b7793b4facf07a4a9d6e27ffa6185c4211a2b5
7
+ data.tar.gz: 2cbe79662dfe9ee1e237b29bd37c2f848bb828eb6336a5a03249460ce96642bbf952892bd013f4db2a7ed99a82be3463eeac6242f70ecb5a5a27537d59fdc285
data/CHANGELOG.md CHANGED
@@ -4,13 +4,23 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [0.1.x] - UNRELEASED
7
+ ## [0.3.0] - UNRELEASED
8
8
  ### Changed
9
9
  - None
10
10
 
11
- ## 0.1.0 - 2020-01-20
11
+ ## 0.2.0 - 2020-02-06
12
12
  ### Added
13
- - Initial Release
13
+ - Calendar
14
+ - Patient
15
+ - Location
16
+ - User
17
+ - Appointment
18
+ - Application
14
19
 
15
- [Unreleased]: https://github.com/WeInfuse/redox/compare/v1.0.2...HEAD
16
- [0.1.1]: https://github.com/WeInfuse/redox/compare/0.1.0...0.1.1
20
+ ## 0.1.0 - 2020-01-21
21
+ ### Added
22
+ - Initial Release with ability to ping Updox api
23
+
24
+ [Unreleased]: https://github.com/WeInfuse/updox/compare/v0.2.0...HEAD
25
+ [0.2.0]: https://github.com/WeInfuse/updox/compare/v0.1.0...v0.2.0
26
+ [0.1.0]: https://github.com/WeInfuse/updox/compare/v0.1.0
data/README.md CHANGED
@@ -26,74 +26,87 @@ Or install it yourself as:
26
26
  Make sure you're [configured](#configuration)!
27
27
 
28
28
  ```ruby
29
- patient = Updox::Models::Patient.new
30
- patient.demographics.first_name = 'Joe'
31
- patient.demographics['LastName'] = 'Joerson'
32
- patient.add_identifier(type: 'TheType', value: 'x13005')
33
-
34
- meta = Updox::Models::Meta.new
35
- meta.set_source(name: 'MySource', id: '123-584')
36
- meta.add_destination(name: 'TheDest', id: '973-238')
29
+ auth = Updox::Models::Auth.new
30
+
31
+ response = auth.ping # No authentication needed!
32
+
33
+ response = auth.ping_with_application_auth # Check if you're app auth is working!
37
34
  ```
38
35
 
39
- ### Create
36
+ ### Practice
37
+ The practice is what Updox calls 'Account' access so anywhere the `account_id` is required is relating back to this practice instance.
38
+
39
+ #### Create
40
+ ```ruby
41
+ practice = Updox::Models::Practice.new(name: 'LOL LTD', account_id: '0001', active: true)
42
+ practice.create
43
+ ```
40
44
 
45
+ #### List
41
46
  ```ruby
42
- response = patient.create(meta: meta)
47
+ practices = Updox::Models::Practice.query.practices
43
48
  ```
44
49
 
45
- ### Update
50
+ ### Location
51
+
52
+ #### Create
46
53
 
47
54
  ```ruby
48
- response = patient.update(meta: meta)
55
+ location = Updox::Models::Location.new(active: true, name: 'My Location', code: 'ML01', id: '27')
56
+ location.save(account_id: practice.account_id)
57
+
58
+ # Bulk
59
+ Location.sync([l0, l1], account_id: practice.account_id)
49
60
  ```
50
61
 
51
- ### Search
62
+ ### Calendar
63
+
64
+ #### Create
52
65
 
53
66
  ```ruby
54
- response = Updox::Models::Patient.query(patient, meta: meta)
67
+ calendar = Updox::Models::Calendar.new(active: true, title: 'My Calendar', id: 'C1')
68
+ calendar.create(account_id: practice.account_id)
55
69
  ```
56
70
 
57
- ### Response
71
+ ### Patient
72
+
73
+ #### Create
58
74
 
59
- The response object is a base `Updox::Models::Model` class.
75
+ ```ruby
76
+ patient = Updox::Models::Patient.new(id: 'X0001', internal_id: 'X0001', first_name: 'Brian', last_name: 'Brianson', mobile_number: 5126914360, active: true)
77
+ patient.save(account_id: practice.account_id)
78
+
79
+ # Bulk
80
+ Patient.sync([p0, p1, p2], account_id: practice.account_id)
81
+ ```
82
+
83
+ ### Appointment
84
+
85
+ #### Create
60
86
 
61
- With the HTTParty response object
62
87
  ```ruby
63
- response.response
64
- #<HTTParty::Response:0x7fa354c1fbe8>
88
+ appointment = Updox::Models::Appointment.new(id: 'A0001', calendar_id: calendar.id, date: Time.now + 20, duration: 60, location_id: location.id, patient_id: patient.id)
89
+ appointment.save(account_id: practice.account_id)
65
90
 
66
- response.response.ok?
67
- true
91
+ # Bulk
92
+ Appointment.sync([appt0, appt1, appt2], account_id: practice.account_id)
68
93
  ```
69
94
 
70
- And any `Model` objects that were returned
95
+ ### Response
96
+ By default we return `Updox::Models::Model.from_response`
97
+
98
+ This class throws if throw an exception on bad responses with a parsed error.
99
+
100
+ If successful it adds helper methods and converts each to the respective class.
101
+
102
+ The raw response is stored in the resulting model but you can get the raw response by setting config option to false
103
+
71
104
  ```ruby
72
- response.patient
73
- {
74
- "Identifiers"=> [
75
- {"IDType"=>"MR", "ID"=>"0000000003"},
76
- {"ID"=>"e3fedf48-c8bf-4728-845f-cb810001b571", "IDType"=>"EHRID"}
77
- ],
78
- "Demographics"=> {
79
- "Race"=>"Black",
80
- "SSN"=>"303-03-0003",
81
- "Nickname"=>"Walt"
82
- ...
83
- }
84
- "PCP"=> {
85
- "NPI"=>nil,
86
- }
87
- }
88
-
89
- response.meta
90
- {
91
- "EventDateTime"=>"2019-04-26T20:03:00.304866Z",
92
- "DataModel"=>"PatientAdmin",
93
- ...
94
- "Transmission"=>{"ID"=>797225234},
95
- "Message"=>{"ID"=>1095117817}
96
- }
105
+ response = Updox::Models::Practice.query
106
+ response.practices # Has the practices as Updox::Models::Practice model
107
+ response.items # Same as practices, always exists on any model if alias is broken
108
+ response.item # If there is no array, we populate this object
109
+ response.response # Raw HTTParty response is here
97
110
  ```
98
111
 
99
112
  ### Configuration
@@ -103,6 +116,7 @@ Updox.configure do |c|
103
116
  c.application_id = ENV['UPDOX_APP_ID']
104
117
  c.application_password = ENV['UPDOX_APP_PASS']
105
118
  c.api_endpoint = 'http://hello.com' # Defaults to Updox endpoint
119
+ c.parse_responses = false # Defaults to true
106
120
  end
107
121
  ```
108
122
 
data/lib/updox.rb CHANGED
@@ -3,16 +3,26 @@ require 'hashie'
3
3
  require 'updox/version'
4
4
  require 'updox/updox_exception'
5
5
  require 'updox/connection'
6
+ require 'updox/models/model'
6
7
  require 'updox/models/auth'
8
+ require 'updox/models/application'
9
+ require 'updox/models/appointment'
10
+ require 'updox/models/calendar'
11
+ require 'updox/models/location'
12
+ require 'updox/models/patient'
7
13
  require 'updox/models/practice'
14
+ require 'updox/models/user'
8
15
 
9
16
  module Updox
10
17
  class Configuration
11
- attr_accessor :application_id, :application_password
18
+ attr_accessor :application_id, :application_password, :parse_responses
19
+
20
+ alias_method :parse_responses?, :parse_responses
12
21
 
13
22
  def initialize
14
23
  @application_id = nil
15
24
  @application_password = nil
25
+ @parse_responses = true
16
26
  end
17
27
 
18
28
  def api_endpoint=(endpoint)
@@ -27,7 +37,8 @@ module Updox
27
37
  return {
28
38
  application_id: @application_id,
29
39
  application_password: @application_password,
30
- api_endpoint: api_endpoint
40
+ api_endpoint: api_endpoint,
41
+ parse_responses: @parse_responses
31
42
  }
32
43
  end
33
44
 
@@ -35,6 +46,7 @@ module Updox
35
46
  self.application_id = h[:application_id]
36
47
  self.application_password = h[:application_password]
37
48
  self.api_endpoint = h[:api_endpoint]
49
+ self.parse_responses = h[:parse_responses]
38
50
 
39
51
  return self
40
52
  end
@@ -0,0 +1,23 @@
1
+ module Updox
2
+ module Models
3
+ class Application < Model
4
+ OPEN_ENDPOINT = '/ApplicationOpen'.freeze
5
+
6
+ property :ipAddress
7
+ property :timeout
8
+ property :metadata
9
+
10
+ def url(account_id: , user_id: , base_uri: nil)
11
+ response = self.open(account_id: account_id, user_id: user_id)
12
+
13
+ base_uri ||= Updox.configuration.api_endpoint.split(URI.parse(Updox.configuration.api_endpoint).path).first
14
+
15
+ "#{base_uri}/sso/applicationOpen/#{Updox.configuration.application_id}/#{response.item.dig('token')}"
16
+ end
17
+
18
+ def open(account_id: , user_id: )
19
+ Model.from_response(UpdoxClient.connection.request(endpoint: OPEN_ENDPOINT, auth: {accountId: account_id, userId: user_id}, required_auths: Updox::Models::Auth::AUTH_FULL))
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
1
+ module Updox
2
+ module Models
3
+ class Appointment < Model
4
+ SYNC_ENDPOINT = '/AppointmentsSync'.freeze
5
+
6
+ property :id, required: true
7
+ property :updoxId, from: :updox_id
8
+ property :calendarId, required: true, from: :calendar_id
9
+ property :date, required: true
10
+ property :duration
11
+ property :patientId
12
+ property :typeId
13
+ property :summary
14
+ property :details
15
+ property :blocked, required: true, transform_with: ->(v) { true == v }, default: false
16
+ property :cancelled, required: true, transform_with: ->(v) { true == v }, default: false
17
+ property :locationId, from: :location_id
18
+ property :reminderTokens, from: :reminder_tokens
19
+
20
+ def to_h
21
+ result = super.to_h
22
+
23
+ result['date'] = result['date'].strftime(Updox::Models::DATETIME_FORMAT) if result['date'].respond_to?(:strftime)
24
+
25
+ result
26
+ end
27
+
28
+ def save(account_id: )
29
+ self.class.sync([self], account_id: account_id)
30
+ end
31
+
32
+ def self.sync(appointments, account_id: )
33
+ from_response(UpdoxClient.connection.request(endpoint: SYNC_ENDPOINT, body: { appointments: appointments.map(&:to_h) }, auth: {accountId: account_id}, required_auths: Updox::Models::Auth::AUTH_ACCT), self)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -6,10 +6,10 @@ module Updox
6
6
  AUTH_ACCT = AUTH_APP + [:accountId]
7
7
  AUTH_FULL = AUTH_ACCT + [:userId]
8
8
 
9
- PING_ENDPOINT = '/ping'
10
- PING_APP_ENDPOINT = '/pingWithApplicationAuth'
11
- PING_ACCT_ENDPOINT = '/pingWithAccountAuth'
12
- PING_FULL_ENDPOINT = '/pingWithAuth'
9
+ PING_ENDPOINT = '/Ping'
10
+ PING_APP_ENDPOINT = PING_ENDPOINT + 'WithApplicationAuth'
11
+ PING_ACCT_ENDPOINT = PING_ENDPOINT + 'WithAccountAuth'
12
+ PING_FULL_ENDPOINT = PING_ENDPOINT + 'WithAuth'
13
13
 
14
14
  property :applicationId, from: :application_id
15
15
  property :applicationPassword, from: :application_password
@@ -0,0 +1,19 @@
1
+ module Updox
2
+ module Models
3
+ class Calendar < Model
4
+ SYNC_ENDPOINT = '/CalendarsSync'.freeze
5
+
6
+ property :id, required: true
7
+ property :title, required: true
8
+ property :color, required: true, default: '#000000'
9
+ property :textColor, required: true, default: '#FFFFFF'
10
+ property :publicCalendar, default: false, from: :public_calendar, with: ->(v) { true == v}, transform_with: ->(v) { true == v }
11
+ property :active, default: true
12
+ property :reminderTurnOff, default: false, from: :reminder_turn_off, with: ->(v) { true == v}, transform_with: ->(v) { true == v }
13
+
14
+ def create(account_id: )
15
+ self.class.from_response(UpdoxClient.connection.request(endpoint: SYNC_ENDPOINT, body: self.to_h, auth: {accountId: account_id}, required_auths: Updox::Models::Auth::AUTH_ACCT))
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module Updox
2
+ module Models
3
+ class Location < Model
4
+ SYNC_ENDPOINT = '/LocationsSync'.freeze
5
+
6
+ property :id
7
+ property :code
8
+ property :name
9
+ property :showInPortal, default: false, from: :show_in_portal, with: ->(v) { true == v }, transform_with: ->(v) { true == v }
10
+ property :active, default: true
11
+
12
+ def save(account_id: )
13
+ self.class.sync([self], account_id: account_id)
14
+ end
15
+
16
+ def self.sync(locations, account_id: )
17
+ from_response(UpdoxClient.connection.request(endpoint: SYNC_ENDPOINT, body: { locations: locations }, auth: {accountId: account_id}, required_auths: Updox::Models::Auth::AUTH_ACCT), self)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,41 @@
1
+ module Updox
2
+ module Models
3
+ DATETIME_FORMAT = '%Y-%m-%d %H:%M'.freeze
4
+
5
+ class Model < Hashie::Trash
6
+ include Hashie::Extensions::IgnoreUndeclared
7
+ include Hashie::Extensions::IndifferentAccess
8
+
9
+ LIST_TYPE = 'undefined'
10
+ LIST_NAME = 'models'
11
+
12
+ property :item, required: false
13
+ property :items, required: false
14
+
15
+ def self.from_response(response, klazz = self)
16
+ return response if false == Updox.configuration.parse_responses?
17
+
18
+ model = Model.new
19
+ model.define_singleton_method(:response) { response }
20
+
21
+ if (response.ok?)
22
+ data = response.parsed_response
23
+
24
+ if data.is_a?(Array)
25
+ model.items = data
26
+ elsif data&.include?(klazz.const_get(:LIST_TYPE))
27
+ model.items = data.dig(klazz.const_get(:LIST_TYPE)).map { |obj| klazz.new(obj) }
28
+ model.define_singleton_method(klazz.const_get(:LIST_NAME)) { self.items }
29
+ else
30
+ model.items = [data]
31
+ model.item = data
32
+ end
33
+ else
34
+ raise UpdoxException.from_response(response)
35
+ end
36
+
37
+ return model
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,26 @@
1
+ module Updox
2
+ module Models
3
+ class Patient < Model
4
+ SYNC_ENDPOINT = '/PatientsSync'.freeze
5
+
6
+ property :id
7
+ property :internalId, required: true, from: :internal_id
8
+ property :firstName, from: :first_name
9
+ property :middleName, from: :middle_name
10
+ property :lastName, from: :last_name
11
+ property :emailAddress, from: :email_address
12
+ property :homePhone, from: :home_phone
13
+ property :workPhone, from: :work_phone
14
+ property :mobileNumber, from: :mobile_number
15
+ property :active, default: true
16
+
17
+ def save(account_id: )
18
+ self.class.sync([self], account_id: account_id)
19
+ end
20
+
21
+ def self.sync(patients, account_id: )
22
+ from_response(UpdoxClient.connection.request(endpoint: SYNC_ENDPOINT, body: { patients: patients }, auth: {accountId: account_id}, required_auths: Updox::Models::Auth::AUTH_ACCT), self)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,12 +1,13 @@
1
1
  module Updox
2
2
  module Models
3
- class Practice < Hashie::Trash
4
- CREATE_ENDPOINT = '/practiceCreate'.freeze
3
+ class Practice < Model
4
+ CREATE_ENDPOINT = '/PracticeCreate'.freeze
5
+ QUERY_ENDPOINT = '/PracticeList'.freeze
5
6
 
6
- include Hashie::Extensions::IndifferentAccess
7
+ LIST_TYPE = 'practiceList'.freeze
8
+ LIST_NAME = 'practices'
7
9
 
8
10
  property :name, required: true
9
- property :active, default: false
10
11
  property :accountId, from: :account_id, with: ->(v) { v.to_s }
11
12
  property :address1
12
13
  property :address2
@@ -27,11 +28,16 @@ module Updox
27
28
  property :practiceSpecialtyCode, from: :practice_specialty_code
28
29
  property :practiceNpi, from: :practice_npi
29
30
  property :defaultConsentMethods
31
+ property :active, default: true
30
32
 
31
33
  alias_method :account_id, :accountId
32
34
 
33
35
  def create
34
- UpdoxClient.connection.request(endpoint: CREATE_ENDPOINT, body: self.to_h)
36
+ UpdoxClient.connection.request(endpoint: CREATE_ENDPOINT, body: self.to_h, required_auths: Updox::Models::Auth::AUTH_APP)
37
+ end
38
+
39
+ def self.query
40
+ from_response(UpdoxClient.connection.request(endpoint: QUERY_ENDPOINT, required_auths: Updox::Models::Auth::AUTH_APP), self)
35
41
  end
36
42
  end
37
43
  end
@@ -0,0 +1,39 @@
1
+ module Updox
2
+ module Models
3
+ class User < Model
4
+ SAVE_ENDPOINT = '/UserSave'.freeze
5
+ QUERY_ENDPOINT = '/UserList'.freeze
6
+
7
+ LIST_TYPE = 'userList'.freeze
8
+ LIST_NAME = 'users'
9
+
10
+ property :userId, from: :user_id
11
+ property :emrUserId, from: :emr_user_id
12
+ property :loginId, from: :login_id
13
+ property :loginPassword, from: :login_password
14
+ property :firstName, required: true, from: :first_name
15
+ property :middleName, from: :middle_name
16
+ property :lastName, required: true, from: :last_name
17
+ property :address1
18
+ property :address2
19
+ property :city
20
+ property :state
21
+ property :postal
22
+ property :provider, default: false
23
+ property :admin, default: false
24
+ property :searchOptOut, from: :search_opt_out, default: true
25
+
26
+ alias_method :user_id, :userId
27
+ alias_method :first_name, :firstName
28
+ alias_method :last_name, :lastName
29
+
30
+ def create
31
+ UpdoxClient.connection.request(endpoint: SAVE_ENDPOINT, body: self.to_h, required_auths: Updox::Models::Auth::AUTH_APP)
32
+ end
33
+
34
+ def self.query
35
+ from_response(UpdoxClient.connection.request(endpoint: QUERY_ENDPOINT, required_auths: Updox::Models::Auth::AUTH_APP), self)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -2,18 +2,24 @@ module Updox
2
2
  class UpdoxException < Exception
3
3
  def self.from_response(response, msg: nil)
4
4
  exception_msg = "Failed #{msg}:"
5
- exception_msg << " HTTP code: #{response.code} MSG: "
5
+ exception_msg << " HTTP code: #{response.code}"
6
6
 
7
7
  begin
8
8
  error_response = JSON.parse(response.body)
9
9
 
10
- #if (error_response.is_a?(Hash) && error_response.include?("Meta") && error_response["Meta"].include?("Errors"))
11
- #exception_msg << error_response["Meta"]["Errors"]
12
- #else
13
- exception_msg << error_response
14
- #end
10
+ if error_response.is_a?(Hash)
11
+ if error_response.include?('responseMessage')
12
+ exception_msg << " MSG: #{error_response['responseMessage']}"
13
+ end
14
+
15
+ if error_response.include?('responseCode')
16
+ exception_msg << " UPDOX CODE: #{error_response['responseCode']}"
17
+ end
18
+ else
19
+ exception_msg << " MSG: #{error_response}"
20
+ end
15
21
  rescue JSON::ParserError
16
- exception_msg << response.body
22
+ exception_msg << " MSG: #{response.body}"
17
23
  end
18
24
 
19
25
  return UpdoxException.new(exception_msg)
data/lib/updox/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Updox
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
data/updox.gemspec CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency 'bundler', '>=1', '<3'
34
34
  spec.add_development_dependency 'byebug', '~> 11'
35
35
  spec.add_development_dependency 'minitest', '~> 5.0'
36
- spec.add_development_dependency 'rake', '~> 10.0'
36
+ spec.add_development_dependency 'rake', '~> 13.0'
37
37
  spec.add_development_dependency 'webmock', '~> 3.1'
38
38
  spec.add_development_dependency 'yard', '~> 0.9'
39
39
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: updox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Crockett
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-01-21 00:00:00.000000000 Z
11
+ date: 2020-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -92,14 +92,14 @@ dependencies:
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '10.0'
95
+ version: '13.0'
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '10.0'
102
+ version: '13.0'
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: webmock
105
105
  requirement: !ruby/object:Gem::Requirement
@@ -142,8 +142,15 @@ files:
142
142
  - Rakefile
143
143
  - lib/updox.rb
144
144
  - lib/updox/connection.rb
145
+ - lib/updox/models/application.rb
146
+ - lib/updox/models/appointment.rb
145
147
  - lib/updox/models/auth.rb
148
+ - lib/updox/models/calendar.rb
149
+ - lib/updox/models/location.rb
150
+ - lib/updox/models/model.rb
151
+ - lib/updox/models/patient.rb
146
152
  - lib/updox/models/practice.rb
153
+ - lib/updox/models/user.rb
147
154
  - lib/updox/updox_exception.rb
148
155
  - lib/updox/version.rb
149
156
  - updox.gemspec