Skip to main content
Loading...

More Python Posts

import subprocess
import json
import re


def parse_bgp_routes(bgp_output):
    """
    Parse BGP route table output and return structured data.
    
    Args:
        bgp_output (str): Raw BGP table output
        
    Returns:
        dict: Structured BGP routes data
    """
    routes = []
    
    # Split into lines and find the route entries
    lines = bgp_output.strip().split('\n')
    
    # Find the header line to identify where routes start
    route_start_idx = None
    for i, line in enumerate(lines):
        if 'Network' in line and 'Next Hop' in line:
            route_start_idx = i + 1
            break
    
    if route_start_idx is None:
        return {"routes": routes}
    
    # Parse each route line
    for line in lines[route_start_idx:]:
        line = line.strip()
        
        # Skip empty lines and summary lines
        if not line or line.startswith('Total number') or line.startswith('IPv6'):
            continue
            
        # Skip lines that don't start with route status indicators
        if not line.startswith('*>'):
            continue
        
        # Parse the route line using regex
        # Pattern matches: *> network next_hop metric [locprf] weight path
        # Note: LocPrf column may be empty, so we'll set it to 0 for all records
        pattern = r'^\*>\s+(\S+)\s+(\S+)\s+(\d+)\s+(\d+)\s+(.+)$'
        match = re.match(pattern, line)
        
        if match:
            network = match.group(1)
            next_hop = match.group(2)
            metric = int(match.group(3))
            loc_prf = 0  # Set to 0 for all records as requested
            weight = int(match.group(4))
            path_info = match.group(5).strip()
            
            # Extract AS path (remove origin code)
            path_parts = path_info.split()
            as_path = []
            for part in path_parts:
                if part.isdigit():
                    as_path.append(part)
            
            path = ' '.join(as_path) if as_path else ""
            
            route = {
                "network": network,
                "nextHop": next_hop,
                "metric": metric,
                "locPrf": loc_prf,
                "weight": weight,
                "path": path
            }
            routes.append(route)
    
    return {"routes": routes}


def get_bgp_routes_json():
    """
    Get BGP routes from sample data and return as JSON.
    
    Returns:
        str: JSON string of BGP routes
    """
    output_text = """IPv4
BGP table version is 0, local router ID is 169.254.0.185
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
            r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete

Network          Next Hop            Metric LocPrf Weight Path
*> 10.0.0.0         169.254.0.186          100             0 65100 ?
*> 10.2.0.0/16      169.254.0.185          100         32768 i
*> 10.5.0.0/16      169.254.0.185          100         32768 i
*> 10.6.0.0/16      169.254.0.185          100         32768 i
*> 10.10.0.0/16     169.254.0.185          100         32768 i
*> 10.42.0.0/16     169.254.0.185          100         32768 i
*> 10.56.0.0/16     169.254.0.185          100         32768 i
*> 10.133.0.0/21    169.254.0.185          100         32768 i
*> 10.192.2.0/23    169.254.0.186          100             0 65100 ?
*> 10.192.6.0/23    169.254.0.186          100             0 65100 ?
*> 10.208.0.0/18    169.254.0.185          100         32768 i
*> 10.210.0.0/20    169.254.0.186          100             0 65100 ?
*> 10.210.64.0/18   169.254.0.186          100             0 65100 ?
*> 10.210.128.0/20  169.254.0.186          100             0 65100 ?
*> 10.211.0.0/17    169.254.0.186          100             0 65100 ?
*> 10.211.128.0/17  169.254.0.186          100             0 65100 ?
*> 10.216.0.0/16    169.254.0.186          100             0 65100 ?
*> 10.224.64.0/19   169.254.0.185          100         32768 i
*> 10.227.128.0/18  169.254.0.185          100         32768 i
*> 10.227.192.0/18  169.254.0.185          100         32768 i
*> 10.228.64.0/18   169.254.0.185          100         32768 i
*> 10.228.128.0/18  169.254.0.185          100         32768 i
*> 10.228.192.0/18  169.254.0.185          100         32768 i
*> 10.229.64.0/18   169.254.0.185          100         32768 i
*> 10.229.128.0/18  169.254.0.185          100         32768 i
*> 10.229.192.0/18  169.254.0.185          100         32768 i
*> 10.230.0.0/18    169.254.0.185          100         32768 i
*> 10.230.64.0/18   169.254.0.185          100         32768 i
*> 10.230.128.0/18  169.254.0.185          100         32768 i
*> 10.230.192.0/18  169.254.0.185          100         32768 i
*> 10.246.48.0/20   169.254.0.185          100         32768 i
*> 10.254.192.0/18  169.254.0.185          100         32768 i
*> 10.255.0.0/18    169.254.0.185          100         32768 i
*> 10.255.64.0/18   169.254.0.185          100         32768 i
*> 10.255.128.0/18  169.254.0.185          100         32768 i
*> 10.255.192.0/19  169.254.0.185          100         32768 i
*> 10.255.224.0/19  169.254.0.185          100         32768 i
*> 100.64.0.64/27   169.254.0.185          100         32768 i
*> 100.64.0.96/27   169.254.0.185          100         32768 i
*> 100.64.0.128/27  169.254.0.185          100         32768 i
*> 100.64.0.160/27  169.254.0.185          100         32768 i
*> 172.16.0.0/12    169.254.0.186          100             0 65100 69999 21222 ?
*> 240.0.0.0/4      169.254.0.186          100             0 65100 69999 21222 ?
*> 253.128.0.0/14   169.254.0.186          100             0 65100 ?
*> 253.132.0.0/20   169.254.0.186          100             0 65100 ?

Total number of prefixes 45

IPv6
No BGP network exists"""
    
    # Parse the BGP output and return as JSON
    parsed_data = parse_bgp_routes(output_text)
    return json.dumps(parsed_data, indent=2)

def get_specific_network(prefix):
    # Get all BGP routes first
    all_routes = json.loads(get_bgp_routes_json())
    
    # Search through routes for matching prefix
    for route in all_routes["routes"]:
        if route["network"] == prefix:
            return route
            
    # Return None if no matching prefix is found
    return None

def main():
    """Main function to demonstrate the BGP parser."""
    print(get_bgp_routes_json())
    print(get_specific_network("10.42.0.0/16"))


if __name__ == "__main__":
    main()
def parse_ike_proposal(proposal):
    """
    Parse an IKE or ESP proposal string to extract encryption, hash, and DH group in human-readable format.
    
    Args:
        proposal (str): IKE or ESP proposal string, e.g., 'IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024',
                        'AES_GCM_16_256/PRF_HMAC_SHA2_256/ECP_384', or 'ESP:AES_CBC_256/HMAC_SHA1_96/NO_EXT_SEQ'
    
    Returns:
        dict: Dictionary with encryption, hash, and DH group in human-readable format
    """
    dh_mapping = {
        'MODP_768': '1',
        'MODP_1024': '2',
        'MODP_1536': '5',
        'MODP_2048': '14',
        'MODP_3072': '15',
        'MODP_4096': '16',
        'MODP_6144': '17',
        'MODP_8192': '18',
        'ECP_256': '19',
        'ECP_384': '20',
        'ECP_521': '21',
        'ECP_192': '25',
        'ECP_224': '26',
        'MODP_1024_160': '22',
        'MODP_2048_224': '23',
        'MODP_2048_256': '24',
        'FFDHE_2048': '256',
        'FFDHE_3072': '257',
        'FFDHE_4096': '258',
        'FFDHE_6144': '259',
        'FFDHE_8192': '260',
        'ECP_224_BP': '27',
        'ECP_256_BP': '28',
        'ECP_384_BP': '29',
        'ECP_512_BP': '30',
        'CURVE_25519': '31',
        'CURVE_448': '32',
    }
    
    enc_mapping = {
        'AES_CBC_128': 'AES-128',
        'AES_CBC_192': 'AES-192',
        'AES_CBC_256': 'AES-256',
        'AES_GCM_16_128': 'AES-GCM-128',
        'AES_GCM_16_192': 'AES-GCM-192',
        'AES_GCM_16_256': 'AES-GCM-256',
        'AES_GCM_8_128': 'AES-GCM-128-8',
        'AES_GCM_8_256': 'AES-GCM-256-8',
        'AES_GCM_12_128': 'AES-GCM-128-12',
        'AES_GCM_12_256': 'AES-GCM-256-12',
        'AES_CCM_16_128': 'AES-CCM-128',
        'AES_CCM_16_256': 'AES-CCM-256',
        'AES_CTR_128': 'AES-CTR-128',
        'AES_CTR_192': 'AES-CTR-192',
        'AES_CTR_256': 'AES-CTR-256',
        '3DES_CBC': '3DES',
        'DES_CBC': 'DES',
        'CAMELLIA_CBC_128': 'CAMELLIA-128',
        'CAMELLIA_CBC_256': 'CAMELLIA-256',
        'CHACHA20_POLY1305': 'CHACHA20-POLY1305',
        'BLOWFISH_CBC': 'BLOWFISH',
        'CAST5_CBC': 'CAST5',
        'NULL': 'NULL'
    }
    
    hash_mapping = {
        'HMAC_MD5': 'MD5',
        'HMAC_MD5_96': 'MD5',
        'HMAC_SHA1': 'SHA1',
        'HMAC_SHA1_96': 'SHA1',
        'HMAC_SHA2_256': 'SHA2-256',
        'HMAC_SHA2_256_128': 'SHA2-256',
        'HMAC_SHA2_384': 'SHA2-384',
        'HMAC_SHA2_384_192': 'SHA2-384',
        'HMAC_SHA2_512': 'SHA2-512',
        'HMAC_SHA2_512_256': 'SHA2-512',
        'HMAC_SHA3_224': 'SHA3-224',
        'HMAC_SHA3_256': 'SHA3-256',
        'HMAC_SHA3_384': 'SHA3-384',
        'HMAC_SHA3_512': 'SHA3-512',
        'AES_GMAC_128': 'GMAC-128',
        'AES_GMAC_192': 'GMAC-192',
        'AES_GMAC_256': 'GMAC-256',
        'POLY1305': 'POLY1305',
        'NONE': 'NULL'
    }
    
    components = proposal.split('/')
    
    result = {
        'encryption': "Unknown",
        'hash': 'None',
        'dh_group': 'None'
    }
    
    is_ike = proposal.startswith('IKE:')
    is_esp = proposal.startswith('ESP:')
    
    if is_ike or is_esp:
        components[0] = components[0].replace('IKE:', '').replace('ESP:', '')
    
    if len(components) == 4:
        result['encryption'] = enc_mapping.get(components[0], 'Unknown')
        result['hash'] = hash_mapping.get(components[1], 'Unknown')
        result['dh_group'] = dh_mapping.get(components[3], 'None')
    elif len(components) == 3:
        result['encryption'] = enc_mapping.get(components[0], 'Unknown')
        if (is_ike or not is_esp) and components[1].startswith('PRF_'):
            result['hash'] = 'None'
            result['dh_group'] = dh_mapping.get(components[2], 'None')
        else:
            result['hash'] = hash_mapping.get(components[1], 'Unknown')
            result['dh_group'] = 'None'
    
    if result['encryption'] == 'Unknown':
        print(f"Unrecognized proposal: {proposal}")
    
    return result

def process_proposals(proposal_list):
    """
    Process a list of IKE or ESP proposals, concatenating encryption and hash values, and listing all unique DH groups.
    
    Args:
        proposal_list (str): Comma-separated string of IKE or ESP proposals
    
    Returns:
        str: Formatted string with concatenated encryption, hash, and DH groups
    """
    proposal_list = proposal_list.replace(',', ', ')
    proposals = proposal_list.strip().split(', ')
    
    # Collect unique encryption, hash, and DH groups
    enc_set = set()
    hash_set = set()
    dh_set = set()
    
    for proposal in proposals:
        parsed = parse_ike_proposal(proposal.strip())
        enc_set.add(parsed['encryption'])
        if parsed['hash'] != 'None':
            hash_set.add(parsed['hash'])
        if parsed['dh_group'] != 'None':
            dh_set.add(parsed['dh_group'])
    
    # Convert sets to sorted lists
    enc_list = sorted(list(enc_set))
    hash_list = sorted(list(hash_set))
    dh_list = sorted(list(dh_set), key=lambda x: int(x))
    
    # Format output as a single concatenated string
    enc_part = f"Encryption {' '.join(enc_list)}" if enc_list else "Encryption None"
    hash_part = f" Hash {' '.join(hash_list)}" if hash_list else " Hash None"
    dh_part = f" DH Group(s) {' '.join(dh_list)}" if dh_list else " DH Group(s) None"
    
    return f"{enc_part}{hash_part}{dh_part}"

# Example usage
if __name__ == "__main__":
    #IKEv1 DEFAULT AWS WORKING
    #proposals = """IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_3072, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_4096, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_6144, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_8192, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_256, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_384, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_521, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_256, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_3072, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_4096, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_6144, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_8192, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_256, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_384, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_521, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_256, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_3072, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_4096, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_6144, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_8192, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_256, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_384, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_521, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_256, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048,IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_3072, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_4096, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_6144, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_8192, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_256, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_384, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_521, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024_160, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_224, IKE:AES_CBC_128/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_3072, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_4096, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_6144, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_8192, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_256, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_384, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/ECP_521, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_3072, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_4096, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_6144, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_8192,IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_256, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_384, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/ECP_521, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_3072, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_4096, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_6144, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_8192, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_256, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_384, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/ECP_521, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA2_384_192/PRF_HMAC_SHA2_384/MODP_2048_256, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_3072, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_4096, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_6144, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_8192, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_256, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_384, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/ECP_521, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_1024_160, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_224, IKE:AES_CBC_256/HMAC_SHA2_512_256/PRF_HMAC_SHA2_512/MODP_2048_256"""
    
    #IKEv2 DEFAULT AWS NOT WORKING
    proposals = """IKE:AES_CBC_128/AES_CBC_256/HMAC_SHA1_96/HMAC_SHA2_256_128/HMAC_SHA2_384_192/HMAC_SHA2_512_256/PRF_HMAC_SHA1/PRF_HMAC_SHA2_256/PRF_HMAC_SHA2_384/PRF_HMAC_SHA2_512/MODP_1024/MODP_2048/MODP_3072/MODP_4096/MODP_6144/MODP_8192/ECP_256/ECP_384/ECP_521/MODP_1024_160/MODP_2048_224/MODP_2048_256, IKE:AES_GCM_16_128/AES_GCM_16_256/PRF_HMAC_SHA1/PRF_HMAC_SHA2_256/PRF_HMAC_SHA2_384/PRF_HMAC_SHA2_512/MODP_1024/MODP_2048/MODP_3072/MODP_4096/MODP_6144/MODP_8192/ECP_256/ECP_384/ECP_521/MODP_1024_160/MODP_2048_224/MODP_2048_256"""

    print(process_proposals(proposals))
import os, json, boto3, requests
from flask import Flask, request, jsonify
from flask_cors import CORS, cross_origin
from random import shuffle

app = Flask(__name__)
cors = CORS(app)

dynamodb = boto3.resource("dynamodb", region_name="us-east-1")

app.url_map.strict_slashes = False
SECRET_KEY = os.environ.get("SECRET_KEY")


@app.route("/teks")
def teks_request():
    teks_file = open("teks.json", "r")
    data = json.load(teks_file)
    return jsonify(data)


@app.route("/teks/find/113.41.<int:teks_id>.<string:section_id>")
def teks_find_request(teks_id, section_id):
    teks_file = open("teks.json", "r")
    data = json.load(teks_file)
    for item in data:
        if item["id"] == teks_id:
            for child in item["children"]:
                if child["id"] == section_id:
                    return {"tek": item, "content": child["content"]}
    return jsonify(
        [
            f"Something went wrong. TEKS section id of {section_id} cannot be found within TEKS section {teks_id}."
        ]
    )

@app.route("/lessonplan/read/<id>")
def read_lesson_plan(id):
    lesson_table = dynamodb.Table("Lesson_Plans")
    items = lesson_table.scan()['Items']
    for lesson in items:
        if (lesson["uuid"] == id):
            return jsonify(lesson)
    return {"error": "id does not exist", "section": id}

@app.route("/teks/<int:teks_id>")
def teks_id_request(teks_id):
    teks_file = open("teks.json", "r")
    data = json.load(teks_file)
    for item in data:
        if item["id"] == teks_id:
            return jsonify(item)
    return jsonify([f"Something went wrong. TEKS id of {teks_id} cannot be found."])


@app.route("/assessment/write", methods=["GET", "POST"])
def assessment_write():
    assessment_json = request.json
    assessment_data = dict(assessment_json)
    assessment_table = dynamodb.Table("Assessments")
    assessment_table.put_item(Item=assessment_data)

    if assessment_data == get_assessment(assessment_data["id"]):
        return "Success"
    else:
        return "Failure"

@app.route("/students/read/<id>")
def students_read(id):
    return jsonify(get_students(id))

@app.route("/students/read")
def all_students_read():
    student_table = dynamodb.Table("Students") 
    items = student_table.scan()['Items']
    return jsonify(items)

@app.route("/assessment/read/<id>")
def assessment_read(id):
    return jsonify(get_assessment(id))


@app.route("/assessment/submit/<id>", methods=["POST"])
def submit_assessment(id):
    assessments_table = dynamodb.Table("Assessments")
    assessment = assessments_table.get_item(Key={"id": id})

    if not assessment.get("Item"):
        return {"error": "id does not exist", "section": id}

    responses = {
        question["id"]: question["response"]
        for question in request.json.get("questions")
    }

    correct_answers = 0

    for response in responses:
        # print(
        #     (
        #         responses[response],
        #         find_question(assessment.get("Item").get("questions"), response).get(
        #             "correctAnswer"
        #         ),
        #     )
        # )
        if responses[response] == find_question(
            assessment.get("Item").get("questions"), response
        ).get("correctAnswer"):

            correct_answers += 1

    score = correct_answers / len(request.json.get("questions"))

    users_table = dynamodb.Table("Students")

    users_table.update_item(
        Key={"uuid": request.json.get("student_id")},
        UpdateExpression="SET completedAssessments = list_append(completedAssessments, :i)",
        ExpressionAttributeValues={
            ":i": [
                {
                    "id": id,
                    "score": round(score * 100),
                }
            ]
        },
    )

    message = None
    if round(score * 100) > 70:
        message = f"Congratulations! You passed your assessment with a {round(score * 100)}%."
    else:
        message = f"You failed your assessment with a {round(score * 100)}%."

    sns = boto3.client("sns", region_name="us-east-1")
    number = "+15125967383"
    sns.publish(PhoneNumber=number, Message=message)

    return {"score": score, "message": message}


def find_question(all, id):
    #print("id to find: ", id)
    for question in all:
        if question["id"] == id:
            #print(question)
            return question


def get_assessment(id):
    assessment_table = dynamodb.Table("Assessments")
    results = assessment_table.get_item(Key={"id": id})

    if results.get("Item") is None:
        return {"error": "id does not exist", "section": id}
    else:
        quiz = results.get("Item")
        return {
            "title": quiz.get("title"),
            "id": quiz.get("id"),
            "questions": [
                {
                    "id": question.get("id"),
                    "title": question.get("title"),
                    "options": random_answers(
                        question.get("incorrectAnswers")
                        + [question.get("correctAnswer")]
                    ),
                }
                for question in quiz.get("questions")
            ],
        }

def get_students(id):
    students_table = dynamodb.Table('Students')
    results = students_table.get_item(Key = {
        "uuid": id
    })

    if(results.get("Item") is None):
        return {'error': 'id does not exist', 'section': id}
    else:
        student = results.get("Item")
        return student

def lesson_plans_read():
    student_table = dynamodb.Table("Lesson_Plans")
    items = student_table.scan()['Items']
    return jsonify(items)

def random_answers(answers):
    shuffle(answers)
    return answers

@app.route("/recommendations/<uuid>")
def get_recommendation(uuid):
    student_info_table = dynamodb.Table('Students')
    lesson_plans_table = dynamodb.Table('Lesson_Plans')
    student = get_students(uuid)
    tek = student.get("struggleTeks")[0]

    lesson_plans = lesson_plans_table.scan( Select='ALL_ATTRIBUTES', FilterExpression='tek = :s', ExpressionAttributeValues={ ":s": tek })

    #print(lesson_plans) 

    return jsonify({"student": student, "lesson_plans": lesson_plans.get("Items")})

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)