Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Itération dans un structure de dictionnaire complexe

+1 vote

J'obtiens une réponse soap sous la forme d'un dictionnaire dont voic le gist.

https://gist.github.com/anonymous/828208d653cfb8a73017a7161bc55af2

Pour info j'utilise la library Zeep qui est un client SOAP.

Mon problème est que la réponse une liste imbriqué de dictionnaire contenant aussi des listes, eux contenant des dictionnaires....Bref une structure de donnée imbriqué complexe.

Mon but étant d'extraire sur un CSV certaine clé de certain dictionnaire et bien sur un itérant sur tout les dictionnaire de la réponse qui se trouve dans la liste 'Order': [....]

J'ai commencé par faire:

    #Select the big Order list which contain all order I want to proceed
myCSV = result['OrderList']['Order']
#Select the right indice for keys
keys = myCSV[0].keys()
#Start writing inside a csv all dictionnary
with open('order.csv', 'wb') as output_file:
    dict_writer = csv.DictWriter(output_file, keys)
    dict_writer.writeheader()
    dict_writer.writerows(myCSV)

#Start a new csv to extract the first colunm of the previous csv
fname = "order.csv"
file = open(fname, "rb")

with open('order2.csv', 'wb') as csv_file:
    try:
        reader = csv.reader(file)
        first_row = next(reader)
        for row in reader:
            # read the first colunm
            customer = row[0]
            #convert the str got to dict
            dict_customer = ast.literal_eval(customer)
            #Start a dictWriter
            writer = csv.DictWriter(csv_file,dict_customer.keys())
            writer.writerow(dict_customer)
    finally:
        file.close()

Dans l'idée c'etait de créer un CSV pour chaque dictionnaire et ensuite d'itérer dans ces CSV pour faire un fichier final. Mais je me rend compte que c'est une usine a gaz.

Du coup j'ai chercher une autre méthode et j'ai trouvé une piste sur SO avec cette fonction:

def search_it(nested, target):
    found = []
    for key, value in nested.iteritems():
        if key == target:
            found.append(value)
        elif isinstance(value, dict):
            found.extend(search_it(value, target))
        elif isinstance(value, list):
            for item in value:
                if isinstance(item, dict):
                    found.extend(search_it(item, target))
        else:
            if key == target:
                found.append(value)
    return found

keys = [ 'EncryptedEmail','SiteCommissionPromisedAmount','ShippingLastName','ShippingFirstName','City',
         'Street','InitialTotalAmount','ZipCode', 'SellerProductId', 'OrderNumber','MobilePhone'  ]

with open('order_new.csv', 'wb') as output_file:
    writer = csv.writer(output_file, delimiter=',',quotechar="'", lineterminator='\n')
    for key in keys:
        f = search_it(result, key)
        print type(f)
        try:
            writer.writerow([f])
        except:
            print"error"
        print 'Key: %s, value: %s' % (key, f)

Mais la aussi vu qu'il y plusieurs fois les meme nom de keys dans les dico imbriqués, ca ne produit pas le resultats escompté. Décalage, doublons etc

Ma question comment faire pour sélectionné une key spécifique dans tous les dictionnaires.

Du genre je veux les "Street" du dico 'ShippingAddress' et pas les "Street" du dico 'BillingAddress'
Parce que avec ma fonction j'obtiens des doublons.

De plus Je n'ai pas la forme de CSV classique cad pour le moment cà me let met en ligne

Street , Name, city
"10 rue du slip", "Dupond", "Marseille"
"52 rue du sommeil", "Leon", "Paris"
"10 av les grands", "Dupond", "Nante"

demandé 11-Nov-2016 par Andronaute (150 points)
edité 13-Nov-2016 par max

pourquoi tu mets pas tout ça dans une base de données ?

Je ne connaissais pas zeep, j'utilise suds pour les appels SOAP.

Faudra que je me penche sur cette lib', merci pour l'info. ;)

@Bufallo974: Oui pourquoi pas. Il faudrait que je regarde de ce coté la. j'ai deja fait qq classe de bdd mais mon probleme reside qd meme dans l'iteration dans les dic. J'ai pas envie de mettre tout dans la bdd. Je veux que certain key:value des dico

@debnet: Suds est plus maintenu, meme en utilisant des fork a droite a gauche tu te retrouve avec des bugs sur des webservice recent( genre boucle infini dans le wsdl, et autres)
Apres si ça marche pour toi, tant mieux. Moi j'essaie de me connecter sur le webservice de Cdiscount pour générer un csv a transmettre a un client et suds fait du recursive loop....

merci

1 Réponse

+2 votes

EDIT: J'ai updaté l'exemple pour avoir un truc qui marche, il manquait l'unpacking sur le *fields)

J'ai développé lifter notamment pour ce type de cas. En gros, il s'agit d'un ORM qui permet d'interroger des iterables:

import lifter.models
from lifter.backends.python import IterableStore

class Order(lifter.models.Model):
     pass

d = data['OrderList']['Order']
store = IterableStore(d)
manager = store.query(Order)

fields = [
    'Customer__EncryptedEmail',
    'SiteCommissionPromisedAmount',
    'Customer__ShippingLastName',
    'Customer__ShippingFirstName',
    'BillingAddress__City',
    'BillingAddress__Street',
    'InitialTotalAmount',
    'BillingAddress__ZipCode',
    'OrderLineList__OrderLine__SellerProductId',
    'OrderNumber',
    'Customer__MobilePhone',
]

rows = manager.all().values_list(*fields)

et voilà ! Ta variable rows contient maintenant les valeurs demandées (via la variable fields). A charge a toi de sortir ça en csv ou autre.

Attention cependant : La gestion des iterables imbriquées n'est pas encore supportée, donc si tu essaie de sortir des valeurs issues d'objets contenus dans un iterable à l'intérieur de chaque ligne, ça ne fonctionne pas. En l'occurence, d'après ton post initial, seul le champ "SellerProductId" est concerné. Quand je dis ça ne fonctionne pas : ça te sort une valeur qui n'est pas utilisable en tant que telle, une instance de la classe IterableAttr (mais normalement, le script ne plante pas)

répondu 12-Nov-2016 par Eliot
edité 13-Nov-2016 par eliotberriot

Bonsoir, merci pour ta réponse

J'obtiens une erreur en executant ton code apparament ca coince au niveau des liste qui se trouve dans les dico non ?

Traceback (most recent call last):
  File "C:/Users/moumou/Google Drive/pycharmpoject/cdiscount_api/test_dict.py", line 4931, in <module>
    rows = manager.all().values_list(fields)
  File "C:\Python27\lib\site-packages\lifter\query.py", line 434, in values_list
    return self.manager.execute(query)
  File "C:\Python27\lib\site-packages\lifter\managers.py", line 26, in execute
    return store.execute_query(query)
  File "C:\Python27\lib\site-packages\lifter\store.py", line 137, in execute_query
    result = handler(query)
  File "C:\Python27\lib\site-packages\lifter\store.py", line 52, in wrapper
    values = map(getter, results)
  File "C:\Python27\lib\site-packages\lifter\store.py", line 51, in <lambda>
    getter = lambda val: tuple(path_to_value(val, path) for path in query.hints['paths'])
  File "C:\Python27\lib\site-packages\lifter\store.py", line 51, in <genexpr>
    getter = lambda val: tuple(path_to_value(val, path) for path in query.hints['paths'])
  File "C:\Python27\lib\site-packages\lifter\store.py", line 29, in path_to_value
    getters = [utils.attrgetter(part) for part in path.path]
AttributeError: 'list' object has no attribute 'path'

Effectivement, tu tombe dans cette issue là : https://github.com/EliotBerriot/lifter/issues/7

Je vais voir si je peux pousser un patch pour répondre à ce cas d'utilisation qui est quand même bine pratique ;)

@Andronaute : après vérification, il ne s'agit pas de l'issue en question. En fait, c'était une typo dan smon code d'exemple, il manquant un unpacking. J'ai complété / édité ma réponse (et j'ai fait le test sur ma machine avec tes données, ça tourne).

@Eliotberriot merci. Par contre du coup ca pose un probleme car justement j'ai besoin d'voir Access a ces donnée imbriqués.

Désolé alors, je n'ai pas de réponse propre à t'apporter :/ Tu peux bien sûr accéder directement à IterableAttr._items pour parcourir les élements en question, mais c'est n'est pas documenté et cela risque de changer prochainement.

...