from django.core.management.base import NoArgsCommand
from crashmanager.models import Bug, BugProvider
from django.conf import settings
from crashmanager.management.common import mgmt_lock_required
import warnings

class Command(NoArgsCommand):
    help = "Check the status of all bugs we have"
    @mgmt_lock_required
    def handle_noargs(self, **options):      
        # Suppress warnings about native datetime vs. timezone
        warnings.filterwarnings("ignore", category=RuntimeWarning, message=".*received a naive datetime.*")
        
        providers = BugProvider.objects.all()
        for provider in providers:
            providerInstance = provider.getInstance()
            providerBugs = Bug.objects.filter(externalType=provider)
            bugIds = list(providerBugs.values_list('externalId', flat=True))
            
            if not bugIds:
                continue
            
            username = getattr(settings, 'BUGZILLA_USERNAME', None)
            password = getattr(settings, 'BUGZILLA_PASSWORD', None)
            
            bugStatus = providerInstance.getBugStatus(bugIds, username, password)
            
            if not bugStatus:
                raise RuntimeError("Error getting bug status from bug provider.")
            
            # Map to memorize which bugs we have duped to others
            bugDupMap = {}
            
            for bugId in bugStatus:
                # It is possible that two buckets are linked to one bug which has been marked
                # as a duplicate of another. Once the first bug has been processed, we don't
                # need to process any more bugs with the same bug id.
                if bugId in bugDupMap:
                    continue
                
                bug = providerBugs.filter(externalId=bugId)[0]
                if bugStatus[bugId] == None and bug.closed != None:
                    bug.closed = None
                    bug.save()
                elif isinstance(bugStatus[bugId], str):
                    # The bug has been marked as a duplicate, so we change the externalId
                    # to match the duped bug. If that bug is also closed, then it will be
                    # picked up the next time this command runs.
                    bugDupMap[bug.externalId] = bugStatus[bugId]
                    bug.externalId = bugStatus[bugId]
                    bug.closed = None
                    bug.save()
                elif bug.closed == None:
                    bug.closed = bugStatus[bugId]
                    bug.save()
                    
@@ -1765,19 +1765,199 @@
         
-bug
+# Due to how duplicating bugs works, we can end up having multiple bug objects%0A                # with the same external bug id. Make sure we consider all of them.%0A                bugs
  = provi
@@ -1992,12 +1992,46 @@
 gId)
-%5B0%5D
 %0A
+                for bug in bugs:%0A    
     
@@ -2106,32 +2106,36 @@
                 
+ 
+   
 bug.closed = Non
@@ -2148,32 +2148,36 @@
                 
+    
 bug.save()%0A     
@@ -2183,24 +2183,28 @@
             
+  
+  
 elif isinsta
@@ -2231,16 +2231,20 @@
 , str):%0A
+    
         
@@ -2345,16 +2345,20 @@
         
+  
+  
 # to mat
@@ -2439,16 +2439,20 @@
         
+    
 # picked
@@ -2500,32 +2500,36 @@
                 
+   
+ 
 bugDupMap%5Bbug.ex
@@ -2573,24 +2573,28 @@
             
+    
 bug.external
@@ -2627,32 +2627,36 @@
                 
+   
+ 
 bug.closed = Non
@@ -2669,32 +2669,36 @@
                 
+    
 bug.save()%0A     
@@ -2684,32 +2684,36 @@
      bug.save()%0A
+    
                 
@@ -2749,32 +2749,36 @@
                 
+    
 bug.closed = bug
@@ -2783,32 +2783,36 @@
 ugStatus%5BbugId%5D%0A
+    
                 

Handle multiple bug objects with same externalId in bug_update_status (#126)