Python tool to compare entities attributes
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:
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.