We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Improve Avocado jobs command
**Describe the bug**
A clear and concise description of what the bug is.

1. `avocado jobs list` returns unsorted list of job results. One improvement is to sort the list by `mtime`. This help find the recent jobs. 
2. `avocado jobs show` shows weird end time because we don't have the `end` attribute in the `results.json` any more. 

I have a fix on the first issue, but looking for inputs for the second one. One feasible fix is to replace the end time with the start time. 

**Steps to reproduce**
Steps to reproduce the behavior with a reproducible whenever possible.

```
# run a few tests
avocado run /bin/true
avocado run /bin/true
avocado run /bin/true
# 1. list
avocado jobs list
# 2. show
avocado jobs show
```

**Expected behavior**

1. 
```
b36e709ed01af2567e4df81842b678fb9dca51eb 2023-09-08 03:04:52.701753   1 (1/0/0/0)
11861a8dd228a86e7c0f2b3583fe6ca7011af99c 2023-09-08 03:00:01.887965   1 (1/0/0/0)
6a29261e8d23e963270c42c9147781fed3ca6118 2023-09-08 02:55:27.117886   1 (1/0/0/0)
```

2. 

```
JOB ID      : latest

Test ID     End Time            Run Time Status
1-/bin/true <correct time> 0.012107 PASS
```

**Current behavior**

1. 
```
b36e709ed01af2567e4df81842b678fb9dca51eb 2023-09-08 03:04:52.701753   1 (1/0/0/0)
6a29261e8d23e963270c42c9147781fed3ca6118 2023-09-08 02:55:27.117886   1 (1/0/0/0)
11861a8dd228a86e7c0f2b3583fe6ca7011af99c 2023-09-08 03:00:01.887965   1 (1/0/0/0)
```

2.
```
JOB ID      : latest

Test ID     End Time            Run Time Status
1-/bin/true 1970/01/01 01:51:03 01:51:03 0.012107 PASS
```

**System information (please complete the following information):**
 - OS: `Ubuntu 18.04.6 LTS`
 - Avocado version: `Avocado 102.0`
 - Avocado installation method: reproduce on github & pip installation



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `avocado/plugins/jobs.py`
Content:
```
1 # This program is free software; you can redistribute it and/or modify
2 # it under the terms of the GNU General Public License as published by
3 # the Free Software Foundation; either version 2 of the License, or
4 # (at your option) any later version.
5 #
6 # This program is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9 #
10 # See LICENSE for more details.
11 #
12 # Copyright: Red Hat Inc. 2020
13 # Authors: Beraldo Leal <bleal@redhat.com>
14 
15 """
16 Jobs subcommand
17 """
18 import json
19 import os
20 from datetime import datetime
21 from glob import glob
22 
23 from avocado.core import exit_codes, output
24 from avocado.core.data_dir import get_job_results_dir, get_logs_dir
25 from avocado.core.output import LOG_UI
26 from avocado.core.plugin_interfaces import CLICmd
27 from avocado.core.settings import settings
28 from avocado.utils import astring
29 
30 
31 class Jobs(CLICmd):
32     """
33     Implements the avocado 'jobs' subcommand
34     """
35 
36     name = "jobs"
37     description = "Manage Avocado jobs"
38 
39     @staticmethod
40     def _get_data_from_file(filename):
41         if not filename or not os.path.isfile(filename):
42             raise FileNotFoundError(f"File not found {filename}")
43 
44         with open(filename, "r", encoding="utf-8") as fp:
45             return json.load(fp)
46 
47     @staticmethod
48     def _print_job_details(details):
49         for key, value in details.items():
50             LOG_UI.info("%-12s: %s", key, value)
51 
52     @staticmethod
53     def _print_job_tests(tests):
54         test_matrix = []
55         date_fmt = "%Y/%m/%d %H:%M:%S"
56         for test in tests:
57             status = test.get("status")
58             decorator = output.TEST_STATUS_DECORATOR_MAPPING.get(status)
59             end = datetime.fromtimestamp(test.get("end"))
60             test_matrix.append(
61                 (
62                     test.get("id"),
63                     end.strftime(date_fmt),
64                     f"{float(test.get('time')):5f}",
65                     decorator(status, ""),
66                 )
67             )
68         header = (
69             output.TERM_SUPPORT.header_str("Test ID"),
70             output.TERM_SUPPORT.header_str("End Time"),
71             output.TERM_SUPPORT.header_str("Run Time"),
72             output.TERM_SUPPORT.header_str("Status"),
73         )
74         for line in astring.iter_tabular_output(test_matrix, header=header, strip=True):
75             LOG_UI.debug(line)
76 
77     def configure(self, parser):
78         """
79         Add the subparser for the assets action.
80 
81         :param parser: The Avocado command line application parser
82         :type parser: :class:`avocado.core.parser.ArgumentParser`
83         """
84         parser = super().configure(parser)
85 
86         subcommands = parser.add_subparsers(
87             dest="jobs_subcommand", metavar="sub-command"
88         )
89         subcommands.required = True
90 
91         help_msg = "List all known jobs by Avocado"
92         subcommands.add_parser("list", help=help_msg)
93 
94         help_msg = (
95             "Show details about a specific job. When passing a Job "
96             'ID, you can use any Job Reference (job_id, "latest", '
97             "or job results path)."
98         )
99         show_parser = subcommands.add_parser("show", help=help_msg)
100         settings.register_option(
101             section="jobs.show",
102             key="job_id",
103             help_msg="JOB id",
104             metavar="JOBID",
105             default="latest",
106             nargs="?",
107             positional_arg=True,
108             parser=show_parser,
109         )
110 
111     @staticmethod
112     def handle_list_command(jobs_results):
113         """Called when 'avocado jobs list' command is executed."""
114 
115         for filename in jobs_results.values():
116             with open(filename, "r", encoding="utf-8") as fp:
117                 job = json.load(fp)
118                 LOG_UI.info(
119                     "%-40s %-26s %3s (%s/%s/%s/%s)",
120                     job["job_id"],
121                     job["start"],
122                     job["total"],
123                     job["pass"],
124                     job["skip"],
125                     job["errors"],
126                     job["failures"],
127                 )
128 
129         return exit_codes.AVOCADO_ALL_OK
130 
131     def handle_show_command(self, config):
132         """Called when 'avocado jobs show' command is executed."""
133 
134         job_id = config.get("jobs.show.job_id")
135         results_dir = get_job_results_dir(job_id)
136         if results_dir is None:
137             LOG_UI.error("Error: Job %s not found", job_id)
138             return exit_codes.AVOCADO_GENERIC_CRASH
139 
140         results_file = os.path.join(results_dir, "results.json")
141         try:
142             results_data = self._get_data_from_file(results_file)
143         except FileNotFoundError as ex:
144             # Results data are important and should exit if not found
145             LOG_UI.error(ex)
146             return exit_codes.AVOCADO_GENERIC_CRASH
147 
148         data = {
149             "JOB ID": job_id,
150             "JOB LOG": results_data.get("debuglog"),
151         }
152 
153         # We could improve this soon with more data and colors
154         self._print_job_details(data)
155         LOG_UI.info("")
156         self._print_job_tests(results_data.get("tests"))
157         results = (
158             "PASS %d | ERROR %d | FAIL %d | SKIP %d |"
159             "WARN %d | INTERRUPT %s | CANCEL %s"
160         )
161         results %= (
162             results_data.get("pass", 0),
163             results_data.get("error", 0),
164             results_data.get("failures", 0),
165             results_data.get("skip", 0),
166             results_data.get("warn", 0),
167             results_data.get("interrupt", 0),
168             results_data.get("cancel", 0),
169         )
170         self._print_job_details({"RESULTS": results})
171         return exit_codes.AVOCADO_ALL_OK
172 
173     def run(self, config):
174         results = {}
175 
176         jobs_dir = get_logs_dir()
177         for result in glob(os.path.join(jobs_dir, "*/results.json")):
178             with open(result, "r", encoding="utf-8") as fp:
179                 job = json.load(fp)
180                 results[job["job_id"]] = result
181 
182         subcommand = config.get("jobs_subcommand")
183         if subcommand == "list":
184             return self.handle_list_command(results)
185         elif subcommand == "show":
186             return self.handle_show_command(config)
187         return exit_codes.AVOCADO_ALL_OK
188
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/avocado/plugins/jobs.py b/avocado/plugins/jobs.py
--- a/avocado/plugins/jobs.py
+++ b/avocado/plugins/jobs.py
@@ -56,7 +56,8 @@
         for test in tests:
             status = test.get("status")
             decorator = output.TEST_STATUS_DECORATOR_MAPPING.get(status)
-            end = datetime.fromtimestamp(test.get("end"))
+            # Retrieve "end" for backward compatibility
+            end = datetime.fromtimestamp(test.get("actual_end", test.get("end")))
             test_matrix.append(
                 (
                     test.get("id"),
@@ -174,7 +175,11 @@
         results = {}
 
         jobs_dir = get_logs_dir()
-        for result in glob(os.path.join(jobs_dir, "*/results.json")):
+        for result in sorted(
+            glob(os.path.join(jobs_dir, "*/results.json")),
+            key=os.path.getmtime,
+            reverse=True,
+        ):
             with open(result, "r", encoding="utf-8") as fp:
                 job = json.load(fp)
                 results[job["job_id"]] = result

{"golden_diff": "diff --git a/avocado/plugins/jobs.py b/avocado/plugins/jobs.py\n--- a/avocado/plugins/jobs.py\n+++ b/avocado/plugins/jobs.py\n@@ -56,7 +56,8 @@\n         for test in tests:\n             status = test.get(\"status\")\n             decorator = output.TEST_STATUS_DECORATOR_MAPPING.get(status)\n-            end = datetime.fromtimestamp(test.get(\"end\"))\n+            # Retrieve \"end\" for backward compatibility\n+            end = datetime.fromtimestamp(test.get(\"actual_end\", test.get(\"end\")))\n             test_matrix.append(\n                 (\n                     test.get(\"id\"),\n@@ -174,7 +175,11 @@\n         results = {}\n \n         jobs_dir = get_logs_dir()\n-        for result in glob(os.path.join(jobs_dir, \"*/results.json\")):\n+        for result in sorted(\n+            glob(os.path.join(jobs_dir, \"*/results.json\")),\n+            key=os.path.getmtime,\n+            reverse=True,\n+        ):\n             with open(result, \"r\", encoding=\"utf-8\") as fp:\n                 job = json.load(fp)\n                 results[job[\"job_id\"]] = result\n", "issue": "Improve Avocado jobs command\n**Describe the bug**\r\nA clear and concise description of what the bug is.\r\n\r\n1. `avocado jobs list` returns unsorted list of job results. One improvement is to sort the list by `mtime`. This help find the recent jobs. \r\n2. `avocado jobs show` shows weird end time because we don't have the `end` attribute in the `results.json` any more. \r\n\r\nI have a fix on the first issue, but looking for inputs for the second one. One feasible fix is to replace the end time with the start time. \r\n\r\n**Steps to reproduce**\r\nSteps to reproduce the behavior with a reproducible whenever possible.\r\n\r\n```\r\n# run a few tests\r\navocado run /bin/true\r\navocado run /bin/true\r\navocado run /bin/true\r\n# 1. list\r\navocado jobs list\r\n# 2. show\r\navocado jobs show\r\n```\r\n\r\n**Expected behavior**\r\n\r\n1. \r\n```\r\nb36e709ed01af2567e4df81842b678fb9dca51eb 2023-09-08 03:04:52.701753   1 (1/0/0/0)\r\n11861a8dd228a86e7c0f2b3583fe6ca7011af99c 2023-09-08 03:00:01.887965   1 (1/0/0/0)\r\n6a29261e8d23e963270c42c9147781fed3ca6118 2023-09-08 02:55:27.117886   1 (1/0/0/0)\r\n```\r\n\r\n2. \r\n\r\n```\r\nJOB ID      : latest\r\n\r\nTest ID     End Time            Run Time Status\r\n1-/bin/true <correct time> 0.012107 PASS\r\n```\r\n\r\n**Current behavior**\r\n\r\n1. \r\n```\r\nb36e709ed01af2567e4df81842b678fb9dca51eb 2023-09-08 03:04:52.701753   1 (1/0/0/0)\r\n6a29261e8d23e963270c42c9147781fed3ca6118 2023-09-08 02:55:27.117886   1 (1/0/0/0)\r\n11861a8dd228a86e7c0f2b3583fe6ca7011af99c 2023-09-08 03:00:01.887965   1 (1/0/0/0)\r\n```\r\n\r\n2.\r\n```\r\nJOB ID      : latest\r\n\r\nTest ID     End Time            Run Time Status\r\n1-/bin/true 1970/01/01 01:51:03 01:51:03 0.012107 PASS\r\n```\r\n\r\n**System information (please complete the following information):**\r\n - OS: `Ubuntu 18.04.6 LTS`\r\n - Avocado version: `Avocado 102.0`\r\n - Avocado installation method: reproduce on github & pip installation\r\n\r\n\n", "before_files": [{"content": "# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n#\n# See LICENSE for more details.\n#\n# Copyright: Red Hat Inc. 2020\n# Authors: Beraldo Leal <bleal@redhat.com>\n\n\"\"\"\nJobs subcommand\n\"\"\"\nimport json\nimport os\nfrom datetime import datetime\nfrom glob import glob\n\nfrom avocado.core import exit_codes, output\nfrom avocado.core.data_dir import get_job_results_dir, get_logs_dir\nfrom avocado.core.output import LOG_UI\nfrom avocado.core.plugin_interfaces import CLICmd\nfrom avocado.core.settings import settings\nfrom avocado.utils import astring\n\n\nclass Jobs(CLICmd):\n    \"\"\"\n    Implements the avocado 'jobs' subcommand\n    \"\"\"\n\n    name = \"jobs\"\n    description = \"Manage Avocado jobs\"\n\n    @staticmethod\n    def _get_data_from_file(filename):\n        if not filename or not os.path.isfile(filename):\n            raise FileNotFoundError(f\"File not found {filename}\")\n\n        with open(filename, \"r\", encoding=\"utf-8\") as fp:\n            return json.load(fp)\n\n    @staticmethod\n    def _print_job_details(details):\n        for key, value in details.items():\n            LOG_UI.info(\"%-12s: %s\", key, value)\n\n    @staticmethod\n    def _print_job_tests(tests):\n        test_matrix = []\n        date_fmt = \"%Y/%m/%d %H:%M:%S\"\n        for test in tests:\n            status = test.get(\"status\")\n            decorator = output.TEST_STATUS_DECORATOR_MAPPING.get(status)\n            end = datetime.fromtimestamp(test.get(\"end\"))\n            test_matrix.append(\n                (\n                    test.get(\"id\"),\n                    end.strftime(date_fmt),\n                    f\"{float(test.get('time')):5f}\",\n                    decorator(status, \"\"),\n                )\n            )\n        header = (\n            output.TERM_SUPPORT.header_str(\"Test ID\"),\n            output.TERM_SUPPORT.header_str(\"End Time\"),\n            output.TERM_SUPPORT.header_str(\"Run Time\"),\n            output.TERM_SUPPORT.header_str(\"Status\"),\n        )\n        for line in astring.iter_tabular_output(test_matrix, header=header, strip=True):\n            LOG_UI.debug(line)\n\n    def configure(self, parser):\n        \"\"\"\n        Add the subparser for the assets action.\n\n        :param parser: The Avocado command line application parser\n        :type parser: :class:`avocado.core.parser.ArgumentParser`\n        \"\"\"\n        parser = super().configure(parser)\n\n        subcommands = parser.add_subparsers(\n            dest=\"jobs_subcommand\", metavar=\"sub-command\"\n        )\n        subcommands.required = True\n\n        help_msg = \"List all known jobs by Avocado\"\n        subcommands.add_parser(\"list\", help=help_msg)\n\n        help_msg = (\n            \"Show details about a specific job. When passing a Job \"\n            'ID, you can use any Job Reference (job_id, \"latest\", '\n            \"or job results path).\"\n        )\n        show_parser = subcommands.add_parser(\"show\", help=help_msg)\n        settings.register_option(\n            section=\"jobs.show\",\n            key=\"job_id\",\n            help_msg=\"JOB id\",\n            metavar=\"JOBID\",\n            default=\"latest\",\n            nargs=\"?\",\n            positional_arg=True,\n            parser=show_parser,\n        )\n\n    @staticmethod\n    def handle_list_command(jobs_results):\n        \"\"\"Called when 'avocado jobs list' command is executed.\"\"\"\n\n        for filename in jobs_results.values():\n            with open(filename, \"r\", encoding=\"utf-8\") as fp:\n                job = json.load(fp)\n                LOG_UI.info(\n                    \"%-40s %-26s %3s (%s/%s/%s/%s)\",\n                    job[\"job_id\"],\n                    job[\"start\"],\n                    job[\"total\"],\n                    job[\"pass\"],\n                    job[\"skip\"],\n                    job[\"errors\"],\n                    job[\"failures\"],\n                )\n\n        return exit_codes.AVOCADO_ALL_OK\n\n    def handle_show_command(self, config):\n        \"\"\"Called when 'avocado jobs show' command is executed.\"\"\"\n\n        job_id = config.get(\"jobs.show.job_id\")\n        results_dir = get_job_results_dir(job_id)\n        if results_dir is None:\n            LOG_UI.error(\"Error: Job %s not found\", job_id)\n            return exit_codes.AVOCADO_GENERIC_CRASH\n\n        results_file = os.path.join(results_dir, \"results.json\")\n        try:\n            results_data = self._get_data_from_file(results_file)\n        except FileNotFoundError as ex:\n            # Results data are important and should exit if not found\n            LOG_UI.error(ex)\n            return exit_codes.AVOCADO_GENERIC_CRASH\n\n        data = {\n            \"JOB ID\": job_id,\n            \"JOB LOG\": results_data.get(\"debuglog\"),\n        }\n\n        # We could improve this soon with more data and colors\n        self._print_job_details(data)\n        LOG_UI.info(\"\")\n        self._print_job_tests(results_data.get(\"tests\"))\n        results = (\n            \"PASS %d | ERROR %d | FAIL %d | SKIP %d |\"\n            \"WARN %d | INTERRUPT %s | CANCEL %s\"\n        )\n        results %= (\n            results_data.get(\"pass\", 0),\n            results_data.get(\"error\", 0),\n            results_data.get(\"failures\", 0),\n            results_data.get(\"skip\", 0),\n            results_data.get(\"warn\", 0),\n            results_data.get(\"interrupt\", 0),\n            results_data.get(\"cancel\", 0),\n        )\n        self._print_job_details({\"RESULTS\": results})\n        return exit_codes.AVOCADO_ALL_OK\n\n    def run(self, config):\n        results = {}\n\n        jobs_dir = get_logs_dir()\n        for result in glob(os.path.join(jobs_dir, \"*/results.json\")):\n            with open(result, \"r\", encoding=\"utf-8\") as fp:\n                job = json.load(fp)\n                results[job[\"job_id\"]] = result\n\n        subcommand = config.get(\"jobs_subcommand\")\n        if subcommand == \"list\":\n            return self.handle_list_command(results)\n        elif subcommand == \"show\":\n            return self.handle_show_command(config)\n        return exit_codes.AVOCADO_ALL_OK\n", "path": "avocado/plugins/jobs.py"}], "after_files": [{"content": "# This program is free software; you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation; either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n#\n# See LICENSE for more details.\n#\n# Copyright: Red Hat Inc. 2020\n# Authors: Beraldo Leal <bleal@redhat.com>\n\n\"\"\"\nJobs subcommand\n\"\"\"\nimport json\nimport os\nfrom datetime import datetime\nfrom glob import glob\n\nfrom avocado.core import exit_codes, output\nfrom avocado.core.data_dir import get_job_results_dir, get_logs_dir\nfrom avocado.core.output import LOG_UI\nfrom avocado.core.plugin_interfaces import CLICmd\nfrom avocado.core.settings import settings\nfrom avocado.utils import astring\n\n\nclass Jobs(CLICmd):\n    \"\"\"\n    Implements the avocado 'jobs' subcommand\n    \"\"\"\n\n    name = \"jobs\"\n    description = \"Manage Avocado jobs\"\n\n    @staticmethod\n    def _get_data_from_file(filename):\n        if not filename or not os.path.isfile(filename):\n            raise FileNotFoundError(f\"File not found {filename}\")\n\n        with open(filename, \"r\", encoding=\"utf-8\") as fp:\n            return json.load(fp)\n\n    @staticmethod\n    def _print_job_details(details):\n        for key, value in details.items():\n            LOG_UI.info(\"%-12s: %s\", key, value)\n\n    @staticmethod\n    def _print_job_tests(tests):\n        test_matrix = []\n        date_fmt = \"%Y/%m/%d %H:%M:%S\"\n        for test in tests:\n            status = test.get(\"status\")\n            decorator = output.TEST_STATUS_DECORATOR_MAPPING.get(status)\n            # Retrieve \"end\" for backward compatibility\n            end = datetime.fromtimestamp(test.get(\"actual_end\", test.get(\"end\")))\n            test_matrix.append(\n                (\n                    test.get(\"id\"),\n                    end.strftime(date_fmt),\n                    f\"{float(test.get('time')):5f}\",\n                    decorator(status, \"\"),\n                )\n            )\n        header = (\n            output.TERM_SUPPORT.header_str(\"Test ID\"),\n            output.TERM_SUPPORT.header_str(\"End Time\"),\n            output.TERM_SUPPORT.header_str(\"Run Time\"),\n            output.TERM_SUPPORT.header_str(\"Status\"),\n        )\n        for line in astring.iter_tabular_output(test_matrix, header=header, strip=True):\n            LOG_UI.debug(line)\n\n    def configure(self, parser):\n        \"\"\"\n        Add the subparser for the assets action.\n\n        :param parser: The Avocado command line application parser\n        :type parser: :class:`avocado.core.parser.ArgumentParser`\n        \"\"\"\n        parser = super().configure(parser)\n\n        subcommands = parser.add_subparsers(\n            dest=\"jobs_subcommand\", metavar=\"sub-command\"\n        )\n        subcommands.required = True\n\n        help_msg = \"List all known jobs by Avocado\"\n        subcommands.add_parser(\"list\", help=help_msg)\n\n        help_msg = (\n            \"Show details about a specific job. When passing a Job \"\n            'ID, you can use any Job Reference (job_id, \"latest\", '\n            \"or job results path).\"\n        )\n        show_parser = subcommands.add_parser(\"show\", help=help_msg)\n        settings.register_option(\n            section=\"jobs.show\",\n            key=\"job_id\",\n            help_msg=\"JOB id\",\n            metavar=\"JOBID\",\n            default=\"latest\",\n            nargs=\"?\",\n            positional_arg=True,\n            parser=show_parser,\n        )\n\n    @staticmethod\n    def handle_list_command(jobs_results):\n        \"\"\"Called when 'avocado jobs list' command is executed.\"\"\"\n\n        for filename in jobs_results.values():\n            with open(filename, \"r\", encoding=\"utf-8\") as fp:\n                job = json.load(fp)\n                LOG_UI.info(\n                    \"%-40s %-26s %3s (%s/%s/%s/%s)\",\n                    job[\"job_id\"],\n                    job[\"start\"],\n                    job[\"total\"],\n                    job[\"pass\"],\n                    job[\"skip\"],\n                    job[\"errors\"],\n                    job[\"failures\"],\n                )\n\n        return exit_codes.AVOCADO_ALL_OK\n\n    def handle_show_command(self, config):\n        \"\"\"Called when 'avocado jobs show' command is executed.\"\"\"\n\n        job_id = config.get(\"jobs.show.job_id\")\n        results_dir = get_job_results_dir(job_id)\n        if results_dir is None:\n            LOG_UI.error(\"Error: Job %s not found\", job_id)\n            return exit_codes.AVOCADO_GENERIC_CRASH\n\n        results_file = os.path.join(results_dir, \"results.json\")\n        try:\n            results_data = self._get_data_from_file(results_file)\n        except FileNotFoundError as ex:\n            # Results data are important and should exit if not found\n            LOG_UI.error(ex)\n            return exit_codes.AVOCADO_GENERIC_CRASH\n\n        data = {\n            \"JOB ID\": job_id,\n            \"JOB LOG\": results_data.get(\"debuglog\"),\n        }\n\n        # We could improve this soon with more data and colors\n        self._print_job_details(data)\n        LOG_UI.info(\"\")\n        self._print_job_tests(results_data.get(\"tests\"))\n        results = (\n            \"PASS %d | ERROR %d | FAIL %d | SKIP %d |\"\n            \"WARN %d | INTERRUPT %s | CANCEL %s\"\n        )\n        results %= (\n            results_data.get(\"pass\", 0),\n            results_data.get(\"error\", 0),\n            results_data.get(\"failures\", 0),\n            results_data.get(\"skip\", 0),\n            results_data.get(\"warn\", 0),\n            results_data.get(\"interrupt\", 0),\n            results_data.get(\"cancel\", 0),\n        )\n        self._print_job_details({\"RESULTS\": results})\n        return exit_codes.AVOCADO_ALL_OK\n\n    def run(self, config):\n        results = {}\n\n        jobs_dir = get_logs_dir()\n        for result in sorted(\n            glob(os.path.join(jobs_dir, \"*/results.json\")),\n            key=os.path.getmtime,\n            reverse=True,\n        ):\n            with open(result, \"r\", encoding=\"utf-8\") as fp:\n                job = json.load(fp)\n                results[job[\"job_id\"]] = result\n\n        subcommand = config.get(\"jobs_subcommand\")\n        if subcommand == \"list\":\n            return self.handle_list_command(results)\n        elif subcommand == \"show\":\n            return self.handle_show_command(config)\n        return exit_codes.AVOCADO_ALL_OK\n", "path": "avocado/plugins/jobs.py"}]}