mazda_upload.py 8.7 KB

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