Convert a PocketTopo `Shot` to a Compass `Shot`

def shot2shot(insurvey, inshot, calculate_lrud=True):
    """Convert a PocketTopo `Shot` to a Compass `Shot`"""
    # FIXME: requires angles in degrees only, no grads

    splays = insurvey.splays[inshot['FROM']]
    if calculate_lrud and not inshot.is_splay and splays:
        # Try our best to convert PocketTopo splay shots into LRUDs
        print '\n\n' 'sta %s has %d splays' % (inshot['FROM'], len(splays))

        left_azm, right_azm = (inshot['AZM'] - 90) % 360, (inshot['AZM'] + 90) % 360
        left_shot, right_shot = None, None
        left_candidates = find_candidate_splays(splays, left_azm, 0)
        if left_candidates:
            left_shot = max(left_candidates, key=lambda shot: hd(shot['INC'], shot['LENGTH']))
            left = hd(left_shot['INC'], left_shot['LENGTH'])
        else:
            left = 0
        right_candidates = find_candidate_splays(splays, right_azm, 0)
        if right_candidates:
            right_shot = max(right_candidates, key=lambda shot: hd(shot['INC'], shot['LENGTH']))
            right = hd(right_shot['INC'], right_shot['LENGTH'])
        else:
            right = 0

        print '\t' 'left=%.1f  azm=%.1f  right=%.1f' % (left_azm, inshot['AZM'], right_azm)
        print '\t' '%d candidate LEFT shots' % len(left_candidates)
        for splay in left_candidates:
            print '\t\t\t' + str(splay)
        print '\t\t' '%.1f - Chose: %s' % (left, str(left_shot))
        print '\t' '%d candidate RIGHT shots' % len(right_candidates)
        for splay in right_candidates:
            print '\t\t\t' + str(splay)
        print '\t\t' '%.1f - Chose: %s' % (right, str(right_shot))

        up_candidates = find_candidate_vert_splays(splays, 90)
        if up_candidates:
            up_shot = max(up_candidates, key=lambda splay: vd(splay['INC'], splay['LENGTH']))
            up = vd(up_shot['INC'], up_shot['LENGTH'])
        else:
            up = 0
        down_candidates = find_candidate_vert_splays(splays, -90)
        if down_candidates:
            down_shot = max(down_candidates, key=lambda splay: vd(splay['INC'], splay['LENGTH']))  # TODO: should vd() give negative and we find min()?
            down = vd(down_shot['INC'], down_shot['LENGTH'])
        else:
            down = 0

        print '\t', inshot, 'LRUD=', ', '.join(('%0.1f' % v) for v in (left, right, up, down))
        assert(all(v >=0 for v in (left, right, up, down)))
    else:
        up, down, left, right = None, None, None, None

    return compass.Shot([
        ('FROM', inshot['FROM']),
        # Compass requires a named TO station, so we must invent one for splays
        ('TO', inshot['TO'] or '%s.s%03d' % (inshot['FROM'], random.randint(0,1000))),
        ('LENGTH', m2ft(inshot.length)),
        # BEARING/AZM named inconsistently in Davies to reflect each program's arbitrary name. We
        # can't use `inshot.azm` here because we need the "raw" compass value without declination
        ('BEARING', inshot['AZM']),
        ('INC', inshot.inc),
        ('LEFT', m2ft(left) if left is not None else -9.90),
        ('UP', m2ft(up) if left is not None else -9.90),
        ('DOWN', m2ft(down) if left is not None else -9.90),
        ('RIGHT', m2ft(right) if left is not None else -9.90),  # Compass requires this order!
        # Compass 'L' flag excludes splays from cave length calculation
        ('FLAGS', (compass.Exclude.LENGTH, compass.Exclude.PLOT) if inshot.is_splay else ()),
        # COMMENTS/COMMENT named inconsistently in Davies to reflect each program's arbitrary name
        ('COMMENTS', inshot['COMMENT'])
    ])