mazda_upload.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import pandas as pd
  2. import numpy as np
  3. import json
  4. from dataclasses import dataclass
  5. from requests_oauthlib import OAuth2Session
  6. from datetime import datetime
  7. @dataclass
  8. class MazdaConfig:
  9. domain: str
  10. webservice: str
  11. module: str
  12. auth_url: str
  13. token_url: str
  14. client_id: str
  15. client_secret: str
  16. username: str
  17. password: str
  18. dealer_number: str
  19. @dataclass
  20. class Token:
  21. access_token: str
  22. token_type: str
  23. refresh_token: str
  24. expires_in: int
  25. sub: str
  26. iss: str
  27. iat: int
  28. defaultDomain: str
  29. userGuid: str
  30. organisations: list[str]
  31. jti: str
  32. scope: list[str]
  33. expires_at: float
  34. cfg = MazdaConfig(
  35. domain='https://mappsacc.mazdaeur.com',
  36. webservice='/dogma-restapi-dms/api',
  37. module='/vehicles/workshop/order-report',
  38. auth_url='/oauth/authorize',
  39. token_url='/oauth/token',
  40. client_id='E7FC943B-B73F-F48E-B71A-419EA4CD4AC7',
  41. client_secret='^bH=rk@c58zrr^Apc#9fzy$c',
  42. username='mmd88888.cdk',
  43. password='MazdaCX30',
  44. dealer_number='88888/MMD'
  45. )
  46. redirect_uri = 'https://localhost/'
  47. base_dir = '/home/robert/projekte/python/mazda/'
  48. def date_format(d: datetime):
  49. if d == 0:
  50. return '' # '0000-00-00T00:00:00.000Z'
  51. date_str = d.isoformat(sep='T')
  52. if len(date_str) == 19:
  53. return date_str + '.000Z'
  54. return date_str[:-3] + 'Z'
  55. # After updating the token you will most likely want to save it.
  56. def token_save(token):
  57. json.dump(token, open(base_dir + 'token.json', 'w'), indent=2)
  58. def token_load() -> Token:
  59. try:
  60. return json.load(open(base_dir + 'token.json', 'r'))
  61. except FileNotFoundError:
  62. return None
  63. def convert_csv(csv_file, json_file, year, month):
  64. date_min = datetime(year, month, 1, 0, 0, 0)
  65. date_max = datetime(year, month + 1, 1, 0, 0, 0)
  66. date_cols = ['invoiceDate', 'orderDate', 'orderCompletionDate', 'vehicleIntakeDate', 'nextMotDueDate']
  67. df = pd.read_csv(csv_file, encoding='latin-1', decimal=',', sep=';', parse_dates=date_cols)
  68. df = df.fillna(0)[(df['invoiceDate'] >= date_min) & (df['invoiceDate'] <= date_max)]
  69. df = df.sort_values(by=['invoiceDate', 'invoiceNumber', 'orderNumber', 'lineNumber'])
  70. df['vin'] = np.where(df['vin'] == 0, '0' * 17, df['vin'])
  71. # print(df[['currency','documentType','invoiceCategory','invoiceDate','invoiceNumber']].drop_duplicates().info())
  72. invoices_filter = ['currency', 'documentType', 'invoiceCategory', 'invoiceDate', 'invoiceNumber']
  73. invoices = df[invoices_filter].drop_duplicates().to_dict('records')
  74. invoice_items_filter = ['invoiceNumber', 'orderLineNumber', 'orderNumber', 'amount', 'discount', 'portion', 'unitPrice']
  75. invoice_items = df[invoice_items_filter].groupby('invoiceNumber')
  76. for invoice in invoices:
  77. invoice['invoiceDate'] = date_format(invoice['invoiceDate'])
  78. items = invoice_items.get_group(invoice['invoiceNumber'])
  79. items.pop('invoiceNumber')
  80. invoice['invoiceItems'] = items.to_dict('records')
  81. orders = df[['orderNumber', 'orderDate', 'orderCompletionDate', 'vehicleIntakeDate']].drop_duplicates().to_dict('records')
  82. orders_vehicle_filter = ['orderNumber', 'licensePlate', 'nextMotDueDate', 'odometer', 'odometerUnit', 'vin']
  83. orders_vehicle = df[orders_vehicle_filter].drop_duplicates().groupby('orderNumber')
  84. orders_items = df[[
  85. 'orderNumber', 'lineNumber', 'orderItemType',
  86. 'category', 'descriptionOperation', 'hours', 'operationCode', 'standardHours',
  87. 'descriptionOther', 'type',
  88. 'descriptionPart', 'isDamageCausal', 'manufacturer', 'partNumber', 'quantity', 'serialNumber', 'unit',
  89. 'company', 'descriptionPurchase', 'invoiceCode', 'invoiceDate', 'invoiceNumber'
  90. ]].drop_duplicates().groupby('orderNumber')
  91. for order in orders:
  92. order['vehicle'] = orders_vehicle.get_group(order['orderNumber']).to_dict('records')[0]
  93. order['vehicle']['nextMotDueDate'] = date_format(order['vehicle']['nextMotDueDate'])
  94. order['orderDate'] = date_format(order['orderDate'])
  95. order['orderCompletionDate'] = date_format(order['orderCompletionDate'])
  96. order['vehicleIntakeDate'] = date_format(order['vehicleIntakeDate'])
  97. items = orders_items.get_group(order['orderNumber']).to_dict('records')
  98. order['items'] = []
  99. for item in items:
  100. if item['orderItemType'] == 'operation':
  101. order['items'].append({
  102. 'lineNumber': item['lineNumber'],
  103. 'operation': {
  104. 'category': item['category'],
  105. 'description': item['descriptionOperation'],
  106. 'hours': item['hours'],
  107. 'operationCode': item['operationCode'],
  108. 'standardHours': item['standardHours']
  109. }
  110. })
  111. elif item['orderItemType'] == 'part':
  112. order['items'].append({
  113. 'lineNumber': item['lineNumber'],
  114. 'part': {
  115. 'description': item['descriptionPart'],
  116. 'isDamageCausal': item['isDamageCausal'],
  117. 'manufacturer': item['manufacturer'],
  118. 'partNumber': item['partNumber'],
  119. 'quantity': item['quantity'],
  120. 'serialNumber': item['serialNumber'],
  121. 'unit': item['unit']
  122. }
  123. })
  124. elif item['orderItemType'] == 'other':
  125. order['items'].append({
  126. 'lineNumber': item['lineNumber'],
  127. 'other': {
  128. 'description': item['descriptionOther'],
  129. 'type': item['type']
  130. }
  131. })
  132. else:
  133. order['items'].append({
  134. 'lineNumber': item['lineNumber'],
  135. 'purchaseInvoice': {
  136. 'company': item['company'],
  137. 'description': item['descriptionPurchase'],
  138. 'invoiceCode': item['invoiceCode'],
  139. 'invoiceDate': date_format(item['invoiceDate']),
  140. 'invoiceNumber': item['invoiceNumber']
  141. }
  142. })
  143. res = {
  144. 'creationDate': date_format(datetime.now()),
  145. 'invoices': invoices,
  146. 'orders': orders,
  147. 'timeRangeBegin': date_format(date_min),
  148. 'timeRangeEnd': date_format(date_max)
  149. }
  150. json.dump(res, open(json_file, 'w'), indent=2)
  151. return res
  152. def upload(data):
  153. headers = {
  154. 'accept': 'application/vnd.mazdaeur.dms.v4+json',
  155. 'x-mme-organisation': cfg.dealer_number,
  156. 'X-mazda-org': cfg.dealer_number,
  157. 'Content-Type': 'application/json',
  158. # 'Authorization': 'Bearer ' + token
  159. }
  160. extra = {
  161. 'client_id': cfg.client_id,
  162. 'client_secret': cfg.client_secret
  163. }
  164. token = token_load()
  165. oauth: OAuth2Session = None
  166. if token is not None:
  167. oauth = OAuth2Session(cfg.client_id, token=token)
  168. if token['expires_at'] < datetime.now().timestamp():
  169. # token = oauth.refresh_token(cfg.domain + cfg.auth_url, auth=(cfg.client_id, cfg.client_secret),
  170. # refresh_token=token['refresh_token'], redirect_response=None)
  171. # token_save(token)
  172. token = None
  173. if token is None or oauth is None:
  174. oauth = OAuth2Session(cfg.client_id, redirect_uri=redirect_uri)
  175. authorization_url, state = oauth.authorization_url(cfg.domain + cfg.auth_url)
  176. print('Please go here and authorize: ' + authorization_url)
  177. redirect_response = input('Paste the full redirect URL here:')
  178. token = oauth.fetch_token(cfg.domain + cfg.token_url, client_secret=cfg.client_secret, authorization_response=redirect_response)
  179. token_save(token)
  180. invoices = data['invoices']
  181. orders = data['orders']
  182. data['orders'] = []
  183. for i in invoices:
  184. data['invoices'] = [i]
  185. r = oauth.post(cfg.domain + cfg.webservice + cfg.module, json.dumps(data), headers=headers)
  186. print(f"{i['invoiceNumber']} => {r.status_code}")
  187. data['invoices'] = []
  188. for o in orders:
  189. data['orders'] = [o]
  190. r = oauth.post(cfg.domain + cfg.webservice + cfg.module, json.dumps(data), headers=headers)
  191. print(f"{o['orderNumber']} => {r.status_code}")
  192. with open(base_dir + 'post_error.log', 'w') as fwh:
  193. fwh.write(r.text)
  194. def main():
  195. data = convert_csv(base_dir + 'Workshop_Order_Report.csv', base_dir + 'mazda_export.json', 2021, 6)
  196. # data = json.load(open(base_dir + 'mazda_export.json', 'r'))
  197. upload(data)
  198. if __name__ == '__main__':
  199. main()