From 493652f7c99e4617e91f123824db9c7a355b1006 Mon Sep 17 00:00:00 2001 From: "pavel.lamonov" Date: Wed, 22 Jul 2026 17:10:40 +0300 Subject: [PATCH] =?UTF-8?q?refs=20#62274=20=D0=92=D1=81=D0=B5=20=D0=B4?= =?UTF-8?q?=D0=B5=D0=B9=D1=81=D1=82=D0=B2=D0=B8=D1=8F=20=D0=BF=D0=BE=D1=81?= =?UTF-8?q?=D0=BB=D0=B5=20=D1=80=D0=B5=D0=BB=D0=B8=D0=B7=D0=B0=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B4=D0=B0=D1=87=D0=B8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.py | 54 ++++++++++++++++++++++++++++++++++++++++++++---- constants.py | 6 ++++++ requirements.txt | 1 + utils.py | 21 +++++++++++++++++++ 4 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 utils.py diff --git a/api.py b/api.py index 480e015..f8e86a1 100644 --- a/api.py +++ b/api.py @@ -1,9 +1,10 @@ import os from dotenv import load_dotenv -from requests import get +from requests import get, put -from constants import RELEASE_STATUS_ID +from constants import RELEASE_STATUS_ID, VETRO_PROJECT_ID, STAND_FIELD_ID, CHECK_STATUS_ID +from utils import get_current_tuesday, date_from_iso, get_responsible load_dotenv() @@ -16,6 +17,17 @@ class RedmineAPI: self._key = key self._headers = {"X-Redmine-API-Key": self._key} + def put(self, *args, **kwargs) -> bool: + if len(args) > 0: + args = tuple([self._base + list(args)[0]] + list(args)[1:]) + + elif "url" in kwargs: + kwargs["url"] = self._base + kwargs["url"] + + kwargs["headers"] = self._headers + + return put(*args, **kwargs).status_code == 204 + def get(self, *args, **kwargs) -> dict: if len(args) > 0: args = tuple([self._base + list(args)[0]] + list(args)[1:]) @@ -27,8 +39,42 @@ class RedmineAPI: return get(*args, **kwargs).json() - def get_release_issues(self) -> list[dict]: - result = self.get("/issues.json", params={"assigned_to_id": "me", "status_id": RELEASE_STATUS_ID}) + def get_issue(self, issue_id: int) -> dict: + result = self.get(f"/issues/{issue_id}.json") + return result["issue"] + + def get_release_issues(self, version_id: int) -> list[dict]: + result = self.get( + "/issues.json", + params={"assigned_to_id": "me", "status_id": RELEASE_STATUS_ID, "fix_version_id": version_id} + ) return result["issues"] + def get_versions(self) -> list[dict]: + result = self.get(f"/projects/{VETRO_PROJECT_ID}/versions.json") + return result["versions"] + + def get_current_version(self) -> dict | None: + versions = self.get_versions() + tuesday = get_current_tuesday() + return next(filter(lambda version: date_from_iso(version["created_on"]) == tuesday, versions), None) + + def get_user(self, last_name: str, first_name: str) -> dict | None: + result = self.get("/users.json", params={"status": 1, "lastname": last_name, "firstname": first_name}) + return next(iter(result["users"]), None) + + def edit_issue(self, issue_id: int, data: dict) -> bool: + return self.put(f"/issues/{issue_id}.json", json={"issue": data}) + + def release_issue(self, issue_id: int) -> bool: + issue = self.get_issue(issue_id) + last_name, first_name = get_responsible(issue) + user = api.get_user(last_name=last_name, first_name=first_name) + assigned_to_id = user["id"] + return self.edit_issue(issue_id, data={ + "assigned_to_id": assigned_to_id, + "status_id": CHECK_STATUS_ID, + "custom_fields": [{"id": STAND_FIELD_ID, "value": "prod"}] + }) + api = RedmineAPI(SITE_URL, KEY) diff --git a/constants.py b/constants.py index ac94035..378b7bf 100644 --- a/constants.py +++ b/constants.py @@ -1 +1,7 @@ RELEASE_STATUS_ID = 9 +CHECK_STATUS_ID = 3 + +VETRO_PROJECT_ID = 10 + +RESPONSIBLE_FIELD_ID = 37 +STAND_FIELD_ID = 6 diff --git a/requirements.txt b/requirements.txt index 4564349..842502c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ dotenv requests +python-dateutil diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..c2cdc01 --- /dev/null +++ b/utils.py @@ -0,0 +1,21 @@ +from datetime import date, datetime +from dateutil.relativedelta import relativedelta, TU + +from constants import RESPONSIBLE_FIELD_ID + +def get_current_tuesday() -> date: + today = date.today() + if today.weekday() == 1: + return today + else: + return today + relativedelta(weekday=TU(-1)) + +def date_from_iso(datetime_: str) -> date: + return datetime.fromisoformat(datetime_).date() + +def get_responsible(issue: dict) -> tuple[str, str] | None: + field = next(filter(lambda field: field["id"] == RESPONSIBLE_FIELD_ID, issue["custom_fields"]), None) + if field: + return tuple(field["value"].split(" ")) + else: + return None