Merge "Added possibility to choose the time range in which bot will search test results PRODX-37645"
diff --git a/testrail_bot/control/celery_tasks/test_rail_api.py b/testrail_bot/control/celery_tasks/test_rail_api.py
index d77b511..c359dbe 100644
--- a/testrail_bot/control/celery_tasks/test_rail_api.py
+++ b/testrail_bot/control/celery_tasks/test_rail_api.py
@@ -1,6 +1,7 @@
 from testrail_api import TestRailAPI, StatusCodeError
 from requests.exceptions import ReadTimeout
 from django.conf import settings
+from django.utils.html import escape
 from typing import Optional, List, Iterator
 
 from functools import lru_cache
@@ -55,6 +56,11 @@
     return api.runs.get_run(run_id)
 
 
+@lru_cache
+def get_run_name(run_id: int) -> str:
+    return get_run_by_id(run_id)["name"]
+
+
 def get_plan_by_id(plan_id: int) -> dict:
     return api.plans.get_plan(plan_id)
 
@@ -147,3 +153,8 @@
     except StatusCodeError as e:
         print(f"{e=}")
         return False
+
+
+def html_link(type: str, id: int, title: str) -> str:
+    return f"<a href='https://mirantis.testrail.com/index.php?/{type}s/view/" \
+           f"{id}'>{escape(title)}</a>"
diff --git a/testrail_bot/control/celery_tasks/testrail_pipeline.py b/testrail_bot/control/celery_tasks/testrail_pipeline.py
index d90f5e7..350f37b 100644
--- a/testrail_bot/control/celery_tasks/testrail_pipeline.py
+++ b/testrail_bot/control/celery_tasks/testrail_pipeline.py
@@ -237,8 +237,7 @@
         if str(run_id) == str(old_run_id):
             continue
         per = round(100.0 * ratio, 2)
-        run_link = f"<a href=https://mirantis.testrail.com/index.php?/runs/" \
-                   f"view/{old_run_id}>{old_run_id} </a>"
+        run_link = test_rail_api.html_link('run', old_run_id, old_run_id)
         if type(sim_result) is not dict:
             f.write(f"Similarity not found due to similarity: {per}, "
                     f"in run {run_link}\n")
@@ -247,9 +246,9 @@
 
         prod_link = "<a href=https://mirantis.jira.com/browse/{defect}>" \
                     "{defect}</a>".format(defect=sim_result["defects"])
-        test_link = "<a href=https://mirantis.testrail.com/index.php?/tests/" \
-                    "view/{test_id}>{test_id}</a>".format(
-            test_id=sim_result["test_id"])
+        test_link = test_rail_api.html_link('test',
+                                            sim_result["test_id"],
+                                            str(sim_result["test_id"]))
         status_id = int(sim_result['status_id'])
         if status_id in [StatusEnum.retest, StatusEnum.failed,
                          StatusEnum.blocked]:
@@ -308,11 +307,12 @@
     """
     case_id = test["case_id"]
     run_id = test["run_id"]
+    run_name = test_rail_api.get_run_name(run_id)
+    test_link = test_rail_api.html_link('test', test['id'], test["title"])
+    run_link = test_rail_api.html_link('run', run_id, run_name)
 
-    f.write("<br><b>Proceeding test {title}<br>with id <a "
-            "href=https://mirantis.testrail.com/"
-            "index.php?/tests/view/{id}>[{id}]"
-            "</a></b>\n".format(id=test['id'], title=test["title"]))
+    f.write(f"<br><b>Proceeding test {test_link} <br>"
+            f"in {run_link} run</b>\n")
     f.flush()
 
     last_comment = get_last_comment(case_id, run_id, text_filters)
@@ -322,11 +322,9 @@
     if found:
         return
     else:
-        f.write("<b style='color:red;'>Automatic test processing failed. "
+        f.write(f"<b style='color:red;'>Automatic test processing failed. "
                 "Please process test manually "
-                "<a href=https://mirantis.testrail.com/"
-                "index.php?/tests/view/{test_id}>{test_id}"
-                "</a></b>\n\n".format(test_id=test["id"]))
+                f"{test_link}</b>\n\n")
         f.flush()
 
 
@@ -353,12 +351,8 @@
         else:
             test_run = test_rail_api.get_run_by_id(bot_test_run.run_id)
             run_type = "run"
-        f.write("Start processing {run_type} <a "
-                "href=https://mirantis.testrail.com/"
-                "index.php?/{run_type}s/view/{id}>{name}"
-                "</a>\n".format(run_type=run_type,
-                                id=test_run['id'],
-                                name=test_run['name']))
+        link = test_rail_api.html_link(run_type, test_run['id'], test_run['name'])
+        f.write(f"Start processing {run_type} {link}\n")
         f.flush()
 
         project_id = get_project_id(f, bot_test_run, report)
diff --git a/testrail_bot/control/migrations/0005_alter_suitepassrate_suite_id_and_more.py b/testrail_bot/control/migrations/0005_alter_suitepassrate_suite_id_and_more.py
new file mode 100644
index 0000000..27d098c
--- /dev/null
+++ b/testrail_bot/control/migrations/0005_alter_suitepassrate_suite_id_and_more.py
@@ -0,0 +1,19 @@
+# Generated by Django 4.2.7 on 2023-11-30 13:03
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('control', '0004_rename_timestamp_testrailtestrun_created_before'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='suitepassrate',
+            name='suite_id',
+            field=models.CharField(choices=[('Tempest', (('10651', '[MCP2.0_ROCKY]Tempest'), ('10635', '[MCP2.0_STEIN]Tempest'), ('10653', '[MCP2.0_TRAIN]Tempest'), ('10710', '[MCP2.0_USSURI]Tempest'), ('10888', '[MCP2.0_VICTORIA]Tempest'), ('11167', '[MCP2.0_WALLABY]Tempest'), ('11188', '[MCP2.0_XENA]Tempest'), ('11170', '[MCP2.0_YOGA]Tempest'), ('11192', '[MCP2.0_ANTELOPE]Tempest'))), ('Stepler', (('10886', '[MCP2.0_USSURI]Stepler'), ('10887', '[MCP2.0_VICTORIA]Stepler'), ('11171', '[MCP2.0_YOGA]Stepler'), ('11193', '[MCP2.0_ANTELOPE]Stepler')))], max_length=20),
+        )
+    ]
diff --git a/testrail_bot/control/models.py b/testrail_bot/control/models.py
index 94bade9..6a2ec82 100644
--- a/testrail_bot/control/models.py
+++ b/testrail_bot/control/models.py
@@ -85,20 +85,23 @@
 
 class SuitePassRate(models.Model):
     SUITE_CHOICES = [
-        ("10651", "[MCP2.0_ROCKY]Tempest"),
-        ("10635", "[MCP2.0_STEIN]Tempest"),
-        ("10653", "[MCP2.0_TRAIN]Tempest"),
-        ("10710", "[MCP2.0_USSURI]Tempest"),
-        ("10888", "[MCP2.0_VICTORIA]Tempest"),
-        ("11167", "[MCP2.0_WALLABY]Tempest"),
-        ("11188", "[MCP2.0_XENA]Tempest"),
-        ("11170", "[MCP2.0_YOGA]Tempest"),
-        ("11192", "[MCP2.0_ANTELOPE]Tempest"),
-
-        ("11193", "[MCP2.0_ANTELOPE]Stepler"),
-        ("10886", "[MCP2.0_USSURI]Stepler"),
-        ("10887", "[MCP2.0_VICTORIA]Stepler"),
-        ("11171", "[MCP2.0_YOGA]Stepler"),
+        ("Tempest", (
+            ("10651", "[MCP2.0_ROCKY]Tempest"),
+            ("10635", "[MCP2.0_STEIN]Tempest"),
+            ("10653", "[MCP2.0_TRAIN]Tempest"),
+            ("10710", "[MCP2.0_USSURI]Tempest"),
+            ("10888", "[MCP2.0_VICTORIA]Tempest"),
+            ("11167", "[MCP2.0_WALLABY]Tempest"),
+            ("11188", "[MCP2.0_XENA]Tempest"),
+            ("11170", "[MCP2.0_YOGA]Tempest"),
+            ("11192", "[MCP2.0_ANTELOPE]Tempest"))
+        ),
+        ("Stepler", (
+            ("10886", "[MCP2.0_USSURI]Stepler"),
+            ("10887", "[MCP2.0_VICTORIA]Stepler"),
+            ("11171", "[MCP2.0_YOGA]Stepler"),
+            ("11193", "[MCP2.0_ANTELOPE]Stepler"))
+        ),
     ]
     suite_id = models.CharField(max_length=20, choices=SUITE_CHOICES)
     suite_name = models.CharField(max_length=100, blank=True)