importir
Errorunknown
python
4 years ago
19 kB
7
Indexable
import json
import re
import requests
import traceback
from typing import Dict, List
from requests.exceptions import ConnectionError, Timeout
from django.conf import settings
from django.utils import timezone
from apps.utils.logger import create_mongo_logger
from apps.utils.image import get_image_url
from apps.utils.cache import cache_wrapper
from apps.helpers import currency, getSetting, updateOrCreateSetting
from apps.models_helper.setting_tb import SettingTb
from sentry_sdk import capture_exception
CONFIG_INCOMPLETE = 'Incomplete configuration for importir API.'
CONNECTION_FAILED = 'Failed connecting to importir.org'
def get_file_name(cd):
"""
Get filename from content-disposition
"""
if not cd:
return None
fname = re.findall('filename=(.+)', cd)
if len(fname) == 0:
return None
return fname[0]
class ImportirAPI:
# docs: https://importir.docs.apiary.io/
def __init__(self):
self.retry_count = 0
self.base_url = getattr(settings, "IMPORTIR_BASE_URL", None)
self.is_demo = getattr(settings, "IMPORTIR_IS_DEMO", True)
self.timeout = getattr(settings, "IMPORTIR_TIMEOUT", 20)
self.api_token = None
setting_token = SettingTb.objects.filter(setting_key='importir_api_token')\
.first()
if setting_token is None:
token_response = self.generate_token()
if token_response.get('action'):
self.api_token = token_response.get('result')\
.get('message', {})\
.get('token', '')
SettingTb.objects.create(
setting_desc='Importir API Token',
setting_key='importir_api_token',
setting_title='Importir API Token',
setting_value=self.api_token
)
else:
self.api_token = setting_token.setting_value
setting_token.last_used_at = timezone.now()
setting_token.save(update_fields=['last_used_at'])
def generate_token(self):
"""
Generate auth token for importir API
"""
ret = {'action': False, 'status_code': 500, 'result': None, 'message': ''}
endpoint = '/api/vendor/generate-token'
api_url = self.base_url + endpoint
user_email = getattr(settings, 'IMPORTIR_USER_EMAIL', None)
api_key = getattr(settings, 'IMPORTIR_API_KEY', None)
if not user_email or not api_key:
ret['message'] = CONFIG_INCOMPLETE
return ret
request_params = {'email': user_email, 'api_key': api_key}
try:
response = requests.get(api_url, params=request_params)
ret['status_code'] = response.status_code
if response.status_code == 200:
ret['action'] = True
ret['result'] = response.json()
if isinstance(ret['result'], dict) and ret.get('result').get('status', True) == False:
ret['action'] = False
except (ConnectionError, Timeout) as err:
ret['message'] = CONNECTION_FAILED
except Exception as err:
capture_exception(traceback.format_exc())
log_data = {
'METHOD': 'GET',
'URL': api_url,
'STATUS_CODE': ret['status_code'],
'REQUEST_PARAMETERS': request_params,
'RESPONSE_DATA': ret
}
# logger = create_mongo_logger('importir', collection_name="mongolog-request")
# logger.info(log_data)
return ret
def send(self, method, endpoint, request_data={}, request_files={}, request_params={}, request_headers={}, is_json=False):
ret = {'action': False, 'status_code': 500, 'message': '', 'result': None}
if not all([self.base_url, self.api_token]):
ret['message'] = CONFIG_INCOMPLETE
return ret
api_headers = {
# 'Content-Type': content_type,
'Authorization': 'Bearer ' + self.api_token
}
api_headers.update({**request_headers})
api_url = self.base_url + endpoint
log_data = {
'METHOD': method,
'HEADERS': api_headers,
'REQUEST_DATA': request_data,
'URL': api_url
}
try:
if method in ['post', 'POST']:
# print("request data: ", request_data)
# print("request files: ", request_files)
if is_json:
response = requests.post(api_url, headers=api_headers, data=json.dumps(request_data), timeout=7) # files=request_files
else:
response = requests.post(api_url, headers=api_headers, data=request_data, files=request_files, timeout=7)
elif method in ['get', 'GET']:
response = requests.get(api_url, headers=api_headers, params=request_params, timeout=self.timeout)
log_data['STATUS_CODE'] = response.status_code
log_data['FULL_URL'] = response.request.url
ret['status_code'] = response.status_code
if response.status_code == 200:
ret['result'] = response.json()
ret['action'] = True
if isinstance(ret['result'], dict):
# validasi api create shipping
if ret.get('result').get('status', True) == False:
ret['action'] = False
# validasi api product 1688 & checkout
importir_success_status = ['success_product_feeds_200', 'success_product_detail_200',
'success_product_description_view_200', 'success_checkout_200',
'success_categories_200', 'success_category_detail_200']
if ret.get('result').get('body') and ret.get('result').get('body').get('status_code') not in importir_success_status:
ret['action'] = False
except (ConnectionError, Timeout) as err:
ret['message'] = CONNECTION_FAILED
except Exception as err:
capture_exception(traceback.format_exc())
log_data['RESPONSE_DATA'] = ret
# logger = create_mongo_logger('importir', collection_name="mongolog-request")
# logger.info(log_data)
# If response is `Token is Expired`, retry request with new token
if self.retry_count == 0 and isinstance(ret.get('result'), dict) and ret.get('result').get('data') == 'Token is Expired':
self.retry_count += 1
token_response = self.generate_token()
if token_response.get('action'):
self.api_token = token_response.get('result')\
.get('message', {})\
.get('token', '')
_ = updateOrCreateSetting('importir_api_token', self.api_token, 'Importir API Token')
return self.send(method, endpoint, request_data, request_files, request_params, request_headers, is_json)
return ret
def get_file(self, file_url):
"""
Return response and filename
"""
response = None
try:
response = requests.get(file_url, allow_redirects=True)
if response.status_code == 200:
file_name = get_file_name(response.headers.get('content-disposition')) or file_url.rsplit('/', 1)[1]
return response, file_name
except (ConnectionError, Timeout) as err:
pass
except Exception as err:
response = None
print("Exception get_file: ", str(err))
return response, None
def create_shipping_step_one(self, cart_tk: "CartTitipkirimin"):
endpoint = '/api/shipping/step/1'
estimation_weight = cart_tk.estimation_weight
estimation_weight_kg = estimation_weight / 1000 if estimation_weight else 0
request_data = {
'freight': 10, # Laut LCL
'volume': cart_tk.estimation_volume,
'weight': estimation_weight_kg,
'is_demo': 1 if self.is_demo else 0,
'use_wood': 1 if cart_tk.is_wood else 0,
'wood_note': cart_tk.wood_note
}
request_headers = {
'lang': 'id',
'currency': 'idr'
}
return self.send('POST', endpoint, request_data, request_headers=request_headers)
def create_shipping_step_two(self, shipping_id: int):
endpoint = '/api/shipping/step/2'
request_data = {
'id': shipping_id, # `shipping_id` from step 1
'other_price_rmb': '',
}
request_headers = {
'lang': 'id',
'currency': 'idr'
}
return self.send('POST', endpoint, request_data, request_headers=request_headers)
def create_shipping_step_three(self, shipping_id: int, cart_tk: "CartTitipkirimin"):
endpoint = '/api/shipping/step/3'
request_data = {
'id': shipping_id, # `shipping_id` from step 1
'supplier_email': cart_tk.shipper_email,
'supplier_phone': cart_tk.shipper_phone_number,
'wechat_id': ''
}
request_files = {
# 'supplier_bank_info': '',
# 'invoice_file': '',
# 'packing_ist_file': ''
}
# content_type = 'application/x-www-form-urlencoded'
if cart_tk.invoice_file:
response, filename = self.get_file(get_image_url(cart_tk.invoice_file))
if response:
request_files.update({'invoice_file': (filename, response.content, response.headers.get('Content-Type'))})
# content_type = 'multipart/form-data'
request_headers = {
'lang': 'id',
'currency': 'idr'
}
return self.send('POST', endpoint, request_data, request_files, request_headers=request_headers)
def create_shipping_step_four(self, shipping_id: int):
endpoint = '/api/shipping/step/4'
request_data = {
'id': shipping_id, # `shipping_id` from step 1
'note': ''
}
request_headers = {
'lang': 'id',
'currency': 'idr'
}
return self.send('POST', endpoint, request_data, request_headers=request_headers)
def shipping_save_product(self, shipping_id: int, cart_detail: "CartDetail"):
endpoint = '/api/shipping/save-product/{}'.format(shipping_id)
product_type = cart_detail.get_product_type
product_price = cart_detail.price_original \
if cart_detail.currency == 'CNY' \
else currency(cart_detail.price_original, from_money=cart_detail.currency, to_money='CNY')
price_per_unit = product_price / cart_detail.quantity
is_lartas = 1 if cart_detail.additional_document == 'laporan' else 0
hscode = cart_detail.hscode # object
hscode_number = ''
if hscode is not None:
hscode_number = (hscode.hscode).replace('.', '')
request_data = {
'product_name': cart_detail.product.product_title,
'hscode': hscode_number,
'is_lartas': is_lartas,
'quantity': cart_detail.quantity,
'price_per_unit': price_per_unit,
'category[]': product_type.name
}
request_files = {
# 'cover_product': ''
}
if cart_detail.product.image_file:
response, filename = self.get_file(get_image_url(cart_detail.product.image_file))
if response:
request_files.update({'cover_product': (filename, response.content, response.headers.get('Content-Type'))})
request_headers = {
'lang': 'id',
'currency': 'idr'
}
return self.send('POST', endpoint, request_data, request_files, request_headers=request_headers)
def chat_list(self, shipping_id: int):
"""
Get chat list from Importir API
Args:
shipping_id (int): Importir shipping ID (see: Cart.additional_json)
"""
endpoint = '/api/shipping-chat/detail/{}'.format(shipping_id)
request_headers = {
'lang': 'id',
'currency': 'idr'
}
return self.send('GET', endpoint, request_headers=request_headers)
def post_chat(self, shipping_id: int, message: str):
"""
Send chat to Importir API
Args:
shipping_id (int): Importir shipping ID (see: Cart.additional_json)
"""
endpoint = '/api/shipping-chat/send-message/{}'.format(shipping_id)
request_data = {
'message': message
}
request_headers = {
'lang': 'id',
'currency': 'idr'
}
return self.send('POST', endpoint, request_data, request_headers=request_headers)
def get_shipping_detail(self, shipping_id: int):
"""
Get shipping detail from ImportirAPI
Args:
shipping_id (int): Importir Shipping ID
"""
endpoint = '/api/shipping/detail/{}'.format(shipping_id)
request_headers = {
'lang': 'id',
'currency': 'idr'
}
return self.send('GET', endpoint, request_headers=request_headers)
def get_shipping_status(self, shipping_id: int):
"""
Get shipping status from ImportirAPI
Args:
shipping_id (int): Importir Shipping ID
"""
endpoint = '/api/shipping-status/{}'.format(shipping_id)
request_headers = {
'lang': 'id',
'currency': 'idr'
}
return self.send('GET', endpoint, request_headers=request_headers)
def get_product_feeds(self, query_params: Dict={}) -> Dict:
"""
Get 1688 product feeds from importir
Args:
query_params (Dict): request query parameters
Returns:
Dict: Json response from importir
"""
endpoint = '/api/product/feeds'
if not isinstance(query_params, dict):
query_params = {}
def cache_validator(response):
if response.get('action'): return True
return False
return cache_wrapper(key='importir:1688_product_feeds', timeout=3600, validator=cache_validator)(self.send)(method='GET', endpoint=endpoint, request_params=query_params, request_data={}, request_files={}, request_headers={}, is_json=False)
def get_product_detail(self, product_id) -> Dict:
"""
Get 1688 product detail from importir
Args:
product_id (int): 1688 product ID
Returns:
Dict: Json response from importir
"""
if not product_id:
ret = {'action': False, 'status_code': 500, 'message': 'Invalid product ID'}
return ret
endpoint = '/api/product/detail/1688/%s' % product_id
return self.send('GET', endpoint)
def get_product_description(self, product_id: int) -> Dict:
"""
Get 1688 product description from importir
Args:
product_id (int): 1688 product ID
Returns:
Dict: Json response from importir
"""
if not product_id or not isinstance(product_id, int):
ret = {'action': False, 'status_code': 500, 'message': 'Invalid product ID'}
return ret
endpoint = '/product/description-view/%s' % product_id
return self.send('GET', endpoint)
def get_categories(self) -> Dict:
"""
Get product categories from importir
"""
endpoint = '/api/category/categories'
return self.send('GET', endpoint)
def get_sub_categories(self, category_slug: str) -> Dict:
"""
Get list sub categories from importir
Args:
category_slug (str): category slug
"""
endpoint = '/api/category/detail'
request_params = {
'slug': category_slug
}
return self.send('GET', endpoint, request_params=request_params)
def checkout(self, product_id: int, cart_details) -> Dict:
"""
Checkout Importir
"""
endpoint = '/api/cart/order-vendor'
quantity = subtotal = weight = 0
is_multi_price = False
spec_list = []
price_multiple_desc_list = []
cart_details = cart_details
first_cart_detail = cart_details.first()
for cart_detail in cart_details:
quantity += cart_detail.quantity
weight += cart_detail.weight
product_type = cart_detail.product_type
margin_percent = product_type.get_1688_margin(cart_detail.customer)
margin = (100 + margin_percent) / 100
subtotal += (cart_detail.sub_total / margin)
additional_info = cart_detail.get_additional_information()
if additional_info.get('spec_id') and additional_info.get('sku_id'):
spec_price = cart_detail.price / margin
spec_list.append({'quantity': cart_detail.quantity, 'price': str(spec_price), 'specId': additional_info.get('spec_id')})
price_multiple_desc_list.append({'id': str(additional_info.get('sku_id')), 'qty': cart_detail.quantity})
if not is_multi_price:
is_multi_price = additional_info.get('is_multi_price') == 1
request_data = {
'product_id': product_id,
'specIds': spec_list,
'note': first_cart_detail.product.product_title if first_cart_detail else '',
'note_ori': first_cart_detail.product.product_title if first_cart_detail else '',
'quantity': quantity,
'price': str(subtotal),
'freight': 'air' if first_cart_detail.package_track == 'udara' else 'sea',
'is_parse': 0,
'is_multiple_price': 1 if not is_multi_price else 0,
'price_type': 'RANGE' if is_multi_price else 'OTHERS',
'price_multiple_desc': price_multiple_desc_list,
'weight': weight,
'cbm': 0
}
request_headers = {
'Content-Type': 'application/json',
'lang': 'id',
'currency': 'idr'
}
return self.send('POST', endpoint, request_data=request_data, request_headers=request_headers, is_json=True)
def get_official_order_status(self, order_id: int) -> Dict:
"""
Get official 1688 order status from importir
Args:
order_id (int): Importir order ID
"""
endpoint = '/api/vendor/tracking/official-last-status'
request_params = {
'order_id': order_id
}
request_headers = {
'lang': 'id',
'currency': 'idr'
}
return self.send('GET', endpoint, request_params=request_params, request_headers=request_headers)Editor is loading...