cat_engine 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 978fd55346645780b7553f8ee5f16efd85c23945
4
+ data.tar.gz: d2ab015f0efd6c4a625a287aa47dbe686a5ab693
5
+ SHA512:
6
+ metadata.gz: fe2ac35c1da6745363a279f1773d7e7786e1bf042ddc7e07627b261783745b2efe61ff60181391f8b3e7f76ebfe83cadc95cd4e24c3a84287c8f5d56e23d8428
7
+ data.tar.gz: abfc555e330a6d84cae8d1c13db2db00fcf63dd684c1299138b3c295107ec5690880c27276146fddb4b6f33d288edb5df3f0696d639eb7de81f82cbaef926e8c
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ !/tmp/.keep
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1 @@
1
+ cat_engine
@@ -0,0 +1 @@
1
+ ruby-2.2.3
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cat_engine.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Micah Geisel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,43 @@
1
+ # Cat Engine
2
+
3
+ Ruby wrapper around C# Cat Engine
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'cat_engine'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install cat_engine
20
+
21
+ ## Usage
22
+
23
+ You must compile the C# componetn before anything will work. There is a rake task for this. Requires `mcs` on Ubuntu.
24
+
25
+ ```bash
26
+ rake compile_cat_engine`
27
+ ```
28
+
29
+ See specs for more usage info
30
+
31
+ ## Development
32
+
33
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
34
+
35
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
36
+
37
+ ## Contributing
38
+
39
+ 1. Fork it ( https://github.com/botandrose/cat_engine/fork )
40
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
41
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
42
+ 4. Push to the branch (`git push origin my-new-feature`)
43
+ 5. Create a new Pull Request
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ RSpec::Core::RakeTask.new
4
+
5
+ desc "Compile the Cat Engine"
6
+ task :compile_cat_engine do
7
+ require "cat_engine/engine"
8
+ CatEngine::Engine.compile
9
+ end
10
+
11
+ task :default => [:compile_cat_engine, :spec]
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "cat_engine"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cat_engine/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cat_engine"
8
+ spec.version = CatEngine::VERSION
9
+ spec.authors = ["Micah Geisel"]
10
+ spec.email = ["micah@botandrose.com"]
11
+
12
+ spec.summary = %q{Ruby wrapper around C# Cat Engine}
13
+ spec.description = %q{Ruby wrapper around C# Cat Engine}
14
+ spec.homepage = "https://github.com/botandrose/cat_engine"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "activemodel"
23
+ spec.add_dependency "nokogiri", "~> 1.0"
24
+ spec.add_dependency "memoist", "~> 0.14.0"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.9"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.0"
29
+ spec.add_development_dependency "rspec-its", "~> 1.0"
30
+ end
@@ -0,0 +1,11 @@
1
+ require "cat_engine/version"
2
+ require "cat_engine/config"
3
+ require "cat_engine/rails" if defined?(Rails)
4
+ require "cat_engine/bank"
5
+ require "cat_engine/engine"
6
+ require "cat_engine/question"
7
+ require "cat_engine/response"
8
+ require "cat_engine/flat_engine"
9
+
10
+ module CatEngine
11
+ end
@@ -0,0 +1,57 @@
1
+ require "cat_engine/engine"
2
+ require "nokogiri"
3
+
4
+ module CatEngine
5
+ class Bank < Struct.new(:id, :history, :full_history)
6
+ def paired?
7
+ id.include? "Illness Impact"
8
+ end
9
+
10
+ def name
11
+ xml.root["Name"]
12
+ end
13
+
14
+ def current_question
15
+ Question.new(xml: xml) unless finished?
16
+ end
17
+
18
+ def valid_answer?(response_id)
19
+ engine.valid_history?(id, [history, response_id].join(" "))
20
+ end
21
+
22
+ def finished?
23
+ xml.root.name == "Results"
24
+ end
25
+
26
+ def theta
27
+ return unless xml.root.key?("Theta")
28
+ (10 * xml.root["Theta"].to_f + 50).round
29
+ end
30
+
31
+ def standard_error
32
+ return unless xml.root.key?("StdError")
33
+ (10 * xml.root["StdError"].to_f).round
34
+ end
35
+
36
+ def to_h
37
+ {
38
+ finished: finished?,
39
+ theta: theta,
40
+ standard_error: standard_error,
41
+ }
42
+ end
43
+
44
+ alias_method :to_param, :id
45
+
46
+ private
47
+
48
+ def xml
49
+ engine.process(id, history)
50
+ end
51
+
52
+ def engine
53
+ id.include?(".flat") ? FlatEngine.new(full_history) : Engine.new
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,12 @@
1
+ require "active_support/core_ext/module/attribute_accessors"
2
+
3
+ module CatEngine
4
+ def self.configure
5
+ yield self
6
+ end
7
+
8
+ mattr_accessor(:cat_path) { "config/cat_engine" }
9
+ mattr_accessor(:cat_engine_source_path) { File.join(__dir__, "../../vendor/cat_engine") }
10
+ mattr_accessor(:cat_engine_executable_path) { File.expand_path("tmp/cat_engine.exe") }
11
+ end
12
+
@@ -0,0 +1,34 @@
1
+ require "nokogiri"
2
+ require "memoist"
3
+ require "cat_engine/config"
4
+
5
+ module CatEngine
6
+ class Engine
7
+ extend Memoist
8
+ class EngineError < StandardError; end
9
+
10
+ def self.compile
11
+ gmcs = [`which gmcs`.chomp, `which mcs`.chomp].find { |path| path != "" }
12
+ system "cd #{CatEngine.cat_engine_source_path} && #{gmcs} -debug -out:#{CatEngine.cat_engine_executable_path} *.cs"
13
+ end
14
+
15
+ def process id, history
16
+ command = %(cd #{CatEngine.cat_path} && mono --debug #{CatEngine.cat_engine_executable_path} "#{id}" #{history} 2>&1)
17
+ output = `#{command}`.chomp
18
+ if $?.success?
19
+ Nokogiri::XML.parse(output, nil, "utf-8")
20
+ else
21
+ raise EngineError.new(output)
22
+ end
23
+ end
24
+ memoize :process
25
+
26
+ def valid_history? id, history
27
+ process id, history
28
+ true
29
+ rescue EngineError => e
30
+ false
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,40 @@
1
+ require "nokogiri"
2
+ require "cat_engine/config"
3
+
4
+ module CatEngine
5
+ class FlatEngine
6
+ def initialize full_history = []
7
+ self.full_history = full_history
8
+ end
9
+ attr_accessor :id, :full_history
10
+
11
+ def process id, history
12
+ self.id = id
13
+
14
+ doc = Nokogiri.parse(form)
15
+ item = doc.root.css("Item").reject do |item|
16
+ full_history.include?(item["FormItemOID"])
17
+ end.first
18
+
19
+ doc.root.children.each(&:unlink)
20
+ if item
21
+ item.parent = doc.root
22
+ else
23
+ doc.root = doc.create_element("Results")
24
+ end
25
+
26
+ doc
27
+ end
28
+
29
+ def valid_history? id, history
30
+ true
31
+ end
32
+
33
+ private
34
+
35
+ def form
36
+ File.read("#{CatEngine.cat_path}/forms/#{id}.xml")
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,83 @@
1
+ require "nokogiri"
2
+ require "active_model"
3
+
4
+ module CatEngine
5
+ class Question < Struct.new(:id, :promis_id, :context, :title, :response_before, :responses)
6
+ include ActiveModel::Conversion
7
+ extend ActiveModel::Naming
8
+
9
+ def initialize attributes = nil
10
+ assign_attributes(attributes || {})
11
+ end
12
+
13
+ def context_and_title
14
+ [context, title].select(&:present?).join(" ")
15
+ end
16
+
17
+ def xml= xml
18
+ assign_attributes XMLMapper.new(xml).to_h
19
+ end
20
+
21
+ def persisted?; false; end
22
+
23
+ attr_accessor :responses_attributes, :selected_response_index, :response
24
+
25
+ def selected_response
26
+ if selected_response_index
27
+ responses_attributes[selected_response_index]
28
+ else
29
+ { id: "00000000-0000-0000-0000-000000000000", title: response || "Skipped" }
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def assign_attributes attributes
36
+ attributes.each do |key, value|
37
+ send :"#{key}=", value
38
+ end
39
+ end
40
+
41
+ class XMLMapper < Struct.new(:xml)
42
+ def id
43
+ xml.xpath("//Item")[0]["FormItemOID"]
44
+ end
45
+
46
+ def promis_id
47
+ xml.xpath("//Item")[0]["ID"]
48
+ end
49
+
50
+ def context
51
+ element_descriptions[0]
52
+ end
53
+
54
+ def title
55
+ element_descriptions[1]
56
+ end
57
+
58
+ def responses
59
+ xml.xpath("//Map").map do |xml|
60
+ Response.new(xml: xml)
61
+ end
62
+ end
63
+
64
+ def to_h
65
+ {
66
+ id: id,
67
+ promis_id: promis_id,
68
+ context: context,
69
+ title: title,
70
+ responses: responses,
71
+ }
72
+ end
73
+
74
+ private
75
+
76
+ def element_descriptions
77
+ elements = xml.xpath("//Element")[0..-2] # drop last container element
78
+ elements.map { |e| e["Description"] } # sometimes one, sometimes two
79
+ end
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,7 @@
1
+ module CatEngine
2
+ class Rails < ::Rails::Railtie
3
+ rake_tasks do
4
+ load File.join(__dir__, "../tasks/cat_engine_tasks.rake")
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,50 @@
1
+ require "nokogiri"
2
+ require "active_model"
3
+
4
+ module CatEngine
5
+ class Response < Struct.new(:id, :promis_id, :before, :title)
6
+ include ActiveModel::Conversion
7
+ extend ActiveModel::Naming
8
+
9
+ def initialize attributes = nil
10
+ assign_attributes(attributes || {})
11
+ end
12
+
13
+ def xml= xml
14
+ assign_attributes XMLMapper.new(xml).to_h
15
+ end
16
+
17
+ def persisted?; false; end
18
+
19
+ private
20
+
21
+ def assign_attributes attributes
22
+ attributes.each do |key, value|
23
+ send :"#{key}=", value
24
+ end
25
+ end
26
+
27
+ class XMLMapper < Struct.new(:xml)
28
+ def id
29
+ xml["ItemResponseOID"]
30
+ end
31
+
32
+ def promis_id
33
+ xml["Value"]
34
+ end
35
+
36
+ def title
37
+ xml["Description"]
38
+ end
39
+
40
+ def to_h
41
+ {
42
+ id: id,
43
+ promis_id: promis_id,
44
+ title: title,
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
50
+