Removed /usr/local from CDPATH
[clearscm.git] / cq / PQA.pm
1 #!/usr/bin/perl
2 ################################################################################
3 #
4 # File:         PQA.pm
5 # Description:  Perl module PQA conversion routines
6 # Author:       Andrew@DeFaria.com
7 # Created:      Thu Oct  6 09:51:38 PDT 2005
8 # Language:     Perl
9 # Modifications:
10 #
11 # (c) Copyright 2005, Andrew@DeFaria.com, all rights reserved
12 #
13 ################################################################################
14 #use strict;
15 use warnings;
16 use CQPerlExt;
17
18 package PQA;
19   use File::Spec;
20
21   require (Exporter);
22   @ISA = qw (Exporter);
23
24   @EXPORT = qw (
25     @old_Prod_defect_fields
26     @old_TO_defect_fields
27     @new_Cont_defect_fields
28     @customer_fields
29     @project_fields
30     %bad_chars
31     AddToFieldChoiceList
32     AddToProject
33     CheckField
34     CheckRecord
35     DeleteDynamicLists
36     DeleteRecords
37     EndSession
38     GetAllDefectRecords
39     GetDefectRecord
40     ProjectExists
41     StartSession
42     TransferAttachments
43     TransferHistory
44     TransferRecords
45   );
46
47   # Forwards
48   sub AddToFieldChoiceList;
49   sub AddToProject;
50   sub CheckField;
51   sub CheckRecord;
52   sub DeleteDynamicLists;
53   sub DeleteRecords;
54   sub EndSession;
55   sub GetAllDefectRecords;
56   sub GetDefectRecord;
57   sub ProjectExists;
58   sub StartSession;
59   sub TransferAttachemnts;
60   sub TransferHistory;
61   sub TransferRecords;
62
63   our ($me, $verbose, $debug);
64   my $abs_path;
65
66   BEGIN {
67     # Check environment variables
68     $verbose    = $ENV {VERBOSE} ? "yes" : "no";
69     $debug      = $ENV {DEBUG}   ? "yes" : "no";
70   } # BEGIN
71
72   use Display;
73   use Logger;
74
75   ## Exported variables ##
76
77   # Field Definitions
78   our @old_Prod_defect_fields = (
79     "ActionNotes",              # SHORT_STRING
80     "AdvancedFeature",          # SHORT_STRING, CONSTANT_LIST
81     "Assigned_Date",            # DATE_TIME
82     "AttachmentBRCM",           # ATTACHMENT_LIST
83     "Audit_Log",                # MULTILINE_STRING
84     "Category",                 # SHORT_STRING, CONSTANT_LIST
85     "Close_Date",               # DATE_TIME
86     "CommitmentLevel",          # SHORT_STRING, CONSTANT_LIST
87     "CommittedDate",            # DATE_TIME
88     "CommittedToProject",       # SHORT_STRING, CONSTANT_LIST
89     "CustomerID",               # SHORT_STRING
90     "DataPendingNote",          # MULTILINE_STRING
91     "DeferredToChip",           # SHORT_STRING
92     "DeferredToProject",        # SHORT_STRING, CONSTANT_LIST
93     "Description",              # MULTILINE_STRING
94     "DoesNotVerifyNote",        # MULTILINE_STRING
95     "Entry_Type",               # SHORT_STRING, CONSTANT_LIST
96     "Est_Time_To_Fix",          # SHORT_STRING
97     "Fixed_In_HW_Version",      # SHORT_STRING
98     "Fixed_In_Project",         # SHORT_STRING, CONSTANT_LIST
99     "Fixed_In_SW_Version",      # SHORT_STRING
100     "GatingItem",               # SHORT_STRING, CONSTANT_LIST
101     "HUT",                      # SHORT_STRING, DYNAMIC_LIST
102     "HUT_Revision",             # SHORT_STRING, CONSTANT_LIST
103     "HUT_Version",              # SHORT_STRING, CONSTANT_LIST
104     "History",                  # JOURNAL
105     "Issue_Classification",     # SHORT_STRING, CONSTANT_LIST
106     "Keywords",                 # MULTILINE_STRING, CONSTANT_LIST
107     "NoteBRCMOnly",             # MULTILINE_STRING
108     "NoteBugReview",            # MULTILINE_STRING
109     "Note_Entry",               # MULTILINE_STRING
110     "Notes_Log",                # MULTILINE_STRING
111     "OEMSubmitterName",         # SHORT_STRING
112     "OS",                       # CONSTANT_LIST
113     "Open_Close_Status",        # SHORT_STRING, CONSTANT_LIST
114     "Owner",                    # REFERENCE
115     "PendingHWSWReleases",      # INT
116     "Priority",                 # SHORT_STRING, CONSTANT_LIST
117     "Project",                  # REFERENCE
118     "Project_Name",             # SHORT_STRING, CONSTANT_LIST
119     "RelatedID",                # MULTILINE_STRING
120     "ReportedBy",               # SHORT_STRING, CONSTANT_LIST
121     "Resolution",               # SHORT_STRING
122     "ResolveNote",              # MULTILINE_STRING
123     "ResolvedBy",               # REFERENCE
124     "Resolved_Date",            # DATE_TIME
125     "SQATestCase",              # SHORT_STRING, CONSTANT_LIST
126     "Service_Pack",             # SHORT_STRING
127     "Severity",                 # SHORT_STRING, CONSTANT_LIST
128     "Software",                 # SHORT_STRING, CONSTANT_LIST
129     "Software_Version",         # SHORT_STRING
130     "Submit_Date",              # DATE_TIME
131     "Submitter",                # REFERENCE
132     "Symptoms",                 # MULTILINE_STRING, CONSTANT_LIST
133     "TCProcedure",              # MULTILINE_STRING
134     "TestBlocking",             # SHORT_STRING, CONSTANT_LIST
135     "TestCaseID",               # INT
136     "TestcaseComment",          # MULTILINE_STRING
137     "TimeFromSubmitToVerify",   # SHORT_STRING
138     "TimeSubmitToResolve",      # SHORT_STRING
139     "TimeSubmitToResolve",      # SHORT_STRING
140     "TimeToVerify",             # SHORT_STRING
141     "Title",                    # SHORT_STRING
142     "VerifiedBy",               # REFERENCE
143     "VerifyNote",               # MULTILINE_STRING
144     "Verified_Date",            # DATE_TIME
145     "Verified_In_HW_Version",   # SHORT_STRING
146     "Verified_In_SW_Version",   # SHORT_STRING
147     "Visibility",               # SHORT_STRING, CONSTANT_LIST
148     "VisibleTo3com",            # INT
149     "VisibleToAltima",          # INT
150     "VisibleToCompaq",          # INT
151     "VisibleToDell",            # INT
152     "customer",                 # REFERENCE
153     "customer_severity",        # SHORT_STRING, CONSTANT_LIST
154     "old_id",                   # SHORT_STRING, CONSTANT_LIST
155   );
156
157   # This decribes the fields in the old TO defect record
158   our @old_TO_defect_fields = (
159     "ActionNotes",              # SHORT_STRING
160     "AdvancedFeature",          # SHORT_STRING, DYNAMIC_LIST
161     "Assigned_Date",            # DATE_TIME
162     "AttachmentsBRCM",          # ATTACHMENT_LIST
163     "Audit_Log",                # MULTILINE_STRING
164     "Category",                 # SHORT_STRING, CONSTANT_LIST
165     "Close_Date",               # DATE_TIME
166     "CommitmentLevel",          # SHORT_STRING, CONSTANT_LIST
167     "CommittedDate",            # DATE_TIME
168     "CommittedToProject",       # SHORT_STRING, DYNAMIC_LIST
169     "CustomerID",               # SHORT_STRING
170     "DataPendingNote",          # MULTILINE_STRING
171     "DeferredToChip",           # SHORT_STRING
172     "DeferredToProject",        # SHORT_STRING, DYNAMIC_LIST
173     "Description",              # MULTILINE_STRING
174     "DoesNotVerifyNote",        # MULTILINE_STRING
175     "Entry_Type",               # SHORT_STRING, CONSTANT_LIST
176     "Est_Time_To_Fix",          # SHORT_STRING
177     "Fixed_In_HW_Version",      # SHORT_STRING
178     "Fixed_In_Project",         # SHORT_STRING, DYNAMIC_LIST
179     "Fixed_In_SW_Version",      # SHORT_STRING
180     "Found_In_Project",         # SHORT_STRING, DYNAMIC_LIST
181     "GatingItem",               # SHORT_STRING, CONSTANT_LIST
182     "HUT",                      # SHORT_STRING, DYNAMIC_LIST
183     "HUT_Revision",             # SHORT_STRING, DYNAMIC_LIST
184     "HUT_Version",              # SHORT_STRING, DYNAMIC_LIST
185     "Headline",                 # SHORT_STRING
186     "History",                  # JOURNAL
187     "Issue_Classification",     # SHORT_STRING, CONSTANT_LIST
188     "Keywords",                 # MULTILINE_STRING, CONSTANT_LIST
189     "NoteBRCMOnly",             # MULTILINE_STRING
190     "NoteBugReview",            # MULTILINE_STRING
191     "Note_Entry",               # MULTILINE_STRING
192     "Notes_Log",                # MULTILINE_STRING
193     "OEMSubmitterName",         # SHORT_STRING
194     "OS",                       # SHORT_STRING, DYNAMIC_LIST
195     "Open_Close_Status",        # SHORT_STRING, CONSTANT_LIST
196     "Owner",                    # REFERENCE
197     "PendingHWSWReleases",      # INT
198     "Priority",                 # SHORT_STRING, CONSTANT_LIST
199     "Project",                  # REFERENCE
200     "ReportedBy",               # REFERENCE
201     "Resolution",               # SHORT_STRING, CONSTANT_LIST
202     "ResolveNote",              # MULTILINE_STRING
203     "ResolvedBy",               # REFERENCE
204     "Resolved_Date",            # DATE_TIME
205     "SQATestCase",              # SHORT_STRING, CONSTANT_LIST
206     "Service_Pack",             # SHORT_STRING, DYNAMIC_LIST
207     "Severity",                 # SHORT_STRING, CONSTANT_LIST
208     "Software",                 # SHORT_STRING, DYNAMIC_LIST
209     "Software_Version",         # SHORT_STRING
210     "Submit_Date",              # DATE_TIME
211     "Submitter",                # REFERENCE
212     "Symptoms",                 # MULTILINE_STRING, CONSTANT_LIST
213     "TCProcedure",              # MULTILINE_STRING
214     "TestBlocking",             # SHORT_STRING, CONSTANT_LIST
215     "TestCaseID",               # INT
216     "TestcaseComment",          # MULTILINE_STRING
217     "TimeFromSubmitToVerify",   # SHORT_STRING
218     "TimeSubmitToResolve",      # SHORT_STRING
219     "TimeToVerify",             # SHORT_STRING
220     "Title_2",                  # SHORT_STRING
221     "VerifiedBy",               # REFERENCE
222     "Verified_Date",            # DATE_TIME
223     "Verified_In_HW_Version",   # SHORT_STRING
224     "Verified_In_SW_Version",   # SHORT_STRING
225     "VerifyNote",               # MULTILINE_STRING
226     "Visibility",               # SHORT_STRING, DYNAMIC_LIST
227     "customer",                 # REFERENCE_LIST
228     "customer_severity",        # SHORT_STRING, CONSTANT_LIST
229     "old_id",                   # SHORT_STRING
230   );
231
232   # This describes the fields in the new Cont Defect record
233   our @new_Cont_defect_fields = (
234     "ActionNotes",              # SHORT_STRING
235 # Prod: <not defined>, TO: <not defined> -> Cont: Active_Deferred_Status
236     "Active_Deferred_Status",   # SHORT_STRING, CONSTANT_LIST
237     "Advanced_Feature",         # SHORT_STRING, DYNAMIC_LIST
238     "Assigned_Date",            # DATE_TIME
239     "AttachmentsBRCM",          # ATTACHMENT_LIST
240     "Audit_Log",                # MULTILINE_STRING
241     "Board_Revision",           # SHORT_STRING, DYNAMIC_LIST
242 # Prod: NoteBRCMOnly, TO: NoteBRCMOnly -> Cont: Broadcom_Only_Note
243     "Broadcom_Only_Note",       # MULTILINE_STRING
244 # Prod: NoteBugReview, TO: NoteBugReview -> Cont: Bug_Review_Note
245     "Bug_Review_Note",          # MULTILINE_STRING
246     "Category",                 # SHORT_STRING, CONSTANT_LIST
247     "Close_Date",               # DATE_TIME
248     "CommitmentLevel",          # SHORT_STRING, CONSTANT_LIST
249     "CommittedDate",            # DATE_TIME
250     "CommittedToProject",       # SHORT_STRING, DYNAMIC_LIST
251     "CustomerID",               # SHORT_STRING
252     "DataPendingNote",          # MULTILINE_STRING
253     "DeferredToChip",           # SHORT_STRING
254     "DeferredToProject",        # SHORT_STRING, DYNAMIC_LIST
255     "Description",              # MULTILINE_STRING
256     "DoesNotVerifyNote",        # MULTILINE_STRING
257     "Entry_Type",               # SHORT_STRING, CONSTANT_LIST
258     "Est_Time_To_Fix",          # SHORT_STRING
259     "Fixed_In_HW_Version",      # SHORT_STRING
260     "Fixed_In_Project",         # SHORT_STRING, DYNAMIC_LIST
261     "Fixed_In_SW_Version",      # SHORT_STRING
262 # Prod: Project (REFERENCE), TO: Project (REFERENCE) -> Cont: Found_In_Project (REFERENCE)
263     "Found_In_Project",         # REFERENCE
264 # Prod: <not defined>, TO: <not defined> -> Cont: Found_On_Gold
265     "Found_On_Gold",            # SHORT_STRING, CONSTANT_LIST
266     "Gating_Item_HW",           # SHORT_STRING, CONSTANT_LIST
267 # Prod: GatingItem, TO: GatingItem -> Cont: Gating_Item_SW, Gating_Item_HW
268     "Gating_Item_SW",           # SHORT_STRING, CONSTANT_LIST
269     "HUT",                      # SHORT_STRING, DYNAMIC_LIST
270     "HUT_Revision",             # SHORT_STRING, DYNAMIC_LIST
271 # Prod: Title, TO: Headline -> Cont: Headline
272     "Headline",                 # SHORT_STRING
273     "Issue_Classification",     # SHORT_STRING, CONSTANT_LIST
274     "Keywords",                 # MULTILINE_STRING, CONSTANT_LIST
275 # Prod: <not defined>, TO: <not defined> -> Cont: Newly_Introduce
276     "Newly_Introduce",          # SHORT_STRING, CONSTANT_LIST
277     "Note_Entry",               # MULTILINE_STRING
278     "Notes_Log",                # MULTILINE_STRING
279     "OEMSubmitterName",         # SHORT_STRING
280     "OS",                       # SHORT_STRING, DYNAMIC_LIST
281 # Prod: <not defined>, TO: <not defined> -> Cont: Other_HUT
282     "Other_HUT",                # MULTILINE_STRING
283     "Owner",                    # REFERENCE
284 # Prod: <not defined>, TO: <not defined> -> Cont: PQATestCase
285     "PQATestCase",              # SHORT_STRING, CONSTANT_LIST
286     "Priority",                 # SHORT_STRING, CONSTANT_LIST
287 # Prod: ReportedBy, TO: ReportedBy -> Cont: Reported_By
288     "Reported_By",              # REFERENCE
289     "Resolution",               # SHORT_STRING, CONSTANT_LIST
290     "ResolveNote",              # MULTILINE_STRING
291     "ResolvedBy",               # REFERENCE
292     "Resolved_Date",            # DATE_TIME
293 # Prod: <not defined>, TO: <not defined> -> Cont: Root_Caused
294     "Root_Caused",              # SHORT_STRING, CONSTANT_LIST
295 # Prod: <not defined>, TO: <not defined> -> Cont: Root_Caused_Note
296     "Root_Caused_Note",         # MULTILINE_STRING
297     "Service_Pack",             # SHORT_STRING, DYNAMIC_LIST
298     "Severity",                 # SHORT_STRING, CONSTANT_LIST
299     "Software",                 # SHORT_STRING, DYNAMIC_LIST
300     "Software_Version",         # SHORT_STRING
301     "Submit_Date",              # DATE_TIME
302     "Submitter",                # REFERENCE
303     "Symptoms",                 # MULTILINE_STRING, CONSTANT_LIST
304     "TCProcedure",              # MULTILINE_STRING
305     "TestCaseID",               # INT
306     "TestcaseComment",          # MULTILINE_STRING
307     "TimeFromSubmitToVerify",   # SHORT_STRING
308     "TimeSubmitToResolve",      # SHORT_STRING
309     "TimeToVerify",             # SHORT_STRING
310 # Prod: Title_2, TO: Title_2 -> Cont: Title
311     "Title",                    # SHORT_STRING
312     "VerifiedBy",               # REFERENCE
313     "Verified_Date",            # DATE_TIME
314     "Verified_In_HW_Version",   # SHORT_STRING
315     "Verified_In_SW_Version",   # SHORT_STRING
316     "VerifyNote",               # MULTILINE_STRING
317 # Prod: <not defined>, TO: <not defined> -> Cont: <added>
318     "Visibility",               # SHORT_STRING, DYNAMIC_LIST
319 # Prod: <not defined>, TO: <not defined> -> Cont: WorkAroundNote
320     "WorkAroundNote",           # MULTILINE_STRING
321     "customer",                 # REFERENCE_LIST
322     "customer_severity",        # SHORT_STRING, CONSTANT_LIST
323     "old_id",                   # SHORT_STRING
324 # Prod: <not defined>, TO: Found_In_Project -> Cont: <Deleted>
325 #   "Found_In_Project",         # SHORT_STRING, DYNAMIC_LIST
326 # Deleted fields:
327 #     "HUT_Version",            # SHORT_STRING, DYNAMIC_LIST
328 #     "Open_Close_Status",      # SHORT_STRING, CONSTANT_LIST
329 #     "PendingHWSWReleases",    # INT
330 #     "SQATestCase",            # SHORT_STRING, CONSTANT_LIST
331 #     "TestBlocking",           # SHORT_STRING, CONSTANT_LIST
332   );
333
334   # Customer and Project records appear in both instances of the old
335   # databases as well as the new Cont database and have not changed.
336   our @customer_fields = (
337     "Name",                     # SHORT_STRING
338     "Phone",                    # SHORT_STRING
339     "Fax",                      # SHORT_STRING
340     "Email",                    # SHORT_STRING
341     "CallTrackingID",           # SHORT_STRING
342     "Description",              # MULTILINE_STRING
343     "Company",                  # SHORT_STRING
344     "Attachment",               # ATTACHMENT_LIST
345   );
346
347   our @project_fields = (
348     "Name",                     # SHORT_STRING
349     "Description",              # MULTILINE_STRING
350   );
351
352   # Collect bad characters
353   our %bad_chars;
354
355   ## Internal variables ##
356   my $login     = "<username>";
357   my $password  = "<password>";
358   my $db_name;
359
360   my $id;
361
362   my $nbr_chars = 40;
363   my $half      = $nbr_chars / 2;
364
365   # Derived from http://hotwired.lycos.com/webmonkey/reference/special_characters/
366   my %char_map = (
367     128 => "&#128;",
368     129 => "&#129;",
369     130 => "&#130;",
370     131 => "&#131;",
371     132 => "&#132;",
372     133 => "&#133;",
373     134 => "&#134;",
374     135 => "&#135;",
375     136 => "&#136;",
376     137 => "&#137;",
377     138 => "&#138;",
378     139 => "&#139;",
379     140 => "&#140;",
380     141 => "&#141;",
381     142 => "&#142;",
382     143 => "&#143;",
383     144 => "&#144;",
384     145 => "'",         # Signal "smart quote" left
385     146 => "'",         # Signal "smart quote" right
386     147 => "\"",        # Double "smart quote" left
387     148 => "\"",        # Double "smart quote" right
388     149 => "&#149;",
389     150 => "&ndash;",   # En dash
390     151 => "&mdash;",   # Em dash
391     152 => "&#152;",
392     153 => "&#153;",
393     154 => "&#154;",
394     155 => "&#155;",
395     156 => "&#156;",
396     157 => "&#157;",
397     158 => "&#158;",
398     159 => "&#159;",
399     160 => "&nbsp;",    # Nonbreaking space
400     161 => "&iexcl;",   # Inverted exclamation (¡)
401     162 => "&cent;",    # Cent sign (¢)
402     163 => "&pound;",   # Pound sterling (£)
403     164 => "&curren;",  # General currency sign (¤)
404     165 => "&yen;",     # Yen sign (¥)
405     166 => "&brkbar;",  # Broken vertical bar (¦)
406     167 => "&sect;",    # Section sign (§)
407     168 => "&uml;",     # Umlaut (¨)
408     169 => "&copy;",    # Copyright (©)
409     170 => "&ordf;",    # Feminine ordinal (ª)
410     171 => "&laquo;",   # Left angle quote («)
411     172 => "&not;",     # Not sign (¬)
412     173 => "&shy;",     # Soft hyphen
413     174 => "&reg;",     # Registered trademark (®)
414     175 => "&macr;",    # Macron accent (¯)
415     176 => "&deg;",     # Degree sign (°)
416     177 => "&plusmn;",  # Plus or minus (±)
417     178 => "&sup2;",    # Superscript two (²)
418     179 => "&sup3;",    # Superscript three (³)
419     180 => "&acute;",   # Acute accent (´)
420     181 => "&micro;",   # Micro sign (µ)
421     182 => "&para;",    # Paragraph sign (¶)
422     183 => "&middot;",  # Middle dot (·)
423     184 => "&cedil;",   # Cedilla (¸)
424     185 => "&sup1;",    # Superscript one (¹)
425     186 => "&ordm;",    # Masculine ordinal (º)
426     187 => "&raquo;",   # Right angle quote (»)
427     188 => "&frac14;",  # One-forth (¼)
428     189 => "&frac12;",  # One-half (½)
429     190 => "&frac24;",  # Three-fourths (¾)
430     191 => "&iquest;",  # Inverted question mark (¿)
431     192 => "&Agrave;",  # Uppercase A, grave accent (À)
432     193 => "&Aacute;",  # Uppercase A, acute accent (Á)
433     194 => "&Acirc;",   # Uppercase A, circumflex accent (Â)
434     195 => "&Atilde;",  # Uppercase A, tilde (Ã)
435     196 => "&Auml;",    # Uppercase A, umlaut (Ä)
436     197 => "&Aring;",   # Uppercase A, ring (Å)
437     198 => "&AElig;",   # Uppercase AE (Æ)
438     199 => "&Ccedil;",  # Uppercase C, cedilla (Ç)
439     200 => "&Egrave;",  # Uppercase E, grave accent (È)
440     201 => "&Eacute;",  # Uppercase E, acute accent (É)
441     202 => "&Ecirc;",   # Uppercase E, circumflex accent (Ê)
442     203 => "&Euml;",    # Uppercase E, umlaut (Ë)
443     204 => "&Igrave;",  # Uppercase I, grave accent (Ì)
444     205 => "&Iacute;",  # Uppercase I, acute accent (Í)
445     206 => "&Icirc;",   # Uppercase I, circumflex accent (Î)
446     207 => "&Iuml;",    # Uppercase I, umlaut (Ï)
447     208 => "&ETH;",     # Uppercase Eth, Icelandic (Ð)
448     209 => "&Ntilde;",  # Uppercase N, tilde (Ñ)
449     210 => "&Ograve;",  # Uppercase O, grave accent (Ò)
450     211 => "&Oacute;",  # Uppercase O, acute accent (Ó)
451     212 => "&Ocirc;",   # Uppercase O, circumflex accent (Ô)
452     213 => "&Otilde;",  # Uppercase O, tilde (Õ)
453     214 => "&Ouml;",    # Uppercase O, umlaut (Ö)
454     215 => "&times;",   # Muliplication sign (×)
455     216 => "&Oslash;",  # Uppercase O, slash (Ø)
456     217 => "&Ugrave;",  # Uppercase U, grave accent (Ù)
457     218 => "&Uacute;",  # Uppercase U, acute accent (Ú)
458     219 => "&Ucirc;",   # Uppercase U, circumflex accent (Û)
459     220 => "&Uuml;",    # Uppercase U, umlaut (Ü)
460     221 => "&Yacute;",  # Uppercase Y, acute accent (Ý)
461     222 => "&THORN;",   # Uppercase THORN, Icelandic (Þ)
462     223 => "&szlig;",   # Lowercase sharps, German (ß)
463     224 => "&agrave;",  # Lowercase a, grave accent (à)
464     225 => "&aacute;",  # Lowercase a, acute accent (á)
465     226 => "&acirc;",   # Lowercase a, circumflex acirc (â)
466     227 => "&atilde;",  # Lowercase a, tilde (ã)
467     228 => "&auml;",    # Lowercase a, umlaut (ä)
468     229 => "&aring;",   # Lowercase a, ring (å)
469     230 => "&aelig;",   # Lowercase ae (æ)
470     231 => "&ccedil;",  # Lowercase c, cedilla (ç)
471     232 => "&egrave;",  # Lowercase e, grave accent (è)
472     233 => "&eacute;",  # Lowercase e, acute accent (é)
473     234 => "&ecirc;",   # Lowercase e, circumflex accent (ê)
474     235 => "&euml;",    # Lowercase e, umlaut (ë)
475     236 => "&igrave;",  # Lowercase i, grave accent (ì)
476     237 => "&iacute;",  # Lowercase i, acute accent (í)
477     238 => "&icirc;",   # Lowercase i, circumflex accent (î)
478     239 => "&iuml;",    # Lowercase i, umlaut (ï)
479     240 => "&eth;",     # Lowercase eth, Icelandic (ð)
480     241 => "&ntilde;",  # Lowercase n, tilde (ñ)
481     242 => "&ograve;",  # Lowercase o, grave accent (ò)
482     243 => "&oacute;",  # Lowercase o, acute accent (ó)
483     244 => "&ocirc;",   # Lowercase o, circumflex accent (ô)
484     245 => "&otilde;",  # Lowercase o, tilde (õ)
485     246 => "&ouml;",    # Lowercase o, umlaut (ö)
486     247 => "&divide;",  # Division sign (÷)
487     248 => "&oslash;",  # Lowercase o, slash (ø)
488     249 => "&ugrave;",  # Lowercase u, grave accent (ù)
489     250 => "&uacute;",  # Lowercase u, acute accent (ú)
490     251 => "&ucirc;",   # Lowercase u, circumflex accent (û)
491     252 => "&uuml;",    # Lowercase u, umlaut (ü)
492     253 => "&yacute;",  # Lowercase y, acute accent (ý)
493     254 => "&thorn;",   # Lowercase thorn, Icelandic (þ)
494     255 => "&yuml;",    # Lowercase y, umlaut (ÿ)
495   );
496
497   ## Exported functions ##
498   # Add a value to a field's dynamic list
499   sub AddToFieldChoiceList {
500     my $session         = shift;
501     my $entity          = shift;
502     my $dynamic_list    = shift;
503     my $name            = shift;
504     my $value           = shift;
505
506     return if $value eq "";
507
508     # It seems that adding the entry to the dynamic list is not enough.
509     # I believe that Clearquest caches entries on a dynamic list so we
510     # need to tell Clearquest about this new entry.
511     my $add_value  = 1;
512     my @values = @{$entity->GetFieldChoiceList ($name)};
513
514     # Ack! Seems now we have values like Service_Pack = "1.A" and
515     # Service_Pack = "1.a", which translate to the same value as far
516     # as a dynamic list is concerned, so we'll do the comparison
517     # ignoring case... Additionally there can be regex meta characters
518     # in the value so we'll need to protect from that.
519     foreach (@values) {
520       if ("\L$value\E" eq "\L$_\E") {
521         $add_value = 0;
522         last;
523       } # if
524     } # foreach
525
526     if ($add_value) {
527       push @values, $value;
528
529       $entity->SetFieldChoiceList ($name, \@values);
530     } # if
531
532     # Get the current values, if any
533     @values = @{$session->GetListMembers ($dynamic_list)};
534
535     # Search to see if the item is already on the list
536     foreach (@values) {
537       return if ("\L$value\E" eq "\L$_\E");
538     } # if
539
540     $session->AddListMember ($dynamic_list, $value);
541
542     push @values, $value;
543
544     $session->SetListMembers ($dynamic_list, \@values);
545   } # AddToDynamicList
546
547   # TO: defect: Found_In_Project is currently a dynamic list but is
548   # going to Cont: defect: Found_In_Project which is a reference to
549   # Cont: Project. So we need to dynamically add those.
550   sub AddToProject {
551     my $log     = shift;
552     my $to      = shift;
553     my $project = shift;
554
555     if (ProjectExists $to, $project) {
556       return;
557     } # if
558
559     my $entity = $to->BuildEntity ("Project");
560
561     $entity->SetFieldValue ("name", $project);
562
563     # Call the Validate method
564     my $errmsg = $entity->Validate;
565
566     $log->err ("Unable to validate Project record: $project:\n$errmsg", 1) if $errmsg ne "";
567
568     # Post record to database
569     $entity->Commit if $errmsg eq "";
570   } # AddToProject
571
572   sub CheckField {
573     my $log             = shift;
574     my $db_name         = shift;
575     my $record_name     = shift;
576     my $id              = shift;
577     my $field_name      = shift;
578     my $str             = shift;
579
580     return $str if length $str eq 0; # Ignore empty strings
581
582     if ($str =~ /[^\t\n\r -\177]/) {
583       for (my $x = 0; $x < length $str; $x++) {
584         my $y = substr $str, $x, 1;
585         if ($y =~ /[^\t\n\r -\177]/) {
586           my $o = ord ($y);
587           display "At char #$x found \"$y\" ($o)";
588           my $s = substr $str, $x - 20, 40;
589           display "\"$s\"";
590         } # if
591       } # for
592       error "$field_name match", 1;
593     } # if
594
595     for (my $i = 0; $i < length $str; $i++) {
596       my $ord = ord (substr $str, $i, 1);
597
598       if ($ord < 0 or $ord > 127) {
599         # $id is undefined at this point...
600         $log->msg ("$db_name:$record_name:$id:$field_name:$i");
601         $log->msg ("Old Contents:\n$str");
602         $str = FixChar ($str, $i);
603         $log->msg ("New Contents:\n$str");
604       } # if
605     } # foreach
606
607     return $str;
608   } # CheckField
609
610   sub CheckRecord {
611     my $log             = shift;
612     my $session         = shift;
613     my $id_name         = shift;
614     my $record_name     = shift;
615     my $id              = shift;
616     my @fields          = @_;
617
618     my $result;
619
620     if (defined $id) {
621       $result = GetDefectRecord $log, $session, $record_name, $id;
622     } else {
623       $result = GetAllDefectRecords $log, $session, $record_name;
624     } # if
625
626     while ($result->MoveNext == $CQPerlExt::CQ_SUCCESS) {
627       # GetEntity by using $id
628       $id               = $result->GetColumnValue (1);
629       my $entity        = $session->GetEntity ($record_name, $id);
630
631       $log->msg ($id);
632
633       foreach (@fields) {
634         my $name        = $_;
635         my $value       = $entity->GetFieldValue ($name)->GetValue;
636
637         $value = CheckField $log, $db_name, $record_name, $id, $name, $value;
638       } # for
639     } # for
640   } # CheckRecord
641
642   sub DeleteDynamicLists {
643     my $log             = shift;
644     my $from            = shift;
645
646     my @dynamic_lists = (
647       "Advanced_Feature",
648       "Board_Revision",
649       "HUT",
650       "HUT_Revision",
651       "OS",
652       "OS_Service_Pack",
653       "Other_HUT",
654       "Project",
655       "Reported_By",
656       "Software",
657       "Visibility",
658     );
659
660     $log->msg ("Clearing dynamic lists...");
661
662     foreach my $name (@dynamic_lists) {
663       my @values = @{$from->GetListMembers ($name)};
664
665       foreach my $value (@values) {
666         $from->DeleteListMember ($name, $value);
667       } # foreach
668     } # foreach
669   } # DeleteDynamicLists
670
671   sub DeleteRecords {
672     my $log             = shift;
673     my $from            = shift;
674     my $record_name     = shift;
675
676     # Create a query for $record_name
677     my $query = $from->BuildQuery ($record_name);
678
679     $query->BuildField ("dbid");
680
681     # Build the result set
682     my $result = $from->BuildResultSet ($query);
683
684     # Execute the query
685     my $record_count = $result->ExecuteAndCountRecords;
686
687     $log->msg ("Found $record_count $record_name records to delete...");
688
689     return if $record_count eq 0;
690
691     my $old_bufffer_status = $|;
692     $| = 1; # Turn off buffering
693
694     # Now for each record returned by the query...
695     while ($result->MoveNext == 1) {
696       my $id = $result->GetColumnValue (1);
697
698       # Get entity
699       my $entity = $from->GetEntityByDbId ($record_name, $id);
700
701       # Delete it
702       my $errmsg = $from->DeleteEntity ($entity, "delete");
703
704       verbose ".", undef, "nolf";
705       $log->err ("\n$errmsg\n") if $errmsg ne "";
706     } # while
707
708     verbose "";
709
710     $| = $old_bufffer_status; # Restore buffering
711   } # DeleteRecords
712
713   sub EndSession {
714     my $session = shift;
715
716     CQSession::Unbuild $session;
717   } # EndSession
718
719   sub GetAllDefectRecords {
720     my $log             = shift;
721     my $from            = shift;
722     my $record_name     = shift;
723
724     # Create a query for the record
725     my $query = $from->BuildQuery ($record_name);
726
727     # Add only dbid to the query. We'll retrieve the whole entity record later.
728     $query->BuildField ("id");
729
730     # Build the result set
731     my $result = $from->BuildResultSet ($query);
732
733     # Execute the query
734     my $record_count = $result->ExecuteAndCountRecords;
735
736     $log->msg ("Found $record_count $record_name records...");
737
738     if ($record_count eq 0) {
739       return undef;
740     } else {
741       return $result;
742     } # if
743   } # GetAllDefectRecords
744
745   sub GetDefectRecord {
746     my $log             = shift;
747     my $from            = shift;
748     my $record_name     = shift;
749     my $id              = shift;
750
751     my $query   = $from->BuildQuery ($record_name);
752     my $filter  = $query->BuildFilterOperator ($CQPerlExt::AD_BOOL_OP_AND);
753
754     $query->BuildField ("id");
755
756     # BuildFilter requires an array reference
757     my @ids;
758     push @ids, $id;
759     $filter->BuildFilter ("id", $CQPerlExt::CQ_COMP_OP_EQ, \@ids);
760
761     my $result = $from->BuildResultSet ($query);
762     my $record_count = $result->ExecuteAndCountRecords;
763
764     $log->msg ("Found $record_count $record_name record...");
765
766     if ($record_count eq 0) {
767       return undef;
768     } else {
769       return $result;
770     } # if
771   } # GetDefectRecord
772
773   sub ProjectExists {
774     my $to      = shift;
775     my $project = shift;
776
777     my $query = $to->BuildQuery ("Project");
778
779     my $filter = $query->BuildFilterOperator ($CQPerlExt::AD_BOOL_OP_AND);
780
781     $query->BuildField  ("name");
782
783     # BuildFilter requires an array reference
784     my @projects;
785     push @projects, $project;
786     $filter->BuildFilter ("name", $CQPerlExt::CQ_COMP_OP_EQ, \@projects);
787
788     my $result = $to->BuildResultSet ($query);
789
790     my $record_count = $result->ExecuteAndCountRecords;
791
792     return $record_count;
793   } # ProjectExists
794
795   sub StartSession {
796     $db_name    = shift;
797     $masterdb   = shift;
798
799     my $session = CQPerlExt::CQSession_Build ();
800
801     $masterdb = "" if !defined $masterdb;
802
803     $session->UserLogon ($login, $password, $db_name, $masterdb);
804
805     return $session;
806   } # StartSession
807
808   sub TransferAttachments {
809     my $log     = shift;
810     my $from    = shift;
811     my $to      = shift;
812
813     my @files_created;
814
815     my $from_attachment_fields  = $from->GetAttachmentFields;
816
817     for (my $i = 0; $i < $from_attachment_fields->Count; $i++) {
818       my $from_attachment_field = $from_attachment_fields->Item ($i);
819       my $field_name            = $from_attachment_field->GetFieldName;
820
821       # At this point we don't have any info about whether we are
822       # coming from Prod or TO, however, there are the following fields:
823       #
824       #          TO                    Prod                   Cont
825       # ----------------------- ----------------------- ----------------
826       # Attachments             Attachments             Attachments
827       # AttachmentsBRCM         AttachmentBRCM          AttachmentsBRCM
828       #
829       # You may notice that Prod: AttachmentBRCM is missing the "s".
830       # Therefore:
831       $field_name = "AttachmentsBRCM" if $field_name eq "AttachmentBRCM";
832
833       my $from_attachments      = $from_attachment_field->GetAttachments;
834
835       my $filename_suffix = 0;
836
837       for (my $j = 0; $j < $from_attachments->Count; $j++) {
838         my $from_attachment     = $from_attachments->Item ($j);
839         my $description         = $from_attachment->GetDescription;
840         my $filename            = $from_attachment->GetFileName;
841
842         debug "Processing attachment #$j: $filename: $description";
843
844         # Extract the attached file to the file named attachment;
845         # Argh! Sometimes people attach files with the same filename!
846         # This works because filename is not really used except when
847         # you initially load the file. So the user could have, for
848         # example, captured say a logfile.txt, attached it,
849         # regenerated a new logfile.txt and attached it! This is
850         # perfectly acceptable since logfile.txt is copied into the
851         # database. However, when we extract it here we just use
852         # $filename. The result is that the second logfile.txt
853         # overwrites the first logfile.txt! We need to check for
854         # clashes (only a handful of them) and generate a new
855         # filename.
856         if (-f $filename) {
857           $filename_suffix++;
858           $filename = "$filename.$filename_suffix";
859         } # if
860
861         $from_attachment->Load ($filename);
862
863         $to->AddAttachmentFieldValue ($field_name, $filename, $description);
864
865         push @files_created, $filename;
866       } # for
867     } # for
868
869     return @files_created;
870   } # TransferAttachments
871
872   sub TransferHistory {
873     my $from_entity     = shift;
874     my $to_entity       = shift;
875     my $filename        = shift;
876
877     my $history_fields          = $from_entity->GetHistoryFields;
878     my $nbr_history_fields      = $history_fields->Count;
879
880     return if $nbr_history_fields eq 0;
881
882     for (my $i = 0; $i < $nbr_history_fields; $i++) {
883       my $histories     = $history_fields->Item ($i)->GetHistories;
884       my $nbr_histories = $histories->Count;
885
886       return if $nbr_histories eq 0;
887
888       # Write out history to History.txt
889       open HISTORY, ">$filename"
890         or error "Unable to open $filename", 1;
891
892       print HISTORY "Previous History:\n";
893       print HISTORY "-----------------\n";
894
895       for (my $j = 0; $j < $nbr_histories; $j++) {
896         my $history_item        = $histories->Item ($j);
897         my $history_value       = $history_item->GetValue;
898
899         # Remove dbid
900         $history_value =~ /\S*\s*(.*$)/;
901         print HISTORY "$1\n";
902       } # for
903
904       close HISTORY;
905     } # for
906
907     # Add previous history as an AttachmentsBRCM
908     $to_entity->AddAttachmentFieldValue ("AttachmentsBRCM", $filename, "Previous history");
909   } # TransferHistory
910
911   sub TransferRecords {
912     my $log             = shift;
913     my $from            = shift;
914     my $to              = shift;
915     my $dbname          = shift;
916     my $record_name     = shift;
917     my @field_list      = @_;
918
919     # Create a query for the record
920     my $query = $from->BuildQuery ($record_name);
921
922     # Always get the $id_name field
923     $query->BuildField ("dbid");
924
925     # Add all of @field_list to the query
926     foreach (@field_list) {
927       $query->BuildField ($_);
928     } # foreach
929
930     # Build the result set
931     my $result = $from->BuildResultSet ($query);
932
933     # Execute the query
934     my $record_count = $result->ExecuteAndCountRecords;
935
936     verbose "Found $record_count $record_name records to merge...";
937
938     return if $record_count eq 0;
939
940     my $old_bufffer_status = $|;
941     $| = 1; # Turn off buffering
942
943     # Now for each record returned by the query...
944     while ($result->MoveNext == 1) {
945       # Create a new entity
946       my $entity = $to->BuildEntity ($record_name);
947
948       my $cols = $result->GetNumberOfColumns;
949
950       my $id = $result->GetColumnValue (1);
951
952       # Get the fields...
953       for (my $i = 2; $i <= $cols; $i++) {
954         my $name  = $result->GetColumnLabel ($i);
955         my $value = $result->GetColumnValue ($i);
956
957         # Check field for non US ASCII characters and fix them
958         $value = CheckField $dbname, $record_name, $id, $name, $value;
959
960         # Set the field's value
961         $entity->SetFieldValue ($name, $value);
962       } # for
963
964       # Call the Validate method
965       my $errmsg = $entity->Validate;
966
967       $log->err ("Unable to validate $record_name record:\n$errmsg", 1) if $errmsg ne "";
968
969       # Post record to database
970       $entity->Commit;
971       verbose ".", undef, "nolf";
972     } # while
973
974     $| = $old_bufffer_status; # Restore buffering
975     verbose " done";
976   } # TransferRecords
977
978   # Internal functions
979   sub DisplayWord {
980     my $str     = shift;
981     my $start   = shift;
982
983     my $ord             = ord (substr $str, $start, 1);
984     my $end             = $start;
985     my $orig_start      = $start;
986
987     # Let's just show a small subset of characters
988     if (length $str < $nbr_chars) {
989       $end   = length $str;
990       $start = 0;
991     } elsif (($start + $half) > length $str) {
992       $end = length $str;
993       my $right = length $str - $start;
994       if (($start - ($half + ($half - $right))) lt 0) {
995         $start = 0;
996       } else {
997         $start = $start - ($half + $right);
998       } # if
999     } elsif (($start - $half) < 0) {
1000       $start = 0;
1001       if ($start + ($half + $start) gt length $str) {
1002         $end = length $str;
1003       } else {
1004         $end = $start + ($half + $start);
1005       } # if
1006     } else {
1007       $end   = $start + $half;
1008       $start = $start - $half;
1009     } # if
1010
1011     my $word = substr $str, $start, $end - $start;
1012
1013     debug "\t@ pos $orig_start ($ord)\n\t\"$word\"\n";
1014   } # DisplayWord
1015
1016   sub FixChar {
1017     my $str     = shift;
1018     my $pos     = shift;
1019
1020     my $ord     = ord (substr $str, $pos, 1);
1021
1022     error "Unknown character found ($ord) \"" . substr ($str, $pos, 1) . "\"", 1
1023       if (!defined $char_map {$ord});
1024
1025     if ($debug eq "yes") {
1026       debug "Before:\n";
1027       DisplayWord $str, $pos;
1028     } # if
1029
1030     substr ($str, $pos, 1) = $char_map {$ord};
1031
1032     if ($debug eq "yes") {
1033        debug "After:\n";
1034       DisplayWord $str, $pos;
1035     } # if
1036
1037     return $str;
1038   } # FixChar
1039
1040 1;