Repository: leshchenko1979/fast_bitrix24
Pull request #204 (2022-09-25 07:08:51)

Исправить результаты `get_by_ID()`, если метод возвращает список
При применении `get_by_ID()` со списочными методами обработка производилась неправильно. Теперь исправлено.

sourcery-ai[bot]: # Sourcery Code Quality Report

❌ &nbsp;Merging this PR will **decrease** code quality in the affected files by **0.29%**.

| Quality metrics | Before | After | Change |
| --- | :---: | :---: | :---: |
| [Complexity](https://docs.sourcery.ai/GitHub/Quality-Report/#complexity) | 1.59 ⭐ | 1.58 ⭐ | -0.01 👍 |
| [Method Length](https://docs.sourcery.ai/GitHub/Quality-Report/#method-length) | 44.00 ⭐ | 44.82 ⭐ | 0.82 👎 |
| [Working memory](https://docs.sourcery.ai/GitHub/Quality-Report/#working-memory) | 5.76 ⭐ | 5.82 ⭐ | 0.06 👎 |
| **[Quality](https://docs.sourcery.ai/GitHub/Quality-Report/#quality-score)** | **84.26%** ⭐| **83.97%** ⭐| **-0.29%** 👎 |

| Other metrics | Before | After | Change |
| --- | :---: | :---: | :---: |
| Lines | 642 | 672 | 30 |

| Changed files | Quality Before | Quality After | Quality Change |
| --- | :---: | :---: | :---: |
| fast_bitrix24/mult_request.py | 82.05% ⭐ | 81.21% ⭐ | -0.84% 👎|
| fast_bitrix24/server_response.py | 87.85% ⭐ | 87.52% ⭐ | -0.33% 👎|
| fast_bitrix24/user_request.py | 82.04% ⭐ | 81.50% ⭐ | -0.54% 👎|
| tests/test_server_responses.py | 87.06% ⭐ | 87.35% ⭐ | 0.29% 👍|

Here are some functions in these files that still need a tune-up:

| File | Function | Complexity | Length | Working Memory | Quality | Recommendation |
| --- | --- | :---: | :---: | :---: | :---: | --- |
| fast_bitrix24/user_request.py | UserRequestAbstract.check\_expected\_clause\_types | 11 🙂 | 94 🙂 | 10 😞 | 59.22% 🙂 | Extract out complex expressions |
| fast_bitrix24/mult_request.py | MultipleServerRequestHandler.\_\_init\_\_ | 0 ⭐ | 86 🙂 | 11 😞 | 69.08% 🙂 | Extract out complex expressions |
| fast_bitrix24/mult_request.py | MultipleServerRequestHandler.generate\_a\_task | 1 ⭐ | 62 🙂 | 12 😞 | 69.96% 🙂 | Extract out complex expressions |
| fast_bitrix24/user_request.py | UserRequestAbstract.\_\_init\_\_ | 0 ⭐ | 80 🙂 | 10 😞 | 71.85% 🙂 | Extract out complex expressions |
| fast_bitrix24/user_request.py | ListAndGetUserRequest.run | 1 ⭐ | 129 😞 | 5 ⭐ | 75.93% ⭐ | Try splitting into smaller methods |

## Legend and Explanation

The emojis denote the absolute quality of the code:

 - ⭐ excellent
 - 🙂 good
 - 😞 poor
 - ⛔ very poor


The 👍 and 👎 indicate whether the quality has improved or gotten worse with this pull request.

***

Please see our documentation [here](https://docs.sourcery.ai/GitHub/Quality-Report/) for details on how these metrics are calculated.

We are actively working on this report - lots more documentation and extra metrics to come!

Help us [improve](https://research.typeform.com/to/j06Spdfr?type=quality_report&github_login=leshchenko1979&base_repo=https%3A%2F%2Fgithub.com%2Fleshchenko1979%2Ffast_bitrix24.git&base_remote_ref=refs%2Fpull%2F204%2Fhead&base_ref=leshchenko1979%2Fissue202&base_sha=446a201ce2eb9bc5667bdc1f452e4c81d5e2bbe0&head_repo=https%3A%2F%2Fgithub.com%2Fleshchenko1979%2Ffast_bitrix24.git&head_ref=sourcery%2Fleshchenko1979%2Fissue202&base_pr_number=204&base_pr_state=open) this quality report!
codecov[bot]: # [Codecov](https://codecov.io/gh/leshchenko1979/fast_bitrix24/pull/204?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko) Report
Base: **89.20**% // Head: **89.06**% // Decreases project coverage by **`-0.13%`** :warning:
> Coverage data is based on head [(`446a201`)](https://codecov.io/gh/leshchenko1979/fast_bitrix24/pull/204?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko) compared to base [(`15e2b2f`)](https://codecov.io/gh/leshchenko1979/fast_bitrix24/commit/15e2b2fda6930a1acd7f0b52e8192e87185f4e97?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko).
> Patch coverage: 100.00% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #204      +/-   ##
==========================================
- Coverage   89.20%   89.06%   -0.14%     
==========================================
  Files           9        9              
  Lines         500      503       +3     
==========================================
+ Hits          446      448       +2     
- Misses         54       55       +1     
```


| [Impacted Files](https://codecov.io/gh/leshchenko1979/fast_bitrix24/pull/204?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko) | Coverage Δ | |
|---|---|---|
| [fast\_bitrix24/mult\_request.py](https://codecov.io/gh/leshchenko1979/fast_bitrix24/pull/204/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko#diff-ZmFzdF9iaXRyaXgyNC9tdWx0X3JlcXVlc3QucHk=) | `94.36% <100.00%> (ø)` | |
| [fast\_bitrix24/server\_response.py](https://codecov.io/gh/leshchenko1979/fast_bitrix24/pull/204/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko#diff-ZmFzdF9iaXRyaXgyNC9zZXJ2ZXJfcmVzcG9uc2UucHk=) | `95.00% <100.00%> (+0.17%)` | :arrow_up: |
| [fast\_bitrix24/user\_request.py](https://codecov.io/gh/leshchenko1979/fast_bitrix24/pull/204/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko#diff-ZmFzdF9iaXRyaXgyNC91c2VyX3JlcXVlc3QucHk=) | `88.96% <100.00%> (+0.07%)` | :arrow_up: |
| [fast\_bitrix24/correct\_asyncio.py](https://codecov.io/gh/leshchenko1979/fast_bitrix24/pull/204/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko#diff-ZmFzdF9iaXRyaXgyNC9jb3JyZWN0X2FzeW5jaW8ucHk=) | `75.00% <0.00%> (-25.00%)` | :arrow_down: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/leshchenko1979/fast_bitrix24/pull/204?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alexey+Leshchenko).


Patch:
diff --git a/fast_bitrix24/mult_request.py b/fast_bitrix24/mult_request.py
index 5dd63c5..b28b8cd 100644
--- a/fast_bitrix24/mult_request.py
+++ b/fast_bitrix24/mult_request.py
@@ -11,7 +11,14 @@ from .utils import http_build_query
 
 class MultipleServerRequestHandler:
     def __init__(
-        self, bitrix, method, item_list, real_len=None, real_start=0, mute=False
+        self,
+        bitrix,
+        method,
+        item_list,
+        real_len=None,
+        real_start=0,
+        mute=False,
+        get_by_ID=False,
     ):
         self.bitrix = bitrix
         self.srh: ServerRequestHandler = bitrix.srh
@@ -20,6 +27,7 @@ class MultipleServerRequestHandler:
         self.real_len = real_len
         self.real_start = real_start
         self.mute = mute
+        self.get_by_ID = get_by_ID
 
         self.results = None
         self.task_iterator = self.generate_a_task()
@@ -82,8 +90,9 @@ class MultipleServerRequestHandler:
 
         extracted_len = 0
         for done_task in done:
-            batch_response = done_task.result()
-            extracted = ServerResponseParser(batch_response).extract_results()
+            extracted = ServerResponseParser(
+                done_task.result(), self.get_by_ID
+            ).extract_results()
 
             if self.results is None:
                 self.results = extracted
@@ -120,8 +129,8 @@ class MutePBar:
 
 
 class MultipleServerRequestHandlerPreserveIDs(MultipleServerRequestHandler):
-    def __init__(self, bitrix, method, item_list, ID_field):
-        super().__init__(bitrix, method, item_list)
+    def __init__(self, bitrix, method, item_list, ID_field, get_by_ID):
+        super().__init__(bitrix, method, item_list, get_by_ID=get_by_ID)
         self.ID_field = ID_field
 
     def batch_command_label(self, i, item):
diff --git a/fast_bitrix24/server_response.py b/fast_bitrix24/server_response.py
index b4087c1..243c670 100644
--- a/fast_bitrix24/server_response.py
+++ b/fast_bitrix24/server_response.py
@@ -1,4 +1,3 @@
-import contextlib
 from itertools import chain
 
 from beartype.typing import Dict, List, Union
@@ -9,8 +8,9 @@ class ErrorInServerResponseException(Exception):
 
 
 class ServerResponseParser:
-    def __init__(self, response: dict):
+    def __init__(self, response: dict, get_by_ID: bool = False):
         self.response = response
+        self.get_by_ID = get_by_ID
 
     def more_results_expected(self) -> bool:
         return self.total and self.total > 50 and self.total != len(self.result)
@@ -42,11 +42,14 @@ class ServerResponseParser:
         """
         self.raise_for_errors()
 
-        if self.is_batch():
-            return self.extract_from_batch_response(self.result["result"])
-        else:
+        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:
+            return self.extract_from_batch_response(self.result["result"])
+
     def raise_for_errors(self):
         errors = self.extract_errors()
         if errors:
diff --git a/fast_bitrix24/user_request.py b/fast_bitrix24/user_request.py
index c24cf11..376b65c 100644
--- a/fast_bitrix24/user_request.py
+++ b/fast_bitrix24/user_request.py
@@ -211,7 +211,11 @@ class GetByIDUserRequest(UserRequestAbstract):
         self.prepare_item_list()
 
         results = await MultipleServerRequestHandlerPreserveIDs(
-            self.bitrix, self.method, self.item_list, ID_field=self.ID_field_name
+            self.bitrix,
+            self.method,
+            self.item_list,
+            ID_field=self.ID_field_name,
+            get_by_ID=True,
         ).run()
 
         return results
@@ -248,12 +252,20 @@ class CallUserRequest(GetByIDUserRequest):
         if is_single_item:
             self.item_list = [self.item_list]
 
-        raw_results = await super().run()
+        self.prepare_item_list()
+
+        raw_results = await MultipleServerRequestHandlerPreserveIDs(
+            self.bitrix,
+            self.method,
+            self.item_list,
+            ID_field=self.ID_field_name,
+            get_by_ID=False,
+        ).run()
 
         if isinstance(raw_results, dict):
             results = tuple(raw_results.values())
         else:
-            # бывают случаи, чт возвращается список
+            # бывают случаи, что возвращается список
             results = raw_results
 
         return results[0] if is_single_item else results


Test patch:
diff --git a/tests/real_responses/crm_dealcategory_stage_list.py b/tests/real_responses/crm_dealcategory_stage_list.py
new file mode 100644
index 0000000..9032505
--- /dev/null
+++ b/tests/real_responses/crm_dealcategory_stage_list.py
@@ -0,0 +1,217 @@
+request = {
+    "method": "batch",
+    "params": {
+        "halt": 0,
+        "cmd": {
+            "2": "crm.dealcategory.stage.list?ID=2&",
+            "4": "crm.dealcategory.stage.list?ID=4&",
+            "8": "crm.dealcategory.stage.list?ID=8&",
+            0: "crm.dealcategory.stage.list?ID=0&",
+        },
+    },
+}
+
+response = {
+    "result": {
+        "result": {
+            "2": [
+                {"NAME": "Сбор документов", "SORT": 10, "STATUS_ID": "C2:NEW"},
+                {
+                    "NAME": "Ожидание оплаты судебных расходов",
+                    "SORT": 20,
+                    "STATUS_ID": "C2:PREPARATION",
+                },
+                {
+                    "NAME": "Подготовка искового заявления",
+                    "SORT": 30,
+                    "STATUS_ID": "C2:PREPAYMENT_INVOICE",
+                },
+                {"NAME": "Согласование с АУ", "SORT": 40, "STATUS_ID": "C2:UC_LAFJPE"},
+                {
+                    "NAME": "Подача заявления в суд",
+                    "SORT": 50,
+                    "STATUS_ID": "C2:EXECUTING",
+                },
+                {
+                    "NAME": "Ожидание даты заседания",
+                    "SORT": 60,
+                    "STATUS_ID": "C2:FINAL_INVOICE",
+                },
+                {"NAME": "Ожидание суда", "SORT": 70, "STATUS_ID": "C2:1"},
+                {"NAME": "Суд отложен", "SORT": 80, "STATUS_ID": "C2:2"},
+                {
+                    "NAME": "Реструктуризация долга",
+                    "SORT": 90,
+                    "STATUS_ID": "C2:UC_KFEKZL",
+                },
+                {
+                    "NAME": "Реализация имущества",
+                    "SORT": 100,
+                    "STATUS_ID": "C2:UC_XW9GZN",
+                },
+                {"NAME": "Долги списаны", "SORT": 110, "STATUS_ID": "C2:WON"},
+                {"NAME": "Ушел к конкурентам", "SORT": 120, "STATUS_ID": "C2:LOSE"},
+                {"NAME": "Не может собрать СИ", "SORT": 130, "STATUS_ID": "C2:APOLOGY"},
+                {
+                    "NAME": "Отговорили друзья/родственники",
+                    "SORT": 140,
+                    "STATUS_ID": "C2:3",
+                },
+                {"NAME": "Не выходит на связь", "SORT": 150, "STATUS_ID": "C2:4"},
+                {
+                    "NAME": "Расторгли мы, так как не платит",
+                    "SORT": 160,
+                    "STATUS_ID": "C2:5",
+                },
+            ],
+            "4": [
+                {"NAME": "1 оплата получена", "SORT": 10, "STATUS_ID": "C4:NEW"},
+                {
+                    "NAME": "2 оплата получена",
+                    "SORT": 20,
+                    "STATUS_ID": "C4:PREPARATION",
+                },
+                {
+                    "NAME": "3 оплата получена",
+                    "SORT": 30,
+                    "STATUS_ID": "C4:PREPAYMENT_INVOICE",
+                },
+                {"NAME": "4 оплата получена", "SORT": 40, "STATUS_ID": "C4:EXECUTING"},
+                {
+                    "NAME": "5 оплата получена",
+                    "SORT": 50,
+                    "STATUS_ID": "C4:FINAL_INVOICE",
+                },
+                {"NAME": "6 оплата получена", "SORT": 60, "STATUS_ID": "C4:1"},
+                {"NAME": "7 оплата получена", "SORT": 70, "STATUS_ID": "C4:2"},
+                {"NAME": "8 оплата получена", "SORT": 80, "STATUS_ID": "C4:3"},
+                {"NAME": "9 оплата получена", "SORT": 90, "STATUS_ID": "C4:4"},
+                {"NAME": "10 оплата получена", "SORT": 100, "STATUS_ID": "C4:5"},
+                {"NAME": "11 оплата получена", "SORT": 110, "STATUS_ID": "C4:6"},
+                {"NAME": "12 оплата получена", "SORT": 120, "STATUS_ID": "C4:7"},
+                {"NAME": "Заморожен", "SORT": 130, "STATUS_ID": "C4:8"},
+                {"NAME": "Клиент на расторжении", "SORT": 140, "STATUS_ID": "C4:9"},
+                {"NAME": "Сделка успешна", "SORT": 150, "STATUS_ID": "C4:WON"},
+                {"NAME": "Ушел к конкурентам", "SORT": 160, "STATUS_ID": "C4:LOSE"},
+                {"NAME": "Не может собрать СИ", "SORT": 170, "STATUS_ID": "C4:APOLOGY"},
+                {
+                    "NAME": "Отговорили друзья/родственники",
+                    "SORT": 180,
+                    "STATUS_ID": "C4:10",
+                },
+                {"NAME": "Не выходит на связь", "SORT": 190, "STATUS_ID": "C4:11"},
+                {
+                    "NAME": "Расторгли мы, так как не платит",
+                    "SORT": 200,
+                    "STATUS_ID": "C4:12",
+                },
+            ],
+            "8": [
+                {"NAME": "В работе", "SORT": 10, "STATUS_ID": "C8:NEW"},
+                {
+                    "NAME": "Встреча назначена",
+                    "SORT": 20,
+                    "STATUS_ID": "C8:PREPARATION",
+                },
+                {
+                    "NAME": "Встреча состоялась",
+                    "SORT": 30,
+                    "STATUS_ID": "C8:PREPAYMENT_INVOICE",
+                },
+                {"NAME": "Договор подписан", "SORT": 40, "STATUS_ID": "C8:EXECUTING"},
+                {
+                    "NAME": "Получен аванс 3000 р",
+                    "SORT": 50,
+                    "STATUS_ID": "C8:FINAL_INVOICE",
+                },
+                {"NAME": "Первая оплата получена", "SORT": 60, "STATUS_ID": "C8:WON"},
+                {"NAME": "Сделка провалена", "SORT": 70, "STATUS_ID": "C8:LOSE"},
+                {
+                    "NAME": "Анализ причины провала",
+                    "SORT": 80,
+                    "STATUS_ID": "C8:APOLOGY",
+                },
+            ],
+            "0": [
+                {"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"},
+                {"NAME": "Безвыходная реструктуризация", "SORT": 90, "STATUS_ID": "1"},
+                {"NAME": "Не готов выводить имущество", "SORT": 100, "STATUS_ID": "2"},
+                {"NAME": "Ушел к конкурентам", "SORT": 110, "STATUS_ID": "3"},
+                {"NAME": "Перестал выходить на связь", "SORT": 120, "STATUS_ID": "4"},
+                {"NAME": "Дорого/Нечем платить", "SORT": 130, "STATUS_ID": "5"},
+                {"NAME": "Услуга стала не актуальна", "SORT": 140, "STATUS_ID": "7"},
+                {"NAME": "Иное", "SORT": 150, "STATUS_ID": "8"},
+            ],
+        },
+        "result_error": [],
+        "result_total": [],
+        "result_next": [],
+        "result_time": {
+            "2": {
+                "start": 1664084858.135664,
+                "finish": 1664084858.14109,
+                "duration": 0.005425930023193359,
+                "processing": 0.004722118377685547,
+                "date_start": "2022-09-25T08:47:38+03:00",
+                "date_finish": "2022-09-25T08:47:38+03:00",
+                "operating_reset_at": 1664085458,
+                "operating": 0,
+            },
+            "4": {
+                "start": 1664084858.144238,
+                "finish": 1664084858.146933,
+                "duration": 0.0026950836181640625,
+                "processing": 0.0006778240203857422,
+                "date_start": "2022-09-25T08:47:38+03:00",
+                "date_finish": "2022-09-25T08:47:38+03:00",
+                "operating_reset_at": 1664085458,
+                "operating": 0,
+            },
+            "8": {
+                "start": 1664084858.149546,
+                "finish": 1664084858.150478,
+                "duration": 0.0009319782257080078,
+                "processing": 0.0004899501800537109,
+                "date_start": "2022-09-25T08:47:38+03:00",
+                "date_finish": "2022-09-25T08:47:38+03:00",
+                "operating_reset_at": 1664085458,
+                "operating": 0,
+            },
+            "0": {
+                "start": 1664084858.151295,
+                "finish": 1664084858.152226,
+                "duration": 0.0009310245513916016,
+                "processing": 0.0004949569702148438,
+                "date_start": "2022-09-25T08:47:38+03:00",
+                "date_finish": "2022-09-25T08:47:38+03:00",
+                "operating_reset_at": 1664085458,
+                "operating": 0,
+            },
+        },
+    },
+    "time": {
+        "start": 1664084858.105772,
+        "finish": 1664084858.153013,
+        "duration": 0.0472409725189209,
+        "processing": 0.017410993576049805,
+        "date_start": "2022-09-25T08:47:38+03:00",
+        "date_finish": "2022-09-25T08:47:38+03:00",
+        "operating_reset_at": 1664085458,
+        "operating": 0,
+    },
+}
diff --git a/tests/test_server_responses.py b/tests/test_server_responses.py
index 2c375c9..f8d714a 100644
--- a/tests/test_server_responses.py
+++ b/tests/test_server_responses.py
@@ -174,3 +174,11 @@ def test_get_all_tasks(bx_dummy):
     )
     assert isinstance(results, list)
     assert len(results) == 1570
+
+
+def test_crm_dealcategory_stage_list(bx_dummy):
+    from tests.real_responses.crm_dealcategory_stage_list import response
+
+    bx_dummy.srh = MockSRH(response)
+    results = bx_dummy.get_by_ID("crm.dealcategory.stage.list", ["2", "4", "8", 0])
+    assert isinstance(results, dict)