Skip to content

Commit

Permalink
Merge branch 'feature/add-switch-send-files' of https://github.com/we…
Browse files Browse the repository at this point in the history
  • Loading branch information
lucaslinhares committed May 15, 2024
2 parents 8f4f74a + db6dd0f commit a5c79c0
Show file tree
Hide file tree
Showing 20 changed files with 968 additions and 30 deletions.
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
v3.6.3
----------
* Ignore 404 error when fetching vtex product details
* Round up prices for weight-calculated products
* First sync vtex products for specific sellers
* Vtex rules for colombia
* Add method to list only active skus IDs

v3.6.2
----------
* Remove vtex webhook log

v3.6.1
----------
* Add vtex webhook log and add retry in facebook methods

v3.6.0
----------
* Permission update consumer
* Check if there is a feed upload in progress before uploading a new

v3.5.0
----------
* Sends webhook and template data to flows
Expand Down
2 changes: 1 addition & 1 deletion marketplace/accounts/usecases/permission_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def update_permission(
project = Project.objects.get(uuid=project_uuid)
user, _ = get_or_create_user_by_email(user_email)

if action == "create" or action == "update": # OK - Verificar no shell
if action == "create" or action == "update":
update_user_permission(role, project, user)

if action == "delete":
Expand Down
55 changes: 41 additions & 14 deletions marketplace/clients/decorators.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,55 @@
import time
import functools
import requests


def retry_on_exception(max_attempts=10, start_sleep_time=1, factor=2):
def retry_on_exception(max_attempts=8, start_sleep_time=1, factor=2):
def decorator_retry(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
attempts, sleep_time = 0, start_sleep_time
last_exception = ""
while attempts < max_attempts:
try:
return func(*args, **kwargs)
except (
Exception
) as e: # TODO: Map only timeout errors or errors from many requests
if attempts > 5:
print(
f"Retrying... Attempt {attempts + 1} after {sleep_time} seconds, {str(e)}"
)
time.sleep(sleep_time)
attempts += 1
sleep_time *= factor

print("Max retry attempts reached. Raising exception.")
raise Exception("Rate limit exceeded, max retry attempts reached.")
except requests.exceptions.Timeout as e:
last_exception = e
if attempts >= 5:
print(f"Timeout error: {str(e)}. Retrying...")
except requests.exceptions.HTTPError as e:
last_exception = e
if attempts >= 5:
if e.response.status_code == 429:
print(f"Too many requests: {str(e)}. Retrying...")
elif e.response.status_code == 500:
print(f"A 500 error occurred: {str(e)}. Retrying...")
else:
raise
except Exception as e:
last_exception = e
if hasattr(e, "status_code") and e.status_code == 404:
print(f"Not Found: {str(e)}. Not retrying this.")
raise

if attempts >= 5:
print(f"An unexpected error has occurred: {e}")

if attempts >= 5:
print(
f"Retrying... Attempt {attempts + 1} after {sleep_time} seconds"
)

time.sleep(sleep_time)
attempts += 1
sleep_time *= factor

message = (
f"Rate limit exceeded, max retry attempts reached. Last error in {func.__name__}:"
f"Last error:{last_exception}, after {attempts} attempts."
)

print(message)
raise Exception(message)

return wrapper

Expand Down
22 changes: 22 additions & 0 deletions marketplace/clients/facebook/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from django.conf import settings

from marketplace.clients.base import RequestClient
from marketplace.clients.decorators import retry_on_exception


WHATSAPP_VERSION = settings.WHATSAPP_VERSION

Expand Down Expand Up @@ -45,6 +47,7 @@ def destroy_catalog(self, catalog_id):

return response.json().get("success")

@retry_on_exception()
def create_product_feed(self, product_catalog_id, name):
url = self.get_url + f"{product_catalog_id}/product_feeds"

Expand All @@ -54,6 +57,7 @@ def create_product_feed(self, product_catalog_id, name):

return response.json()

@retry_on_exception()
def upload_product_feed(
self, feed_id, file, file_name, file_content_type, update_only=False
):
Expand Down Expand Up @@ -293,3 +297,21 @@ def get_upload_status_by_feed(self, feed_id, upload_id):
return "end_time" in upload

return False

def get_uploads_in_progress_by_feed(self, feed_id):
url = self.get_url + f"{feed_id}/uploads"

headers = self._get_headers()
response = self.make_request(url, method="GET", headers=headers)
data = response.json()
upload = next(
(
upload
for upload in data.get("data", [])
if upload.get("end_time") is None
),
None,
)
if upload:
if "end_time" not in upload:
return upload.get("id")
27 changes: 27 additions & 0 deletions marketplace/clients/vtex/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def _get_headers(self):


class VtexCommonClient(RequestClient):
@retry_on_exception()
def check_domain(self, domain):
try:
url = f"https://{domain}/api/catalog_system/pub/products/search/"
Expand All @@ -35,6 +36,7 @@ def search_product_by_sku_id(self, skuid, domain, sellerid=1):


class VtexPrivateClient(VtexAuthorization, VtexCommonClient):
@retry_on_exception()
def is_valid_credentials(self, domain):
try:
url = (
Expand Down Expand Up @@ -103,3 +105,28 @@ def pub_simulate_cart_for_seller(self, sku_id, seller_id, domain):
"price": 0,
"list_price": 0,
}

@retry_on_exception()
def list_all_active_products(self, domain):
unique_skus = set()
step = 250
current_from = 1

while True:
current_to = current_from + step - 1
url = (
f"https://{domain}/api/catalog_system/pvt/products/"
f"GetProductAndSkuIds?_from={current_from}&_to={current_to}&status=1"
)
headers = self._get_headers()
response = self.make_request(url, method="GET", headers=headers)

data = response.json().get("data", {})
if not data:
break

for _, skus in data.items():
unique_skus.update(skus)
current_from += step

return list(unique_skus)
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class ConfigSerializer(serializers.Serializer):
customCss = serializers.CharField(required=False)
timeBetweenMessages = serializers.IntegerField(default=1)
tooltipMessage = serializers.CharField(required=False)
showAttachmentsButton = serializers.BooleanField(default=True)

def to_internal_value(self, data):
self.app = self.parent.instance
Expand Down
3 changes: 3 additions & 0 deletions marketplace/services/facebook/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ def upload_product_feed(
def get_upload_status_by_feed(self, feed_id, upload_id) -> bool:
return self.client.get_upload_status_by_feed(feed_id, upload_id)

def get_in_process_uploads_by_feed(self, feed_id) -> str:
return self.client.get_uploads_in_progress_by_feed(feed_id)

# ================================
# Private Methods
# ================================
Expand Down
88 changes: 88 additions & 0 deletions marketplace/services/vtex/business/rules/calculate_by_weight_co.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
from .interface import Rule
from marketplace.services.vtex.utils.data_processor import FacebookProductDTO
from typing import Union


class CalculateByWeightCO(Rule):
def apply(self, product: FacebookProductDTO, **kwargs) -> bool:
if self._calculates_by_weight(product):
unit_multiplier = self._get_multiplier(product)
weight = self._get_weight(product) * unit_multiplier

# 10% increase for specific categories
if self._is_increased_price_category(product) and weight >= 500:
increase_factor = 1.10 # 10%
product.price *= unit_multiplier * increase_factor
product.sale_price *= unit_multiplier * increase_factor
else:
product.price *= unit_multiplier
product.sale_price *= unit_multiplier

price_per_kg = 0
if weight > 0:
formatted_price = float(f"{product.sale_price / 100:.2f}")
price_per_kg = formatted_price / unit_multiplier

product.description = (
f"{product.title} - Aprox. {self._format_grams(weight)}, "
f"Precio por KG: COP {self._format_price(price_per_kg)}"
)
product.title = f"{product.title} Unidad"

return True

def _is_increased_price_category(self, product: FacebookProductDTO) -> bool:
categories = [
"carne y pollo",
"carne res",
"pescados y mariscos",
"pescado congelado",
]
product_categories = {
k: v.lower()
for k, v in product.product_details["ProductCategories"].items()
}

return any(category in product_categories.values() for category in categories)

def _get_multiplier(self, product: FacebookProductDTO) -> float:
return product.product_details.get("UnitMultiplier", 1.0)

def _get_weight(self, product: FacebookProductDTO) -> float:
return product.product_details["Dimension"]["weight"]

def _calculates_by_weight(self, product: FacebookProductDTO) -> bool:
all_categories = [
"carne y pollo",
"carne res",
"pescados y mariscos",
"pescado congelado",
"verduras",
"frutas",
]
product_categories = {
k: v.lower()
for k, v in product.product_details["ProductCategories"].items()
}

for category in all_categories:
if category in product_categories.values():
return True

return False

def _format_price(self, price: Union[int, float]) -> str:
return f"{price:.2f}"

def _format_grams(self, value: float) -> str:
if 0 < value < 1:
grams = int(value * 1000)
else:
grams = int(value)

if grams > 999:
formatted = f"{grams:,}".replace(",", ".")
else:
formatted = str(grams)

return f"{formatted}g"
15 changes: 15 additions & 0 deletions marketplace/services/vtex/business/rules/currency_co.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from .interface import Rule
from typing import Union
from marketplace.services.vtex.utils.data_processor import FacebookProductDTO


class CurrencyCOP(Rule):
def apply(self, product: FacebookProductDTO, **kwargs) -> bool:
product.price = self.format_price(product.price)
product.sale_price = self.format_price(product.sale_price)
return True

@staticmethod
def format_price(price: Union[int, float]) -> str:
formatted_price = f"{price / 100:.2f} COP"
return formatted_price
Loading

0 comments on commit a5c79c0

Please sign in to comment.