echi-converter 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ class CreateEchiAcds < ActiveRecord::Migration
2
+ def self.up
3
+ #We create the table from the one defined in the application.yml file
4
+ create_table "echi_acds", :force => true do |t|
5
+ @@echi_schema["echi_acds"].each do | field |
6
+ case field["type"]
7
+ when 'int'
8
+ t.column field["name"], :integer, :limit => field["length"], :precision => field["length"], :scale => 0
9
+ when 'str'
10
+ t.column field["name"], :string, :limit => field["length"]
11
+ when 'datetime'
12
+ t.column field["name"], :datetime
13
+ when 'bool'
14
+ t.column field["name"], :string, :limit => 1
15
+ when 'boolint'
16
+ t.column field["name"], :string, :limit => 1
17
+ end
18
+ end
19
+ end
20
+ add_index "echi_acds", "acd_id"
21
+ end
22
+
23
+ def self.down
24
+ remove_index "echi_acds", "acd_id"
25
+ drop_table "echi_acds"
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ class CreateEchiSplits < ActiveRecord::Migration
2
+ def self.up
3
+ #We create the table from the one defined in the application.yml file
4
+ create_table "echi_splits", :force => true do |t|
5
+ @@echi_schema["echi_splits"].each do | field |
6
+ case field["type"]
7
+ when 'int'
8
+ t.column field["name"], :integer, :limit => field["length"], :precision => field["length"], :scale => 0
9
+ when 'str'
10
+ t.column field["name"], :string, :limit => field["length"]
11
+ when 'datetime'
12
+ t.column field["name"], :datetime
13
+ when 'bool'
14
+ t.column field["name"], :string, :limit => 1
15
+ when 'boolint'
16
+ t.column field["name"], :string, :limit => 1
17
+ end
18
+ end
19
+ end
20
+ add_index "echi_splits", "acd_number"
21
+ end
22
+
23
+ def self.down
24
+ remove_index "echi_splits", "acd_number"
25
+ drop_table "echi_splits"
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ class CreateEchiTrunkGroups < ActiveRecord::Migration
2
+ def self.up
3
+ #We create the table from the one defined in the application.yml file
4
+ create_table "echi_trunk_groups", :force => true do |t|
5
+ @@echi_schema["echi_trunk_groups"].each do | field |
6
+ case field["type"]
7
+ when 'int'
8
+ t.column field["name"], :integer, :limit => field["length"], :precision => field["length"], :scale => 0
9
+ when 'str'
10
+ t.column field["name"], :string, :limit => field["length"]
11
+ when 'datetime'
12
+ t.column field["name"], :datetime
13
+ when 'bool'
14
+ t.column field["name"], :string, :limit => 1
15
+ when 'boolint'
16
+ t.column field["name"], :string, :limit => 1
17
+ end
18
+ end
19
+ end
20
+ add_index "echi_trunk_groups", "acd_number"
21
+ end
22
+
23
+ def self.down
24
+ remove_index "echi_trunk_groups", "acd_number"
25
+ drop_table "echi_trunk_groups"
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ class CreateEchiVectors < ActiveRecord::Migration
2
+ def self.up
3
+ #We create the table from the one defined in the application.yml file
4
+ create_table "echi_vectors", :force => true do |t|
5
+ @@echi_schema["echi_vectors"].each do | field |
6
+ case field["type"]
7
+ when 'int'
8
+ t.column field["name"], :integer, :limit => field["length"], :precision => field["length"], :scale => 0
9
+ when 'str'
10
+ t.column field["name"], :string, :limit => field["length"]
11
+ when 'datetime'
12
+ t.column field["name"], :datetime
13
+ when 'bool'
14
+ t.column field["name"], :string, :limit => 1
15
+ when 'boolint'
16
+ t.column field["name"], :string, :limit => 1
17
+ end
18
+ end
19
+ end
20
+ add_index "echi_vectors", "acd_number"
21
+ end
22
+
23
+ def self.down
24
+ remove_index "echi_vectors", "acd_number"
25
+ drop_table "echi_vectors"
26
+ end
27
+ end
@@ -14,7 +14,7 @@ ActiveRecord:: Base.establish_connection(
14
14
 
15
15
  #define a simple model
16
16
  class EchiRecord < ActiveRecord::Base
17
- set_table_name "PCO_ECHIRECORD" # comment out if not using oracle
17
+ set_table_name "PCO_ECHIRECORD" # comment out if not using oracle
18
18
  end
19
19
 
20
20
  begin
data/lib/database.rb CHANGED
@@ -5,6 +5,9 @@ class EchiLog < ActiveRecord::Base
5
5
  end
6
6
 
7
7
  #These database tables may not always be present
8
+ class EchiAcd < ActiveRecord::Base
9
+ end
10
+
8
11
  class EchiAgent < ActiveRecord::Base
9
12
  end
10
13
 
@@ -14,5 +17,14 @@ end
14
17
  class EchiCwc < ActiveRecord::Base
15
18
  end
16
19
 
20
+ class EchiSplit < ActiveRecord::Base
21
+ end
22
+
23
+ class EchiTrunkGroup < ActiveRecord::Base
24
+ end
25
+
17
26
  class EchiVdn < ActiveRecord::Base
18
27
  end
28
+
29
+ class EchiVector < ActiveRecord::Base
30
+ end
@@ -37,7 +37,9 @@ module EchiConverter
37
37
  @log.info "Initialized the database"
38
38
  rescue => err
39
39
  @log.fatal "Could not connect to the database - " + err
40
- send_email_alert "DATABASE"
40
+ if $config["send_email"] == true
41
+ send_email_alert "DATABASE"
42
+ end
41
43
  end
42
44
  end
43
45
 
@@ -61,6 +63,7 @@ module EchiConverter
61
63
 
62
64
  #Method to send alert emails
63
65
  def send_email_alert reason
66
+ @log.debug "send_email_alert method"
64
67
  begin
65
68
  Net::SMTP.start($config["smtp_server"], $config["smtp_port"]) do |smtp|
66
69
  smtp.open_message_stream('donotreply@echi-converter.rubyforge.org', [$config["alert_email_address"]]) do |f|
@@ -85,6 +88,7 @@ module EchiConverter
85
88
  #Set the working directory to copy processed files to, if it does not exist creat it
86
89
  #Directory names based on year/month so as not to exceed 5K files in a single directory
87
90
  def set_directory working_directory
91
+ @log.debug "set_directory method"
88
92
  time = Time.now
89
93
  directory_year = working_directory + "/../files/processed/" + time.year.to_s
90
94
  directory_month = directory_year + "/" + time.month.to_s
@@ -101,6 +105,7 @@ module EchiConverter
101
105
 
102
106
  #Method to get FTP files
103
107
  def get_ftp_files
108
+ @log.debug "get_ftp_files method"
104
109
  filelist_fetcher = FtpFetcher.new
105
110
  filequeue = filelist_fetcher.fetch_list @log
106
111
 
@@ -140,12 +145,15 @@ module EchiConverter
140
145
  result = fetcher.fetch_ftp_files filequeue, @log
141
146
  end
142
147
  if result == false
143
- send_email_alert "FTP"
148
+ if $config["send_email"] == true
149
+ send_email_alert "FTP"
150
+ end
144
151
  end
145
152
  end
146
153
 
147
154
  #Method to write to the log table
148
155
  def log_processed_file type, filedata
156
+ @log.debug "log_processed file method"
149
157
  begin
150
158
  echi_log = EchiLog.new
151
159
  echi_log.filename = filedata["name"]
@@ -165,6 +173,7 @@ module EchiConverter
165
173
 
166
174
  #Method for parsing the various datatypes from the ECH file
167
175
  def dump_binary type, length
176
+ @log.debug "dump_binary method"
168
177
  case type
169
178
  when 'int'
170
179
  #Process integers, assigning appropriate profile based on length
@@ -205,6 +214,7 @@ module EchiConverter
205
214
 
206
215
  #Mehtod that performs the conversions
207
216
  def convert_binary_file filename
217
+ @log.debug "convert_binary_file"
208
218
  #Open the file to process
209
219
  echi_file = $workingdir + "/../files/to_process/" + filename
210
220
  @binary_file = open(echi_file,"rb")
@@ -280,6 +290,7 @@ module EchiConverter
280
290
  end
281
291
 
282
292
  def process_ascii filename
293
+ @log.debug "process_ascii method"
283
294
  echi_file = $workingdir + "/../files/to_process/" + filename
284
295
 
285
296
  begin
@@ -334,17 +345,25 @@ module EchiConverter
334
345
  end
335
346
 
336
347
  def insert_dat_data tablename, row
337
-
348
+ @log.debug "insert_dat_data method"
338
349
  begin
339
350
  case tablename
351
+ when "echi_acds"
352
+ echi_dat_record = EchiAcd.new
340
353
  when "echi_agents"
341
354
  echi_dat_record = EchiAgent.new
342
355
  when "echi_aux_reasons"
343
356
  echi_dat_record = EchiAuxReason.new
344
357
  when "echi_cwcs"
345
358
  echi_dat_record = EchiCwc.new
359
+ when "echi_splits"
360
+ echi_dat_record = EchiSplit.new
361
+ when "echi_trunk_groups"
362
+ echi_dat_record = EchiTrunkGroup.new
346
363
  when "echi_vdns"
347
364
  echi_dat_record = EchiVdn.new
365
+ when "echi_vectors"
366
+ echi_dat_record = EchiVector.new
348
367
  end
349
368
  cnt = 0
350
369
  @echi_schema[tablename].each do | field |
@@ -358,76 +377,114 @@ module EchiConverter
358
377
 
359
378
  end
360
379
 
361
- #Process the appropriate table name
362
- def process_proper_table file
363
- @record_cnt = 0
364
- process_file = File.open(file["filename"])
365
- process_file.each do |row|
366
- if row != nil
367
- field = row.rstrip.split('|')
368
- @log.debug '<====================START ' + file["name"] + ' RECORD ' + @record_cnt.to_s + ' ====================>'
369
- case file["name"]
370
- when "echi_agents"
371
- record = EchiAgent.find(:first, :conditions => [ "login_id = ? AND group_id = ?", field[1], field[0]])
372
- when "echi_aux_reasons"
373
- record = EchiAuxReason.find(:first, :conditions => [ "aux_reason = ? AND group_id = ?", field[1], field[0]])
374
- when "echi_cwcs"
375
- record = EchiCwc.find(:first, :conditions => [ "cwc = ? AND group_id = ?", field[1], field[0]])
376
- when "echi_vdns"
377
- record = EchiVdn.find(:first, :conditions => [ "vdn = ? AND group_id = ?", field[1], field[0]])
378
- end
379
- if record != nil
380
- if record.name != field[2]
381
- record.name = field[2]
382
- record.update
383
- @record_cnt += 1
384
- @log.debug "Updated record - " + field.inspect
385
- else
386
- @log.debug "No update required for - " + field.inspect
387
- end
388
- else
389
- insert_dat_data file["name"], field
390
- @record_cnt += 1
391
- @log.debug "Inserted new record - " + field.inspect
392
- end
393
- end
394
- @log.debug '<====================STOP ' + file["name"] + ' RECORD ' + @record_cnt.to_s + ' ====================>'
395
- end
396
- process_file.close
397
-
380
+ #Move the file to the archive location
381
+ def archive_file file, record_cnt
382
+ @log.debug "archive_file method"
398
383
  case file["name"]
384
+ when "echi_acds"
385
+ filename_elements = $config["echi_acd_dat"].split(".")
399
386
  when "echi_agents"
400
387
  filename_elements = $config["echi_agent_dat"].split(".")
401
388
  when "echi_aux_reasons"
402
389
  filename_elements = $config["echi_aux_rsn_dat"].split(".")
403
390
  when "echi_cwcs"
404
391
  filename_elements = $config["echi_cwc_dat"].split(".")
392
+ when "echi_splits"
393
+ filename_elements = $config["echi_split_dat"].split(".")
405
394
  when "echi_vdns"
406
395
  filename_elements = $config["echi_vdn_dat"].split(".")
396
+ when "echi_trunk_groups"
397
+ filename_elements = $config["echi_trunk_group_dat"].split(".")
398
+ when "echi_vectors"
399
+ filename_elements = $config["echi_vector_dat"].split(".")
407
400
  end
408
401
  new_filename = filename_elements[0] + "_" + UUID.timestamp_create.to_s + "." + filename_elements[1]
409
402
  target_file = @processeddirectory + "/" + new_filename
410
403
  begin
411
404
  FileUtils.mv(file["filename"], target_file)
412
405
  if $config["echi_process_log"] == "Y"
413
- log_processed_file nil, { "name" => new_filename, "cnt" => @record_cnt }
406
+ log_processed_file nil, { "name" => new_filename, "cnt" => record_cnt }
414
407
  end
415
408
  rescue => err
416
409
  @log.info "Unable to move processed file - " + err
417
410
  end
411
+
412
+ end
413
+
414
+ #Process the appropriate table name
415
+ def process_proper_table file
416
+ @log.debug "process_proper_table method"
417
+ @record_cnt = 0
418
+ process_file = File.open(file["filename"])
419
+ process_file.each do |row|
420
+ if row != nil
421
+ begin
422
+ field = row.rstrip.split('|')
423
+ @log.debug '<====================START ' + file["name"] + ' RECORD ' + @record_cnt.to_s + ' ====================>'
424
+ case file["name"]
425
+ when "echi_acds"
426
+ record = EchiAcd.find(:first, :conditions => [ "number = ? AND acd_id = ?", field[1], field[0]])
427
+ when "echi_agents"
428
+ record = EchiAgent.find(:first, :conditions => [ "login_id = ? AND group_id = ?", field[1], field[0]])
429
+ when "echi_aux_reasons"
430
+ record = EchiAuxReason.find(:first, :conditions => [ "aux_reason = ? AND group_id = ?", field[1], field[0]])
431
+ when "echi_cwcs"
432
+ record = EchiCwc.find(:first, :conditions => [ "cwc = ? AND group_id = ?", field[1], field[0]])
433
+ when "echi_splits"
434
+ record = EchiSplit.find(:first, :conditions => [ "number = ? AND acd_number = ?", field[1], field[0]])
435
+ when "echi_trunk_groups"
436
+ record = EchiTrunkGroup.find(:first, :conditions => [ "trunk_group = ? AND acd_number = ?", field[1], field[0]])
437
+ when "echi_vdns"
438
+ record = EchiVdn.find(:first, :conditions => [ "vdn = ? AND group_id = ?", field[1], field[0]])
439
+ when "echi_vectors"
440
+ record = EchiVector.find(:first, :conditions => [ "number = ? AND acd_number = ?", field[1], field[0]])
441
+ end
442
+ if record != nil
443
+ if record.name != field[2]
444
+ record.name = field[2]
445
+ record.update
446
+ @record_cnt += 1
447
+ @log.debug "Updated record - " + field.inspect
448
+ else
449
+ @log.debug "No update required for - " + field.inspect
450
+ end
451
+ else
452
+ insert_dat_data file["name"], field
453
+ @record_cnt += 1
454
+ @log.debug "Inserted new record - " + field.inspect
455
+ end
456
+ rescue => err
457
+ @log.info "Error processing ECHI record in process_proper_table_method - " + err
458
+ end
459
+ end
460
+ @log.debug '<====================STOP ' + file["name"] + ' RECORD ' + @record_cnt.to_s + ' ====================>'
461
+ end
462
+
463
+ process_file.close
464
+
465
+ archive_file file, @record_cnt
418
466
  end
419
467
 
420
468
  #Method to insert data into 'echi_agents' based on agname.dat
421
469
  def process_dat_files
470
+ @log.debug "process_dat_files method"
422
471
  dat_files = Array.new
423
- dat_files[0] = { "name" => "echi_agents", "filename" => $workingdir + "/../files/to_process/" + $config["echi_agent_dat"] }
424
- dat_files[1] = { "name" => "echi_aux_reasons", "filename" => $workingdir + "/../files/to_process/" + $config["echi_aux_rsn_dat"] }
425
- dat_files[2] = { "name" =>"echi_cwcs", "filename" => $workingdir + "/../files/to_process/" + $config["echi_cwc_dat"] }
426
- dat_files[3] = { "name" =>"echi_vdns", "filename" => $workingdir + "/../files/to_process/" + $config["echi_vdn_dat"] }
427
-
472
+ dat_files[0] = { "name" => "echi_acds", "filename" => $workingdir + "/../files/to_process/" + $config["echi_acd_dat"] }
473
+ dat_files[1] = { "name" => "echi_agents", "filename" => $workingdir + "/../files/to_process/" + $config["echi_agent_dat"] }
474
+ dat_files[2] = { "name" => "echi_aux_reasons", "filename" => $workingdir + "/../files/to_process/" + $config["echi_aux_rsn_dat"] }
475
+ dat_files[3] = { "name" => "echi_cwcs", "filename" => $workingdir + "/../files/to_process/" + $config["echi_cwc_dat"] }
476
+ dat_files[4] = { "name" => "echi_splits", "filename" => $workingdir + "/../files/to_process/" + $config["echi_split_dat"] }
477
+ dat_files[5] = { "name" => "echi_trunk_groups", "filename" => $workingdir + "/../files/to_process/" + $config["echi_trunk_group_dat"] }
478
+ dat_files[6] = { "name" => "echi_vdns", "filename" => $workingdir + "/../files/to_process/" + $config["echi_vdn_dat"] }
479
+ dat_files[7] = { "name" => "echi_vectors", "filename" => $workingdir + "/../files/to_process/" + $config["echi_vector_dat"] }
480
+
428
481
  dat_files.each do |file|
429
- if File.exists?(file["filename"])
482
+ if File.exists?(file["filename"]) && File.size(file["filename"]) > 0
430
483
  case file["name"]
484
+ when "echi_acds"
485
+ EchiAcd.transaction do
486
+ process_proper_table file
487
+ end
431
488
  when "echi_agents"
432
489
  EchiAgent.transaction do
433
490
  process_proper_table file
@@ -440,11 +497,25 @@ module EchiConverter
440
497
  EchiCwc.transaction do
441
498
  process_proper_table file
442
499
  end
500
+ when "echi_splits"
501
+ EchiSplit.transaction do
502
+ process_proper_table file
503
+ end
504
+ when "echi_trunk_groups"
505
+ EchiTrunkGroup.transaction do
506
+ process_proper_table file
507
+ end
443
508
  when "echi_vdns"
444
509
  EchiVdn.transaction do
445
510
  process_proper_table file
446
511
  end
512
+ when "echi_vectors"
513
+ EchiVector.transaction do
514
+ process_proper_table file
515
+ end
447
516
  end
517
+ else
518
+ archive_file file, 0
448
519
  end
449
520
  end
450
521
  end
@@ -2,7 +2,7 @@ module EchiConverter #:nodoc:
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 3
5
- TINY = 6
5
+ TINY = 7
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/website/index.html CHANGED
@@ -33,7 +33,7 @@
33
33
  <h1>ECHI Converter</h1>
34
34
  <div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/echi-converter"; return false'>
35
35
  <p>Get Version</p>
36
- <a href="http://rubyforge.org/projects/echi-converter" class="numbers">0.3.6</a>
36
+ <a href="http://rubyforge.org/projects/echi-converter" class="numbers">0.3.7</a>
37
37
  </div>
38
38
  <h1>&#x2192; &#8216;echi-converter&#8217;</h1>
39
39
 
@@ -41,13 +41,15 @@
41
41
  <h2>The <span class="caps">ECHI</span> (External Call History Interface) Converter</h2>
42
42
 
43
43
 
44
- <p>Provides a Ruby based utility for fetching Avaya <span class="caps">CMS</span> / <span class="caps">ECHI</span> files in binary/ASCII form from an <span class="caps">FTP</span> server, converting them, if necessary, to <span class="caps">ASCII</span> and then inserting them into a database via ActiveRecord.</p>
44
+ <p>Provides a Ruby based utility for fetching Avaya <span class="caps">CMS</span> / <span class="caps">ECHI</span> files in binary/ASCII form from an <span class="caps">FTP</span> server, converting them, if necessary, to <span class="caps">ASCII</span> and then inserting them into a database via ActiveRecord. With this
45
+ utility you only need the standard Avaya <span class="caps">CMS</span> Release 13 or better without any additional software or utilities
46
+ from Avaya, as this utility will process either binary or <span class="caps">ASCII</span> output from the Avaya <span class="caps">CMS</span>.</p>
45
47
 
46
48
 
47
49
  <h2>Status</h2>
48
50
 
49
51
 
50
- <p>This is the beta release now in production use within Call Centers using Avaya <span class="caps">CMS</span>. The utility successfully and reliably imports the data provided by the Avaya <span class="caps">CMS ECHI</span> into various databases, including Oracle and MySQL. This provides the repository of call segments that may then be used to provide detailed Cradle to Grave reporting for the call center.</p>
52
+ <p>This release is now in production use within Call Centers using the Avaya <span class="caps">CMS</span>. The utility successfully and reliably imports the data provided by the Avaya <span class="caps">CMS ECHI</span> into various databases, including Oracle and MySQL. This provides the repository of call segments that may then be used to provide detailed Cradle to Grave reporting for the call center.</p>
51
53
 
52
54
 
53
55
  <h2>Features</h2>
@@ -57,14 +59,14 @@
57
59
 
58
60
 
59
61
  <ol>
60
- <li>Support of ActiveRecord (means you may use Oracle, MySQL, MS-SQL, Postgres, <span class="caps">DB2</span>, etc)</li>
62
+ <li>Support of ActiveRecord (means you may use Oracle, MySQL, MS-SQL, Postgres, <span class="caps">DB2</span>, ODBC, etc)</li>
61
63
  <li>Generate your schema via ActiveRecord Migrations</li>
62
64
  <li>Fetch Binary or <span class="caps">ASCII CSV</span> files from the Avaya <span class="caps">CMS</span> platform via <span class="caps">FTP</span></li>
63
- <li>Convert from the defined Binary format to <span class="caps">ASCII</span></li>
64
65
  <li>Insert the records into the defined database table using database transactions, via ActiveRecord, on a per file basis to support recovery on failure</li>
65
66
  <li>Change schema structure via <span class="caps">YML</span> configuration file to accommodate various releases of the <span class="caps">ECHI</span> format</li>
66
- <li>Supports inserting data from the various &#8217;.dat&#8217; files provided</li>
67
- <li>Runs as a daemon (via fork) on *NIX and a service on Windows</li>
67
+ <li>Supports inserting data from the various &#8217;.dat&#8217; files provided by the Avaya <span class="caps">CMS</span></li>
68
+ <li>Runs as a daemon (via fork) on Posix and a service on Windows</li>
69
+ <li>Has a watchdog process on Posix or you may set a service watch on Windows</li>
68
70
  <li>Allows for multiple <span class="caps">FTP</span> sessions to be used for greater performance (via <a href="http://en.wikipedia.org/wiki/Green_threads">green threads</a>)</li>
69
71
  </ol>
70
72
 
@@ -74,10 +76,14 @@
74
76
  <ol>
75
77
  <li>echi_records &#8211; stores all <span class="caps">ECHI</span> data </li>
76
78
  <li>echi_logs &#8211; stores a log entry for each file processed</li>
79
+ <li>echi_acds &#8211; stores the data from the acd.dat file</li>
77
80
  <li>echi_agents &#8211; stores the data from the agname.dat file </li>
78
81
  <li>echi_aux_reasons &#8211; stores the data from the aux_rsn.dat file</li>
79
82
  <li>echi_cwcs &#8211; stores data from the cwc.dat file</li>
83
+ <li>echi_splits &#8211; stores data from the split.dat file</li>
84
+ <li>echi_trunk_groups &#8211; stores data from the tkgrp.dat file</li>
80
85
  <li>echi_vdns &#8211; stores data from the vdn.dat file</li>
86
+ <li>echi_vectors &#8211; stores data from the vector.dat file</li>
81
87
  </ol></li>
82
88
  </ol>
83
89
 
@@ -87,14 +93,14 @@
87
93
 
88
94
  <ol>
89
95
  <li><a href="http://www.ruby-lang.org/">Ruby v1.8.6+</a></li>
90
- <li><a href="http://www.rubygems.org/">Rubygems v0.9.4+</a></li>
96
+ <li><a href="http://www.rubygems.org/">Rubygems v1.0.1+</a> </li>
91
97
  <li><a href="http://activerecord.rubyforge.org/">ActiveRecord v1.15.3+</a></li>
92
98
  <li><a href="http://activesupport.rubyforge.org/">ActiveSupport v1.4.2+</a></li>
93
99
  <li><a href="http://daemons.rubyforge.org/">Daemons v1.0.7+</a></li>
94
100
  <li><a href="http://fastercsv.rubyforge.org/">FasterCSV v1.2.0+</a></li>
95
101
  <li><a href="http://rake.rubyforge.org/">Rake v0.7.3+</a></li>
96
102
  <li><a href="http://sporkmonger.com/projects/uuidtools/">UUIDTools v1.0.1+</a></li>
97
- <li><a href="http://win32utils.rubyforge.org/">Win32-service v.0.5.0+</a> (Manual install for Windows only)</li>
103
+ <li><a href="http://win32utils.rubyforge.org/">Win32-service v.0.5.x &#8211; <strong><span class="caps">ONLY</span></strong> -</a> (Manual install for Windows only)</li>
98
104
  <li><a href="http://rubyforge.org/projects/seattlerb/">Hoe v1.2.2+</a></li>
99
105
  </ol>
100
106
 
@@ -130,6 +136,7 @@
130
136
  <li>config/database.yml
131
137
  <ol>
132
138
  <li>Change to match your local database and database login credentials, full ActiveRecord support</li>
139
+ <li>Note: Your database user and database must exist before running rake, as rake will then create the schema</li>
133
140
  </ol></li>
134
141
  </ol>
135
142
 
@@ -143,7 +150,7 @@
143
150
  </ol>
144
151
 
145
152
 
146
- <p>Note: When using a Windows <span class="caps">FTP</span> server, you must configure the <span class="caps">FTP</span> server to provide a <span class="caps">UNIX</span> directory listing format</p>
153
+ <p>Note: When using a Windows <span class="caps">FTP</span> server, you must configure the <span class="caps">FTP</span> server to provide a <span class="caps">UNIX</span> directory listing format.</p>
147
154
 
148
155
 
149
156
  <h2>Usage</h2>
@@ -259,8 +266,17 @@
259
266
 
260
267
 
261
268
  <p>Comments are welcome. Send an email to <a href="mailto:jason@goecke.net">jason [at] goecke.net</a>.</p>
269
+
270
+
271
+ <h2>Other Recommended Open Source Projects</h2>
272
+
273
+
274
+ <ol>
275
+ <li>Asterisk &#8211; is the world&#8217;s leading open source PBXi, telephony engine, and telephony applications toolkit, link <a href="http://www.asterisk.org">here.</a></li>
276
+ <li>Adhersion &#8211; is an open-source, unconventional voice framework that ties technologies together neatly, link <a href="http://www.adhearsion.com">here.</a></li>
277
+ </ol>
262
278
  <p class="coda">
263
- <a href="mailto:drnicwilliams@gmail.com">Dr Nic</a>, 4th December 2007<br>
279
+ <a href="mailto:drnicwilliams@gmail.com">Dr Nic</a>, 17th March 2008<br>
264
280
  Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
265
281
  </p>
266
282
  </div>