ndr_workflow 1.2.5

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3222ceba0ab6c26802391cce5268e1e414b142f0463ae18a7c9ebea06e4e0ae9
4
+ data.tar.gz: 66c4bcb8152c2a0734968bb4f121324b3535ca2888f72e98dc4bb4b669a259ff
5
+ SHA512:
6
+ metadata.gz: 23d8b24e27d6c9ab8e3f306eed4d9301068d6c1966707cb53abdf440dfcb6baacc3e805deba4f4c32280f6871945e825cd20f840efab5d4533912e519f0f0e10
7
+ data.tar.gz: cf6d925666512f0db92e6045cc84054cd394ba2dc82ba2d387f614001981a39e10fd1fdf1afbc1d8b64c7b77f717367f7638efc9f1dc17d0b762552bf9ca3a96
data/CHANGELOG.md ADDED
@@ -0,0 +1,30 @@
1
+ ## [Unreleased]
2
+ * No unreleased changes
3
+
4
+ ## 1.2.5 / 2026-07-13
5
+ ### Fixed
6
+ * Support Ruby 3.4. Drop support for Rails 7.0, Ruby 3.1
7
+ * Support Rails 8.1, Ruby 4.0. Drop support for Ruby 3.2
8
+
9
+ ## 1.2.4 / 2024-11-13
10
+ ### Fixed
11
+ * Support Rails 7.1, 7.2, 8.0, Ruby 3.2. Drop support for Rails 6.1
12
+
13
+ ## 1.2.3 / 2022-12-02
14
+ ### Fixed
15
+ * Support Ruby 3.1, Rails 7.0
16
+ * Drop support for Ruby 2.6, Rails 5.2
17
+ * Replace Public Health England naming with NHS Digital
18
+
19
+ ## 1.2.2 / 2021-01-25
20
+ ### Fixed
21
+ * Ensure app/ subdirectories are autoloadable by `zeitwerk`
22
+
23
+ ## 1.2.1 / 2021-01-11
24
+ ### Fixed
25
+ * Remove deprecations in Rails 6
26
+
27
+ ## 1.2.0 / 2020-07-01
28
+ ### Added
29
+ * Add `e_batch.e_actions.restart` method
30
+ * Start of CHANGELOG
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,118 @@
1
+ # NdrWorkflow
2
+
3
+ ** THIS ENGINE IS NOT FOR PUBLIC DEPLOYMENT IN ITS CURRENT FORMAT **
4
+
5
+ This is the NHS Digital (NHS-D) National Disease Registers (NDR) Workflow ruby gem, providing:
6
+
7
+ 1. core model definitions derived from ERA to allow for workflows and batch processing
8
+ Core tables included:
9
+ EWorkflow, EBatch, EAction
10
+ Supporting lookup tables include:
11
+ ZeActiontype, ZeType, Zprovider, Zuser
12
+
13
+ Currently excluded:
14
+ Conformance data
15
+ Queued_Job
16
+ Back end daemons
17
+ ZPostCode - referenced heavily in Zprovider
18
+
19
+ ## Considerations
20
+ 1. Postgres only implementation for creating underlying db tables (will work with oracle but no migrations)
21
+ 2. FK constraints managed through DB rather than ActiveRecord
22
+ 3. Table names set in Engine models - not standard rails
23
+ 4.
24
+
25
+ ## Installation
26
+
27
+ Add this line to your application's Gemfile:
28
+
29
+ ```ruby
30
+ gem 'ndr_workflow', path: "PATH_TO_YOUR_GIT_SVN/non-era/gems/ndr_workflow"
31
+ ```
32
+
33
+ And then execute:
34
+
35
+ $ bundle install
36
+
37
+ If the application needs the physical tables creating in Postgres:
38
+
39
+ $ rails g ndr_workflow:install
40
+ $ rake db:migrate
41
+
42
+ ## Usage
43
+
44
+ # Rails 3
45
+
46
+ Protected attributes
47
+ Disabe globally in your app in conf/application.rb, with
48
+ config.active_record.whitelist_attributes = false
49
+ OR
50
+ for specific models using attr_accessible : create new model definition in your app dir based on template files in enginer in :
51
+ test/dummy/app/model/NAME.rb
52
+
53
+ # Rails 4
54
+
55
+ Will need to use strong parameters
56
+
57
+
58
+ # When extending already present models (ERA)
59
+ copy and uncomment the first line from test/dummy/app/model/NAME.rb to app/model/NAME.rb eg:
60
+ ```require NdrWorkflow::Engine.root.join('app', 'models', 'e_action')```
61
+
62
+ # When you want to extend the Engine
63
+ create new model definition in your app dir based on file in engines dummy app ie test/dummy/app/model/NAME.rb
64
+
65
+
66
+ # Test Data
67
+
68
+ All test data in this repository is fictitious. Any resemblance to real persons, living or dead, is purely coincidental.
69
+
70
+ Note: Real codes exist in the tests, postcodes for example, but bear no relation to real patient data. Please ensure that you *always* only ever commit dummy data when contributing to this project.
71
+
72
+
73
+ = Using same class names in app and engine tricky .... (hence use of require)
74
+ http://stackoverflow.com/questions/5045068/extending-controllers-of-a-rails-3-engine-in-the-main-app
75
+
76
+ = TESTING
77
+ BUNDLE_GEMFILE=gemfiles/Gemfile.rails-3.0.x bundle install
78
+ BUNDLE_GEMFILE=gemfiles/Gemfile.rails-3.0.x bundle exec rake test
79
+
80
+ NDR_WORKFLOW
81
+ ================================================================================================================================
82
+ -- Dummy lookup data
83
+ ZeType.create(:shortdesc => "National Prostate Cancer Audit"){|ze_type| ze_type.ze_typeid = "NPCA"}
84
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "NEW"}
85
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "AIMP"}
86
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "PRECHECK"}
87
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "APOST"}
88
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "MPOST"}
89
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "CREA"}
90
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "RECO"}
91
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "CHECK"}
92
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "AMCH"}
93
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "MMCH"}
94
+ ZeActiontype.create(:shortdesc => "Initialize batch"){|ze_actiontype| ze_actiontype.ze_actiontypeid = "STOP"}
95
+ -- NO POSTCODES
96
+ Zprovider.create(:shortdesc => "ECRIC CAMBRIDGE", :exportid => "0402", :startdate => "007-06-01", :sort => 1, :role => "H", :linac => 0){|ze_provider| ze_provider.zproviderid = "0402"}
97
+ Zuser.create(:zuserid => "ben", :shortdesc => "bens_test_user", :registryid => "0402")
98
+
99
+ -- Workflow model NPCA
100
+ SEQUENCE = %w{NEW AIMP PRECHECK APOST MPOST CREA RECO CHECK AMCH MMCH STOP}
101
+ SEQUENCE.each_with_index do |action, index|
102
+ EWorkflow.create(:e_type => 'NPCA', :last_e_actiontype => action, :next_e_actiontype => SEQUENCE[index+1]) unless SEQUENCE[index+1].nil?
103
+ end
104
+
105
+ -- Create batch for workflow
106
+ EBatch.create(:e_type => "NPCA", :provider => "0402", :registryid => "0402", :media => 'Email', :original_filename => 'textfile_batch.txt',
107
+ :cleaned_filename => '', :numberofrecords => 3, :date_reference1 => '2005.05.05', :date_reference2 => '2007.01.01',
108
+ :e_batchid_traced => '', :comments => 'Addenbrookes, plain text file', :digest => '1b2bd8aecffcaf099b4d529498615ef2c765d1b5',
109
+ :lock_version => 0)
110
+
111
+ -- Create first action for batch
112
+ EAction.create!(:e_batchid => 1, :e_actiontype => "NEW", :started => Time.now, :finished => nil, :startedby => 'ben', :status => '')
113
+ --
114
+ EBatch.new(:e_type => 'NPCA', :original_filename => 'svn/')
115
+ EBatch.first.e_actions.start_and_finish('NEW','ben')
116
+
117
+
118
+
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env rake
2
+ require 'ndr_dev_support/tasks'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+ begin
9
+ require 'rdoc/task'
10
+ rescue LoadError
11
+ require 'rdoc/rdoc'
12
+ require 'rake/rdoctask'
13
+ RDoc::Task = Rake::RDocTask
14
+ end
15
+
16
+ RDoc::Task.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'NdrWorkflow'
19
+ rdoc.options << '--line-numbers'
20
+ rdoc.rdoc_files.include('README.rdoc')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
25
+ load 'rails/tasks/engine.rake'
26
+
27
+
28
+
29
+ Bundler::GemHelper.install_tasks
30
+
31
+ require 'rake/testtask'
32
+
33
+ Rake::TestTask.new(:test) do |t|
34
+ t.libs << 'lib'
35
+ t.libs << 'test'
36
+ t.pattern = 'test/**/*_test.rb'
37
+ t.verbose = false
38
+ t.warning = false
39
+ end
40
+
41
+
42
+ task :default => :test
data/SECURITY.md ADDED
@@ -0,0 +1,35 @@
1
+ # Security
2
+
3
+ NHS England takes security and the protection of private data extremely seriously. If you believe you have found a vulnerability or other issue which has compromised or could compromise the security of any of our systems and/or private data managed by our systems, please do not hesitate to contact us using the methods outlined below.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Security](#security)
8
+ - [Table of Contents](#table-of-contents)
9
+ - [Reporting a vulnerability](#reporting-a-vulnerability)
10
+ - [Email](#email)
11
+ - [NCSC](#ncsc)
12
+ - [General Security Enquiries](#general-security-enquiries)
13
+
14
+ ## Reporting a vulnerability
15
+
16
+ Please note, email is our preferred method of receiving reports.
17
+
18
+ ### Email
19
+
20
+ If you wish to notify us of a vulnerability via email, please include detailed information on the nature of the vulnerability and any steps required to reproduce it.
21
+
22
+ You can reach us at:
23
+
24
+ - [Brian.Shand@nhs.net](mailto:Brian.Shand@nhs.net)
25
+ - [cybersecurity@nhs.net](mailto:cybersecurity@nhs.net)
26
+
27
+ ### NCSC
28
+
29
+ You can send your report to the National Cyber Security Centre, who will assess your report and pass it on to NHS England if necessary.
30
+
31
+ You can report vulnerabilities here: [https://www.ncsc.gov.uk/information/vulnerability-reporting](https://www.ncsc.gov.uk/information/vulnerability-reporting)
32
+
33
+ ## General Security Enquiries
34
+
35
+ If you have general enquiries regarding our cybersecurity, please reach out to us at [cybersecurity@nhs.net](mailto:cybersecurity@nhs.net)
@@ -0,0 +1,51 @@
1
+ # Make this exist, so autoloaders are happy
2
+ module EsourceConstants; end
3
+
4
+ # Constants that are used in a number of e-source-related programs.
5
+ # The model classes corresponding to 'e_type' strings in e_batch records
6
+ PERMITTED_LISTS_FROM_LOOKUPS = [:laterality] unless defined?(PERMITTED_LISTS_FROM_LOOKUPS)
7
+
8
+ ERROR_INFO = {
9
+ :started => ['Action in progress', 'The action has been started by someone else.'],
10
+ :nobatch => ['Invalid batch', 'Unable to find certain batch in database.'],
11
+ :nocommit => ['Invalid access', "You didn't specify an valid operation."],
12
+ :perform => ['Invalid operation', "You didn't specify an valid operation."],
13
+ :yaml => ['Yaml file', 'Failed to load mapping file.'],
14
+ :wrongdate => ['Invalid date', 'Incorrect file received date.'],
15
+ :nofile => ['Invalid file', 'No file selected.'],
16
+ :nolibrary => ['Missing software', 'Cannot understand this file as no software driver was found.'],
17
+ :wrongformat => ['Invalid file format', 'File format is not accepted by this source.'],
18
+ :fileexisted => ['File existed', 'The file is currently being processed.'],
19
+ :digest => ['File existed', 'File with same digest has been found in repository.'],
20
+ :extinrep => ['File existed', 'File with same name has been found in repository.'],
21
+ :batch => ['Create Batch', 'Failed to create a batch record.'],
22
+ :action_create => ['Create Action', 'Failed to create an action record.'],
23
+ :action_update => ['Update Action', 'Failed to update an action record.'],
24
+ :imported => ['Import', 'This table has already been imported.'],
25
+ :pattern => ['Pattern error', "Data doesn't match the pattern specified in yaml file."],
26
+ :nomapping => ['Unrecognised batch', 'No columns mapping provided for this source.'],
27
+ :nodatereference => ['Invalid date', 'Unspecified date reference.'],
28
+ :baddatereference => ['Invalid date', 'Bad date(s) specified in Date Reference field. '],
29
+ :password => ['Invalid password', "Password doesn't match selected xls file."],
30
+ :blankfile => ['Blank file', 'No data found in specified file.'],
31
+ :header => ['Header line error', 'No header line found or header line conflicts with specified source'],
32
+ :difference => ['Uneven records', 'Some records have at least 3 columns less than others in that batch']
33
+ } unless defined?(ERROR_INFO)
34
+
35
+ DATA_MEDIA = ['Email', 'Floppy Disk', 'CD/DVD', 'Others'] unless defined?(DATA_MEDIA)
36
+
37
+ MATCH_STATUS = {
38
+ :perfect => 'Definite Match', # AMCH_STATUS
39
+ :possible => 'Possible Matches', # AMCH_STATUS
40
+ :none => 'No Match', # AMCH_STATUS
41
+ :alias => 'Alias', # MMCH_STATUS
42
+ :new => 'New', # MMCH_STATUS & DEDU_STATUS
43
+ :manual => 'Manual', # MMCH_STATUS & DEDU_STATUS
44
+ :match => 'Match', # MMCH_STATUS & DEDU_STATUS
45
+ :reset => nil # MMCH_STATUS & DEDU_STATUS, used for "change this decision"
46
+ } unless defined?(MATCH_STATUS)
47
+
48
+ # Batch stats default timeline base
49
+ # If the typical_batch_interval is 1.month,
50
+ # Then the default timeline is 3 * 1.month = 3 months
51
+ BATCH_STATS_TIMELINE_BASE = 3 unless defined?(BATCH_STATS_TIMELINE_BASE)
@@ -0,0 +1,116 @@
1
+ # EAction table.
2
+ class EAction < NdrWorkflow.abstract_model_class
3
+ include NdrWorkflow::EActionProgress
4
+
5
+ self.table_name = 'e_action'
6
+ self.sequence_name = 'e_action_seq'
7
+ self.primary_key = 'e_actionid'
8
+
9
+ belongs_to :e_batch,
10
+ foreign_key: 'e_batchid'
11
+
12
+ # using 'completed' instead of 'finished' because 'finished'
13
+ # should be named 'finished_at'
14
+ scope :completed, -> { where('finished is not null') }
15
+
16
+ before_save :ensure_timestamp_consistency
17
+ validate :ensure_actiontype_not_nil
18
+ validate :ensure_valid_actiontype
19
+ validate :ensure_startedby_not_nil
20
+ validate :ensure_valid_startedby
21
+ validate :ensure_batchid_not_nil
22
+ validate :ensure_batch_not_nil
23
+ validate :ensure_started_not_nil
24
+ validate :ensure_action_not_in_future
25
+ validate :ensure_started_not_after_finished
26
+
27
+ def ensure_timestamp_consistency
28
+ if started_changed? && (e_batch.e_actions.collect(&:finished).compact.max || started) > started then
29
+ raise RuntimeError, "EBatch already has an action with a timestamp greater than #{started} - Possible clock drift or other inconsitency"
30
+ end
31
+ end
32
+
33
+ # rdoc validation
34
+ # e_actiontype cannot be nil.
35
+ def ensure_actiontype_not_nil
36
+ errors.add(:e_action, 'e_actiontype can not be nil.') if e_actiontype.blank?
37
+ end
38
+
39
+ # rdoc validation
40
+ # e_actiontype must exist in our lookup table.
41
+ def ensure_valid_actiontype
42
+ return if ZeActiontype.valid_value?(e_actiontype)
43
+ errors.add(:e_action, 'e_actiontype does not exist.')
44
+ end
45
+
46
+ # rdoc validation
47
+ # startedby user cannot be nil.
48
+ def ensure_startedby_not_nil
49
+ errors.add(:e_action, 'user can not be nil.') if startedby.blank?
50
+ end
51
+
52
+ # rdoc validation
53
+ # startedby user must exist in our lookup table.
54
+ def ensure_valid_startedby
55
+ return if Zuser.valid_value?(startedby)
56
+ errors.add(:e_action, 'user does not exist.')
57
+ end
58
+
59
+ # rdoc validation
60
+ # e_batchid cannot be nil.
61
+ def ensure_batchid_not_nil
62
+ errors.add(:e_action, 'e_batchid cannot be nil.') if e_batchid.blank?
63
+ end
64
+
65
+ # rdoc validation
66
+ # batch cannot be nil.
67
+ def ensure_batch_not_nil
68
+ errors.add(:e_action, 'does not belong to any batch.') if e_batch.nil?
69
+ end
70
+
71
+ # rdoc validation
72
+ # started cannot be nil.
73
+ def ensure_started_not_nil
74
+ if started.blank?
75
+ errors.add(:e_action, 'does not have a start time.')
76
+ return
77
+ end
78
+ end
79
+
80
+ # rdoc validation
81
+ # started or finished cannot be in the future
82
+ def ensure_action_not_in_future
83
+ unless finished.blank? || started.blank?
84
+ if started > Time.now || finished > Time.now
85
+ errors.add(:e_action, 'action can not be in the future.')
86
+ end
87
+ end
88
+ end
89
+
90
+ # rdoc validation
91
+ # started must not be after finished
92
+ def ensure_started_not_after_finished
93
+ unless finished.blank? || started.blank?
94
+ if started > finished
95
+ errors.add(:e_action, 'action can not be finished before it started.')
96
+ end
97
+ end
98
+ end
99
+
100
+ # A quick and simple way to create a single EAction.
101
+ def self.make_and_save!(e_batchid, actiontype, started, finished, userid, status = nil)
102
+ a = create(
103
+ e_batchid: e_batchid,
104
+ e_actiontype: actiontype,
105
+ started: started,
106
+ finished: finished,
107
+ startedby: userid,
108
+ status: status
109
+ )
110
+ if a.errors.to_a.blank?
111
+ a
112
+ else
113
+ raise and return a.errors
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,207 @@
1
+ require 'ndr_support/daterange'
2
+ require 'ndr_support/string/conversions'
3
+
4
+ # The EBatch table
5
+ class EBatch < NdrWorkflow.abstract_model_class
6
+ self.table_name = 'e_batch'
7
+ self.primary_key = 'e_batchid'
8
+ self.sequence_name = 'e_batch_seq'
9
+
10
+ before_save :clean_filename_for_import!
11
+
12
+ # Rails 4 implementation of has_many with scope (order)
13
+ has_many :e_actions, -> { order('e_actionid') }, class_name: 'EAction',
14
+ foreign_key: 'e_batchid',
15
+ dependent: :destroy do
16
+ # starts an action
17
+ def start(actiontype, userid)
18
+ proxy_association.owner.assign_to(userid)
19
+
20
+ attributes = { started: Time.now, startedby: userid, comments: nil }
21
+
22
+ if actiontype == 'CREA'
23
+ create! attributes.merge(e_actiontype: actiontype)
24
+ else
25
+ find_or_create_by!(e_actiontype: actiontype) do |record|
26
+ record.assign_attributes(attributes)
27
+ end
28
+ end
29
+ end
30
+
31
+ # restart an action
32
+ def restart(actiontype, userid)
33
+ action = where(e_actiontype: actiontype).order('e_actionid desc').first!
34
+ proxy_association.owner.assign_to(userid)
35
+ comments = "#{action.comments} Action was started by #{action.startedby} on #{action.started};"
36
+ action.update!(started: Time.now, startedby: userid, comments: comments)
37
+ action
38
+ end
39
+
40
+ # updating the progress of an action could go here?
41
+ # finishes an action
42
+ def finish(actiontype, comments = nil, status = nil)
43
+ action = where(e_actiontype: actiontype).order('e_actionid desc').first!
44
+ action.update!(finished: Time.now, comments: comments, status: status)
45
+
46
+ begin
47
+ e_batch = proxy_association.owner
48
+ e_batch.unlock
49
+ rescue ActiveRecord::StaleObjectError
50
+ Rails.logger.warn("NdrWorkflow: failed to unlock stale batch #{e_batch.id}!")
51
+ end
52
+ end
53
+
54
+ # manual tasks are frequently completed by symultaneously
55
+ # starting and finishing an action
56
+ def start_and_finish(actiontype, userid, comments = nil, status = nil)
57
+ start(actiontype, userid)
58
+ finish(actiontype, comments, status)
59
+ end
60
+ end
61
+
62
+ validate :ensure_provider_not_blank
63
+ validate :ensure_registryid_not_blank
64
+ validate :ensure_valid_type
65
+ validate :ensure_valid_provider
66
+ validate :ensure_valid_registryid
67
+ # validate :ensure_datereference_present_once_required
68
+ # --
69
+ # TODO: Replace the code below with x_daterange :date_reference or x_daterange :datereference
70
+ # Either updating calls to datereference with calls to date_reference or rename table columns
71
+ # ++
72
+ composed_of :datereference,
73
+ class_name: 'Daterange',
74
+ mapping:
75
+ [
76
+ [:date_reference1, :date1],
77
+ [:date_reference2, :date2]
78
+ ]
79
+
80
+ # rdoc validation
81
+ # provider cannot be nil.
82
+ def ensure_provider_not_blank
83
+ errors.add(:provider, 'cannot be blank.') if provider.blank?
84
+ end
85
+
86
+ # rdoc validation
87
+ # registryid cannot be nil.
88
+ def ensure_registryid_not_blank
89
+ errors.add(:registry, 'cannot be blank.') if registryid.blank?
90
+ end
91
+
92
+ # rdoc validation
93
+ # e_type must exist in our lookup table.
94
+ def ensure_valid_type
95
+ return if ZeType.valid_value?(e_type)
96
+ errors.add(:e_type, 'does not exist.')
97
+ end
98
+
99
+ # rdoc validation
100
+ # provider must exist in our lookup table.
101
+ def ensure_valid_provider
102
+ return if Zprovider.valid_value?(provider)
103
+ errors.add(:provider, 'does not exist.')
104
+ end
105
+
106
+ # rdoc validation
107
+ # registryid must exist in our lookup table.
108
+ def ensure_valid_registryid
109
+ return if Zprovider.valid_value?(registryid)
110
+ errors.add(:registry, 'does not exist.')
111
+ end
112
+
113
+ def provider_desc
114
+ lookup = Zprovider.find_by_zproviderid(provider)
115
+ if lookup
116
+ description = lookup.shortdesc
117
+ return (description.upcase == description ? description.titlecase : description)
118
+ else
119
+ return provider
120
+ end
121
+ end
122
+
123
+ def assign_to(user)
124
+ update_attribute(:inprogress, user)
125
+ end
126
+
127
+ def unlock
128
+ assign_to(nil)
129
+ end
130
+
131
+ def last_finished_action
132
+ e_actions.completed.except(:order).order('finished desc, e_actionid desc').first
133
+ end
134
+
135
+
136
+
137
+
138
+ # Returns the actiontype that is available for this batch (if known)
139
+ # Method is updated on 2 Mar, 2009: each action will only have one proceeded action
140
+ # in Unified Esources. (Removed old PAS's RECE and MANU actions)
141
+ def next_available_action
142
+ begin
143
+ # TODO: Output of this sometimes disagrees with ROVBATCHACTION, resulting
144
+ # in inconsistent orderings, and batch automator failures. At very
145
+ # least, the batch automator should object more vigorously if the
146
+ # next_available_action disagrees with the ROVBATCHACTION output.
147
+ if la = last_finished_action
148
+ EWorkflow.next_action(e_type, la.e_actiontype)
149
+ end
150
+ end
151
+ end
152
+
153
+ # Is the batch past `target_action' in the workflow?
154
+ def done_action?(target_action)
155
+ return false if last_finished_action.nil? # Haven't start any e_actions
156
+
157
+ at_new = false # Reached the start of the workflow?
158
+ done_it = false # Have we done `target_action'?
159
+ # Where are we in the workflow?
160
+ action = next_available_action || last_finished_action.e_actiontype
161
+
162
+ # Traverse the workflow in reverse from the current action to see
163
+ # if there is a prior `target_action` step for this batch type:
164
+ begin
165
+ step = EWorkflow.find_by_e_type_and_next_e_actiontype(e_type, action)
166
+
167
+ at_new = step.nil?
168
+ action = step.try(:last_e_actiontype)
169
+ done_it = (target_action == action)
170
+ end until (done_it || at_new)
171
+
172
+ done_it
173
+ end
174
+
175
+
176
+
177
+ def in_progress?
178
+ !inprogress.nil?
179
+ end
180
+
181
+ def belongs_to?(user)
182
+ inprogress == user.to_s
183
+ end
184
+ def free?(user)
185
+ !in_progress? || belongs_to?(user)
186
+ end
187
+
188
+
189
+
190
+
191
+
192
+ # Check if a batch is in progress.
193
+ # Return true if the batch is free or was launched by the current user
194
+ def self.free?(batchid, user)
195
+ find(batchid).free?(user)
196
+ end
197
+
198
+ def clean_filename_for_import!
199
+ unless original_filename.blank?
200
+ # The file path may be pasted in from the Windows command prompt,
201
+ # so swap back slashes for forward slashes
202
+ original_filename.gsub!('\\', '/')
203
+ original_filename.sub!(/\Asvn[\/]/i, 'svn/') # TODO: required?
204
+ original_filename.strip!
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,23 @@
1
+ # EWorkflow describes transitions between E_ACTIONS for the different E_BATCH.E_TYPES.
2
+ class EWorkflow < NdrWorkflow.abstract_model_class
3
+ self.table_name = 'e_workflow'
4
+ self.sequence_name = 'e_workflow_seq'
5
+ self.primary_key = 'e_workflowid'
6
+
7
+ class << self
8
+ # Returns the name of the next step on the waterfall. Caches all results.
9
+ # EWorkflow.next_action('COSD_XML', 'AIMP')
10
+ # #=> "PRECHECK"
11
+ # EWorkflow.next_action('COSD_XML', 'GENA')
12
+ # #=> nil
13
+
14
+ def next_action(e_type, last_e_actiontype)
15
+ @next_actions ||= Hash.new do |hash, (type, last_action)|
16
+ scope = { e_type: type, last_e_actiontype: last_action }
17
+ hash[[type, last_action]] = where(scope).pluck(:next_e_actiontype).first
18
+ end
19
+
20
+ @next_actions[[e_type, last_e_actiontype]]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ class ZeActiontype < ZlookupCached
2
+ self.table_name = 'ze_actiontype'
3
+ self.primary_key = 'ze_actiontypeid'
4
+ # This method caches the ZeActiontype ids and their descriptions.
5
+ # With the new class loader of Rails 2.2 and later, we cannot prepopulate a
6
+ # constant with this data (as we did in Rails 1.2 and 2.1). Instead, we cache
7
+ # the result in the model.
8
+ def self.type_descriptions
9
+ @@type_descriptions ||= all.inject({}) do |hash, l|
10
+ hash.merge!(l.id.to_sym => l.shortdesc)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ # ZeType
2
+ class ZeType < ZlookupCached
3
+ self.table_name = 'ze_type'
4
+ self.primary_key = 'ze_typeid'
5
+
6
+ end
@@ -0,0 +1,9 @@
1
+ # Zlookup pattern copied from ERA to allow easy integration of Engine with ERA
2
+ class Zlookup < NdrWorkflow.abstract_model_class
3
+ self.abstract_class = true
4
+ def self.valid_value?(key, strict = false)
5
+ return true if key.blank?
6
+ scope = strict ? current : self
7
+ scope.where(primary_key => key).count == 1
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ # Zlookup pattern copied from ERA to allow easy integration of Engine with ERA
2
+ class ZlookupCached < Zlookup
3
+ # This class doesn't have a table, subclasses will have their own
4
+ self.abstract_class = true
5
+ end
@@ -0,0 +1,9 @@
1
+ # Zprovider represents providers of health services, e.g. hospitals, GP
2
+ # see modell definition in ERA for standard Zprovider infomration / methods,
3
+ # it was thought this was too far from workflow to be extracted - can always
4
+ # be added back later
5
+ class Zprovider < Zlookup
6
+ self.table_name = 'zprovider'
7
+ self.primary_key = 'zproviderid'
8
+
9
+ end
@@ -0,0 +1,5 @@
1
+ # Standard Zuser class def, originally from ERA
2
+ class Zuser < ZlookupCached
3
+ self.table_name = 'zuser'
4
+ self.primary_key = 'zuserid'
5
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,25 @@
1
+ # EBatch
2
+ class CreateEBatch < NdrWorkflow.migration_class
3
+ def change
4
+ create_table :e_batch, id: false do |t|
5
+ t.primary_key :e_batchid, limit: 8
6
+ t.string :e_type, limit: 255
7
+ t.string :provider, limit: 255
8
+ t.string :media, limit: 255
9
+ t.string :original_filename, limit: 255
10
+ t.string :cleaned_filename, limit: 255
11
+ t.integer :numberofrecords, limit: 8 # bigint
12
+ t.datetime :date_reference1
13
+ t.datetime :date_reference2
14
+ t.integer :e_batchid_traced, limit: 8
15
+ t.string :comments, limit: 255
16
+ t.string :digest, limit: 40
17
+ t.integer :lock_version, limit: 8, default: 0, null: false
18
+ t.string :inprogress, limit: 50
19
+ t.string :registryid, limit: 255
20
+ t.integer :on_hold, limit: 2 # smallint
21
+ end
22
+
23
+ add_index :e_batch, [:registryid, :e_type, :provider]
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ # EAction migration
2
+ class CreateEAction < NdrWorkflow.migration_class
3
+ def change
4
+ create_table :e_action, id: false do |t|
5
+ t.primary_key :e_actionid, limit: 8
6
+ t.integer :e_batchid, limit: 8, index: true
7
+ t.string :e_actiontype, limit: 255
8
+ t.datetime :started
9
+ t.string :startedby, limit: 255
10
+ t.datetime :finished
11
+ t.string :comments, limit: 4000
12
+ t.string :status, limit: 255, index: true
13
+ t.integer :lock_version, limit: 8, default: 0, null: false
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # EWorflow
2
+ class CreateEWorkflow < NdrWorkflow.migration_class
3
+ def change
4
+ create_table :e_workflow, id: false do |t|
5
+ t.primary_key :e_workflowid, limit: 8
6
+ t.string :e_type, limit: 255
7
+ t.string :provider, limit: 255
8
+ t.string :last_e_actiontype, limit: 255
9
+ t.string :next_e_actiontype, limit: 255
10
+ t.string :comments, limit: 255
11
+ t.integer :sort, limit: 2 # smallint
12
+ end
13
+
14
+ add_index :e_workflow, [:e_type, :last_e_actiontype, :next_e_actiontype],
15
+ :name => 'e_workflow_etype_leat_neat_ix'
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ # ZUser
2
+ class CreateZuser < NdrWorkflow.migration_class
3
+ def change
4
+ create_table :zuser, id: false do |t|
5
+ t.string :zuserid, limit: 255, primary_key: true, default: 1, null: false
6
+ t.string :shortdesc, limit: 64
7
+ t.string :description, limit: 2000
8
+ t.string :exportid, limit: 64
9
+ t.datetime :startdate
10
+ t.datetime :enddate
11
+ t.integer :sort, limit: 8 # bigint
12
+ t.string :registryid, limit: 5
13
+ t.string :qa_supervisorid, limit: 255
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ # ZEActionType
2
+ class CreateZeActiontype < NdrWorkflow.migration_class
3
+ def change
4
+ create_table :ze_actiontype, id: false do |t|
5
+ t.string :ze_actiontypeid, limit: 255, primary_key: true, default: 1, null: false
6
+ t.string :shortdesc, limit: 64
7
+ t.string :description, limit: 255
8
+ t.datetime :startdate
9
+ t.datetime :enddate
10
+ t.integer :sort, limit: 8 # bigint
11
+ t.string :comments, limit: 255
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # ZEType
2
+ class CreateZeType < NdrWorkflow.migration_class
3
+ def change
4
+ create_table :ze_type, id: false do |t|
5
+ t.string :ze_typeid, limit: 255, primary_key: true, default: 1, null: false
6
+ t.string :shortdesc, limit: 64
7
+ t.string :description, limit: 255
8
+ t.datetime :startdate
9
+ t.datetime :enddate
10
+ t.integer :sort, limit: 8 # bigint
11
+ t.string :comments, limit: 255
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ # ZProvider
2
+ class CreateZProvider < NdrWorkflow.migration_class
3
+ def change
4
+ create_table :zprovider, id: false do |t|
5
+ t.string :zproviderid, limit: 255, primary_key: true, default: 1, null: false
6
+ t.string :shortdesc, limit: 128
7
+ t.string :description, limit: 2000
8
+ t.string :exportid, limit: 64
9
+ t.datetime :startdate
10
+ t.datetime :enddate
11
+ t.integer :sort, limit: 8 # bigint
12
+ t.string :role, limit: 1
13
+ t.integer :local_hospital, limit: 2, default: 0, null: false # smallint
14
+ t.integer :breast_screening_unit, limit: 2, default: 0, null: false
15
+ t.integer :historical, limit: 2, default: 0, null: false
16
+ t.string :lpi_providercode, limit: 255
17
+ t.string :zpostcodeid, limit: 255
18
+ # Provider has a linear particle accelerator
19
+ t.integer :linac, limit: 2, default: 0, null: false
20
+ t.string :analysisid, limit: 255
21
+ t.integer :nacscode, limit: 2
22
+ t.string :nacs5id, limit: 5
23
+ t.string :successorid, limit: 255
24
+ t.string :local_registryid, limit: 5
25
+ t.string :source, limit: 255
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ # Add FK constraints to tables
2
+ class CreateConstraint < NdrWorkflow.migration_class
3
+ def change
4
+ unless respond_to?(:add_foreign_key)
5
+ warn "CreateConstraint: `add_foreign_key` is not supported on #{Rails.version}, skipping."
6
+ return
7
+ end
8
+
9
+ add_foreign_key :e_action, :e_batch, column: :e_batchid, primary_key: 'e_batchid'
10
+ add_foreign_key :e_action, :ze_actiontype, column: :e_actiontype, primary_key: 'ze_actiontypeid'
11
+
12
+ add_foreign_key :e_batch, :ze_type, column: :e_type, primary_key: 'ze_typeid'
13
+ add_foreign_key :e_batch, :zprovider, column: :provider, primary_key: 'zproviderid'
14
+ add_foreign_key :e_batch, :zprovider, column: :registryid, primary_key: 'zproviderid'
15
+
16
+ add_foreign_key :e_workflow, :ze_actiontype, column: :last_e_actiontype, primary_key: 'ze_actiontypeid'
17
+ add_foreign_key :e_workflow, :ze_actiontype, column: :next_e_actiontype, primary_key: 'ze_actiontypeid'
18
+ add_foreign_key :e_workflow, :ze_type, column: :e_type, primary_key: 'ze_typeid'
19
+ add_foreign_key :e_workflow, :zprovider, column: :provider, primary_key: 'zproviderid'
20
+
21
+ add_foreign_key :zuser, :zuser, column: :qa_supervisorid, primary_key: 'zuserid'
22
+ end
23
+ end
@@ -0,0 +1,121 @@
1
+ require 'ndr_support/ourdate'
2
+ require 'ndr_support/ourtime'
3
+
4
+ module NdrWorkflow
5
+ # Logic to be shared by EAction and QueuedJob:
6
+ module EActionProgress
7
+ # Classes allowed inside YAML-serialized comments
8
+ COMMENT_CLASSES = [Ourdate, DateTime, Time, Date, ActiveSupport::TimeWithZone,
9
+ ActiveSupport::TimeZone, ActiveSupport::HashWithIndifferentAccess,
10
+ Symbol].freeze
11
+
12
+ # Set the progress percentage of an activity. Progress will range from
13
+ # min_percent to max_percent as i varies from 0 to total.
14
+ # Sample usage:
15
+ # total = items.length
16
+ # items.each_with_index do |item, i|
17
+ # do_something
18
+ # action.update_percentage(i+1, total)
19
+ # end
20
+ def update_percentage(i, total, min_percent = 0, max_percent = 100)
21
+ @last_updated_at ||= progress['updated_at']
22
+ @last_percentage ||= progress['percentage']
23
+
24
+ current_percentage = total == 0 ? 0 : (min_percent + i * (max_percent - min_percent) / total).to_i
25
+ if @last_updated_at.nil? || @last_percentage.nil? ||
26
+ (current_percentage < @last_percentage) || # Always show negative changes
27
+ (current_percentage > @last_percentage && @last_updated_at < 5.minutes.ago) || # Rate limit positive changes to once every 5 minutes...
28
+ (current_percentage.divmod(5).first > @last_percentage.divmod(5).first && @last_updated_at < 1.minute.ago) # ...but allow >5% changes every 1 minute
29
+ @last_updated_at = Time.now
30
+ @last_percentage = current_percentage
31
+ # yaml = YAML.dump( progress.merge({'percentage' => @last_percentage, 'updated_at' => @last_updated_at}))
32
+ yaml = YAML.dump({'percentage' => @last_percentage, 'updated_at' => @last_updated_at})
33
+ begin
34
+ raise(ActiveRecord::StatementInvalid, <<-DESC) if Rails.env.test?
35
+ libutils is not reliably available in the test schema, so we
36
+ test the fallback behaviour. This ensures it is the fallback
37
+ behaviour that we get, even if libutils is present.
38
+ DESC
39
+
40
+ # Try to update the progress in an anonymous transaction, providing feedback when we
41
+ # wouldn't otherwise get any (i.e. when the update is in a transaction)
42
+ # TODO: Test for the corner case where the e_action is updateed within a transaction and
43
+ # where this is blocked, causing it to fail over to a standard (transaction based) update.
44
+ # The SQL statement is sanitized for security.
45
+ # SECURE: BNS 2012-10-09
46
+ sanitized_sql = self.class.send(:sanitize_sql_array,[
47
+ "BEGIN libutils.set_e_action_comments(?, ?); END;", self.e_actionid, yaml
48
+ ])
49
+ ActiveRecord::Base.connection.execute(sanitized_sql)
50
+ rescue ActiveRecord::StatementInvalid => e
51
+ # Update the old way, using standard update
52
+ Rails.logger.warn("EAction#update_percentage failed to use libutils.set_e_action_comments (#{e.message})")
53
+ puts "EAction#update_percentage failed to use libutils.set_e_action_comments (#{e.message})" unless Rails.env.test?
54
+
55
+ # The EAction could be stale as a result of PREP. Eg ChildhoodPreallocator
56
+ reload unless changed?
57
+ update(comments: yaml)
58
+ end
59
+ end
60
+ end
61
+
62
+ def progress
63
+ if comments
64
+ deserialised_comments = YAML.safe_load(comments, permitted_classes: COMMENT_CLASSES.freeze,
65
+ aliases: true)
66
+ case deserialised_comments
67
+ when Integer
68
+ # Legacy number
69
+ return {'percentage' => comments.to_i}
70
+ when String
71
+ return {'message' => deserialised_comments}
72
+ when Hash
73
+ return deserialised_comments
74
+ end
75
+ end
76
+ return {}
77
+ end
78
+
79
+ # This method takes an exception and updates the e_action so that it can be
80
+ # seen easily through the UI.
81
+ def log_error(error, extramessage = nil)
82
+ self.reload unless new_record?
83
+
84
+ error_info = {
85
+ 'error_message' => [error.message, extramessage].compact.join(': '),
86
+ 'updated_at' => Time.now
87
+ }
88
+
89
+ begin
90
+ if defined?(NdrError)
91
+ fingerprint, log = NdrError.log(error, { :user_id => self.startedby, :user_roles => '(BATCH)' }, nil)
92
+ error_info['error_fingerprint'] = fingerprint.id if fingerprint
93
+ else
94
+ logger.warn("Failed to log EAction exception in the DB! (#{$!.to_s})")
95
+ end
96
+ rescue
97
+ logger.warn("Failed to log EAction exception in the DB! (#{$!.to_s})")
98
+ end
99
+
100
+ data = progress.merge(error_info)
101
+ max_len = (EAction.columns_hash['comments'].sql_type.match(/([0-9]*)\)/).nil? ? 0 : EAction.columns_hash['comments'].sql_type.match(/([0-9]*)\)/)[1]).to_i
102
+ # if Era.db_oracle?
103
+ # max_len = EAction.columns_hash['comments'].sql_type.match(/VARCHAR2\(([0-9]*)\)/)[1].to_i
104
+ # elsif Era.db_postgres?
105
+ # max_len = EAction.columns_hash['comments'].sql_type.match(/character varying\(([0-9]*)\)/)[1].to_i
106
+ # end
107
+ if YAML.dump(data).length > max_len
108
+ # Horribly inefficient; preserve as much information as possible.
109
+ len2 = max_len - YAML.dump(data.merge('error_message' => nil)).size
110
+ while YAML.dump(data).length > max_len do
111
+ len2 -= 1
112
+ data['error_message'] = [error.message, extramessage].compact.collect{|s|
113
+ "#{s.to_s[0..len2]}#{'...' if len2 < s.to_s.length}"
114
+ }.join(': ')
115
+ end
116
+ logger.warn("EAction#log_error: Warning: Truncated error message: #{[error.message, extramessage].inspect} for EAction(#{id})")
117
+ end
118
+ update(comments: YAML.dump(data))
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,5 @@
1
+ module NdrWorkflow
2
+ # NdrWorkflow is an engine; add config here
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NdrWorkflow
4
+ VERSION = '1.2.5'
5
+ end
@@ -0,0 +1,19 @@
1
+ require 'ndr_workflow/e_action_progress'
2
+ require 'ndr_workflow/engine'
3
+ require 'ndr_workflow/version'
4
+
5
+ # NdrWorkflow
6
+ module NdrWorkflow
7
+ # Allows the host to define a parent for all models.
8
+ # Defaults to ActiveRecord::Base.
9
+ mattr_accessor :abstract_model_class
10
+ self.abstract_model_class = ActiveRecord::Base
11
+
12
+ # Rails 5 uses versioned migrations (required as of 5.1). This helper method returns an
13
+ # appropriate migration superclass for the Rails version of the host application. Note that the
14
+ # 4.2 compatability layer is targetted since all the bundled migrations were written prior to
15
+ # Rails 5.
16
+ def self.migration_class
17
+ Rails::VERSION::MAJOR < 5 ? ActiveRecord::Migration : ActiveRecord::Migration[4.2]
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :ndr_workflow_e do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ndr_workflow
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.5
5
+ platform: ruby
6
+ authors:
7
+ - NCRS Development team
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.1'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '8.2'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ version: '7.1'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '8.2'
32
+ - !ruby/object:Gem::Dependency
33
+ name: ndr_support
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '5.0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - "~>"
44
+ - !ruby/object:Gem::Version
45
+ version: '5.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ type: :development
54
+ prerelease: false
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ - !ruby/object:Gem::Dependency
61
+ name: ndr_dev_support
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '6.0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '6.0'
74
+ - !ruby/object:Gem::Dependency
75
+ name: test-unit
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ type: :development
82
+ prerelease: false
83
+ version_requirements: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ - !ruby/object:Gem::Dependency
89
+ name: pg
90
+ requirement: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ type: :development
96
+ prerelease: false
97
+ version_requirements: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ description: Core EWorkflow, Ebatch and EAction models mapped from ERA to postgres
103
+ implem.
104
+ email: []
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - CHANGELOG.md
110
+ - MIT-LICENSE
111
+ - README.rdoc
112
+ - Rakefile
113
+ - SECURITY.md
114
+ - app/constants/esource_constants.rb
115
+ - app/models/e_action.rb
116
+ - app/models/e_batch.rb
117
+ - app/models/e_workflow.rb
118
+ - app/models/ze_actiontype.rb
119
+ - app/models/ze_type.rb
120
+ - app/models/zlookup.rb
121
+ - app/models/zlookup_cached.rb
122
+ - app/models/zprovider.rb
123
+ - app/models/zuser.rb
124
+ - config/routes.rb
125
+ - db/migrate/20160707141753_create_e_batch.rb
126
+ - db/migrate/20160707141754_create_e_action.rb
127
+ - db/migrate/20160707141755_create_e_workflow.rb
128
+ - db/migrate/20160707141756_create_zuser.rb
129
+ - db/migrate/20160707141757_create_ze_actiontype.rb
130
+ - db/migrate/20160707141758_create_ze_type.rb
131
+ - db/migrate/20160707141759_create_z_provider.rb
132
+ - db/migrate/20160707141800_create_constraint.rb
133
+ - lib/ndr_workflow.rb
134
+ - lib/ndr_workflow/e_action_progress.rb
135
+ - lib/ndr_workflow/engine.rb
136
+ - lib/ndr_workflow/version.rb
137
+ - lib/tasks/ndr_workflow_tasks.rake
138
+ licenses: []
139
+ metadata: {}
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '3.3'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubygems_version: 3.6.9
155
+ specification_version: 4
156
+ summary: Base workflow schema
157
+ test_files: []