xdt 1.0.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.
- data/.DS_Store +0 -0
- data/History.txt +6 -0
- data/Manifest.txt +23 -0
- data/README.txt +48 -0
- data/Rakefile +26 -0
- data/bin/gdt2http +5 -0
- data/lib/gdt/field_definitions.rb +35 -0
- data/lib/gdt/field_handling.rb +81 -0
- data/lib/gdt/parser.rb +36 -0
- data/lib/gdt2http.rb +104 -0
- data/lib/gdt_interface.rb +20 -0
- data/lib/xdt.rb +8 -0
- data/lib/xdt/ldt.rb +80 -0
- data/lib/xdt/markup.rb +7 -0
- data/lib/xdt/xdt_fields.rb +75 -0
- data/lib/xdt/xdt_sections.rb +63 -0
- data/spec/examples/BARCQPCN.001 +14 -0
- data/spec/gdt_field_definitions_spec.rb +106 -0
- data/spec/gdt_parser_spec.rb +50 -0
- data/spec/gdt_spec.rb +15 -0
- data/spec/lg_report_spec.rb +38 -0
- data/spec/xdt_spec.rb +23 -0
- data/xdt.gemspec +34 -0
- metadata +108 -0
data/.DS_Store
ADDED
Binary file
|
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
.DS_Store
|
2
|
+
History.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
bin/gdt2http
|
7
|
+
lib/gdt/field_definitions.rb
|
8
|
+
lib/gdt/field_handling.rb
|
9
|
+
lib/gdt/parser.rb
|
10
|
+
lib/gdt2http.rb
|
11
|
+
lib/gdt_interface.rb
|
12
|
+
lib/xdt.rb
|
13
|
+
lib/xdt/ldt.rb
|
14
|
+
lib/xdt/markup.rb
|
15
|
+
lib/xdt/xdt_fields.rb
|
16
|
+
lib/xdt/xdt_sections.rb
|
17
|
+
spec/examples/BARCQPCN.001
|
18
|
+
spec/gdt_field_definitions_spec.rb
|
19
|
+
spec/gdt_parser_spec.rb
|
20
|
+
spec/gdt_spec.rb
|
21
|
+
spec/lg_report_spec.rb
|
22
|
+
spec/xdt_spec.rb
|
23
|
+
xdt.gemspec
|
data/README.txt
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= xdt
|
2
|
+
|
3
|
+
* http://levinalex.net/src/xdt
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
xDT is a library that reads and writes LDT, GDT and BDT data.
|
8
|
+
|
9
|
+
== FEATURES/PROBLEMS:
|
10
|
+
|
11
|
+
* none
|
12
|
+
|
13
|
+
== SYNOPSIS:
|
14
|
+
|
15
|
+
no code yet
|
16
|
+
|
17
|
+
== REQUIREMENTS:
|
18
|
+
|
19
|
+
* none
|
20
|
+
|
21
|
+
== INSTALL:
|
22
|
+
|
23
|
+
* sudo gem install levinalex-xdt
|
24
|
+
|
25
|
+
== LICENSE:
|
26
|
+
|
27
|
+
(The MIT License)
|
28
|
+
|
29
|
+
Copyright (c) 2008 Levin Alexander
|
30
|
+
|
31
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
32
|
+
a copy of this software and associated documentation files (the
|
33
|
+
'Software'), to deal in the Software without restriction, including
|
34
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
35
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
36
|
+
permit persons to whom the Software is furnished to do so, subject to
|
37
|
+
the following conditions:
|
38
|
+
|
39
|
+
The above copyright notice and this permission notice shall be
|
40
|
+
included in all copies or substantial portions of the Software.
|
41
|
+
|
42
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
43
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
44
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
45
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
46
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
47
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
48
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$LOAD_PATH.unshift './lib'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require 'xdt'
|
6
|
+
require 'spec/rake/spectask'
|
7
|
+
|
8
|
+
Hoe.new('xdt', Xdt::VERSION) do |p|
|
9
|
+
p.developer('Levin Alexander', 'mail@levinalex.net')
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
Rake.application.instance_eval { @tasks["test"] = nil }
|
14
|
+
|
15
|
+
Spec::Rake::SpecTask.new do |t|
|
16
|
+
t.warning = true
|
17
|
+
t.spec_opts = %w(-c -f specdoc)
|
18
|
+
end
|
19
|
+
task :test => :spec
|
20
|
+
|
21
|
+
|
22
|
+
task :cultivate do
|
23
|
+
system "touch Manifest.txt; rake check_manifest | grep -v \"(in \" | patch"
|
24
|
+
system "rake debug_gem | grep -v \"(in \" > `basename \\`pwd\\``.gemspec"
|
25
|
+
end
|
26
|
+
|
data/bin/gdt2http
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'date'
|
2
|
+
require File.join(File.dirname(__FILE__), 'field_handling.rb')
|
3
|
+
|
4
|
+
module Gdt
|
5
|
+
class GdtFields < AbstractField
|
6
|
+
|
7
|
+
field 3000, :nr, "Patientennummer/Patientenkennung", (0..10), :alnum
|
8
|
+
field 3100, :name_prefix, "Namenszusatz/Vorsatzwort des Patienten", (0..15)
|
9
|
+
field 3101, :last_name, "Name des Patienten", (0..28)
|
10
|
+
field 3102, :first_name, "Vorname des Patienten", (0..28)
|
11
|
+
field 3103, :birthday, "Geburtsdatum des Patienten", 8, :datum
|
12
|
+
field 3104, :title, "Titel des Patienten", (0..15)
|
13
|
+
field 3105, :insurance_id, "Versichertennummer des Patienten", (0..12)
|
14
|
+
field 3106, :patient_postal_code_city, "Wohnort des Patienten", (0..30)
|
15
|
+
field 3107, :patient_street, "Strasse des Patienten", (0..28)
|
16
|
+
field 3108, :insurance_type, "Versichertenart MFR", 1, :num
|
17
|
+
field 3110, :sex, "Geschlecht des Patienten", 1, :num
|
18
|
+
# ...
|
19
|
+
|
20
|
+
field 8000, :gdt_type, "Satzidentifikation", 4
|
21
|
+
field 8100, :gdt_length, "Satzlänge", 5, :num
|
22
|
+
field 8315, :receiver_id, "GDT-ID des Empfängers", (0..8) # violates spec, should be "8"
|
23
|
+
field 8316, :sender_id, "GDT-ID des Senders", (0..8) # violates the spec, should be "8"
|
24
|
+
# ...
|
25
|
+
|
26
|
+
field 9218, :gdt_version, "Version GDT", 5
|
27
|
+
|
28
|
+
|
29
|
+
# bogus fields to suppress errors
|
30
|
+
field 9901, :unknown, "unknown", (0..60), :alnum
|
31
|
+
field 8402, :unknown, "unknown", (0..60), :alnum
|
32
|
+
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'iconv'
|
3
|
+
|
4
|
+
module Gdt
|
5
|
+
|
6
|
+
class GdtField < Struct.new(:name, :description, :length, :type, :rules)
|
7
|
+
TYPES = {
|
8
|
+
:num => lambda { |v| v.to_i },
|
9
|
+
:alnum => lambda { |v| ::Iconv.new("UTF-8","CP850").iconv(v) },
|
10
|
+
:datum => lambda { |v| ::Date.new(*v.scan(/(..)(..)(....)/)[0].map {|x| x.to_i }.reverse) rescue nil }
|
11
|
+
}
|
12
|
+
|
13
|
+
def type=(value)
|
14
|
+
raise ArgumentError, "unrecognized data type '#{value.inspect}'" unless TYPES.include?(value)
|
15
|
+
@type = value
|
16
|
+
end
|
17
|
+
def type
|
18
|
+
@type || :alnum
|
19
|
+
end
|
20
|
+
|
21
|
+
def length=(value)
|
22
|
+
if value
|
23
|
+
@length = (Range === value) ? value : Range.new(value,value)
|
24
|
+
else
|
25
|
+
@length = nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def verify_and_convert(value)
|
30
|
+
# check length
|
31
|
+
#
|
32
|
+
if @length
|
33
|
+
message = "the field #{name.inspect} does not have the correct length, expected (#{@length.inspect})"
|
34
|
+
# ignore length checks for now
|
35
|
+
# raise ArgumentError, message unless @length.include?(value.length)
|
36
|
+
end
|
37
|
+
TYPES[self.type].call(value)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class AbstractField
|
43
|
+
|
44
|
+
def self.field(gdt_id, name, description, length, type = :alnum, rules = nil)
|
45
|
+
field = (fields[gdt_id] ||= GdtField.new)
|
46
|
+
|
47
|
+
field.name = name
|
48
|
+
field.type = type
|
49
|
+
field.length = length
|
50
|
+
|
51
|
+
define_method name do
|
52
|
+
values[name]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.lookup(field_id)
|
57
|
+
@gdt_fields[field_id].name
|
58
|
+
end
|
59
|
+
def self.fields
|
60
|
+
@gdt_fields ||= Hash.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def values
|
64
|
+
@values ||= Hash.new
|
65
|
+
end
|
66
|
+
|
67
|
+
def set_field(gdt_id, value)
|
68
|
+
field = self.class.fields[gdt_id]
|
69
|
+
raise ArgumentError, "undefined field '#{gdt_id}'" unless field
|
70
|
+
|
71
|
+
values[field.name] = field.verify_and_convert(value)
|
72
|
+
end
|
73
|
+
|
74
|
+
def initialize(gdt_hash)
|
75
|
+
gdt_hash.each { |gdt_id, value|
|
76
|
+
set_field(gdt_id, value)
|
77
|
+
}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
data/lib/gdt/parser.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
module Gdt
|
3
|
+
|
4
|
+
class ParseError < ArgumentError
|
5
|
+
end
|
6
|
+
|
7
|
+
class Parser
|
8
|
+
def self.parse(string)
|
9
|
+
# canonical line endings
|
10
|
+
string.gsub!("\r\n","\n")
|
11
|
+
|
12
|
+
string.split(/\n/).
|
13
|
+
# ignore empty lines and lines consisting only of whitespace
|
14
|
+
delete_if { |line| line =~ /^\s*$/ }.
|
15
|
+
inject({}) do |h, line|
|
16
|
+
# parse the line into tokens
|
17
|
+
#
|
18
|
+
length, type, data = line.scan(/(\d{3})(\d{4})(.*)/).first
|
19
|
+
|
20
|
+
raise ParseError, "line does not match expected GDT format: '#{line}'" unless length && type && data
|
21
|
+
|
22
|
+
length = length.to_i
|
23
|
+
type = type.to_i
|
24
|
+
|
25
|
+
# 3 bytes length + 4 bytes record id + data length (bytes) + CR LF
|
26
|
+
expected_length = 3 + 4 + data.length + 2
|
27
|
+
|
28
|
+
raise ParseError, "wrong length in GDT data: '#{line}'" unless length == expected_length
|
29
|
+
|
30
|
+
h[type] = data
|
31
|
+
h
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/gdt2http.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'net/http'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
require File.join(File.dirname(__FILE__),'gdt_interface.rb')
|
8
|
+
|
9
|
+
module Gdt
|
10
|
+
ConfigFilename = ".gdt2http"
|
11
|
+
ConfigFile = File.join(ENV['HOME'] || ENV['APPDATA'], ConfigFilename)
|
12
|
+
|
13
|
+
# defaults are used when they are not overwritten in a config file
|
14
|
+
# or with command line options
|
15
|
+
#
|
16
|
+
DefaultConfig = {
|
17
|
+
:files => ["**/*.GDT"],
|
18
|
+
:endpoint => "http://localhost:3000/gdt",
|
19
|
+
:delete_files => true
|
20
|
+
}
|
21
|
+
|
22
|
+
class Gdt2Http
|
23
|
+
|
24
|
+
# load configuration from configfile, merge with
|
25
|
+
# default options
|
26
|
+
#
|
27
|
+
def load_configuration
|
28
|
+
# try to read configuration from file
|
29
|
+
@options_from_file = YAML.load_file(ConfigFile) || {} rescue {}
|
30
|
+
|
31
|
+
# if an option is not given on the command line
|
32
|
+
# it is taken from the config file, or the default is used
|
33
|
+
@options = Hash.new() { |h,k| @options_from_file[k] || DefaultConfig[k] }
|
34
|
+
end
|
35
|
+
|
36
|
+
# parse command line options
|
37
|
+
#
|
38
|
+
def initialize
|
39
|
+
load_configuration
|
40
|
+
|
41
|
+
@opts = OptionParser.new do |opts|
|
42
|
+
opts.on "-V","--version","Display version and exit" do
|
43
|
+
puts "#{self.class} #{::Gdt::VERSION}"
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
opts.on "-f", "--files PATTERN", Array,
|
47
|
+
"a list of files or shell globs to look for",
|
48
|
+
"default is '**/*.GDT'" do |arg|
|
49
|
+
@options[:files] = arg
|
50
|
+
end
|
51
|
+
opts.on "-u", "--uri URI", "URI of the HTTP-Endpoint to which the parsed data is sent" do |arg|
|
52
|
+
@options[:endpoint] = arg
|
53
|
+
end
|
54
|
+
opts.on_tail "-p", "--print-config", "Print the current configuration",
|
55
|
+
"in a format that can be used as a configuration file" do
|
56
|
+
puts @options_from_file.merge(@options).to_yaml
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def files
|
63
|
+
@options[:files].map { |p| Dir.glob(p) }.flatten.compact.uniq
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle_file(filename)
|
67
|
+
# open and parse the given file
|
68
|
+
str = File.read(filename)
|
69
|
+
|
70
|
+
begin
|
71
|
+
data = Gdt.new(str)
|
72
|
+
|
73
|
+
begin
|
74
|
+
res = ::Net::HTTP.post_form(URI.parse(@options[:endpoint]), data.to_hash )
|
75
|
+
puts res.header.to_hash.map { |k,v| "#{k}: #{v}" }
|
76
|
+
puts
|
77
|
+
case res
|
78
|
+
when ::Net::HTTPSuccess
|
79
|
+
File.delete(filename) if @options[:delete_files]
|
80
|
+
puts res.body
|
81
|
+
when ::Net::HTTPClientError
|
82
|
+
warn "Client Error"
|
83
|
+
when ::Net::HTTPServerError
|
84
|
+
warn "Server Error"
|
85
|
+
end
|
86
|
+
|
87
|
+
rescue ::Errno::ECONNREFUSED
|
88
|
+
puts "Unable to connect to server '#{@options[:endpoint]}' (connection refused)"
|
89
|
+
end
|
90
|
+
rescue ParseError => e
|
91
|
+
warn "Parse error in '#{filename}'"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# run the application
|
96
|
+
#
|
97
|
+
def run!(args = ARGV)
|
98
|
+
@opts.parse!(args)
|
99
|
+
files.each { |f|
|
100
|
+
handle_file(f)
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
GDT_ROOT = File.dirname(File.expand_path(__FILE__))
|
2
|
+
|
3
|
+
require File.join(GDT_ROOT, 'gdt', 'parser.rb')
|
4
|
+
require File.join(GDT_ROOT, 'gdt', 'field_definitions.rb')
|
5
|
+
|
6
|
+
|
7
|
+
module Gdt
|
8
|
+
VERSION = '0.0.8'
|
9
|
+
|
10
|
+
class Gdt
|
11
|
+
def initialize(string)
|
12
|
+
@data = GdtFields.new( Parser.parse(string))
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_hash
|
16
|
+
@data.values
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/lib/xdt.rb
ADDED
data/lib/xdt/ldt.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# class Xdt::Ldt
|
2
|
+
# ldt_block_type '0020', :media_start, "Datenträger Header"
|
3
|
+
# ldt_block_type '0021', :media_end, "Datenträger Abschluss"
|
4
|
+
# ldt_block_type '8220', :l_package_start, "L-Datenpaket-Header"
|
5
|
+
# ldt_block_type '8221', :l_package_end, "L-Datenpaket-Abschluss"
|
6
|
+
# ldt_block_type '8230', :p_package_start, "P-Datenpaket-Header"
|
7
|
+
# ldt_block_type '8231', :p_package_end, "P-Datenpaket-Abschluss"
|
8
|
+
# ldt_block_type '8201', :lab_report, "Labor-Facharzt-Bericht"
|
9
|
+
# ldt_block_type '8202', :lg_report, "LG-Bericht"
|
10
|
+
# ldt_block_type '8203', :microbiology_report, "Mikrobiologie-Bericht"
|
11
|
+
# ldt_block_type '8204', :referrer_report, "Facharzt-Bericht 'sonstige Einsendepraxen'"
|
12
|
+
# ldt_block_type '8218', :electronic_referral, "Elektronische Überweisung"
|
13
|
+
# ldt_block_type '8219', :lab_request, "Auftrag an eine Laborgemeinschaft"
|
14
|
+
# end
|
15
|
+
|
16
|
+
require 'date'
|
17
|
+
require 'xdt/markup'
|
18
|
+
|
19
|
+
module Xdt
|
20
|
+
module Ldt
|
21
|
+
module Package
|
22
|
+
end
|
23
|
+
|
24
|
+
class LGReport
|
25
|
+
|
26
|
+
# only write the file if it contains any sections
|
27
|
+
#
|
28
|
+
def write_file(filename)
|
29
|
+
return false unless @sections.length > 2
|
30
|
+
|
31
|
+
File.open(filename, "w+") do |f|
|
32
|
+
f.write self.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
|
38
|
+
def section(id, &blk)
|
39
|
+
@sections << Xdt::Section.new(id, &blk)
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
@sections = []
|
44
|
+
|
45
|
+
section("8220") do |s|
|
46
|
+
s.field("9211", "07/99")
|
47
|
+
# s.field("0201", "") # Arztnummer
|
48
|
+
s.field("0203", "Alexander") # Arztname
|
49
|
+
s.field("0204", "Nuklearmediziner") # Arztgruppe
|
50
|
+
s.field("0205", "Schönhauser Allee 82") # Strasse
|
51
|
+
s.field("0206", "10439 Berlin") # PLZ Ort
|
52
|
+
s.field("8300", "LABOR Schoenhauser Allee 82")
|
53
|
+
# s.field("0101", "") # KBV Prüfnummer
|
54
|
+
s.field("9106", "3") # Charset (iso-8859-1)
|
55
|
+
s.field("8312", "1") # Kundennummer
|
56
|
+
s.field("9103", Date.today.strftime("%D%M%Y"))
|
57
|
+
end
|
58
|
+
|
59
|
+
yield self if block_given?
|
60
|
+
|
61
|
+
section("8221") do |s|
|
62
|
+
overhead = 44
|
63
|
+
s.field("9202", (length + overhead).to_s.rjust(8,"0"))
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def length
|
69
|
+
@sections.inject(0) { |sum, section| sum + section.length }
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
@sections.map { |pkg| pkg.to_s }.join
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
data/lib/xdt/markup.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'xdt/markup'
|
2
|
+
|
3
|
+
module Xdt
|
4
|
+
class FieldType
|
5
|
+
def initialize(id, name, title, length, type = :string)
|
6
|
+
@id = id.to_i
|
7
|
+
@name = name
|
8
|
+
@title = title
|
9
|
+
@type = type
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid?(contents)
|
13
|
+
true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module FieldHandling
|
18
|
+
def define_field(id, *args)
|
19
|
+
@defined_fields ||= Hash.new { |h,k| raise "Redefined Field #{k}" }
|
20
|
+
@defined_fields[id.to_i] = Xdt::FieldType.new(id, *args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module Fields
|
25
|
+
def included(other)
|
26
|
+
define_field 101, :kbv_id, "KBV-Prüfnummer", 8, :alnum
|
27
|
+
|
28
|
+
define_field 201, :physician_id, "Arztnummer", (7..9), :num
|
29
|
+
define_field 203, :physician_name, "Arztname", (0..60), :alnum
|
30
|
+
define_field 204, :physician_define_field, "Arztgruppe", (0..60), :alnum
|
31
|
+
define_field 205, :street, "Strasse", (0..60), :alnum
|
32
|
+
define_field 206, :zip, "PLZ Ort", (0..60), :alnum
|
33
|
+
|
34
|
+
define_field 3000, :nr, "Patientennummer/Patientenkennung", (0..10), :alnum
|
35
|
+
define_field 3100, :name_prefix, "Namenszusatz/Vorsatzwort des Patienten", (0..15)
|
36
|
+
define_field 3101, :last_name, "Name des Patienten", (0..28)
|
37
|
+
define_field 3102, :first_name, "Vorname des Patienten", (0..28)
|
38
|
+
define_field 3103, :birthday, "Geburtsdatum des Patienten", 8, :date
|
39
|
+
define_field 3104, :title, "Titel des Patienten", (0..15)
|
40
|
+
define_field 3105, :insurance_id, "Versichertennummer des Patienten", (0..12)
|
41
|
+
define_field 3106, :patient_postal_code_city, "Wohnort des Patienten", (0..30)
|
42
|
+
define_field 3107, :patient_street, "Strasse des Patienten", (0..28)
|
43
|
+
define_field 3108, :insurance_type, "Versichertenart MFR", 1, :num
|
44
|
+
define_field 3110, :sex, "Geschlecht des Patienten", 1, :num
|
45
|
+
|
46
|
+
define_field 8000, :gdt_type, "Satzidentifikation", 4
|
47
|
+
define_field 8100, :gdt_length, "Satzlänge", 5, :num
|
48
|
+
define_field 8315, :receiver_id, "GDT-ID des Empfängers", (0..8) # violates spec, should be "8"
|
49
|
+
define_field 8316, :sender_id, "GDT-ID des Senders", (0..8) # violates the spec, should be "8"
|
50
|
+
|
51
|
+
define_field 9218, :gdt_version, "Version GDT", 5
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Field
|
56
|
+
class << self
|
57
|
+
include Xdt::FieldHandling
|
58
|
+
end
|
59
|
+
|
60
|
+
include Xdt::Fields
|
61
|
+
|
62
|
+
def initialize(type, data)
|
63
|
+
@id = type
|
64
|
+
@data = data.to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
def length
|
68
|
+
@data.length + 9
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_s
|
72
|
+
"#{ '%03d' % self.length }#{ '%04d' % @id.to_i }#{@data}\x0D\x0A"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Xdt
|
2
|
+
class SectionType
|
3
|
+
def initialize(id, name, title, length, type = :string)
|
4
|
+
@id = id.to_i
|
5
|
+
@name = name
|
6
|
+
@title = title
|
7
|
+
@type = type
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?(contents)
|
11
|
+
true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module SectionHandling
|
16
|
+
def define_section(id, name, title, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Section
|
21
|
+
class << self
|
22
|
+
include Xdt::SectionHandling
|
23
|
+
|
24
|
+
alias [] new
|
25
|
+
end
|
26
|
+
|
27
|
+
define_section 8220, :l_packet_header, "L-Datenpaket-Header" do |s|
|
28
|
+
s.field [9211, 201, 203, 204, 205, 206, 8300, 101, 9106, 8312, 9103], :cardinality => '1'
|
29
|
+
s.field 9472, :cardinality => 'n'
|
30
|
+
s.field 9300, :cardinality => '?'
|
31
|
+
s.field 9301, :cardinality => '?'
|
32
|
+
end
|
33
|
+
|
34
|
+
define_section 8221, :l_packet_footer, "L-Datenpaket Abschluss" do |s|
|
35
|
+
s.field 9202, :cardinality => '1', :default => proc { |section| section.length + 44 }
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(type)
|
39
|
+
@type = type
|
40
|
+
@fields = []
|
41
|
+
yield self if block_given?
|
42
|
+
end
|
43
|
+
|
44
|
+
def field(*args)
|
45
|
+
@fields << Field.new(*args)
|
46
|
+
end
|
47
|
+
|
48
|
+
def length
|
49
|
+
to_s.length
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
header_length = 27
|
54
|
+
|
55
|
+
data = @fields.map { |field| field.to_s }.join
|
56
|
+
header = Field.new("8000", '%04d' % @type.to_i).to_s +
|
57
|
+
Field.new("8100", '%05d' % (data.length + header_length) ).to_s
|
58
|
+
|
59
|
+
header + data
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
context "Gdt field ids should map to names" do
|
2
|
+
field_names = {
|
3
|
+
# 0102
|
4
|
+
# 0103
|
5
|
+
# 0132
|
6
|
+
3000 => :nr,
|
7
|
+
3100 => :name_prefix,
|
8
|
+
3101 => :last_name,
|
9
|
+
3102 => :first_name,
|
10
|
+
3103 => :birthday,
|
11
|
+
3104 => :title,
|
12
|
+
3105 => :insurance_id,
|
13
|
+
3106 => :patient_postal_code_city,
|
14
|
+
3107 => :patient_street,
|
15
|
+
3108 => :insurance_type,
|
16
|
+
3110 => :sex,
|
17
|
+
# 3622
|
18
|
+
# 3623
|
19
|
+
# 3628
|
20
|
+
# 6200
|
21
|
+
8000 => :gdt_type,
|
22
|
+
8100 => :gdt_length,
|
23
|
+
8315 => :receiver_id,
|
24
|
+
8316 => :sender_id,
|
25
|
+
|
26
|
+
9218 => :gdt_version
|
27
|
+
}
|
28
|
+
|
29
|
+
field_names.each do |id, name|
|
30
|
+
specify("#{id.to_s} => #{name.inspect}") do
|
31
|
+
Gdt::GdtFields.lookup(id).should == name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "Parsing a Hash with Gdt-Data" do
|
37
|
+
setup do
|
38
|
+
@parsed_data = Gdt::GdtFields.new( { 3000 => "98", 3101 => "Sierra", 3110 => "2" } )
|
39
|
+
end
|
40
|
+
|
41
|
+
specify "should convert field IDs to names" do
|
42
|
+
@parsed_data.last_name.should == "Sierra"
|
43
|
+
@parsed_data.nr.should == "98"
|
44
|
+
end
|
45
|
+
specify "should convert numeric fields to numbers" do
|
46
|
+
@parsed_data.sex.should == 2
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "Unknown field IDs" do
|
51
|
+
setup do
|
52
|
+
@c = Class.new(Gdt::AbstractField) do |c|
|
53
|
+
# no fields are defined
|
54
|
+
end
|
55
|
+
end
|
56
|
+
specify "should raise an error on parsing" do
|
57
|
+
lambda { @c.new( { 42 => "data" } ) }.should raise_error(ArgumentError)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "Fields with a fixed length" do
|
62
|
+
setup do
|
63
|
+
@c = Class.new(Gdt::AbstractField) do |c|
|
64
|
+
c.field 1, :number, "some number", 2, :num
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
specify "should raise no error if the length is correct" do
|
69
|
+
@c.new( { 1 => "03" } ).number.should == 3
|
70
|
+
end
|
71
|
+
#specify "should raise an error if the length is to small or too long" do
|
72
|
+
# lambda { @c.new( { 1 => "0" } ) }.should raise_error(ArgumentError)
|
73
|
+
# lambda { @c.new( { 1 => "045" } ) }.should raise_error(ArgumentError)
|
74
|
+
#end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "Fields with a maximum length" do
|
78
|
+
setup do
|
79
|
+
@c = Class.new(Gdt::AbstractField) do |c|
|
80
|
+
c.field 1, :data, "a string", (0..21), :alnum
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
specify "should allow empty strings" do
|
85
|
+
@c.new( { 1 => "" } ).data.should == ""
|
86
|
+
end
|
87
|
+
specify "should allow fields inside the bounds" do
|
88
|
+
lambda { @c.new( { 1 => "exactly 21 characters" } ).data }.should_not raise_error
|
89
|
+
lambda { @c.new( { 1 => "fewer characters" } ).data }.should_not raise_error
|
90
|
+
end
|
91
|
+
#specify "should raise an error if length is too big" do
|
92
|
+
# lambda { @c.new( { 1 => "exactly 22 characters!" } ).data }.should raise_error(ArgumentError)
|
93
|
+
#end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "Date fields" do
|
97
|
+
setup do
|
98
|
+
@c = Class.new(Gdt::AbstractField) do |c|
|
99
|
+
c.field 1, :data, "a date field", 8, :datum
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
specify "should return an instance of the Date class" do
|
104
|
+
@c.new( { 1 => "31011994" }).data.should == Date.parse("1994-01-31")
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
context "Valid GDT tokens" do
|
2
|
+
setup do
|
3
|
+
@line = "01380006301\r\n"
|
4
|
+
end
|
5
|
+
|
6
|
+
specify "should parse without errors" do
|
7
|
+
lambda { Gdt::Parser.parse(@line) }.should_not raise_error
|
8
|
+
end
|
9
|
+
|
10
|
+
specify "should return a hash with the correct data" do
|
11
|
+
Gdt::Parser.parse(@line).should == {8000 => "6301"}
|
12
|
+
end
|
13
|
+
|
14
|
+
specify "parser should ignore empty lines" do
|
15
|
+
result = nil
|
16
|
+
lambda { result = Gdt::Parser.parse(" \r\n\r\n ") }.should_not raise_error
|
17
|
+
result.should == {}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "Malformed data" do
|
22
|
+
specify "should raise an exception on malformed length" do
|
23
|
+
lambda { Gdt::Parser.parse("01480006301") }.should raise_error(Gdt::ParseError)
|
24
|
+
end
|
25
|
+
|
26
|
+
specify "that does not conform to the format at all should raise an exception" do
|
27
|
+
lambda { Gdt::Parser.parse("useless garbage\r\nwith multiple lines\r\n") }.should raise_error(Gdt::ParseError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "a valid Gdt file from Quincy PCNet" do
|
32
|
+
setup do
|
33
|
+
@gdt_data = File.read(File.dirname(__FILE__) + '/examples/BARCQPCN.001')
|
34
|
+
end
|
35
|
+
|
36
|
+
specify "should parse without error" do
|
37
|
+
lambda { Gdt::Parser.parse( @gdt_data ) }.should_not raise_error
|
38
|
+
end
|
39
|
+
|
40
|
+
specify "should contain 14 fields" do
|
41
|
+
Gdt::Parser.parse( @gdt_data ).length.should equal(14)
|
42
|
+
end
|
43
|
+
|
44
|
+
specify "should contain correct data" do
|
45
|
+
Gdt::Parser.parse(@gdt_data).should satisfy { |gdt|
|
46
|
+
gdt[3000] == "98" && gdt[8000] == "6301" && gdt[3101] == "Sierra"
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/spec/gdt_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'lib/gdt_interface.rb'
|
2
|
+
|
3
|
+
context "reading GDT data from a file" do
|
4
|
+
def read_file
|
5
|
+
Gdt::Gdt.new(File.read('./spec/examples/BARCQPCN.001'))
|
6
|
+
end
|
7
|
+
|
8
|
+
specify "should work without errors" do
|
9
|
+
lambda { Gdt::Gdt.new(File.read('./spec/examples/BARCQPCN.001')) }.should_not raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "data should be representable as a hash" do
|
13
|
+
read_file.to_hash.should be_instance_of(Hash)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
describe "creating an LG-report" do
|
3
|
+
before do
|
4
|
+
@lg = Xdt::Ldt::LGReport.new do |lg|
|
5
|
+
lg.section("8202") do |s|
|
6
|
+
s.field("8310", "12345") # Anforderungs-ID
|
7
|
+
s.field("8301", "09062008") # Eingangsdatum im Labor
|
8
|
+
s.field("8302", "10062008") # Berichtsdatum
|
9
|
+
s.field("3000", "98") # PAT-ID (Nospec)
|
10
|
+
s.field("3103", "10111982") # Geburtsdatum des Pat
|
11
|
+
s.field("8401", "E") # Befundart
|
12
|
+
|
13
|
+
s.field("8410", "TPO") # Test-Ident
|
14
|
+
s.field("8411", "ANTITPO") # Testbezeichnung
|
15
|
+
s.field("8418", "K") # Tststatus (Fehlt, Korrigiert, Berichtigt)
|
16
|
+
s.field("8420", "12.4")
|
17
|
+
s.field("8421", "U/l") # Einkeit
|
18
|
+
s.field("8480", "Ergebnistext") # Ergebnistext
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should not raise any errors" do
|
25
|
+
proc { @lg.to_s }.should_not raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have the correct string representation" do
|
29
|
+
@lg.to_s.should == <<-EOF
|
30
|
+
01380008220
|
31
|
+
|
32
|
+
EOF
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should have correct length" do
|
36
|
+
@lg.to_s[/9202\d{8}/][-8..-1].to_i.should == @lg.to_s.length
|
37
|
+
end
|
38
|
+
end
|
data/spec/xdt_spec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'lib/xdt'
|
2
|
+
|
3
|
+
describe "an XDT record with an ID and some data" do
|
4
|
+
before do
|
5
|
+
@field = Xdt::Field.new("8000", "8221")
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should have a string representation that includes length and ends in CR LF" do
|
9
|
+
@field.to_s.should == "01380008221\r\n"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "creating a XDT block" do
|
14
|
+
before do
|
15
|
+
@block = Xdt::Section.new("0020") do |b|
|
16
|
+
b.field "9105", "123"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have a string representation where the length field exists and has the correct value" do
|
21
|
+
@block.to_s.should == "01380000020\r\n014810000039\r\n0129105123\r\n"
|
22
|
+
end
|
23
|
+
end
|
data/xdt.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = %q{xdt}
|
3
|
+
s.version = "1.0.5"
|
4
|
+
|
5
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
6
|
+
s.authors = ["Levin Alexander"]
|
7
|
+
s.date = %q{2008-11-10}
|
8
|
+
s.default_executable = %q{gdt2http}
|
9
|
+
s.description = %q{xDT is a library that reads and writes LDT, GDT and BDT data.}
|
10
|
+
s.email = ["mail@levinalex.net"]
|
11
|
+
s.executables = ["gdt2http"]
|
12
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
|
13
|
+
s.files = [".DS_Store", "History.txt", "Manifest.txt", "README.txt", "Rakefile", "bin/gdt2http", "lib/gdt/field_definitions.rb", "lib/gdt/field_handling.rb", "lib/gdt/parser.rb", "lib/gdt2http.rb", "lib/gdt_interface.rb", "lib/xdt.rb", "lib/xdt/ldt.rb", "lib/xdt/markup.rb", "lib/xdt/xdt_fields.rb", "lib/xdt/xdt_sections.rb", "spec/examples/BARCQPCN.001", "spec/gdt_field_definitions_spec.rb", "spec/gdt_parser_spec.rb", "spec/gdt_spec.rb", "spec/lg_report_spec.rb", "spec/xdt_spec.rb", "xdt.gemspec"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://levinalex.net/src/xdt}
|
16
|
+
s.rdoc_options = ["--main", "README.txt"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{xdt}
|
19
|
+
s.rubygems_version = %q{1.2.0}
|
20
|
+
s.summary = %q{xDT is a library that reads and writes LDT, GDT and BDT data.}
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 2
|
25
|
+
|
26
|
+
if current_version >= 3 then
|
27
|
+
s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
30
|
+
end
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xdt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 5
|
10
|
+
version: 1.0.5
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Levin Alexander
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2008-11-10 00:00:00 +01:00
|
19
|
+
default_executable: gdt2http
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: hoe
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 55
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 8
|
33
|
+
- 0
|
34
|
+
version: 1.8.0
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
description: xDT is a library that reads and writes LDT, GDT and BDT data.
|
38
|
+
email:
|
39
|
+
- mail@levinalex.net
|
40
|
+
executables:
|
41
|
+
- gdt2http
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files:
|
45
|
+
- History.txt
|
46
|
+
- Manifest.txt
|
47
|
+
- README.txt
|
48
|
+
files:
|
49
|
+
- .DS_Store
|
50
|
+
- History.txt
|
51
|
+
- Manifest.txt
|
52
|
+
- README.txt
|
53
|
+
- Rakefile
|
54
|
+
- bin/gdt2http
|
55
|
+
- lib/gdt/field_definitions.rb
|
56
|
+
- lib/gdt/field_handling.rb
|
57
|
+
- lib/gdt/parser.rb
|
58
|
+
- lib/gdt2http.rb
|
59
|
+
- lib/gdt_interface.rb
|
60
|
+
- lib/xdt.rb
|
61
|
+
- lib/xdt/ldt.rb
|
62
|
+
- lib/xdt/markup.rb
|
63
|
+
- lib/xdt/xdt_fields.rb
|
64
|
+
- lib/xdt/xdt_sections.rb
|
65
|
+
- spec/examples/BARCQPCN.001
|
66
|
+
- spec/gdt_field_definitions_spec.rb
|
67
|
+
- spec/gdt_parser_spec.rb
|
68
|
+
- spec/gdt_spec.rb
|
69
|
+
- spec/lg_report_spec.rb
|
70
|
+
- spec/xdt_spec.rb
|
71
|
+
- xdt.gemspec
|
72
|
+
has_rdoc: true
|
73
|
+
homepage: http://levinalex.net/src/xdt
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options:
|
78
|
+
- --main
|
79
|
+
- README.txt
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 3
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
hash: 3
|
97
|
+
segments:
|
98
|
+
- 0
|
99
|
+
version: "0"
|
100
|
+
requirements: []
|
101
|
+
|
102
|
+
rubyforge_project: xdt
|
103
|
+
rubygems_version: 1.3.7
|
104
|
+
signing_key:
|
105
|
+
specification_version: 2
|
106
|
+
summary: xDT is a library that reads and writes LDT, GDT and BDT data.
|
107
|
+
test_files: []
|
108
|
+
|