Fluent Commerce Logo
Docs
Sign In

Python tool to compare entities attributes

Essential knowledge

Author:

Fluent Commerce

Changed on:

12 Dec 2023

Overview

This is a tool for comparing 2 entities' attributes with the OMS. For example, customer support would like to compare order attributes between Production and UAT.

Key points

  • Pycharm app to help user to compare two entity attributes

Pre-requisite

  • Pycharm installed on your computer
  • vscode installed with the command line. (alternative diff can be used. see the compare section in the code)


Steps to run Pycharm app:

Create a new pycharm project and copy and paste the below code into the main.py.  Install all dependency libraries.   Update retailer variables and Run the program

1import json
2import requests
3import os
4from graphqlclient import GraphQLClient
5from http.client import IncompleteRead
6from datetime import datetime
7
8
9#
10# Prereq:  install vscode cli version: https://code.visualstudio.com/docs/setup/mac
11#
12# Program: This program will extract 2 entities and compare the attributes by using vscode diff.
13#
14
15# provide either account or retailer oauth as long as the login is able to get the entity attributes
16#########################
17
18accountId_A = '{{accountID_A}}'
19oauth_url_A = '{{oauth_A}}'
20graphql_url_A = 'https://{{url_A}}/graphql'
21entity_name_A = 'orders'
22entity_ref_A = 'CC_75014'
23# can compare any entities with attributes:
24#entity_name_A = 'fulfilments'
25#entity_ref_A = '61d07f67-f529-4f68-8682-0e32d8ca3110'
26
27accountId_B = '{{accountID_B}}'
28oauth_url_B = '{{oauth_B}}'
29graphql_url_B = 'https://{{url_B}}/graphql'
30entity_name_B = 'orders'
31entity_ref_B = 'CC_43422'
32
33
34#***********************************************************************************
35# After configuring the above variables, you can simply run the apps to review the result.
36####################################################################################
37####################################################################################
38
39
40
41
42#unique batch no for each run
43batch_no = datetime.now().strftime("%Y%m%d%H%M%S")
44####################################################################################
45# standard def to extract API data:
46####################################################################################
47def get_page_of_data(pquery,client, after, first):
48    res = client.execute(pquery, {'after': after, 'first': first})
49    data = json.loads(res)['data']
50    return data
51
52### ------  Get Dataset for account A ---- ###
53def get_token_A():
54    auth_token_response = requests.post(oauth_url_A)
55    if auth_token_response.status_code == 200:
56        print(auth_token_response.json())
57    else:
58        print("Couldn't get auth token {}".format(auth_token_response.status_code))
59    access_token = 'Bearer ' + auth_token_response.json()['access_token']
60    print("Access token: {}".format(access_token))
61    return access_token
62
63def init_client_A():
64    client = GraphQLClient(graphql_url_A)
65    client.inject_token(get_token_A())
66    return client
67
68def get_all_data_A(pquery,pentity, client=init_client_A(), all_labels=[], cursor=None, first=50, retries_left=5):
69    global data
70    try:
71        data = get_page_of_data(pquery,client, cursor, first)
72    except IncompleteRead:
73        print('reconnect and keep tracking')
74        if retries_left > 0:
75            retries_left = retries_left - 1
76            client = init_client_A()
77            get_all_data_A(pquery,pentity,client, all_labels, cursor, retries_left)
78        else:
79            print('retries exhausted')
80            return all_labels
81    except:
82        print("error occurred for cursor: {} retrying with retries left:{}".format(cursor, retries_left))
83        if retries_left > 0:
84            retries_left = retries_left - 1
85            get_all_data_A(pquery,pentity,client, all_labels, cursor, retries_left)
86        else:
87            print('retries exhausted')
88            return all_labels
89    if data is not None:
90        new_labels = [] or data[pentity]['edges']
91        has_next_page = data[pentity]['pageInfo']['hasNextPage']
92        all_labels = all_labels + new_labels
93        if has_next_page:
94            if len(new_labels) > 0:
95                cursor = get_cursor(new_labels)
96            all_labels = get_all_data_A(pquery,pentity,client, all_labels, cursor)
97    return all_labels
98
99
100def get_cursor(new_labels):
101    if len(new_labels) > 0:
102        last = len(new_labels) - 1
103        while True:
104            if 'cursor' in new_labels[last]:
105                return new_labels[last]['cursor']
106            else:
107                last -= 1
108                if 'cursor' in new_labels[last]:
109                    return new_labels[last]['cursor']
110    else:
111        pass
112
113
114### ------  Get Dataset for account B ---- ###
115def get_token_B():
116    auth_token_response = requests.post(oauth_url_B)
117    if auth_token_response.status_code == 200:
118        print(auth_token_response.json())
119    else:
120        print("Couldn't get auth token {}".format(auth_token_response.status_code))
121    access_token = 'Bearer ' + auth_token_response.json()['access_token']
122    print("Access token: {}".format(access_token))
123    return access_token
124
125def init_client_B():
126    client = GraphQLClient(graphql_url_B)
127    client.inject_token(get_token_B())
128    return client
129
130def get_all_data_B(pquery,pentity,client=init_client_B(), all_labels=[], cursor=None, first=50, retries_left=5):
131    global data
132    try:
133        data = get_page_of_data(pquery,client, cursor, first)
134    except IncompleteRead:
135        print('reconnect and keep tracking')
136        if retries_left > 0:
137            retries_left = retries_left - 1
138            client = init_client_B()
139            get_all_data_B(pquery,pentity,client, all_labels, cursor, retries_left)
140        else:
141            print('retries exhausted')
142            return all_labels
143    except:
144        print("error occurred for cursor: {} retrying with retries left:{}".format(cursor, retries_left))
145        if retries_left > 0:
146            retries_left = retries_left - 1
147            get_all_data_B(pquery,pentity,client, all_labels, cursor, retries_left)
148        else:
149            print('retries exhausted')
150            return all_labels
151    if data is not None:
152        new_labels = [] or data[pentity]['edges']
153        has_next_page = data[pentity]['pageInfo']['hasNextPage']
154        all_labels = all_labels + new_labels
155        if has_next_page:
156            if len(new_labels) > 0:
157                cursor = get_cursor(new_labels)
158            all_labels = get_all_data_B(pquery,pentity,client, all_labels, cursor)
159    return all_labels
160
161
162
163def compareFiles(file1, file2):
164    cmd = 'code -d ' + file1 + ' ' + file2
165    os.system(cmd)
166    os.system('echo ' + cmd + '>> ' + batch_no+"_log")
167    os.system('chmod 755 ' + batch_no + "_log")
168##  Alternative way to compare file by using diff command:
169#cmd = 'diff -y ' + file1 + ' ' + file2 + \
170#                '> '+ (batch_no + '_diff_' + file1 + '_' + file2 + '_') + '.txt'
171#os.system(cmd)
172
173#########################################################################################################
174#### Entity
175#########################################################################################################
176
177entity_query = '''
178    edges {
179       node {
180            id
181            ref
182            createdOn
183            updatedOn
184            status
185            attributes{
186                attr_name: name
187                attr_type: type
188                attr_value: value
189            }
190       }
191       cursor
192     }
193     pageInfo {
194       hasNextPage
195     }
196   }
197 }
198 '''
199
200entity_query_headerA = 'query extractEntity($after:String, $first:Int) {'+entity_name_A+'(after:$after, first:$first, ref:"'+entity_ref_A+'"){' +entity_query
201entity_query_headerB = 'query extractEntity($after:String, $first:Int) {'+entity_name_B+'(after:$after, first:$first, ref:"'+entity_ref_B+'"){' + entity_query
202entity_ListA = []
203entity_ListB = []
204entity_filenameA = batch_no+'_' + accountId_A + '_' + entity_name_A + '_' + entity_ref_A + '_A.csv'
205entity_filenameB = batch_no+'_' + accountId_B + '_' + entity_name_B + '_' + entity_ref_B + '_B.csv'
206
207def process_entity_result(all_labels,pfilename, pentityname,paccountId,pgraphqlURL ):
208    file = open(pfilename, "w")
209    file.write("ACCOUNT:" + paccountId +"\n")
210    file.write("URL:" + pgraphqlURL + "\n")
211    attributes = []
212    for data in all_labels:
213        if len(data.keys()) > 0:
214            file.write("ENTITY:" + pentityname +"\n")
215            file.write("ID:" + str(data['node']['id']) +"\n")
216            file.write("REF:" + str(data['node']['ref']) +"\n")
217            file.write("CREATEDON:" + str(data['node']['createdOn']) +"\n")
218            file.write("UPDATEDON:" + str(data['node']['updatedOn']) +"\n")
219            file.write("STATUS:" + str(data['node']['status']) +"\n")
220            file.write("ATTRIBUTES count("+str(len(data['node']['attributes']))+"):\n")
221            #print(data['node']['attributes'])
222            #print(len(data['node']['attributes']))
223
224            #get all attributes and sort it.
225            n = 0
226            while  n < len(data['node']['attributes']):
227                attributes.append(data['node']['attributes'][n]['attr_name']+","+data['node']['attributes'][n]['attr_type']+","+str(data['node']['attributes'][n]['attr_value']))
228                n += 1
229            attributes.sort()
230
231            #write the sorted attributes into the file.
232            n = 0
233            while n < len(attributes):
234                file.write(attributes[n] + "\n")
235                n += 1
236            #print(attributes)
237    file.close()
238
239# get entity A
240result = get_all_data_A(pquery=entity_query_headerA,pentity=entity_name_A)
241process_entity_result(result,entity_filenameA,entity_name_A, accountId_A, graphql_url_A)
242
243# get entity B
244result = get_all_data_B(pquery=entity_query_headerB,pentity=entity_name_B)
245process_entity_result(result,entity_filenameB,entity_name_B,accountId_B, graphql_url_B)
246
247# compare Files
248compareFiles(entity_filenameA, entity_filenameB)
249
250
251
252
253
254

Language: python

Name: Python compare attributes

Description:

[Warning: empty required content area]

The result will also launch vscode with a visualisation of file comparison:

No alt provided


The <Batch_no>_log file has been changed to executable: which means, you can get reopen the result by running the <batch_no>_log file in the command line. note: this is tested in Mac. User might need to re-tune the permission in Windows.


Fluent Commerce

Fluent Commerce

Copyright © 2024 Fluent Retail Pty Ltd (trading as Fluent Commerce). All rights reserved. No materials on this docs.fluentcommerce.com site may be used in any way and/or for any purpose without prior written authorisation from Fluent Commerce. Current customers and partners shall use these materials strictly in accordance with the terms and conditions of their written agreements with Fluent Commerce or its affiliates.

Fluent Logo