Repository: reanahub/reana-client

Issue:
cli: `run_number` and human readable `size` not being properly sorted
Currently, since `run_number` and `size` (when the human readable flag is activated) are represented as strings, they aren't properly sorted. For example, by running the `list` command with these flags:

```
reana-client list --sort run_number
NAME                          RUN_NUMBER   CREATED               STARTED               ENDED                 STATUS  
root6-roofit                  9            2022-07-20T12:08:38   2022-07-20T12:09:03   2022-07-20T12:09:19   finished
root6-roofit                  8            2022-07-20T12:08:36   2022-07-20T12:08:58   2022-07-20T12:09:14   finished
root6-roofit                  7            2022-07-20T12:08:34   2022-07-20T12:08:53   2022-07-20T12:09:12   finished
root6-roofit                  6            2022-07-20T12:08:31   2022-07-20T12:08:47   2022-07-20T12:09:03   finished
root6-roofit                  5            2022-07-20T12:08:29   2022-07-20T12:08:42   2022-07-20T12:08:58   finished
root6-roofit                  4.2          2022-07-20T12:07:05   2022-07-20T12:07:19   2022-07-20T12:07:38   finished
root6-roofit                  4.1          2022-07-20T12:06:08   2022-07-20T12:06:21   2022-07-20T12:06:36   finished
root6-roofit                  4            2022-07-06T12:30:19   2022-07-06T12:30:34   2022-07-06T12:30:53   finished
root6-roofit                  3            2022-07-06T12:11:26   2022-07-06T12:11:40   2022-07-06T12:11:41   failed  
root6-roofit                  2.1          2022-07-06T12:10:39   -                     -                     deleted 
my-atlas-recast-analysis      2            2022-07-20T12:03:21   2022-07-20T12:03:34   2022-07-20T12:06:49   finished
root6-roofit                  2            2022-07-06T12:09:03   2022-07-06T12:09:18   2022-07-06T12:10:04   finished
root6-roofit                  10           2022-07-20T12:08:40   2022-07-20T12:09:09   2022-07-20T12:09:24   finished
workflow                      1            2022-07-20T12:08:06   2022-07-20T12:08:37   2022-07-20T12:08:56   finished
```
As you can see, the row with the run number 10 should be the first one, but they're being sorted as a string. The human readable size has a similar problem.
These columns shouldn't simply be sorted based on their value types. Note that the run_number has to take dots into consideration (e.g. 1.10 should be greater than 1.2) and that the human readable size comes in the format `X KiB`.


Discussion:
codecov-commenter: ## [Codecov](https://codecov.io/gh/reanahub/reana-client/pull/656?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=reanahub) Report
> Merging [#656](https://codecov.io/gh/reanahub/reana-client/pull/656?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=reanahub) (1f6158f) into [master](https://codecov.io/gh/reanahub/reana-client/commit/98d82eecda2ce71521ebceb2e063b50ff571f1e3?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=reanahub) (98d82ee) will **increase** coverage by `0.14%`.
> The diff coverage is `100.00%`.

:mega: This organization is not using Codecov’s [GitHub App Integration](https://github.com/apps/codecov). We recommend you install it so Codecov can continue to function properly for your repositories. [Learn more](https://about.codecov.io/blog/codecov-is-updating-its-github-integration/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=reanahub)

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


[![Impacted file tree graph](https://codecov.io/gh/reanahub/reana-client/pull/656/graphs/tree.svg?width=650&height=150&src=pr&token=b85cSEiOhs&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=reanahub)](https://codecov.io/gh/reanahub/reana-client/pull/656?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=reanahub)

```diff
@@            Coverage Diff             @@
##           master     #656      +/-   ##
==========================================
+ Coverage   57.71%   57.85%   +0.14%     
==========================================
  Files          24       24              
  Lines        2327     2335       +8     
==========================================
+ Hits         1343     1351       +8     
  Misses        984      984              
```


| [Impacted Files](https://codecov.io/gh/reanahub/reana-client/pull/656?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=reanahub) | Coverage Δ | |
|---|---|---|
| [reana\_client/cli/workflow.py](https://codecov.io/gh/reanahub/reana-client/pull/656?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=reanahub#diff-cmVhbmFfY2xpZW50L2NsaS93b3JrZmxvdy5weQ==) | `63.55% <100.00%> (+0.52%)` | :arrow_up: |

</details>

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index e7850f0..4af8dfb 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -3,6 +3,7 @@ Changes
 
 Version 0.9.1 (UNRELEASED)
 --------------------------
+- Fixes ``list`` command to correctly list workflows when sorting them by their run number or by the size of their workspace.
 - Fixes ``du`` command help message typo.
 
 Version 0.9.0 (2023-01-26)
diff --git a/reana_client/cli/workflow.py b/reana_client/cli/workflow.py
index 653b5ff..f0bf692 100644
--- a/reana_client/cli/workflow.py
+++ b/reana_client/cli/workflow.py
@@ -244,7 +244,6 @@ def workflows_list(  # noqa: C901
 
         data = []
         for workflow in response:
-            workflow["size"] = workflow["size"][human_readable_or_raw]
             name, run_number = get_workflow_name_and_run_number(workflow["name"])
             workflow["name"] = name
             workflow["run_number"] = run_number
@@ -271,19 +270,30 @@ def workflows_list(  # noqa: C901
             data.append(row)
 
         # Sort by given column, making sure that `None` is at the bottom of the list.
+        def get_sort_key(x, column_id):
+            if sort_columm_name == "run_number":
+                return list(map(int, x[column_id].split(".")))
+            elif sort_columm_name in workspace_size_header:
+                return x[column_id]["raw"]
+            return x[column_id] is not None, x[column_id]
+
         sort_column_id = 2
         if sort_columm_name.lower() in headers[type]:
             sort_column_id = headers[type].index(sort_columm_name.lower())
         data = sorted(
             data,
-            key=lambda x: (x[sort_column_id] is not None, x[sort_column_id]),
+            key=lambda x: get_sort_key(x, sort_column_id),
             reverse=True,
         )
 
-        # Substitute `None` with "-"
         for row in data:
             for i, value in enumerate(row):
-                row[i] = value or "-"
+                # Substitute `None` with "-"
+                content = value or "-"
+                # Replace raw size with human-readable size if requested
+                if headers[type][i] in workspace_size_header:
+                    content = value.get(human_readable_or_raw)
+                row[i] = content
 
         workflow_ids = ["{0}.{1}".format(w[0], w[1]) for w in data]
         if os.getenv("REANA_WORKON", "") in workflow_ids:


Test patch:
diff --git a/tests/test_cli_workflows.py b/tests/test_cli_workflows.py
index 1983141..cb1c733 100644
--- a/tests/test_cli_workflows.py
+++ b/tests/test_cli_workflows.py
@@ -114,7 +114,36 @@ def test_deleted_workflows(cli_options: List[str], expected_status_filter: List[
             assert kwargs["status"] == expected_status_filter
 
 
-def test_workflows_sorting():
+@pytest.mark.parametrize(
+    "cli_options, expected_output",
+    [
+        (
+            ["list", "-t", "000000", "--sort", "run_number"],
+            (
+                "mytest   15           2018-06-13T10:55:37   -                     -                     running\n"
+                "mytest   2            2018-06-13T09:55:35   -                     -                     running\n"
+                "mytest   1 *          2018-06-13T09:47:35   2018-06-13T09:47:40   2018-06-13T10:30:03   running"
+            ),
+        ),
+        (
+            [
+                "list",
+                "-t",
+                "000000",
+                "--include-workspace-size",
+                "-h",
+                "--sort",
+                "size",
+            ],
+            (
+                "mytest   1 *          2018-06-13T09:47:35   2018-06-13T09:47:40   2018-06-13T10:30:03   running   1.55 MiB\n"
+                "mytest   2            2018-06-13T09:55:35   -                     -                     running   540 KiB \n"
+                "mytest   15           2018-06-13T10:55:37   -                     -                     running   192 KiB "
+            ),
+        ),
+    ],
+)
+def test_workflows_sorting(cli_options: List[str], expected_output: str):
     """Test workflows sorting."""
     response = {
         "items": [
@@ -128,7 +157,7 @@ def test_workflows_sorting():
                     "run_started_at": "2018-06-13T09:47:40",
                     "run_finished_at": "2018-06-13T10:30:03",
                 },
-                "size": {"raw": 0, "human_readable": "0 Bytes"},
+                "size": {"raw": 1622016, "human_readable": "1.55 MiB"},
             },
             {
                 "status": "running",
@@ -136,7 +165,15 @@ def test_workflows_sorting():
                 "user": "00000000-0000-0000-0000-000000000000",
                 "name": "mytest.2",
                 "id": "256b25f4-4cfb-4684-b7a8-73872ef455a2",
-                "size": {"raw": 0, "human_readable": "0 Bytes"},
+                "size": {"raw": 552960, "human_readable": "540 KiB"},
+            },
+            {
+                "status": "running",
+                "created": "2018-06-13T10:55:37",
+                "user": "00000000-0000-0000-0000-000000000000",
+                "name": "mytest.15",
+                "id": "256b25f4-4cfb-4684-b7a8-73872ef455a3",
+                "size": {"raw": 196608, "human_readable": "192 KiB"},
             },
         ]
     }
@@ -145,22 +182,15 @@ def test_workflows_sorting():
     mock_http_response.status_code = status_code
     mock_response = response
     env = {"REANA_SERVER_URL": "localhost", "REANA_WORKON": "mytest.1"}
-    reana_token = "000000"
     runner = CliRunner(env=env)
     with runner.isolation():
         with patch(
             "reana_client.api.client.current_rs_api_client",
             make_mock_api_client("reana-server")(mock_response, mock_http_response),
         ):
-            result = runner.invoke(
-                cli, ["list", "-t", reana_token, "--sort", "run_number"]
-            )
-            message = (
-                "mytest   2            2018-06-13T09:55:35   -                     -                     running\n"
-                "mytest   1 *          2018-06-13T09:47:35   2018-06-13T09:47:40   2018-06-13T10:30:03   running"
-            )
+            result = runner.invoke(cli, cli_options)
             assert result.exit_code == 0
-            assert message in result.output
+            assert expected_output in result.output
 
 
 def test_workflows_sessions():