From 2df4de649df6fa25f01eb65890f3d2ee42f27653 Mon Sep 17 00:00:00 2001 From: ImMin5 Date: Fri, 26 Jan 2024 16:20:32 +0900 Subject: [PATCH 1/8] build: add template path at setup.py --- src/setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/setup.py b/src/setup.py index eb626289..03fbd853 100644 --- a/src/setup.py +++ b/src/setup.py @@ -27,5 +27,10 @@ license="Apache License 2.0", packages=find_packages(), install_requires=["spaceone-core", "spaceone-api", "pandas", "numpy"], + package_data={ + "spaceone": [ + "cost_analysis/template/*.html", + ] + }, zip_safe=False, ) From 0661edcf93dd871ce5cda5f7ba7355c4a7d17f1a Mon Sep 17 00:00:00 2001 From: ImMin5 Date: Sun, 28 Jan 2024 08:38:20 +0900 Subject: [PATCH 2/8] feat: add 'currency' field at CostReport model --- src/spaceone/cost_analysis/model/cost_report/database.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/spaceone/cost_analysis/model/cost_report/database.py b/src/spaceone/cost_analysis/model/cost_report/database.py index 7c531d71..1c37cb04 100644 --- a/src/spaceone/cost_analysis/model/cost_report/database.py +++ b/src/spaceone/cost_analysis/model/cost_report/database.py @@ -9,6 +9,7 @@ class CostReport(MongoModel): max_length=20, choices=("IN_PROGRESS", "SUCCESS"), default="IN_PROGRESS" ) report_number = StringField(max_length=255) + currency = StringField(choices=["KRW", "USD", "JPY"], default="KRW") issue_date = StringField(max_length=10) report_year = StringField(max_length=10) report_month = StringField(max_length=10) From 0bb50903f17023759da943bf3769c0bd34af1346 Mon Sep 17 00:00:00 2001 From: ImMin5 Date: Sun, 28 Jan 2024 08:39:44 +0900 Subject: [PATCH 3/8] fix: modify url format for get_url api --- src/spaceone/cost_analysis/service/cost_report_serivce.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/spaceone/cost_analysis/service/cost_report_serivce.py b/src/spaceone/cost_analysis/service/cost_report_serivce.py index ae8dc5da..9d9ea890 100644 --- a/src/spaceone/cost_analysis/service/cost_report_serivce.py +++ b/src/spaceone/cost_analysis/service/cost_report_serivce.py @@ -65,8 +65,9 @@ def send(self, params: CostReportSendRequest) -> None: conditions = { "cost_report_id": params.cost_report_id, "domain_id": domain_id, - # {"k": "status", "v": "SUCCESS", "o": "eq"}, + "status": "SUCCESS", } + if workspace_id is not None: conditions.update({"workspace_id": workspace_id}) @@ -369,7 +370,7 @@ def _get_console_cost_report_url( console_domain = config.get_global("EMAIL_CONSOLE_DOMAIN") console_domain = console_domain.format(domain_name=domain_name) - return f"{console_domain}cost-report?sso_access_token={token}&cost_report_id={cost_report_id}" + return f"{console_domain}/cost-report?sso_access_token={token}&cost_report_id={cost_report_id}" @staticmethod def _get_current_and_last_month() -> Tuple[str, str]: From ee262a4dd10ae4c6b28a71e4681ddb079ff6c8a8 Mon Sep 17 00:00:00 2001 From: ImMin5 Date: Sun, 28 Jan 2024 09:07:39 +0900 Subject: [PATCH 4/8] fix: modify identity manager initialize method --- .../service/cost_report_data_service.py | 19 +++--- .../service/cost_report_serivce.py | 64 +++++++++---------- 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/spaceone/cost_analysis/service/cost_report_data_service.py b/src/spaceone/cost_analysis/service/cost_report_data_service.py index 97e27cf6..56e822fc 100644 --- a/src/spaceone/cost_analysis/service/cost_report_data_service.py +++ b/src/spaceone/cost_analysis/service/cost_report_data_service.py @@ -41,7 +41,9 @@ def create_cost_report_data_by_cost_report_config(self, params: dict) -> None: """Create cost report by cost report config""" for cost_report_config_vo in self._get_all_cost_report_configs(): - self.create_cost_report_data(cost_report_config_vo) + issue_day = self._get_issue_day(cost_report_config_vo) + if issue_day == datetime.utcnow().day: + self.create_cost_report_data(cost_report_config_vo) @transaction( permission="cost-analysis:CostReportData.read", @@ -281,9 +283,8 @@ def _get_issue_day(cost_report_config_vo: CostReportConfig) -> int: else: return min(cost_report_config_vo.issue_day, last_day) - @staticmethod - def _get_workspace_name_map(domain_id: str) -> Tuple[dict, list]: - identity_mgr = IdentityManager() + def _get_workspace_name_map(self, domain_id: str) -> Tuple[dict, list]: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") workspace_name_map = {} workspaces = identity_mgr.list_workspaces( {"query": {"filter": [{"k": "state", "v": "ENABLED", "o": "eq"}]}}, @@ -329,9 +330,8 @@ def _get_data_source_currency_map( return data_source_currency_map, data_source_ids - @staticmethod - def _get_project_name_map(workspace_ids, domain_id: str) -> dict: - identity_mgr = IdentityManager() + def _get_project_name_map(self, workspace_ids, domain_id: str) -> dict: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") project_name_map = {} projects = identity_mgr.list_projects( { @@ -348,9 +348,8 @@ def _get_project_name_map(workspace_ids, domain_id: str) -> dict: project_name_map[project["project_id"]] = project["name"] return project_name_map - @staticmethod - def _get_service_account_name_map(workspace_ids, domain_id: str) -> dict: - identity_mgr = IdentityManager() + def _get_service_account_name_map(self, workspace_ids, domain_id: str) -> dict: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") service_account_name_map = {} service_accounts = identity_mgr.list_service_accounts( { diff --git a/src/spaceone/cost_analysis/service/cost_report_serivce.py b/src/spaceone/cost_analysis/service/cost_report_serivce.py index 9d9ea890..1e379117 100644 --- a/src/spaceone/cost_analysis/service/cost_report_serivce.py +++ b/src/spaceone/cost_analysis/service/cost_report_serivce.py @@ -320,7 +320,7 @@ def send_cost_report(self, cost_report_vo: CostReport) -> None: emails = recipients.get("emails", []) # list workspace owner role bindings - identity_mgr = IdentityManager() + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") workspace_ids = [] if workspace_id is not None: @@ -362,6 +362,19 @@ def send_cost_report(self, cost_report_vo: CostReport) -> None: f"[send_cost_report] send cost report ({workspace_id}/{cost_report_vo.cost_report_id}) to {users_info.get('total_count', 0)} users" ) + def _get_workspace_name_map(self, domain_id: str) -> Tuple[dict, list]: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") + workspace_name_map = {} + workspaces = identity_mgr.list_workspaces( + {"query": {"filter": [{"k": "state", "v": "ENABLED", "o": "eq"}]}}, + domain_id, + ) + workspace_ids = [] + for workspace in workspaces.get("results", []): + workspace_name_map[workspace["workspace_id"]] = workspace["name"] + workspace_ids.append(workspace["workspace_id"]) + return workspace_name_map, workspace_ids + def _get_console_cost_report_url( self, domain_id: str, cost_report_id: str, token: str ) -> str: @@ -372,6 +385,23 @@ def _get_console_cost_report_url( return f"{console_domain}/cost-report?sso_access_token={token}&cost_report_id={cost_report_id}" + def _get_domain_name(self, domain_id: str) -> str: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") + domain_name = identity_mgr.get_domain_name(domain_id) + return domain_name + + def _get_temporary_sso_access_token(self, domain_id: str) -> str: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") + system_token = config.get_global("TOKEN") + params = { + "grant_type": "SYSTEM_TOKEN", + "scope": "SYSTEM", + "token": system_token, + } + # todo : make temporary token + token = identity_mgr.grant_token(params) + return token + @staticmethod def _get_current_and_last_month() -> Tuple[str, str]: current_month = datetime.utcnow().strftime("%Y-%m") @@ -398,20 +428,6 @@ def generate_report_number(report_month: str, issue_day: int) -> str: return f"CostReport_{date_object.strftime('%y%m%d%H%M')}" - @staticmethod - def _get_workspace_name_map(domain_id: str) -> Tuple[dict, list]: - identity_mgr = IdentityManager() - workspace_name_map = {} - workspaces = identity_mgr.list_workspaces( - {"query": {"filter": [{"k": "state", "v": "ENABLED", "o": "eq"}]}}, - domain_id, - ) - workspace_ids = [] - for workspace in workspaces.get("results", []): - workspace_name_map[workspace["workspace_id"]] = workspace["name"] - workspace_ids.append(workspace["workspace_id"]) - return workspace_name_map, workspace_ids - @staticmethod def _get_data_source_currency_map( data_source_filter: dict, workspace_ids: list, domain_id: str @@ -457,21 +473,3 @@ def _aggregate_result_by_currency(results: list) -> list: workspace_result_map[workspace_id] = result.copy() return [workspace_result for workspace_result in workspace_result_map.values()] - - @staticmethod - def _get_domain_name(domain_id: str) -> str: - identity_mgr = IdentityManager() - domain_name = identity_mgr.get_domain_name(domain_id) - return domain_name - - def _get_temporary_sso_access_token(self, domain_id: str) -> str: - identity_mgr = IdentityManager() - system_token = config.get_global("TOKEN") - params = { - "grant_type": "SYSTEM_TOKEN", - "scope": "SYSTEM", - "token": system_token, - } - # todo : make temporary token - token = identity_mgr.grant_token(params) - return token From 7162a3a9fc001a166d1a3225e709002d100445f4 Mon Sep 17 00:00:00 2001 From: ImMin5 Date: Sun, 28 Jan 2024 08:38:20 +0900 Subject: [PATCH 5/8] feat: add 'currency' field at CostReport model --- src/spaceone/cost_analysis/model/cost_report/database.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/spaceone/cost_analysis/model/cost_report/database.py b/src/spaceone/cost_analysis/model/cost_report/database.py index 7c531d71..1c37cb04 100644 --- a/src/spaceone/cost_analysis/model/cost_report/database.py +++ b/src/spaceone/cost_analysis/model/cost_report/database.py @@ -9,6 +9,7 @@ class CostReport(MongoModel): max_length=20, choices=("IN_PROGRESS", "SUCCESS"), default="IN_PROGRESS" ) report_number = StringField(max_length=255) + currency = StringField(choices=["KRW", "USD", "JPY"], default="KRW") issue_date = StringField(max_length=10) report_year = StringField(max_length=10) report_month = StringField(max_length=10) From 31bcd1828083099d2842b755a18d7db1c88f5b1f Mon Sep 17 00:00:00 2001 From: ImMin5 Date: Sun, 28 Jan 2024 08:39:44 +0900 Subject: [PATCH 6/8] fix: modify url format for get_url api --- src/spaceone/cost_analysis/service/cost_report_serivce.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/spaceone/cost_analysis/service/cost_report_serivce.py b/src/spaceone/cost_analysis/service/cost_report_serivce.py index c67f4cf8..f52b9d72 100644 --- a/src/spaceone/cost_analysis/service/cost_report_serivce.py +++ b/src/spaceone/cost_analysis/service/cost_report_serivce.py @@ -67,8 +67,9 @@ def send(self, params: CostReportSendRequest) -> None: conditions = { "cost_report_id": params.cost_report_id, "domain_id": domain_id, - # {"k": "status", "v": "SUCCESS", "o": "eq"}, + "status": "SUCCESS", } + if workspace_id is not None: conditions.update({"workspace_id": workspace_id}) @@ -376,7 +377,7 @@ def _get_console_cost_report_url( console_domain = config.get_global("EMAIL_CONSOLE_DOMAIN") console_domain = console_domain.format(domain_name=domain_name) - return f"{console_domain}cost-report?sso_access_token={token}&cost_report_id={cost_report_id}" + return f"{console_domain}/cost-report?sso_access_token={token}&cost_report_id={cost_report_id}" @staticmethod def _get_current_and_last_month() -> Tuple[str, str]: From abe60497ebaff7c01e2f42c11175a868d827b6d6 Mon Sep 17 00:00:00 2001 From: ImMin5 Date: Sun, 28 Jan 2024 09:07:39 +0900 Subject: [PATCH 7/8] fix: modify identity manager initialize method --- .../service/cost_report_data_service.py | 19 +++--- .../service/cost_report_serivce.py | 64 +++++++++---------- 2 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/spaceone/cost_analysis/service/cost_report_data_service.py b/src/spaceone/cost_analysis/service/cost_report_data_service.py index 80cbf306..76134ee5 100644 --- a/src/spaceone/cost_analysis/service/cost_report_data_service.py +++ b/src/spaceone/cost_analysis/service/cost_report_data_service.py @@ -42,7 +42,9 @@ def create_cost_report_data_by_cost_report_config(self, params: dict) -> None: """Create cost report by cost report config""" for cost_report_config_vo in self._get_all_cost_report_configs(): - self.create_cost_report_data(cost_report_config_vo) + issue_day = self._get_issue_day(cost_report_config_vo) + if issue_day == datetime.utcnow().day: + self.create_cost_report_data(cost_report_config_vo) @transaction( permission="cost-analysis:CostReportData.read", @@ -286,9 +288,8 @@ def _get_issue_day(cost_report_config_vo: CostReportConfig) -> int: else: return min(cost_report_config_vo.issue_day, last_day) - @staticmethod - def _get_workspace_name_map(domain_id: str) -> Tuple[dict, list]: - identity_mgr = IdentityManager() + def _get_workspace_name_map(self, domain_id: str) -> Tuple[dict, list]: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") workspace_name_map = {} workspaces = identity_mgr.list_workspaces( {"query": {"filter": [{"k": "state", "v": "ENABLED", "o": "eq"}]}}, @@ -334,9 +335,8 @@ def _get_data_source_currency_map( return data_source_currency_map, data_source_ids - @staticmethod - def _get_project_name_map(workspace_ids, domain_id: str) -> dict: - identity_mgr = IdentityManager() + def _get_project_name_map(self, workspace_ids, domain_id: str) -> dict: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") project_name_map = {} projects = identity_mgr.list_projects( { @@ -353,9 +353,8 @@ def _get_project_name_map(workspace_ids, domain_id: str) -> dict: project_name_map[project["project_id"]] = project["name"] return project_name_map - @staticmethod - def _get_service_account_name_map(workspace_ids, domain_id: str) -> dict: - identity_mgr = IdentityManager() + def _get_service_account_name_map(self, workspace_ids, domain_id: str) -> dict: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") service_account_name_map = {} service_accounts = identity_mgr.list_service_accounts( { diff --git a/src/spaceone/cost_analysis/service/cost_report_serivce.py b/src/spaceone/cost_analysis/service/cost_report_serivce.py index f52b9d72..ab4f00ee 100644 --- a/src/spaceone/cost_analysis/service/cost_report_serivce.py +++ b/src/spaceone/cost_analysis/service/cost_report_serivce.py @@ -326,7 +326,7 @@ def send_cost_report(self, cost_report_vo: CostReport) -> None: emails = recipients.get("emails", []) # list workspace owner role bindings - identity_mgr = IdentityManager() + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") workspace_ids = [] if workspace_id is not None: @@ -369,6 +369,19 @@ def send_cost_report(self, cost_report_vo: CostReport) -> None: {users_info.get('total_count', 0)} users" ) + def _get_workspace_name_map(self, domain_id: str) -> Tuple[dict, list]: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") + workspace_name_map = {} + workspaces = identity_mgr.list_workspaces( + {"query": {"filter": [{"k": "state", "v": "ENABLED", "o": "eq"}]}}, + domain_id, + ) + workspace_ids = [] + for workspace in workspaces.get("results", []): + workspace_name_map[workspace["workspace_id"]] = workspace["name"] + workspace_ids.append(workspace["workspace_id"]) + return workspace_name_map, workspace_ids + def _get_console_cost_report_url( self, domain_id: str, cost_report_id: str, token: str ) -> str: @@ -379,6 +392,23 @@ def _get_console_cost_report_url( return f"{console_domain}/cost-report?sso_access_token={token}&cost_report_id={cost_report_id}" + def _get_domain_name(self, domain_id: str) -> str: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") + domain_name = identity_mgr.get_domain_name(domain_id) + return domain_name + + def _get_temporary_sso_access_token(self, domain_id: str) -> str: + identity_mgr: IdentityManager = self.locator.get_manager("IdentityManager") + system_token = config.get_global("TOKEN") + params = { + "grant_type": "SYSTEM_TOKEN", + "scope": "SYSTEM", + "token": system_token, + } + # todo : make temporary token + token = identity_mgr.grant_token(params) + return token + @staticmethod def _get_current_and_last_month() -> Tuple[str, str]: current_month = datetime.utcnow().strftime("%Y-%m") @@ -405,20 +435,6 @@ def generate_report_number(report_month: str, issue_day: int) -> str: return f"CostReport_{date_object.strftime('%y%m%d%H%M')}" - @staticmethod - def _get_workspace_name_map(domain_id: str) -> Tuple[dict, list]: - identity_mgr = IdentityManager() - workspace_name_map = {} - workspaces = identity_mgr.list_workspaces( - {"query": {"filter": [{"k": "state", "v": "ENABLED", "o": "eq"}]}}, - domain_id, - ) - workspace_ids = [] - for workspace in workspaces.get("results", []): - workspace_name_map[workspace["workspace_id"]] = workspace["name"] - workspace_ids.append(workspace["workspace_id"]) - return workspace_name_map, workspace_ids - @staticmethod def _get_data_source_currency_map( data_source_filter: dict, workspace_ids: list, domain_id: str @@ -464,21 +480,3 @@ def _aggregate_result_by_currency(results: list) -> list: workspace_result_map[workspace_id] = result.copy() return [workspace_result for workspace_result in workspace_result_map.values()] - - @staticmethod - def _get_domain_name(domain_id: str) -> str: - identity_mgr = IdentityManager() - domain_name = identity_mgr.get_domain_name(domain_id) - return domain_name - - def _get_temporary_sso_access_token(self, domain_id: str) -> str: - identity_mgr = IdentityManager() - system_token = config.get_global("TOKEN") - params = { - "grant_type": "SYSTEM_TOKEN", - "scope": "SYSTEM", - "token": system_token, - } - # todo : make temporary token - token = identity_mgr.grant_token(params) - return token From f566412527179634e278a894b0c5f042e5cc7642 Mon Sep 17 00:00:00 2001 From: ImMin5 Date: Sun, 28 Jan 2024 21:50:05 +0900 Subject: [PATCH 8/8] build: add python package 'bs4' --- pkg/pip_requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/pip_requirements.txt b/pkg/pip_requirements.txt index b6b7d2e5..37d5d89b 100644 --- a/pkg/pip_requirements.txt +++ b/pkg/pip_requirements.txt @@ -4,3 +4,4 @@ numpy jinja2 finance-datareader plotly +bs4