[go: up one dir, main page]

Menu

[337d9f]: / Perl / common_windows.pm  Maximize  Restore  History

Download this file

309 lines (278 with data), 9.5 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# Copyright (c) 2013-2014 OpenM++
# This code is licensed under MIT license (see LICENSE.txt for details)
# Common shared components for ompp Perl utilities, e.g. test_models.pl and friends
# Contains functions which are specific to the Windows platform,
# or which require different implementations due to different Perl behaviour
# on Windows and Linux.
# Determine the version of Modgen from Modgen.exe
# arg0 - the file Modgen.exe
# returns - version string
sub modgen_version {
my $modgen_exe = shift(@_);
use Win32::Exe;
return Win32::Exe->new($modgen_exe)->version_info->get('FileVersion');
}
# Run a SQL statement on a Jet (.mdb) database
# arg0 - the Jet database
# arg1 - the SQL statement
# arg2 - the return value from
# returns - multi-line string
sub run_jet_statement {
my $db = shift(@_);
my $sql = shift(@_);
my $result;
use Win32::OLE;
use Win32::OLE::Const 'Microsoft ActiveX Data Objects';
#my $sConnect = "Provider = Microsoft.Jet.OLEDB.4.0; Data source = ${db}";
my $sConnect = "Provider = Microsoft.ACE.OLEDB.12.0; Data source = ${db}";
my $ADO_Conn = Win32::OLE->new('ADODB.Connection'); # creates a connection object
my $ADO_RS = Win32::OLE->new('ADODB.Recordset'); # creates a recordset object
$ADO_Conn->Open($sConnect);
if (Win32::OLE->LastError()) {
print "Fatal Error: ", Win32::OLE->LastError(), "\n";
return 1;
}
$ADO_RS = $ADO_Conn->Execute($sql);
if (Win32::OLE->LastError()) {
print "Fatal Error: ", Win32::OLE->LastError(), "\n";
return 1;
}
my $fields = $ADO_RS->Fields->count;
while ( !$ADO_RS->EOF ) {
for (my $field_ordinal = 0; $field_ordinal < $fields; $field_ordinal++) {
my $value = $ADO_RS->Fields($field_ordinal)->value;
$result .= $value;
if ($field_ordinal < $fields - 1) {
$result .= ","
}
}
$result .= "\n";
#my $value = $ADO_RS->Fields(0)->value;
#$result .= "${value}\n";
$ADO_RS->MoveNext;
}
return $result;
}
# Extract Modgen output tables from a mdb Modgen database to a folder
# arg0 - the Modgen database
# arg1 - the destination folder
# arg2 - the number of significant digits to output (optional)
# arg3 - flag to create non-rounded version of csv (optional)
# returns - 0 for success, otherwise non-zero
sub modgen_tables_to_csv
{
my $db = shift(@_);
my $dir = shift(@_);
my $do_rounding = 0;
my $do_unrounded_file = 0;
my $round_prec = 0;
if ($#_ >= 0) {
$round_prec = shift(@_);
if ($round_prec > 0) {
$do_rounding = 1;
}
}
if ($#_ >= 0) {
$do_unrounded_file = shift(@_);
}
my $retval;
my $suppress_margins = 0;
if (! -d $dir) {
if (!mkdir $dir) {
logmsg error, "unable to create directory ${dir}";
return 1;
}
}
use Win32::OLE;
use Win32::OLE::Const 'Microsoft ActiveX Data Objects';
#my $sConnect = "Provider = Microsoft.Jet.OLEDB.4.0; Data source = ${db}";
my $sConnect = "Provider = Microsoft.ACE.OLEDB.12.0; Data source = ${db}";
my $ADO_Conn = Win32::OLE->new('ADODB.Connection'); # creates a connection object
$ADO_Conn->Open($sConnect);
if (Win32::OLE->LastError()) {
logmsg error, "OLE", Win32::OLE->LastError();
return 1;
}
my $ADO_RS = Win32::OLE->new('ADODB.Recordset');
# Get all of the output table names
my @tables;
my @ranks;
my @expr_positions;
my @table_ids;
my @user_table_flags;
foreach my $which ('TableDic', 'UserTableDic') {
my $user_table_flag = 0;
$user_table_flag = 1 if $which eq 'UserTableDic';
my $sql = "Select Name, Rank, AnalysisDimensionPosition, TableID From ${which} Where LanguageID = 0;";
$ADO_RS = $ADO_Conn->Execute($sql);
if (Win32::OLE->LastError()) {
logmsg error, "OLE", Win32::OLE->LastError();
return 1;
}
my $fields = $ADO_RS->Fields->count;
# Iterate the recordset and create lists of table names and ranks
while ( !$ADO_RS->EOF ) {
push @tables, $ADO_RS->Fields(0)->value;
push @ranks, $ADO_RS->Fields(1)->value;
push @expr_positions, $ADO_RS->Fields(2)->value;
push @table_ids, $ADO_RS->Fields(3)->value;
push @user_table_flags, $user_table_flag;
$ADO_RS->MoveNext;
}
}
#logmsg info, "tables", join("\n", @tables);
# Iterate the tables
for my $j (0..$#tables) {
my $table = @tables[$j];
my $rank = @ranks[$j];
my $expr_position = @expr_positions[$j];
my $table_id = @table_ids[$j];
my $user_table_flag = @user_table_flags[$j];
my $out_csv = "${dir}/${table}.csv";
if (!open OUT_CSV, ">${out_csv}") {
logmsg error, "unable to open ${out_csv}";
return 1;
}
if ($do_unrounded_file) {
my $out_csv_unrounded = "${dir}/_${table}.csv";
if (!open OUT_CSV_UNROUNDED, ">${out_csv_unrounded}") {
logmsg error, "unable to open ${out_csv_unrounded}";
return 1;
};
}
# For each classification dimension of the table, determine if it has a margin
my $which = 'TableClassDimDic';
$which = 'UserTableClassDimDic' if $user_table_flag == 1;
my $sql = "
Select Totals
From ${which}
Where LanguageID = 0 And TableID = ${table_id}
;
";
$ADO_RS = $ADO_Conn->Execute($sql);
if (Win32::OLE->LastError()) {
logmsg error, "OLE", Win32::OLE->LastError();
return 1;
}
my @has_margin;
while ( !$ADO_RS->EOF ) {
push @has_margin, $ADO_RS->Fields(0)->value;
$ADO_RS->MoveNext;
}
#logmsg info, ${table}, "has_margin", join(",", @has_margin);
# construct permuted dimension list which puts analysis dimension last
# construct max index list at same time
my $dim_list;
my $max_dim_list;
for (my $dim = 0; $dim < $rank; ++$dim) {
if ($dim > 0) {
$dim_list .= ", ";
$max_dim_list .= ", ";
}
my $permuted_dim;
if ($dim < $expr_position) {
# dimensions before the analysis dimension are unchanged for permuted order
$permuted_dim = $dim;
}
elsif ($dim == $rank - 1) {
# analysis dimension is last dimension for permuted order
$permuted_dim = $expr_position;
}
elsif ($dim >= $expr_position) {
# skip over the analysis dimension for permuted order
$permuted_dim = $dim + 1;
}
$dim_list .= "Dim${permuted_dim}";
$max_dim_list .= "Max(Dim${permuted_dim})";
}
#logmsg info, ${table}, "dim_list", $dim_list;
#logmsg info, ${table}, "max_dim_list", $max_dim_list;
# Determine maximum value of each dimension (to identify margin index)
my @max_dims;
if ($rank > 0) {
my $sql = "Select ${max_dim_list} From ${table};";
$ADO_RS = $ADO_Conn->Execute($sql);
if (Win32::OLE->LastError()) {
logmsg error, "OLE", Win32::OLE->LastError();
return 1;
}
my $fields = $ADO_RS->Fields->count;
for (my $field_ordinal = 0; $field_ordinal < $fields; $field_ordinal++) {
my $value = $ADO_RS->Fields($field_ordinal)->value;
push @max_dims, $value;
}
}
#logmsg info, ${table}, "max_dims", join(",", @max_dims);
my $sql = "Select ";
if ($rank > 0) {
$sql .= "${dim_list}, ";
}
$sql .= "Value From ${table}";
if ($rank > 0) {
$sql .= " Order By ${dim_list}";
}
$sql .= ";";
#logmsg info, "sql", $sql;
$ADO_RS = $ADO_Conn->Execute($sql);
my $fields = $ADO_RS->Fields->count;
# First output line contains field names
# Note that these are not the permuted names.
# This is to generate csv's which look like those from ompp,
# where the analysis dimension is always last.
for (my $dim = 0; $dim < $rank; ++$dim) {
print OUT_CSV "Dim${dim},";
print OUT_CSV_UNROUNDED "Dim${dim}," if $do_unrounded_file;
}
print OUT_CSV "Value\n";
print OUT_CSV_UNROUNDED "Value\n" if $do_unrounded_file;
# data lines
while ( !$ADO_RS->EOF ) {
my @fields;
my $suppress_line = 0;
my $value;
my $unrounded_value;
for (my $field_ordinal = 0; $field_ordinal < $fields; $field_ordinal++) {
$value = $ADO_RS->Fields($field_ordinal)->value;
$unrounded_value = $value;
if (length($value) && $field_ordinal == $fields - 1) {
if ($value eq '-1.#IND' ) {
# is a NaN, output in OUT_CSV as an empty field (NULL)
$value = '';
$unrounded_value = $value;
}
else {
$unrounded_value = $value;
if ($do_rounding) {
$value = $value + 0.0;
# standard rounding
# $value = sprintf("%.${round_prec}g", $value);
# 2-stage rounding
$value = sprintf("%.15g", $value);
$value = sprintf("%.${round_prec}g", $value);
# hierarchical rounding
#for (my $j = 15; $j >= $round_prec; $j--) {
# $value = sprintf("%.*g", $j, $value);
#}
$value = 0.0 + $value;
}
# Windows Perl does 7.836e-007 and Linux Perl 7.836e-07, so make uniform
$value =~ s/e([-+])0(\d\d)/e\1\2/;
$unrounded_value =~ s/e([-+])0(\d\d)/e\1\2/ if ($do_rounding);
}
}
$suppress_line = 1 if $suppress_margins && $has_margin[$field_ordinal] && $value == $max_dims[$field_ordinal];
if ($field_ordinal < $fields - 1) {
push @fields, $value;
}
}
print OUT_CSV join(',', @fields).','.$value."\n" if !$suppress_line;
print OUT_CSV_UNROUNDED join(',', @fields).','.$unrounded_value."\n" if $do_unrounded_file && !$suppress_line;
$ADO_RS->MoveNext;
}
close OUT_CSV;
close OUT_CSV_UNROUNDED if $do_unrounded_file;
}
# Success
return 0;
}
return 1;