code: def group_issues_by_fingerprint(issues):
      
      issues_by_fingerprint = defaultdict(list)
      for issue in issues:
          if not 'fingerprint' in issue:
              raise AttributeError("No fingerprint defined for issue with analyzer %s and code %s!" %
                  (issue.get('analyzer','(undefined)'),issue['code']))
          fp_code = "%s:%s" % (issue['fingerprint'],issue['code'])
          if fp_code in issues_by_fingerprint:
              grouped_issue = issues_by_fingerprint[fp_code]
          else:
              grouped_issue = issue.copy()
              grouped_issue['occurrences'] = []
              if 'location' in grouped_issue:
                  del grouped_issue['location']
              issues_by_fingerprint[fp_code] = grouped_issue
  
          locations = issue.get('location',[])
          if locations:
              for i,start_stop in enumerate(locations):
  
                  occurrence = {
                      'from_row' : None,
                      'to_row' : None,
                      'from_column' : None,
                      'to_column' : None,
                      'sequence' : i
                  }
  
                  grouped_issue['occurrences'].append(occurrence)
  
                  if not isinstance(start_stop,(list,tuple)) or not len(start_stop) == 2:
                      continue
  
                  start,stop = start_stop
  
                  if isinstance(start,(list,tuple)) and len(start) == 2:
                      occurrence['from_row'] = start[0]
                      occurrence['from_column'] = start[1]
  
                  if isinstance(stop,(list,tuple)) and len(stop) == 2:
                      occurrence['to_row'] = stop[0]
                      occurrence['to_column'] = stop[1]
  
              grouped_issue['occurrences'] = sorted(grouped_issue['occurrences'],key = lambda x: (x['from_row'],x['from_column']))
  
      return issues_by_fingerprint.values()
docs: Groups issues by fingerprint. Grouping is done by issue code in addition.
      IMPORTANT: It is assumed that all issues come from the SAME analyzer.