loader_controller.rb.diff

loader_controler.rb diff file - Xabier Elkano, 2010-04-22 08:24

Download (13.6 KB)

View differences:

loader_controller2.rb 2010-04-22 08:11:59.079745694 +0200
3 3
#          Hipposoft 2008                                              #
4 4
#                                                                      #
5 5
# History: 04-Jan-2008 (ADH): Created.                                 #
6
#          Feb 2009 (SJS): Hacked into plugin for redmine              #
6
#          Feb 2009 (SJS): Hacked into plugin for redmine              # 
7
#	   Apr 2010 (XEP): Fix some bugs  			       #
7 8
########################################################################
8 9

  
9 10
class TaskImport
......
25 26
  require 'tempfile'
26 27
  require 'rexml/document'
27 28
  
29

  
28 30
  # Set up the import view. If there is no task data, this will consist of
29 31
  # a file entry field and nothing else. If there is parsed file data (a
30 32
  # preliminary task list), then this is included too.
......
33 35
    # This can and probably SHOULD be replaced with some URL rewrite magic
34 36
    # now that the project loader is Redmine project based.
35 37
    find_project()
38

  
36 39
  end
37 40
  
38 41
  # Take the task data from the 'new' view form and 'create' an "import
......
43 46
  def create
44 47
    # This can and probably SHOULD be replaced with some URL rewrite magic
45 48
    # now that the project loader is Redmine project based.
49

  
46 50
    find_project()
47 51

  
48 52
    # Set up a new TaskImport session object and read the XML file details
49 53
    
50 54
    xmlfile = params[ :import ][ :xmlfile ]
51 55
    @import = TaskImport.new
52
        
53 56
    unless ( xmlfile.nil? )
54

  
55 57
      # The user selected a file to upload, so process it
56
      
58

  
57 59
      begin
58 60
        
59 61
        # We assume XML files always begin with "<" in the first byte and
60 62
        # if that's missing then it's GZip compressed. That's true in the
61 63
        # limited case of project files.
62
        
64
	
65
 
63 66
        byte = xmlfile.getc()
64 67
        xmlfile.rewind()
68
	
65 69
        
66 70
        xmlfile       = Zlib::GzipReader.new( xmlfile ) if ( byte != '<'[ 0 ] )
67 71
        xmldoc        = REXML::Document.new( xmlfile.read() )
68 72
        @import.tasks, @import.new_categories = get_tasks_from_xml( xmldoc )
69 73

  
74

  
75

  
70 76
        if ( @import.tasks.nil? or @import.tasks.empty? )
77

  
71 78
          flash[ :error  ] = 'No usable tasks were found in that file'
72 79
        else
73 80
          flash[ :notice ] = 'Tasks read successfully. Please choose items to import.'
81

  
74 82
        end
75 83
        
76 84
      rescue => error
77
        
85
       
86
 
78 87
        # REXML errors can be huge, including a full backtrace. It can cause
79 88
        # session cookie overflow and we don't want the user to see it. Cut
80 89
        # the message off at the first newline.
81 90
        
82 91
        lines = error.message.split("\n")
83 92
        flash[ :error  ] = "Failed to read file: #{ lines[ 0 ] }"
93
	logger.debug "DEBUG: ERROR ->  #{ lines[ 0 ] }"
84 94
      end
85 95

  
86
      render( { :action => :new } )
96
      render  :action => :new  
97

  
87 98
      flash.delete( :error  )
88 99
      flash.delete( :notice )
89 100
      
......
180 191
        Issue.transaction do
181 192
          to_import.each do | source_issue |
182 193

  
194
	    # We comment those lines because they are not necesary now.
183 195
            # Add the category entry if necessary
184
            category_entry = IssueCategory.find :first, :conditions => { :project_id => @project.id, :name => source_issue.category }
185 196

  
186
            if (category_entry.nil?)
197
	    logger.debug "DEBUG: Issue to be imported: #{source_issue.inspect}"
198
	    if ( source_issue.category != "" )
199
		logger.debug "DEBUG: Search category id by name: #{source_issue.category}"
200
            	category_entry = IssueCategory.find :first, :conditions => { :project_id => @project.id, :name => source_issue.category }
201
		logger.debug "DEBUG: Category found: #{category_entry.inspect}"
202
	    else
203
		category_entry = nil
204
	    end
205

  
206
            #if (category_entry.nil?)
187 207
              # Need to create it
188
              category_entry = IssueCategory.new do |i|
189
                i.name = source_issue.category
190
                i.project_id = @project.id
191
              end
192 208

  
193
              category_entry.save!
194
            end
209
            #  category_entry = IssueCategory.new do |i|
210
            #    i.name = source_issue.category
211
            #    i.project_id = @project.id
212
            #  end
213

  
214
            # category_entry.save!
215

  
216
            #end
195 217

  
196 218
            destination_issue          = Issue.new do |i|
197 219
              i.tracker_id = default_tracker_id
198
              i.category_id = category_entry.id
220
              i.category_id = category_entry.id unless category_entry.nil?
199 221
              i.subject    = source_issue.title.slice(0, 255) # Max length of this field is 255
200 222
              i.estimated_hours = source_issue.duration
201 223
              i.project_id = @project.id
......
206 228
              i.start_date = source_issue.start
207 229
              i.due_date = source_issue.finish unless source_issue.finish.nil?
208 230
              i.due_date = (Date.parse(source_issue.start, false) + ((source_issue.duration.to_f/40.0)*7.0).to_i).to_s unless i.due_date != nil
209

  
231
	      logger.debug "DEBUG: Assigned_to field: #{source_issue.assigned_to}"
210 232
              if ( source_issue.assigned_to != "" )
211 233
                i.assigned_to_id = source_issue.assigned_to
212
                i.status_id = IssueStatus.find_by_name("Assigned").id
234
                i.status_id = IssueStatus.find_by_name("Asignada").id
213 235
              end
214 236
            end
215 237

  
216 238
            destination_issue.save!
239
	    logger.debug "DEBUG: Issue #{destination_issue.description} imported"
240

  
217 241
            
218 242
            # Now that we know this issue's Redmine issue ID, save it off for later
219 243
            uidToIssueIdMap[ source_issue.uid ] = destination_issue.id
......
238 262
            end
239 263
          end
240 264
        end
241
    
242
        redirect_to( "/projects/#{@project.identifier}/issues" )
265
 
266
       redirect_to( "/projects/#{@project.identifier}/issues" )
243 267
        
244 268
        
245 269
      rescue => error
246 270
        flash[ :error ] = "Unable to import tasks: #{ error }"
271
	logger.debug "DEBUG: Unable to import tasks: #{ error }"
247 272
        render( { :action => :new } )
248
        flash.delete( :error )
273
        #flash.delete( :error )
249 274
        
250 275
      end
251 276
    end
......
266 291
    
267 292
    # Extract details of every task into a flat array
268 293
    tasks = []
269
    
294
 
295
    logger.debug "DEBUG: BEGIN get_tasks_from_xml"
296

  
270 297
    doc.each_element( 'Project/Tasks/Task' ) do | task |
271 298
      begin
299

  
300
	logger.debug "Project/Tasks/Task found"
272 301
        struct = OpenStruct.new
273
        struct.level  = task.get_elements( 'OutlineLevel' )[ 0 ].text.to_i
274
        struct.tid    = task.get_elements( 'ID'           )[ 0 ].text.to_i
275
        struct.uid    = task.get_elements( 'UID'          )[ 0 ].text.to_i
276
        struct.title  = task.get_elements( 'Name'         )[ 0 ].text.strip
277
        struct.start  = task.get_elements( 'Start'        )[ 0 ].text.split("T")[0]
302
        struct.level  = task.get_elements( 'OutlineLevel' )[ 0 ].text.to_i unless task.get_elements( 'OutlineLevel' )[ 0 ].nil?
303
        struct.tid    = task.get_elements( 'ID'           )[ 0 ].text.to_i unless task.get_elements( 'ID'           )[ 0 ].nil?
304
        struct.uid    = task.get_elements( 'UID'          )[ 0 ].text.to_i unless task.get_elements( 'UID'          )[ 0 ].nil?
305
        struct.title  = task.get_elements( 'Name'         )[ 0 ].text.strip unless task.get_elements( 'Name'         )[ 0 ].nil?
306
        struct.start  = task.get_elements( 'Start'        )[ 0 ].text.split("T")[0] unless  task.get_elements( 'Start'        )[ 0 ].nil?
278 307
        
279 308
        struct.finish  = task.get_elements( 'Finish'        )[ 0 ].text.split("T")[0] unless task.get_elements( 'Finish')[ 0 ].nil?
280
        struct.percentcomplete = task.get_elements( 'PercentComplete')[0].text.to_i
281

  
309
	# On Openproj xml files PercentComplete field could be not appear so we test if it exists.
310
	logger.debug "DEBUG: Task Title: #{struct.title}"
311
	if (task.get_elements( 'PercentComplete')[ 0 ].nil?)
312
                duration =  task.get_elements( 'Duration' )[ 0 ].text
313
                remainingDuration = task.get_elements( 'RemainingDuration' )[ 0 ].text
314
                logger.debug "DEBUG: Duration retrieved: #{duration}"
315
                logger.debug "DEBUG: Remaining duration retrieved: #{remainingDuration}"
316
      		# Parse the "RemainingDuration" string: "PT<num>H<num>M<num>S", but with some
317
      		# leniency to allow any data before or after the H/M/S stuff.
318
      		hours = 0
319
      		mins = 0
320
      		secs = 0
321
      		strs = duration.scan(/.*?(\d+)H(\d+)M(\d+)S.*?/).flatten unless duration.nil?
322
      		hours, mins, secs = strs.map { | str | str.to_i } unless strs.nil?
323
      		duration = ( ( ( hours * 3600 ) + ( mins * 60 ) + secs ) / 3600 ).prec_f
324
		logger.debug "DEBUG: Task duration: #{duration}"
325

  
326
		hours = 0
327
                mins = 0
328
                secs = 0
329
                strs = remainingDuration.scan(/.*?(\d+)H(\d+)M(\d+)S.*?/).flatten unless remainingDuration.nil?
330
                hours, mins, secs = strs.map { | str | str.to_i } unless strs.nil?
331
                remainingDuration = ( ( ( hours * 3600 ) + ( mins * 60 ) + secs ) / 3600 ).prec_f
332
		logger.debug "DEBUG: Task Remaining Duration: #{remainingDuration}"
333
		if ( duration > 0 )
334
			percentcomplete = ( (duration - remainingDuration) * 100 ) / duration
335
		else
336
			percentcomplete = 0
337
		end
338
		logger.debug "DEBUG: PercentComplete: #{percentcomplete.to_i}"
339
		struct.percentcomplete= percentcomplete.to_i
340
	else
341
		struct.percentcomplete=task.get_elements( 'PercentComplete')[ 0 ].text.to_i
342
		logger.debug "DEBUG: PercentComplete retrieved: #{struct.percentcomplete}"
343
	end
282 344
        # Handle dependencies
283 345
        struct.predecessors = []
284
        task.each_element( 'PredecessorLink' ) do | predecessor |
285
          begin
286
            struct.predecessors.push( predecessor.get_elements('PredecessorUID')[0].text.to_i )
287
          end
288
        end
289
          
346
        #task.each_element( 'PredecessorLink' ) do | predecessor |
347
        #  begin
348
        #    struct.predecessors.push( predecessor.get_elements('PredecessorUID')[0].text.to_i )
349
        #  end
350
        #end
351
	
290 352
        tasks.push( struct )
291
      rescue
353

  
354
      rescue => error
292 355
        # Ignore errors; they tend to indicate malformed tasks, or at least,
293 356
        # XML file task entries that we do not understand.
357
	logger.debug "DEBUG: Unrecovered error getting tasks: #{error}"
294 358
      end
295 359
    end
296
    
360
   
361
 
297 362
    # Sort the array by ID. By sorting the array this way, the order
298 363
    # order will match the task order displayed to the user in the
299 364
    # project editor software which generated the XML file.
300
    
365

  
301 366
    tasks = tasks.sort_by { | task | task.tid }
302 367
    
303 368
    # Step through the sorted tasks. Each time we find one where the
......
314 379
      next_task = tasks[ index + 1 ]
315 380
      
316 381
      if ( next_task and next_task.level > task.level )
317
        category         = task.title.strip.gsub(/:$/, '') # Kill any trailing :'s which are common in some project files
318
        all_categories.push(category)   # Keep track of all categories so we know which ones might need to be added
382
        # category         = task.title.strip.gsub(/:$/, '') # Kill any trailing :'s which are common in some project files
383
	# We dont want imported categories so we coment this line.
384
        #all_categories.push(category)   # Keep track of all categories so we know which ones might need to be added
319 385
        tasks[ index ] = nil
320 386
      else
321 387
        task.category = category
......
345 411
    
346 412
    real_tasks = []
347 413
    
348
    doc.each_element( 'Project/Assignments/Assignment' ) do | as |
349
      task_uid = as.get_elements( 'TaskUID' )[ 0 ].text.to_i
350
      task = uid_tasks[ task_uid ] unless task_uid.nil?
351
      next if ( task.nil? )
414
    #assig = doc.get_elements ( 'Project/Assignments/Assignment' )
415
    #if ( !assig.nil? )
416
    	doc.each_element( 'Project/Assignments/Assignment' ) do | as |
417
      		task_uid = as.get_elements( 'TaskUID' )[ 0 ].text.to_i
418
      		task = uid_tasks[ task_uid ] unless task_uid.nil?
419
      		next if ( task.nil? )
352 420
      
353
      work = as.get_elements( 'Work' )[ 0 ].text
421
      		work = as.get_elements( 'Work' )[ 0 ].text
354 422
      
355
      # Parse the "Work" string: "PT<num>H<num>M<num>S", but with some
356
      # leniency to allow any data before or after the H/M/S stuff.
357
      hours = 0
358
      mins = 0
359
      secs = 0
423
      		# Parse the "Work" string: "PT<num>H<num>M<num>S", but with some
424
      		# leniency to allow any data before or after the H/M/S stuff.
425
      		hours = 0
426
      		mins = 0
427
      		secs = 0
360 428
      
361
      strs = work.scan(/.*?(\d+)H(\d+)M(\d+)S.*?/).flatten unless work.nil?
362
      hours, mins, secs = strs.map { | str | str.to_i } unless strs.nil?
429
      		strs = work.scan(/.*?(\d+)H(\d+)M(\d+)S.*?/).flatten unless work.nil?
430
      		hours, mins, secs = strs.map { | str | str.to_i } unless strs.nil?
363 431
      
364
      #next if ( hours == 0 and mins == 0 and secs == 0 )
432
      		#next if ( hours == 0 and mins == 0 and secs == 0 )
365 433
      
366
      # Woohoo, real task!
434
      		# Woohoo, real task!
367 435
      
368
      task.duration = ( ( ( hours * 3600 ) + ( mins * 60 ) + secs ) / 3600 ).prec_f
436
      		task.duration = ( ( ( hours * 3600 ) + ( mins * 60 ) + secs ) / 3600 ).prec_f
369 437
      
370
      real_tasks.push( task )
371
    end
372
    
373
    real_tasks = tasks if real_tasks.nil?
438
      		real_tasks.push( task )
439
    	end
440
    #end
441

  
442
    logger.debug "DEBUG: Real tasks: #{real_tasks.inspect}" 
443
    logger.debug "DEBUG: Tasks: #{tasks.inspect}"
444

  
445
    real_tasks = tasks if real_tasks.empty?
374 446
    real_tasks = real_tasks.uniq unless real_tasks.nil?
375 447
    all_categories = all_categories.uniq.sort
376 448

  
449
    logger.debug "DEBUG: END get_tasks_from_xml"	
450
  
377 451
    return real_tasks, all_categories
378 452
  end
379 453
end