Skip to content

Commit

Permalink
Fix server response parsing (#221)
Browse files Browse the repository at this point in the history
  • Loading branch information
leshchenko1979 authored Mar 18, 2024
1 parent 5b62295 commit 4a450b3
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 24 deletions.
29 changes: 13 additions & 16 deletions fast_bitrix24/mult_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,28 @@ def __init__(
self.get_by_ID = get_by_ID

self.results = None
self.task_iterator = self.generate_a_task()
self.task_iterator = self.generate_tasks()
self.tasks = set()

def generate_a_task(self):
"""Объединяем элементы item_list в батчи и по одной создаем и
возвращаем задачи asyncio с запросами к серверу для каждого батча."""
def generate_tasks(self):
"""Group items in batches and create asyncio tasks for each batch"""

batches = (
{
"halt": 0,
"cmd": {
self.batch_command_label(
i, item
): f"{self.method}?{http_build_query(item)}"
for i, item in enumerate(next_batch)
},
}
for next_batch in chunked(self.item_list, BITRIX_MAX_BATCH_SIZE)
self.package_batch(chunk)
for chunk in chunked(self.item_list, BITRIX_MAX_BATCH_SIZE)
)

for batch in batches:
yield ensure_future(self.srh.single_request("batch", batch))

def batch_command_label(self, i: int, item) -> str:
return f"cmd{i:010}"
def package_batch(self, chunk):
return {
"halt": 0,
"cmd": {
f"cmd{i:010}": f"{self.method}?{http_build_query(item)}"
for i, item in enumerate(chunk)
},
}

async def run(self) -> Union[Dict, List]:
self.top_up_tasks()
Expand Down
17 changes: 9 additions & 8 deletions fast_bitrix24/server_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ def extract_results(self) -> Union[Dict, List[Dict]]:
if not self.is_batch():
return self.extract_from_single_response(self.result)

if self.get_by_ID:
return self.extract_from_single_response(self.result["result"])
else:
if not self.get_by_ID:
return self.extract_from_batch_response(self.result["result"])

extracted = self.extract_from_single_response(self.result["result"])

return extracted[0] if isinstance(extracted, list) else extracted

def raise_for_errors(self):
errors = self.extract_errors()
if errors:
Expand All @@ -67,17 +69,16 @@ def extract_errors(self):
def is_batch(self) -> bool:
return isinstance(self.result, dict) and "result" in self.result

@staticmethod
def extract_from_single_response(result: dict):
def extract_from_single_response(self, result: dict):
# если результат вызова содержит только словарь {ключ: список},
# то вернуть этот список.
# См. https://github.com/leshchenko1979/fast_bitrix24/issues/132

# метод `crm.stagehistory.list` возвращает dict["items", list] --
# разворачиваем его в список

if ServerResponseParser.is_nested(result):
contents = ServerResponseParser.extract_from_nested(result)
if self.is_nested(result):
contents = self.extract_from_nested(result)
if isinstance(contents, list):
return contents

Expand All @@ -88,7 +89,7 @@ def is_nested(result) -> bool:
return isinstance(result, dict) and len(result) == 1

@staticmethod
def extract_from_nested(result):
def extract_from_nested(result: dict):
return next(iter(result.values()))


Expand Down
50 changes: 50 additions & 0 deletions tests/real_responses/crm_dealcategory_stage_list_batch_of_one.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
request = {"halt": 0, "cmd": {0: "crm.dealcategory.stage.list?ID=0&"}}
response = {
"result": {
"result": [
[
{"NAME": "Новая заявка", "SORT": 10, "STATUS_ID": "NEW"},
{"NAME": "потенциальные", "SORT": 20, "STATUS_ID": "PREPARATION"},
{
"NAME": "Запись на прием",
"SORT": 30,
"STATUS_ID": "PREPAYMENT_INVOICE",
},
{"NAME": "Прием состоялся", "SORT": 40, "STATUS_ID": "EXECUTING"},
{
"NAME": "Запись на последующий прием",
"SORT": 50,
"STATUS_ID": "FINAL_INVOICE",
},
{"NAME": "неотвеченные", "SORT": 60, "STATUS_ID": "WON"},
{"NAME": "удалить столбик-лишний", "SORT": 70, "STATUS_ID": "LOSE"},
{"NAME": "спам", "SORT": 80, "STATUS_ID": "APOLOGY"},
]
],
"result_error": [],
"result_total": [],
"result_next": [],
"result_time": [
{
"start": 1670596304.721726,
"finish": 1670596304.729467,
"duration": 0.007740974426269531,
"processing": 0.006625175476074219,
"date_start": "2022-12-09T17:31:44+03:00",
"date_finish": "2022-12-09T17:31:44+03:00",
"operating_reset_at": 1670596904,
"operating": 0,
}
],
},
"time": {
"start": 1670596304.683407,
"finish": 1670596304.731127,
"duration": 0.04771995544433594,
"processing": 0.009470939636230469,
"date_start": "2022-12-09T17:31:44+03:00",
"date_finish": "2022-12-09T17:31:44+03:00",
"operating_reset_at": 1670596904,
"operating": 0,
},
}
11 changes: 11 additions & 0 deletions tests/test_server_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ def test_crm_dealcategory_stage_list(bx_dummy):
results = bx_dummy.get_by_ID("crm.dealcategory.stage.list", ["2", "4", "8", 0])
assert isinstance(results, dict)


def test_crm_dealcategory_stage_list_batch_of_one(bx_dummy):
from tests.real_responses.crm_dealcategory_stage_list_batch_of_one import response

bx_dummy.srh = MockSRH(response)
results = bx_dummy.get_by_ID("crm.dealcategory.stage.list", [0])

# should return a list of dicts
assert isinstance(results, list) and isinstance(results[0], dict)


def test_catalog_document_element_list(bx_dummy):
from tests.real_responses.catalog_document_element_list import response

Expand Down

0 comments on commit 4a450b3

Please sign in to comment.