sqlite2dbf 0.1.8 → 0.2
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 +4 -4
- data/lib/argparser.rb +83 -73
- data/lib/config +104 -0
- data/lib/configuration.rb +114 -0
- data/lib/constants.rb +2 -2
- data/lib/file_checking.rb +48 -48
- data/lib/log.conf +3 -0
- data/lib/logging.rb +131 -131
- data/lib/mapping.rb +94 -0
- data/lib/sqlite2dbf.rb +156 -129
- data/lib/translating.rb +56 -52
- data/lib/translations +132 -43
- data/sqlite2dbf.gemspec +2 -2
- metadata +8 -5
data/lib/sqlite2dbf.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
1
|
#encoding: UTF-8
|
3
2
|
=begin
|
4
3
|
/***************************************************************************
|
@@ -24,140 +23,168 @@
|
|
24
23
|
|
25
24
|
require_relative 'file_checking'
|
26
25
|
require_relative 'logging'
|
26
|
+
require_relative 'configuration'
|
27
|
+
require_relative 'mapping'
|
27
28
|
require 'shp'
|
28
29
|
require 'sqlite3'
|
29
30
|
require_relative 'argparser'
|
30
31
|
|
31
32
|
=begin
|
32
|
-
|
33
|
+
The main program class. Does it.
|
33
34
|
=end
|
34
35
|
|
35
36
|
class SQLite2DBF
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
37
|
+
include Logging
|
38
|
+
include Translating
|
39
|
+
|
40
|
+
def initialize(*args)
|
41
|
+
options = ArgParser::parse(args)
|
42
|
+
@config = Configuration.instance()
|
43
|
+
@config.set(options)
|
44
|
+
|
45
|
+
init_logger
|
46
|
+
level = (@config.debug ? Logger::DEBUG : @log.level)
|
47
|
+
@log.level = level
|
48
|
+
|
49
|
+
@date_fields = @config.date ? @config.date : []
|
50
|
+
@time_fields = @config.time ? @config.time : []
|
51
|
+
|
52
|
+
if(@config.source)
|
53
|
+
@dbf_path = nil
|
54
|
+
sqlite_file = @config.source
|
55
|
+
|
56
|
+
msg = File_Checking::file_check(sqlite_file, :exist, :readable)
|
57
|
+
if msg
|
58
|
+
@log.error(trl("ERROR! Cannot read the source-file" ) << ": " << msg)
|
59
|
+
exit false
|
60
|
+
end
|
61
|
+
|
62
|
+
SQLite3::Database.new(sqlite_file) do |db|
|
63
|
+
tables = list(db)
|
64
|
+
if(@config.list)
|
65
|
+
puts "\n" << trl("Tables in the database") << ":\n\t" << tables.join("\n\t") << "\n\n"
|
66
|
+
exit true
|
67
|
+
elsif tables.include?(@config.name)
|
68
|
+
@mapping = mapping(db)
|
69
|
+
else
|
70
|
+
@log.error(trl("Verify table-name! %s is not found in the database!") %(@config.name))
|
71
|
+
@log.error(trl("Tables are %s") %tables.join(', ') )
|
72
|
+
|
73
|
+
exit false
|
74
|
+
end
|
75
|
+
|
76
|
+
dbf_file = @config.target if @config.target
|
77
|
+
if(@config.out)
|
78
|
+
dbf_file = @config.out << File::Separator << @config.name
|
79
|
+
end
|
80
|
+
dbf_file ||= File.dirname(sqlite_file) << File::Separator << File.basename(sqlite_file, '.*')
|
81
|
+
|
82
|
+
msg = nil
|
83
|
+
|
84
|
+
if(File.exist?(dbf_file))
|
85
|
+
msg = File_Checking.file_check(dbf_file, :file, :writable)
|
86
|
+
elsif(File.exist?(File.dirname(dbf_file)))
|
87
|
+
msg = File_Checking.file_check(File.dirname(dbf_file), :directory, :writable)
|
88
|
+
end
|
89
|
+
|
90
|
+
if(!msg)
|
91
|
+
@log.debug('will transform ' << sqlite_file << ' to ' << dbf_file.to_s << '.dbf')
|
92
|
+
transform(db, dbf_file)
|
93
|
+
else
|
94
|
+
msg = trl("ERROR! Unsuitable file") << " : %s" %msg
|
95
|
+
@log.error(msg)
|
96
|
+
exit false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
else
|
100
|
+
log.error trl("ERROR! Source-file is a mandatory program parameter!")
|
101
|
+
log.error trl("Start this program with parameter -h or --help to see the usage-message.")
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Create a list of table-names from SQLite
|
109
|
+
def list(db)
|
110
|
+
table_names = []
|
111
|
+
table_names = db.execute('select name from sqlite_master WHERE type="table"').map {|tn| tn[0]}
|
112
|
+
@log.debug('sqlite contains ' << table_names.size.to_s << ' table(s): ' << table_names.join(', '))
|
113
|
+
return table_names
|
114
|
+
end
|
115
|
+
|
116
|
+
def mapping(db)
|
117
|
+
return Mapping.new(@config, db.table_info(@config.name), db.execute('select * from ' << @config.name ) )
|
118
|
+
end
|
119
|
+
|
120
|
+
def create_base(dbase)
|
121
|
+
@log.debug('creating fields')
|
122
|
+
@mapping.each do |field|
|
123
|
+
dbase.add_field(field.name, field.type, 200, 0)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def date(db, value)
|
128
|
+
dbf_value = db.execute("select strftime('%Y-%m-%d %H:%M:%S', " << value.to_s << ", 'unixepoch')").join
|
129
|
+
@log.warn(trl("ATTN! SQLite2DBF cannot convert the date-value %s into DBF-compatible format.") %value.to_s) if dbf_value.start_with?('-')
|
130
|
+
dbf_value = db.execute("select strftime('%s', " << value.to_s << ")").join if dbf_value.start_with?('-')
|
131
|
+
end
|
132
|
+
|
133
|
+
def time(db, value)
|
134
|
+
db.execute("select strftime('%s', " << value.to_s << ", 'unixepoch')").join
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
|
139
|
+
def transform(db, dbf_file)
|
140
|
+
|
141
|
+
begin
|
142
|
+
tname = @config.name
|
143
|
+
table_info = db.table_info(tname)
|
144
|
+
content = db.execute('select * from ' << tname )
|
145
|
+
@log.debug('have content')
|
146
|
+
|
147
|
+
if(:table == dbf_file)
|
148
|
+
dbf_file = @dbf_path << File::Separator << tname << (content.empty? ? '_empty' : '')<< '.dbf'
|
149
|
+
end
|
150
|
+
@log.debug('dbf will be ' << dbf_file)
|
151
|
+
dbase = SHP::DBF.create(dbf_file)
|
152
|
+
|
153
|
+
create_base(dbase)
|
154
|
+
|
155
|
+
content.each_with_index do |row, record_no|
|
156
|
+
@log.debug('row is ' << row.to_s)
|
157
|
+
|
158
|
+
row.each_with_index do |svalue, field_index|
|
159
|
+
@log.debug('object is ' << @mapping[field_index].to_s)
|
160
|
+
dbf_type = @mapping[field_index].type
|
161
|
+
field_name = @mapping[field_index].name
|
162
|
+
|
163
|
+
if(svalue && !svalue.to_s.empty? && dbf_type)
|
164
|
+
svalue = date(db, svalue) if @date_fields.include?(field_name)
|
165
|
+
svalue = date(db, svalue) if @time_fields.include?(field_name)
|
166
|
+
|
167
|
+
@log.debug('field is ' << field_name << ', svalue is ' << svalue.to_s << ', type is ' << dbf_type.to_s)
|
168
|
+
begin
|
169
|
+
case dbf_type
|
170
|
+
when 0
|
171
|
+
dbase.write_string_attribute(record_no, field_index, svalue )
|
172
|
+
when 1
|
173
|
+
dbase.write_integer_attribute(record_no, field_index, svalue)
|
174
|
+
when 2
|
175
|
+
dbase.write_double_attribute(record_no, field_index, svalue)
|
176
|
+
else
|
177
|
+
dbase.write_null_attribute(record_no, field_index)
|
178
|
+
end
|
179
|
+
rescue StandardError => ex
|
180
|
+
@log.warn(trl("ATTN! Field %s - Cannot write value of type %s") %[field_name, dbf_type] << ': ' << ex.message )
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
puts trl('DONE. Bye.')
|
186
|
+
rescue SQLite3::Exception => er
|
187
|
+
@log.error trl("ERROR! Cannot read the source-file") << ' (' << db.filename << "): %s" %er.message << '.'
|
188
|
+
end
|
189
|
+
end
|
163
190
|
end
|
data/lib/translating.rb
CHANGED
@@ -26,6 +26,7 @@ RD = File.expand_path(File.dirname(__FILE__) ) + File::SEPARATOR if !defined?(R
|
|
26
26
|
|
27
27
|
require 'yaml'
|
28
28
|
require_relative 'file_checking'
|
29
|
+
require_relative 'logging'
|
29
30
|
|
30
31
|
|
31
32
|
=begin
|
@@ -33,59 +34,62 @@ require_relative 'file_checking'
|
|
33
34
|
Translations are read from a file "translations" in the program folder.
|
34
35
|
=end
|
35
36
|
module Translating
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
self.extend(Logging)
|
38
|
+
@@log = init_logger(STDOUT)
|
39
|
+
# There are better ways to extend a translated
|
40
|
+
# string, but I keep the 'wild-card' for
|
41
|
+
# historical reasons.
|
42
|
+
@@awild = 'XX'
|
40
43
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
44
|
+
@@lang = nil
|
45
|
+
@@lang_file = format("%s%s", RD, 'LANG')
|
46
|
+
@@tr = YAML::load_file("#{RD}translations")
|
47
|
+
# find the current language-setting and return it.
|
48
|
+
def self.language()
|
49
|
+
if @@lang == nil
|
50
|
+
r = ENV['LANG']
|
51
|
+
if(r)
|
52
|
+
@@lang = r[0, 2]
|
53
|
+
elsif( !File_Checking::file_check(@@lang_file, [:exist?, :readable?]) && File::size(@@lang_file) >= 2)
|
54
|
+
File::open(@@lang_file, 'r') {|f| @@lang = f.readline}
|
55
|
+
@@lang.chomp!.downcase! if @@lang
|
56
|
+
end
|
57
|
+
end
|
58
|
+
@@lang = 'en' if !@@lang
|
59
|
+
end
|
58
60
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
61
|
+
# Translate a string to the currently set langage.
|
62
|
+
# The args parameter may contain replacement-text which
|
63
|
+
# will appear at the positions indicated by wildcard-characters
|
64
|
+
# in the original string.
|
65
|
+
def self.trl(t, *args)
|
66
|
+
|
67
|
+
@@log.debug('@tr is ' << @@tr.to_s)
|
68
|
+
Translating::language()
|
69
|
+
lt = @@tr[t]
|
70
|
+
if(lt)
|
71
|
+
lt = lt[@@lang]
|
72
|
+
else
|
73
|
+
# File.open('/tmp/mtf', 'a+') {|f| f << t << "\n"}
|
74
|
+
puts "\nTRANSLATION MISSING: \"" << t << "\""
|
75
|
+
end
|
76
|
+
lt ||= t
|
77
|
+
if(args && !args.empty?)
|
78
|
+
i = -1
|
79
|
+
lt = lt.gsub(@@awild) do |a|
|
80
|
+
i += 1
|
81
|
+
args.flatten[i]
|
82
|
+
end
|
83
|
+
lt += args[i + 1, args.length].join
|
84
|
+
end
|
85
|
+
return lt
|
86
|
+
end
|
83
87
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
# Translate a string to the currently set langage.
|
89
|
+
# The args parameter may contain replacement-text which
|
90
|
+
# will appear at the positions indicated by wildcard-characters
|
91
|
+
# in the original string.
|
92
|
+
def trl(t, *args )
|
93
|
+
Translating::trl(t, args)
|
94
|
+
end
|
91
95
|
end
|