Ad

IndexError When Using Secrets.choice From Dictionary

- 1 answer

I have a dictionary of providers, which will contain elements like the one below:

{ 'p_4_0_0': {'technicians': ['683707d2-be18-49b7-bf67-a32048103ca1', '23d06b03-41f4-48cc-b83f-347ebfcb3084']}, 'p_4_0_1': {'technicians': ['d226a9b6-58f3-4f94-bf81-abd4dc825dca']}, ... }

The format used for the keys is string_id_level_pid, and I use secrets.choice to get a random element where the level part of the string is equal to the current level in my loop. However, I get the following error:

Traceback (most recent call last): File "path/to/file/graph.py", line 122, in generate_graph(100) File "path/to/file/graph.py", line 112, in generate_graph provider = providers[secrets.choice(list({k:v for (k,v) in providers.items() if (k.split('_'))[2] == str(level)}))] File "C:\Users\\AppData\Local\Programs\Python\Python37\lib\random.py", line 261, in choice raise IndexError('Cannot choose from an empty sequence') from None IndexError: Cannot choose from an empty sequence [Finished in 1.564s]

The full working code is here:

import networkx as nx
import matplotlib.pyplot as plt
from random import randrange,choices,randint
from pathlib import Path
import random
import secrets
import uuid
import math
import json
import os

# ============================================================================ #

# create a technician with id and provider id
def create_technician():
    return str(uuid.uuid4())

# create a provider with n technicians
def create_provider(region_id, level, number, n):
    id = 'p_'+str(region_id)+'_'+str(level)+'_'+str(number)

    # Create n technicians
    technicians = []
    for i in range(n):
        technicians.append(create_technician())

    return {id:{'technicians': technicians}}

def create_nmi_technicians(n):
    technicians = []
    for i in range(n):
        technicians.append(create_technician())
    return technicians

# Create report with parents + technician
def create_report(device_id, level, technician, parents):
    return {'id': device_id, 'level': level, 'technician': technician, 'parents': parents}

# n = number of field devices
def generate_graph(num_field_devices):
    max_chain_length = int(2*math.log(num_field_devices, 10))

    ## Info ##
    # ====== #
    # Field level = 0
    # NMI = max_chain_length-1

    nmi_level = str(max_chain_length-1)
    regions = {
        'USA': {'id': '1', 'NMI': {'level': nmi_level, 'acronym': 'NIST', 'technicians': create_nmi_technicians(5)}},
        'UK': {'id': '2', 'NMI': {'level': nmi_level, 'acronym': 'NPL', 'technicians': create_nmi_technicians(4)}},
        'China': {'id': '3', 'NMI': {'level': nmi_level, 'acronym': 'NIM', 'technicians': create_nmi_technicians(3)}},
        'France': {'id': '4', 'NMI': {'level': nmi_level, 'acronym': 'LNE', 'technicians': create_nmi_technicians(3)}}
    }

    # For each region, create a set of
    # 1 to m calibration service
    # providers for each level up to
    # the field level
    for region, data in regions.items():
        # Create providers dict
        providers = {}

        # For range between field (0) and
        # max_chain_length-2, create providers
        for i in range(0, max_chain_length-1):
            # Choose between 2 and 5 providers
            m = randint(2, 5)
            x = 0
            for j in range(m):
                # create provider with 1-3 technicians
                providers.update(create_provider(data['id'], i, x, randint(1,3)))
                x = x + 1

        # Add providers to region
        data.update({'providers': providers})

    #print(json.dumps(regions, indent=4))

    # Cool, now this is done, create the calibration
    # reports for the chain!
    devices = {}
    for level in range(max_chain_length):
        devices.update({level: []})
        # Field level
        if level == 0:
            for i in range(num_field_devices):
                devices[level].append(str(uuid.uuid4()))
        else:
            k = int(math.pow((2/3), level) * num_field_devices)
            for i in range(k):
                devices[level].append(str(uuid.uuid4()))

    # Create reports for these devices with parents + technician
    reports = {}
    for level,devs in devices.items():
        for device in devs:
            if level == 0:
                # Choose 2-3 parents from upper level
                parents = choices(devices[level+1], k=randrange(2,4))
            elif level == max_chain_length-1:
                # NMI level has no parents
                parents = []
            else:
                # Choose 1-2 parents from upper level
                parents = choices(devices[level+1], k=randrange(1,3))

            # Choose random region
            region = regions[secrets.choice(list(regions))]
            # Choose random provider at same level
            providers = region['providers']
            provider = providers[secrets.choice(list({k:v for (k,v) in providers.items() if (k.split('_'))[2] == str(level)}))]
            # Choose technician at the same level
            technician = secrets.choice(provider['technicians'])
            reports.update({'id': create_report(device, level, technician, parents)})

    print(reports)

## Run ##
# ===== #

generate_graph(100)

EDIT: the last element in providers is {} (empty dict) - what is the reason for this?

Ad

Answer

The highest level has its technicians in the NMI region, not in a provider dict. The solution here was the following:

# Choose random region
region = regions[secrets.choice(list(regions))]

# If NMI, use NMI technician, otherwise
# choose a provider from the level
if level == max_chain_length-1:
    technician = secrets.choice(region['NMI']['technicians'])
else:
    # Choose random provider at same level
    providers = region['providers']
    provider = providers[secrets.choice(list({k:v for (k,v) in providers.items() if (k.split('_'))[2] == str(level)}))]
    # Choose technician at the same level
    technician = secrets.choice(provider['technicians'])
Ad
source: stackoverflow.com
Ad