psc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +13 -0
  2. data/.rvmrc +2 -0
  3. data/.yardopts +4 -0
  4. data/CHANGELOG.md +4 -0
  5. data/Gemfile +20 -0
  6. data/LICENSE +20 -0
  7. data/README.md +163 -0
  8. data/Rakefile +69 -0
  9. data/cucumber.yml +13 -0
  10. data/features/step_definitions/eval_steps.rb +14 -0
  11. data/features/step_definitions/setup_steps.rb +7 -0
  12. data/features/studies.feature +21 -0
  13. data/features/support/env.rb +33 -0
  14. data/features/support/int_psc.rb +165 -0
  15. data/int-psc/README-int-psc.md +57 -0
  16. data/int-psc/hsqldb/baseline.log +197 -0
  17. data/int-psc/hsqldb/baseline.properties +17 -0
  18. data/int-psc/hsqldb/baseline.script +295 -0
  19. data/int-psc/hsqldb/datasource.properties +18 -0
  20. data/int-psc/hsqldb/datasource.script +2205 -0
  21. data/int-psc/jetty/jetty-runner-7.4.0.v20110414.jar +0 -0
  22. data/int-psc/state/ABC 1200.xml +115 -0
  23. data/int-psc/state/int-psc-state.xml +64 -0
  24. data/lib/psc.rb +47 -0
  25. data/lib/psc/client.rb +35 -0
  26. data/lib/psc/connection.rb +96 -0
  27. data/lib/psc/faraday.rb +11 -0
  28. data/lib/psc/faraday/http_basic.rb +35 -0
  29. data/lib/psc/faraday/psc_token.rb +42 -0
  30. data/lib/psc/faraday/string_is_xml.rb +25 -0
  31. data/lib/psc/version.rb +3 -0
  32. data/meta.rakefile +30 -0
  33. data/psc.gemspec +33 -0
  34. data/spec/fixtures/studies-json.http +37 -0
  35. data/spec/middleware_helper.rb +18 -0
  36. data/spec/psc/client_spec.rb +39 -0
  37. data/spec/psc/connection_spec.rb +156 -0
  38. data/spec/psc/faraday/http_basic_spec.rb +15 -0
  39. data/spec/psc/faraday/psc_token_spec.rb +38 -0
  40. data/spec/psc/faraday/string_is_xml_spec.rb +49 -0
  41. data/spec/psc/version_spec.rb +11 -0
  42. data/spec/psc_spec.rb +16 -0
  43. data/spec/spec_helper.rb +15 -0
  44. data/tasks/int-psc.rake +84 -0
  45. data/tasks/psc/TODO +3 -0
  46. data/tasks/psc/state.rb +393 -0
  47. metadata +311 -0
@@ -0,0 +1,115 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <study xmlns="http://bioinformatics.northwestern.edu/ns/psc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" assigned-identifier="ABC 1200" last-modified-date="2011-05-12T11:10:29.740Z" xsi:schemaLocation="http://bioinformatics.northwestern.edu/ns/psc http://bioinformatics.northwestern.edu/ns/psc/psc.xsd">
4
+ <planned-calendar id="9c9c8042-75f1-477a-8855-40e6084f0954"/>
5
+ <amendment name="[Original]" date="2011-05-12" mandatory="true" released-date="2011-05-12T11:10:29.740Z" updated-date="2011-05-12T10:44:34.568Z">
6
+ <planned-calendar-delta id="d7adaa30-e2f0-4415-a751-53ccee9433ca" node-id="9c9c8042-75f1-477a-8855-40e6084f0954">
7
+ <add id="234ae5fa-e712-473d-b395-7e5edf9ef532" index="0">
8
+ <epoch id="72e7e90a-5f09-49b7-9dc4-785d56cf4414" name="Treatment">
9
+ <study-segment id="f174f7c2-ceed-4c6c-8392-1688f34b7905" name="A">
10
+ <period id="e74bbe38-46ad-410c-acd8-fb70f9527c0b" name="Surgery" repetitions="1" start-day="1" duration-quantity="1" duration-unit="day">
11
+ <planned-activity id="db2ffa63-8c28-43cd-94c4-e8f6801ce42b" day="1">
12
+ <activity-reference code="1041" source="Northwestern University"/>
13
+ </planned-activity>
14
+ </period>
15
+ <period id="1d8227b3-8f40-424c-a238-bffcda982014" name="Frequent monitoring" repetitions="24" start-day="1" duration-quantity="1" duration-unit="week">
16
+ <planned-activity id="2da856a4-3d40-4d04-8804-c9ee2b9f90b3" day="1">
17
+ <activity-reference code="360" source="Northwestern University"/>
18
+ </planned-activity>
19
+ <planned-activity id="09791bfb-eaf6-418a-8516-fae296df23f0" day="1">
20
+ <activity-reference code="253" source="Northwestern University"/>
21
+ </planned-activity>
22
+ </period>
23
+ <period id="e93b9f37-43de-4873-a776-1e7fcefbec59" name="Infrequent monitoring" repetitions="6" start-day="1" duration-quantity="1" duration-unit="month">
24
+ <planned-activity id="3ad03935-5bdf-448e-88ee-ee656d12afe3" day="1">
25
+ <activity-reference code="701" source="Northwestern University"/>
26
+ </planned-activity>
27
+ <planned-activity id="6fdbfd73-6cc3-41a6-a388-b19cf2bf7b25" day="1">
28
+ <activity-reference code="451" source="Northwestern University"/>
29
+ </planned-activity>
30
+ </period>
31
+ <period id="0c7852e2-eca1-4b57-9e7a-2d222abf43ea" name="Intervention" repetitions="8" start-day="8" duration-quantity="3" duration-unit="week">
32
+ <planned-activity id="845b63f0-389d-4b7c-b9df-986b7dd3e4ad" day="1">
33
+ <activity-reference code="211" source="Northwestern University"/>
34
+ </planned-activity>
35
+ <planned-activity id="0be23936-1621-49b3-862e-90eee0b03bf4" day="15">
36
+ <activity-reference code="211" source="Northwestern University"/>
37
+ </planned-activity>
38
+ <planned-activity id="7f599ee3-f45f-4af1-bcfd-3d377b3bbc4a" day="8">
39
+ <activity-reference code="43" source="Northwestern University"/>
40
+ </planned-activity>
41
+ </period>
42
+ </study-segment>
43
+ <study-segment id="fe329f59-1253-405a-9933-ea2d70bdf0dc" name="B">
44
+ <period id="538baa4e-6e4f-4e67-a112-b5a5d1200ba9" name="Frequent monitoring" repetitions="24" start-day="1" duration-quantity="1" duration-unit="week">
45
+ <planned-activity id="e742696d-c066-4e59-9bd2-f064a468ca87" day="1">
46
+ <activity-reference code="360" source="Northwestern University"/>
47
+ </planned-activity>
48
+ <planned-activity id="d882f175-13ae-4c16-9aa2-38f27c2ea2a6" day="1">
49
+ <activity-reference code="253" source="Northwestern University"/>
50
+ </planned-activity>
51
+ </period>
52
+ <period id="d54584c4-acc5-4de9-8323-ed36cbced4bc" name="Infrequent monitoring" repetitions="6" start-day="1" duration-quantity="1" duration-unit="month">
53
+ <planned-activity id="b23569f5-1268-4264-8c4d-bae166616d16" day="1">
54
+ <activity-reference code="701" source="Northwestern University"/>
55
+ </planned-activity>
56
+ <planned-activity id="3c54d7b4-f2ec-4b9e-9fe4-b6d13b92aeba" day="1">
57
+ <activity-reference code="451" source="Northwestern University"/>
58
+ </planned-activity>
59
+ </period>
60
+ <period id="b85661ef-ff19-462f-83a4-3e53ed69cd84" name="Intervention" repetitions="8" start-day="8" duration-quantity="3" duration-unit="week">
61
+ <planned-activity id="23a0aac4-87f6-4cd3-8df1-71bee82505ae" day="1">
62
+ <activity-reference code="211" source="Northwestern University"/>
63
+ </planned-activity>
64
+ <planned-activity id="24deeecc-cba8-4d8c-bc55-35048970bb8e" day="15">
65
+ <activity-reference code="211" source="Northwestern University"/>
66
+ </planned-activity>
67
+ <planned-activity id="40cce9be-3a32-4477-9c8d-79104b8625f0" day="8">
68
+ <activity-reference code="43" source="Northwestern University"/>
69
+ </planned-activity>
70
+ </period>
71
+ </study-segment>
72
+ </epoch>
73
+ </add>
74
+ <add id="1b02eb68-d1c8-4d11-88f2-4785ff91f0bb" index="1">
75
+ <epoch id="1d0d08cc-ed40-4ebb-923f-ebe0c29e814d" name="Follow up">
76
+ <study-segment id="4c01e031-1fe9-41ad-970c-368cb57c0c8e" name="Follow up">
77
+ <period id="3b8bff21-4d30-475f-a9b7-a39afed63b94" name="QoL" repetitions="18" start-day="1" duration-quantity="1" duration-unit="quarter">
78
+ <planned-activity id="a33a6a83-2b27-4678-a395-4025b399882b" details="Administer by phone" day="1">
79
+ <activity-reference code="941" source="Northwestern University"/>
80
+ </planned-activity>
81
+ </period>
82
+ </study-segment>
83
+ </epoch>
84
+ </add>
85
+ <add id="aefbcdc5-ac5b-4c0d-b092-b6e9e337969e" index="2">
86
+ <epoch id="9d7bf8eb-6413-4286-97c0-f44f489883e6" name="Run-in">
87
+ <study-segment id="14969b7b-e49e-48e0-b081-ba94d9448479" name="Run-in">
88
+ <period id="3970c8a8-89e3-4d7e-98ac-4f27232cc624" repetitions="3" start-day="1" duration-quantity="7" duration-unit="day">
89
+ <planned-activity id="cdd73e03-1410-4ae6-9d86-4a8929cd1678" day="1">
90
+ <activity-reference code="360" source="Northwestern University"/>
91
+ </planned-activity>
92
+ <planned-activity id="0df8ccf3-2067-47c6-bbf3-dfbf8512968e" day="7">
93
+ <activity-reference code="593" source="Northwestern University"/>
94
+ </planned-activity>
95
+ </period>
96
+ </study-segment>
97
+ </epoch>
98
+ </add>
99
+ <reorder id="a455e585-1bee-4770-b191-63fb8ec70066" child-id="9d7bf8eb-6413-4286-97c0-f44f489883e6" old-index="2" new-index="0"/>
100
+ </planned-calendar-delta>
101
+ </amendment>
102
+ <sources>
103
+ <source name="Northwestern University">
104
+ <activity name="Ketones (Acetone)" code="593" type="Lab Test"/>
105
+ <activity name="Taxol" code="211" type="Intervention"/>
106
+ <activity name="Dialysis Chemistry Panel" code="451" type="Lab Test"/>
107
+ <activity name="24 hr urine, protein electrophoresis" code="253" type="Lab Test"/>
108
+ <activity name="Carboplatin" code="43" type="Intervention"/>
109
+ <activity name="Pregnancy Test" code="701" type="Lab Test"/>
110
+ <activity name="CBC" code="360" type="Lab Test"/>
111
+ <activity name="QOL Survey" code="941" type="Other"/>
112
+ <activity name="Surgery" code="1041" type="Procedure"/>
113
+ </source>
114
+ </sources>
115
+ </study>
@@ -0,0 +1,64 @@
1
+ <!-- To rebuild the integration test PSC when you change this data, run `rake int-psc:rebuild` -->
2
+
3
+ <psc-state>
4
+ <!--
5
+ General note: Anywhere a date is specified, the value may either
6
+ be a date string in the form yyyy-mm-dd or an integer. An
7
+ integer indicates a date relative to today (negative, before
8
+ today; positive, after today).
9
+ -->
10
+
11
+ <site name="Northwestern University" assigned-identifier="IL036"/>
12
+ <site name="Mayo Clinic Rochester" assigned-identifier="MN026"/>
13
+ <site name="Thomas Jefferson University" assigned-identifier="PA121"/>
14
+
15
+ <!--
16
+ The assigned-identifier attribute for template is mandatory;
17
+ file is optional. If the file is omitted, it is assumed to be a
18
+ file named [assigned-identifier].xml in the same directory as
19
+ this state XML file.
20
+ -->
21
+ <template assigned-identifier="ABC 1200">
22
+ <!--
23
+ approval indicates whether the template should be marked as
24
+ approved for use by the site. It can be either a date or
25
+ false; the default is 0 (i.e., today). All released amendments
26
+ will have the same approval status.
27
+ -->
28
+ <participating-site assigned-identifier="IL036" approval="2008-01-07"/>
29
+ <participating-site assigned-identifier="PA121" approval="2010-01-02"/>
30
+ </template>
31
+
32
+
33
+ <registration>
34
+ <subject
35
+ first-name="Jo"
36
+ last-name="Fredricksson"
37
+ gender="Female"
38
+ birth-date="1950-06-01"
39
+ person-id="XC56700077"
40
+ >
41
+ <subject-property name="Hat size" value="7"/>
42
+ </subject>
43
+ <!-- template-identifier and site-identifier are mandatory attributes -->
44
+ <study-site
45
+ template="ABC 1200"
46
+ site="IL036"
47
+ study-subject-identifier="A0001"
48
+ desired-assignment-identifier="POP-2702">
49
+ <!--
50
+ * segment is either the ID for the segment or its "epoch: segment" name (mandatory).
51
+ * start is the start date for the segment (default: 0 for the first segment;
52
+ the end of the previous for subsequent). [TODO: the second part isn't implemented yet]
53
+ * mode is the transition mode for the segment (default: per-protocol).
54
+ -->
55
+ <scheduled-segment segment="Run-in" start="2010-01-01"/>
56
+ <scheduled-segment segment="Treatment: A" start="2010-02-01" mode="per-protocol"/>
57
+ <scheduled-segment segment="Follow up" start="2010-03-15" mode="immediate"/>
58
+ </study-site>
59
+
60
+ <!-- multiple study-sites allowed per registration to reuse the same subject -->
61
+ </registration>
62
+
63
+ <!-- multiple registrations allowed -->
64
+ </psc-state>
@@ -0,0 +1,47 @@
1
+ require 'builder'
2
+
3
+ ##
4
+ # The namespace for `psc.rb`.
5
+ module Psc
6
+ autoload :Faraday, 'psc/faraday'
7
+ autoload :Client, 'psc/client'
8
+ autoload :Connection, 'psc/connection'
9
+
10
+ ##
11
+ # Wraps `Builder::XmlMarkup` to ease the construction of PSC XML
12
+ # entities. For example:
13
+ #
14
+ # Psc.xml('study-snapshot', :assigned_identifier => 'YUV 1234') do |snap|
15
+ # snap.tag!('long-title', 'Why you validly counting?')
16
+ # snap.tag!('planned-calendar') { |pc|
17
+ # pc.epoch(:name => 'Run-in') { |e|
18
+ # # and so on
19
+ # }
20
+ # }
21
+ # snap.sources { |sources|
22
+ # sources.source { |src|
23
+ # # and so on
24
+ # }
25
+ # }
26
+ # }
27
+ #
28
+ # @see http://builder.rubyforge.org/classes/Builder/XmlMarkup.html
29
+ # @return [String] the constructed XML
30
+ def self.xml(root_name, root_attributes={}, &block)
31
+ root_attributes['xmlns'] = xml_namespace['psc']
32
+ Builder::XmlMarkup.new(:indent => 2).tag!(root_name, root_attributes, &block)
33
+ end
34
+
35
+ ##
36
+ # @private
37
+ XML_NAMESPACE = { 'psc' => 'http://bioinformatics.northwestern.edu/ns/psc' }.freeze
38
+
39
+ ##
40
+ # Provides an XML namespace mapping suitable for use with
41
+ # Nokogiri. The prefix `psc` is mapped to PSC's namespace.
42
+ #
43
+ # @return [Hash<String, String>] the mapping
44
+ def self.xml_namespace
45
+ XML_NAMESPACE
46
+ end
47
+ end
@@ -0,0 +1,35 @@
1
+ require 'psc'
2
+
3
+ module Psc
4
+ ##
5
+ # A high-level interface to a PSC instance's API. This class does
6
+ # not expose everything you can do with the API &mdash; only a few
7
+ # of the more common elements. Refer to the API documentation and
8
+ # use {Psc::Connection} for complete access.
9
+ class Client
10
+ ##
11
+ # The connection that the client is using to access PSC. Use it to
12
+ # make requests that the high-level interface doesn't support.
13
+ #
14
+ # @return [Psc::Connection]
15
+ attr_reader :connection
16
+
17
+ ##
18
+ # Create a new client instance. The given url and options will be
19
+ # used to create a {Psc::Connection} for the client to use; see
20
+ # that class for more details about what is permitted
21
+ def initialize(url, options, &block)
22
+ @connection = Psc::Connection.new(url, options, &block)
23
+ end
24
+
25
+ ##
26
+ # Returns an array of hashes describing the studies in the system.
27
+ # The contents are from the JSON representation of the `/studies`
28
+ # resource.
29
+ #
30
+ # @return [Array<Hash>]
31
+ def studies
32
+ connection.get('studies.json').body['studies']
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,96 @@
1
+ require 'psc'
2
+ require 'faraday'
3
+ require 'faraday_stack'
4
+
5
+ module Psc
6
+ ##
7
+ # A `Faraday::Connection` set up for use with Patient Study
8
+ # Calendar. See the {file:README.md} for a couple of examples.
9
+ #
10
+ # @see https://github.com/technoweenie/faraday
11
+ # @see https://github.com/mislav/faraday-stack
12
+ class Connection < ::Faraday::Connection
13
+ ##
14
+ # Create a new PSC connection. This is a `Faraday::Connection`
15
+ # with the following middleware:
16
+ #
17
+ # * Either {Psc::Faraday::HttpBasic} or {Psc::Faraday::PscToken}
18
+ # depending on the contents of the `:authenticator` option
19
+ # * {Psc::Faraday::StringIsXml}
20
+ # * `::Faraday::Request::JSON`
21
+ # * `::Faraday::Request::UrlEncoded`
22
+ # * `::FaradayStack::ResponseXML` for content-type text/xml
23
+ # * `::FaradayStack::ResponseJSON` for content-type application/json
24
+ # * `::Faraday::Adapter::NetHttp`
25
+ #
26
+ # If a block is provided, it will receive the Faraday builder so
27
+ # that you can append more middleware. If you provide your own
28
+ # adapter, the `net/http` adapter will not be appended.
29
+ #
30
+ # @param [String] url the base URL for your PSC instance
31
+ # @param [Hash] options the options for the connection. These are
32
+ # same as accepted by `Faraday::Connection`, plus:
33
+ # @option options [Hash] :authenticator parameters for the
34
+ # authentication middleware. These are detailed in the {file:README.md}.
35
+ # @yield [builder] (optional)
36
+ def initialize(url, options)
37
+ super do |builder|
38
+ builder.use *authentication_middleware(options[:authenticator])
39
+ builder.use Psc::Faraday::StringIsXml
40
+ builder.request :json
41
+ builder.request :url_encoded
42
+ builder.use FaradayStack::ResponseXML, :content_type => 'text/xml'
43
+ builder.use FaradayStack::ResponseJSON, :content_type => 'application/json'
44
+
45
+ if block_given?
46
+ yield builder
47
+ end
48
+
49
+ builder.adapter :net_http unless has_adapter?(builder)
50
+ end
51
+
52
+ unless self.path_prefix =~ %r{/api/v1$}
53
+ self.path_prefix = if self.path_prefix == '/'
54
+ '/api/v1'
55
+ else
56
+ self.path_prefix + '/api/v1'
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def authentication_middleware(authenticator)
64
+ unless authenticator
65
+ raise 'No authentication method specified. Please include the :authenticator option.'
66
+ end
67
+
68
+ kind = authenticator.keys.first
69
+
70
+ case kind
71
+ when :basic
72
+ [Psc::Faraday::HttpBasic, *authenticator[kind]]
73
+ when :token
74
+ [Psc::Faraday::PscToken, authenticator[kind]]
75
+ else
76
+ raise "Unsupported authentication method #{kind.inspect}."
77
+ end
78
+ end
79
+
80
+ def has_adapter?(builder)
81
+ builder.handlers.detect { |h| has_superclass?(h.klass, ::Faraday::Adapter) }
82
+ end
83
+
84
+ # It seems like there must be a builtin for this, but I'm not
85
+ # finding it.
86
+ def has_superclass?(child, ancestor)
87
+ if child.superclass == ancestor
88
+ true
89
+ elsif child.superclass.nil?
90
+ false
91
+ else
92
+ has_superclass?(child.superclass, ancestor)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,11 @@
1
+ require 'psc'
2
+
3
+ require 'faraday'
4
+
5
+ ##
6
+ # The custom Faraday middleware used in `psc.rb`.
7
+ module Psc::Faraday
8
+ autoload :HttpBasic, 'psc/faraday/http_basic'
9
+ autoload :PscToken, 'psc/faraday/psc_token'
10
+ autoload :StringIsXml, 'psc/faraday/string_is_xml'
11
+ end
@@ -0,0 +1,35 @@
1
+ require 'psc/faraday'
2
+
3
+ require 'base64'
4
+
5
+ module Psc::Faraday
6
+ ##
7
+ # Faraday middleware that implements [HTTP Basic][]. This is not
8
+ # PSC-specific, and Faraday even includes HTTP Basic support, but
9
+ # Faraday's support is not implemented as middleware. Using
10
+ # middleware makes setting up a connection based on the
11
+ # `:authenticator` option cleaner.
12
+ #
13
+ # [HTTP Basic]: http://www.ietf.org/rfc/rfc2617.txt
14
+ class HttpBasic
15
+ ##
16
+ # Create an instance of the middleware.
17
+ #
18
+ # @param [#call] app
19
+ # @param [String] username
20
+ # @param [String] password
21
+ def initialize(app, username, password)
22
+ @app = app
23
+ @header_value = "Basic #{Base64.encode64([username, password].join(':')).strip}"
24
+ end
25
+
26
+ ##
27
+ # Sets the `Authorization` request header using the HTTP Basic
28
+ # scheme.
29
+ def call(env)
30
+ env[:request_headers]['Authorization'] = @header_value
31
+
32
+ @app.call(env)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ require 'psc/faraday'
2
+
3
+ module Psc
4
+ module Faraday
5
+ ##
6
+ # Faraday middleware that implements the `psc_token`
7
+ # authentication type. Tokens may either be static or computed
8
+ # per request.
9
+ class PscToken
10
+ ##
11
+ # Create a new instance of the middleware.
12
+ #
13
+ # @param [#call] app
14
+ # @param [#call,String] token_or_creator if the value for this
15
+ # parameter responds to `call`, it will be called to create a
16
+ # token on each request. Otherwise it will be used as a static
17
+ # token value.
18
+ def initialize(app, token_or_creator)
19
+ @app = app
20
+ if token_or_creator.respond_to?(:call)
21
+ @token_creator = token_or_creator
22
+ else
23
+ @token_creator = lambda { token_or_creator }
24
+ end
25
+ end
26
+
27
+ ##
28
+ # Adds the `Authorization` header using the configured token creator.
29
+ def call(env)
30
+ env[:request_headers]['Authorization'] = "psc_token #{token}"
31
+
32
+ @app.call
33
+ end
34
+
35
+ private
36
+
37
+ def token
38
+ @token_creator.call
39
+ end
40
+ end
41
+ end
42
+ end