From cdf3177855a3f745a694b6fa55761fe5ce0b7169 Mon Sep 17 00:00:00 2001 From: Iakov Gan Date: Wed, 5 Jul 2023 01:13:18 +0200 Subject: [PATCH 01/11] add cost anomalies --- dashboards/cost-anomalies.yaml | 121 +++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 dashboards/cost-anomalies.yaml diff --git a/dashboards/cost-anomalies.yaml b/dashboards/cost-anomalies.yaml new file mode 100644 index 00000000..aed10752 --- /dev/null +++ b/dashboards/cost-anomalies.yaml @@ -0,0 +1,121 @@ +dashboards: + AWS COST ANOMALIES DASHBOARD: + dependsOn: + datasets: + - ca_summary_view + name: AWS Cost Anomalies Dashboard + dashboardId: aws-cost-anomalies-dashboard + templateId: aws-cost-anomalies-dashboard + sourceAccountId: '774028859993' + region: us-east-1 +datasets: + ca_summary_view: + data: + DataSetId: ca_summary_view + Name: ca_summary_view + PhysicalTableMap: + 3cec5d27-cc1d-4503-9b2e-c2bf0ce7b389: + RelationalTable: + DataSourceArn: ${athena_datasource_arn} + Catalog: AwsDataCatalog + Schema: ${athena_database_name} + Name: cost_anomalies + InputColumns: + - Name: year + Type: STRING + - Name: month + Type: STRING + - Name: anomalyid + Type: STRING + - Name: dimensionvalue + Type: STRING + - Name: anomaly_start_date + Type: DATETIME + - Name: anomalylastupdatedate + Type: DATETIME + - Name: maximpact + Type: INTEGER + - Name: totalactualspend + Type: INTEGER + - Name: totalexpectedspend + Type: INTEGER + - Name: tota_impact + Type: INTEGER + - Name: totalimpactpercentage + Type: INTEGER + - Name: duration + Type: INTEGER + - Name: monitorarn + Type: STRING + - Name: region + Type: STRING + - Name: service + Type: STRING + - Name: linkedaccount + Type: STRING + - Name: linkedaccountname + Type: STRING + - Name: usagetype + Type: STRING + LogicalTableMap: + 0e0f5f65-03fe-4bbd-b40d-34a68730292e: + Alias: cost_anomalies + DataTransforms: + - TagColumnOperation: + ColumnName: region + Tags: + - ColumnGeographicRole: STATE + - ProjectOperation: + ProjectedColumns: + - year + - month + - anomalyid + - dimensionvalue + - anomaly_start_date + - anomalylastupdatedate + - maximpact + - totalactualspend + - totalexpectedspend + - tota_impact + - totalimpactpercentage + - duration + - monitorarn + - region + - service + - linkedaccount + - linkedaccountname + - usagetype + Source: + PhysicalTableId: 3cec5d27-cc1d-4503-9b2e-c2bf0ce7b389 + ImportMode: SPICE + dependsOn: + views: + - cost_anomalies +views: + cost_anomalies: + data: |- + CREATE OR REPLACE VIEW cost_anomalies AS + SELECT DISTINCT + "year" + , "month" + , payer_id + , anomalyid + , dimensionvalue + , CAST("from_iso8601_timestamp"("anomalystartdate") AS date) anomaly_start_date + , CAST("from_iso8601_timestamp"("anomalyenddate") AS date) anomalylastupdatedate + , CAST(maximpact AS int) maximpact + , CAST(totalactualspend AS int) totalactualspend + , CAST(totalexpectedspend AS int) totalexpectedspend + , CAST(totalimpact AS int) tota_impact + , CAST(totalimpactpercentage AS int) totalimpactpercentage + , EXTRACT(DAY FROM (CAST("from_iso8601_timestamp"("anomalyenddate") AS date) - CAST("from_iso8601_timestamp"("anomalystartdate") AS date))) duration + , monitorarn + , region + , service + , linkedaccount + , linkedaccountname + , usagetype + FROM + "optimization_data"."cost_anomaly_data" + WHERE ("date_parse"("concat"("year", "month", "day"), '%Y%m%d') >= (current_timestamp - INTERVAL '1' MONTH)) + GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 \ No newline at end of file From 0d8cece4880376a18ca71d6626d7599275a5aa69 Mon Sep 17 00:00:00 2001 From: Iakov GAN <82834333+iakov-aws@users.noreply.github.com> Date: Wed, 5 Jul 2023 12:11:14 +0200 Subject: [PATCH 02/11] update SQL view.yaml --- dashboards/cost-anomalies.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dashboards/cost-anomalies.yaml b/dashboards/cost-anomalies.yaml index aed10752..4dfe1340 100644 --- a/dashboards/cost-anomalies.yaml +++ b/dashboards/cost-anomalies.yaml @@ -117,5 +117,4 @@ views: , usagetype FROM "optimization_data"."cost_anomaly_data" - WHERE ("date_parse"("concat"("year", "month", "day"), '%Y%m%d') >= (current_timestamp - INTERVAL '1' MONTH)) - GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 \ No newline at end of file + WHERE ("date_parse"("concat"("year", "month", "day"), '%Y%m%d') >= (current_timestamp - INTERVAL '3' MONTH)) From 925582fe2746bfe1ee9aa525b2bddcdc3534d7c7 Mon Sep 17 00:00:00 2001 From: Iakov Gan Date: Wed, 5 Jul 2023 16:23:28 +0200 Subject: [PATCH 03/11] publish dashboard as text --- dashboards/cost-anomalies.yaml | 1327 +++++++++++++++++++++++++++++++- 1 file changed, 1321 insertions(+), 6 deletions(-) diff --git a/dashboards/cost-anomalies.yaml b/dashboards/cost-anomalies.yaml index aed10752..7cc43f69 100644 --- a/dashboards/cost-anomalies.yaml +++ b/dashboards/cost-anomalies.yaml @@ -3,11 +3,1324 @@ dashboards: dependsOn: datasets: - ca_summary_view - name: AWS Cost Anomalies Dashboard - dashboardId: aws-cost-anomalies-dashboard - templateId: aws-cost-anomalies-dashboard - sourceAccountId: '774028859993' - region: us-east-1 + name: AWS Cost Anomalies + dashboardId: aws-cost-anomalies + data: |- + AnalysisDefaults: + DefaultNewSheetConfiguration: + InteractiveLayoutConfiguration: + Grid: + CanvasSizeOptions: + ScreenCanvasSizeOptions: + OptimizedViewPortWidth: 1600px + ResizeOption: FIXED + SheetContentType: INTERACTIVE + CalculatedFields: + - DataSetIdentifier: ca_summary_view + Expression: ifelse({payer_id} ='0', 'All','All') + Name: All + - DataSetIdentifier: ca_summary_view + Expression: |- + ifelse(${GroupBy}='Account ID',linkedaccount, + ${GroupBy}='Account Name',linkedaccountname, + ${GroupBy}= 'Payer Account ID', {payer_id}, + ${GroupBy}= 'All', All, + All) + Name: Group by + - DataSetIdentifier: ca_summary_view + Expression: |2- + totalimpactpercentage * 100 + Name: percentage + - DataSetIdentifier: ca_summary_view + Expression: |2- + ifelse ( + dateDiff(anomalylastupdatedate,now(),'DD') >= ${numberofdays},"Closed","Open" + ) + Name: status + ColumnConfigurations: + - Column: + ColumnName: tota_impact + DataSetIdentifier: ca_summary_view + FormatConfiguration: + NumberFormatConfiguration: + FormatConfiguration: + CurrencyDisplayFormatConfiguration: + DecimalPlacesConfiguration: + DecimalPlaces: 2 + NegativeValueConfiguration: + DisplayMode: POSITIVE + SeparatorConfiguration: + DecimalSeparator: DOT + ThousandsSeparator: + Symbol: COMMA + Visibility: VISIBLE + Symbol: USD + DataSetIdentifierDeclarations: + - DataSetArn: arn:aws:quicksight:::dataset/19a16987-6c8f-45f6-8a60-f43b755f9f19 + Identifier: ca_summary_view + FilterGroups: + - CrossDataset: SINGLE_DATASET + FilterGroupId: 8f2c0181-52c9-4b73-9426-d7d89a82b450 + Filters: + - RelativeDatesFilter: + AnchorDateConfiguration: + AnchorOption: NOW + Column: + ColumnName: anomaly_start_date + DataSetIdentifier: ca_summary_view + FilterId: 92ca5e80-1cdc-4fd2-8b47-f692053d46b7 + MinimumGranularity: DAY + NullOption: NON_NULLS_ONLY + RelativeDateType: LAST + RelativeDateValue: 4 + TimeGranularity: MONTH + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: SELECTED_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + VisualIds: + - 24488232-a887-4a68-b5d2-bc818d94a7d3 + Status: ENABLED + - CrossDataset: SINGLE_DATASET + FilterGroupId: dd8a5f73-e526-4103-ba5b-c2e415d6c938 + Filters: + - TopBottomFilter: + AggregationSortConfigurations: + - AggregationFunction: + NumericalAggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: tota_impact + DataSetIdentifier: ca_summary_view + SortDirection: DESC + Column: + ColumnName: linkedaccountname + DataSetIdentifier: ca_summary_view + FilterId: c11b19f2-11da-4b5b-ac37-60251c788188 + Limit: 10 + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: SELECTED_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + VisualIds: + - 120d7e67-9cc0-4777-ae7f-5e1af2e2bb25 + Status: ENABLED + - CrossDataset: ALL_DATASETS + FilterGroupId: 06509af4-ecd5-4adc-be02-4520ca5fc166 + Filters: + - TimeRangeFilter: + Column: + ColumnName: anomaly_start_date + DataSetIdentifier: ca_summary_view + FilterId: 8ef5b670-d34a-4ce7-8134-555c1bed2182 + IncludeMaximum: true + IncludeMinimum: true + NullOption: NON_NULLS_ONLY + RangeMaximumValue: + Parameter: enddate + RangeMinimumValue: + Parameter: startdate + TimeGranularity: DAY + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: ALL_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + Status: ENABLED + - CrossDataset: ALL_DATASETS + FilterGroupId: 3f3e05ee-c9fa-4196-85db-115626420d99 + Filters: + - CategoryFilter: + Column: + ColumnName: linkedaccount + DataSetIdentifier: ca_summary_view + Configuration: + CustomFilterConfiguration: + MatchOperator: EQUALS + NullOption: NON_NULLS_ONLY + ParameterName: linkedaccount + FilterId: 18c88df6-29a8-44af-a423-260f4f507ec2 + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: ALL_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + Status: ENABLED + - CrossDataset: ALL_DATASETS + FilterGroupId: 0d15e209-4893-4faa-8104-da1bc7e3f32c + Filters: + - CategoryFilter: + Column: + ColumnName: linkedaccountname + DataSetIdentifier: ca_summary_view + Configuration: + CustomFilterConfiguration: + MatchOperator: EQUALS + NullOption: NON_NULLS_ONLY + ParameterName: AccountName + FilterId: 8dba3e09-b28b-44cb-9739-90526a196b48 + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: ALL_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + Status: ENABLED + - CrossDataset: ALL_DATASETS + FilterGroupId: 8112811a-8c42-484f-ac6d-4aa7de80204e + Filters: + - CategoryFilter: + Column: + ColumnName: status + DataSetIdentifier: ca_summary_view + Configuration: + CustomFilterConfiguration: + MatchOperator: EQUALS + NullOption: NON_NULLS_ONLY + ParameterName: status + FilterId: ab6a6283-78c6-4131-85ad-01c5a747845d + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: ALL_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + Status: ENABLED + - CrossDataset: ALL_DATASETS + FilterGroupId: 700a562d-68f8-4883-8ee9-c3914b3bcdb3 + Filters: + - NumericRangeFilter: + Column: + ColumnName: tota_impact + DataSetIdentifier: ca_summary_view + FilterId: dc3e1d3f-2289-4c80-a3ae-3edd8e6629b4 + IncludeMaximum: false + IncludeMinimum: false + NullOption: ALL_VALUES + RangeMinimum: + Parameter: totalimpact + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: ALL_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + Status: ENABLED + - CrossDataset: SINGLE_DATASET + FilterGroupId: 78824e95-a6fe-4ed3-a52a-5895000fd727 + Filters: + - TopBottomFilter: + AggregationSortConfigurations: + - AggregationFunction: + NumericalAggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: totalactualspend + DataSetIdentifier: ca_summary_view + SortDirection: DESC + Column: + ColumnName: service + DataSetIdentifier: ca_summary_view + FilterId: 89033000-dc1e-4c09-8e1a-eae4e9edc308 + Limit: 10 + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: SELECTED_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + VisualIds: + - a98ae705-0b31-4a15-b1ba-eafdf37da041 + Status: ENABLED + - CrossDataset: ALL_DATASETS + FilterGroupId: e9aba4b4-ed11-4c12-8763-516605af7b7e + Filters: + - CategoryFilter: + Column: + ColumnName: payer_id + DataSetIdentifier: ca_summary_view + Configuration: + CustomFilterConfiguration: + MatchOperator: EQUALS + NullOption: NON_NULLS_ONLY + ParameterName: payeraccount + FilterId: 9066dc04-85a9-46d9-86f8-beadd7bfccaf + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: ALL_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + Status: ENABLED + - CrossDataset: SINGLE_DATASET + FilterGroupId: f9f5db40-12d1-4aad-85e9-7d76f2678586 + Filters: + - TopBottomFilter: + AggregationSortConfigurations: + - AggregationFunction: + NumericalAggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: tota_impact + DataSetIdentifier: ca_summary_view + SortDirection: DESC + Column: + ColumnName: region + DataSetIdentifier: ca_summary_view + FilterId: b8147bad-8857-4d40-89b3-66a3fead5458 + Limit: 10 + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: SELECTED_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + VisualIds: + - 92cc7652-7613-4d12-b595-b50c322a2181 + Status: ENABLED + - CrossDataset: SINGLE_DATASET + FilterGroupId: 02132560-a0a0-4f5c-8b26-1117e90df876 + Filters: + - TopBottomFilter: + AggregationSortConfigurations: + - AggregationFunction: + NumericalAggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: tota_impact + DataSetIdentifier: ca_summary_view + SortDirection: DESC + Column: + ColumnName: service + DataSetIdentifier: ca_summary_view + FilterId: 383e43fa-fefd-4d5b-acfa-dbfdd0cee1a6 + Limit: 10 + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: SELECTED_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + VisualIds: + - d21a3831-b55a-439d-b59a-7c674bdc4fbb + Status: ENABLED + - CrossDataset: ALL_DATASETS + FilterGroupId: ff0d10dd-ab44-4432-a211-3ef44bdbebf5 + Filters: + - CategoryFilter: + Column: + ColumnName: Group by + DataSetIdentifier: ca_summary_view + Configuration: + FilterListConfiguration: + MatchOperator: CONTAINS + SelectAllOptions: FILTER_ALL_VALUES + FilterId: b5f7d6e5-e9a0-409b-8c9d-7da66e1da28d + ScopeConfiguration: + SelectedSheets: + SheetVisualScopingConfigurations: + - Scope: ALL_VISUALS + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + Status: ENABLED + ParameterDeclarations: + - DecimalParameterDeclaration: + DefaultValues: + StaticValues: + - 200.0 + Name: totalimpact + ParameterValueType: SINGLE_VALUED + ValueWhenUnset: + ValueWhenUnsetOption: RECOMMENDED_VALUE + - IntegerParameterDeclaration: + DefaultValues: + StaticValues: + - 7 + Name: numberofdays + ParameterValueType: SINGLE_VALUED + ValueWhenUnset: + ValueWhenUnsetOption: RECOMMENDED_VALUE + - StringParameterDeclaration: + DefaultValues: {} + Name: linkedaccount + ParameterValueType: MULTI_VALUED + ValueWhenUnset: + ValueWhenUnsetOption: RECOMMENDED_VALUE + - StringParameterDeclaration: + DefaultValues: + StaticValues: [] + Name: AccountName + ParameterValueType: SINGLE_VALUED + ValueWhenUnset: + ValueWhenUnsetOption: RECOMMENDED_VALUE + - StringParameterDeclaration: + DefaultValues: + StaticValues: [] + Name: payeraccount + ParameterValueType: SINGLE_VALUED + ValueWhenUnset: + ValueWhenUnsetOption: RECOMMENDED_VALUE + - StringParameterDeclaration: + DefaultValues: + StaticValues: + - Closed + - Open + Name: status + ParameterValueType: MULTI_VALUED + ValueWhenUnset: + ValueWhenUnsetOption: RECOMMENDED_VALUE + - StringParameterDeclaration: + DefaultValues: + StaticValues: + - All + Name: GroupBy + ParameterValueType: SINGLE_VALUED + ValueWhenUnset: + ValueWhenUnsetOption: 'NULL' + - DateTimeParameterDeclaration: + DefaultValues: + RollingDate: + Expression: addDateTime(-1, 'MM', truncDate('MM', now())) + StaticValues: [] + Name: AnomalyStartDateRange + TimeGranularity: DAY + - DateTimeParameterDeclaration: + DefaultValues: + RollingDate: + Expression: addDateTime(-90, 'DD', truncDate('DD', now())) + StaticValues: [] + Name: startdate + TimeGranularity: DAY + - DateTimeParameterDeclaration: + DefaultValues: + RollingDate: + Expression: truncDate('DD', now()) + StaticValues: [] + Name: enddate + TimeGranularity: DAY + Sheets: + - ContentType: INTERACTIVE + Layouts: + - Configuration: + GridLayout: + CanvasSizeOptions: + ScreenCanvasSizeOptions: + OptimizedViewPortWidth: 1600px + ResizeOption: FIXED + Elements: + - ColumnIndex: 0 + ColumnSpan: 8 + ElementId: 0242b4bf-9667-4886-afc8-156564e737d9 + ElementType: PARAMETER_CONTROL + RowIndex: 0 + RowSpan: 3 + - ColumnIndex: 8 + ColumnSpan: 13 + ElementId: a10b6e7d-f99d-45ec-80fa-cde786f87d2a + ElementType: TEXT_BOX + RowIndex: 0 + RowSpan: 1 + - ColumnIndex: 21 + ColumnSpan: 15 + ElementId: 75496596-ec3b-4932-9723-3a11e50c0474 + ElementType: VISUAL + RowIndex: 0 + RowSpan: 5 + - ColumnIndex: 8 + ColumnSpan: 13 + ElementId: 643a37ff-5efa-46c2-ab0b-7a892205b5e4 + ElementType: VISUAL + RowIndex: 1 + RowSpan: 6 + - ColumnIndex: 0 + ColumnSpan: 8 + ElementId: daed692c-42a2-4974-816e-2ea370479e97 + ElementType: PARAMETER_CONTROL + RowIndex: 3 + RowSpan: 2 + - ColumnIndex: 0 + ColumnSpan: 8 + ElementId: 2f70293a-fd1b-43c3-bd35-73679e64d7d6 + ElementType: PARAMETER_CONTROL + RowIndex: 5 + RowSpan: 2 + - ColumnIndex: 21 + ColumnSpan: 7 + ElementId: d21a3831-b55a-439d-b59a-7c674bdc4fbb + ElementType: VISUAL + RowIndex: 5 + RowSpan: 6 + - ColumnIndex: 28 + ColumnSpan: 8 + ElementId: 120d7e67-9cc0-4777-ae7f-5e1af2e2bb25 + ElementType: VISUAL + RowIndex: 5 + RowSpan: 3 + - ColumnIndex: 0 + ColumnSpan: 8 + ElementId: 7efa69f9-81df-4230-ae46-f5c47d97851e + ElementType: PARAMETER_CONTROL + RowIndex: 7 + RowSpan: 2 + - ColumnIndex: 8 + ColumnSpan: 13 + ElementId: 1ffd5f49-7d1b-448e-8f65-88650bb12191 + ElementType: VISUAL + RowIndex: 7 + RowSpan: 4 + - ColumnIndex: 28 + ColumnSpan: 8 + ElementId: 92cc7652-7613-4d12-b595-b50c322a2181 + ElementType: VISUAL + RowIndex: 8 + RowSpan: 3 + - ColumnIndex: 0 + ColumnSpan: 8 + ElementId: bcda09e9-b05d-4d2f-9838-dc65ebbfdab7 + ElementType: PARAMETER_CONTROL + RowIndex: 9 + RowSpan: 2 + - ColumnIndex: 0 + ColumnSpan: 19 + ElementId: 3d702097-ea7a-4a34-b4fb-435e3ae2005c + ElementType: VISUAL + RowIndex: 11 + RowSpan: 10 + - ColumnIndex: 19 + ColumnSpan: 17 + ElementId: a98ae705-0b31-4a15-b1ba-eafdf37da041 + ElementType: VISUAL + RowIndex: 11 + RowSpan: 10 + - ColumnIndex: 0 + ColumnSpan: 36 + ElementId: 24488232-a887-4a68-b5d2-bc818d94a7d3 + ElementType: VISUAL + RowIndex: 21 + RowSpan: 11 + Name: AWS Cost Anomalies + ParameterControls: + - DateTimePicker: + DisplayOptions: + DateTimeFormat: YYYY/MM/DD HH:mm:ss + TitleOptions: + FontConfiguration: + FontSize: + Relative: MEDIUM + Visibility: VISIBLE + ParameterControlId: 7efa69f9-81df-4230-ae46-f5c47d97851e + SourceParameterName: enddate + Title: End Date + - DateTimePicker: + DisplayOptions: + DateTimeFormat: YYYY/MM/DD HH:mm:ss + TitleOptions: + FontConfiguration: + FontSize: + Relative: MEDIUM + Visibility: VISIBLE + ParameterControlId: 2f70293a-fd1b-43c3-bd35-73679e64d7d6 + SourceParameterName: startdate + Title: Start Date + - List: + DisplayOptions: + SearchOptions: + Visibility: HIDDEN + SelectAllOptions: + Visibility: VISIBLE + TitleOptions: + FontConfiguration: + FontSize: + Relative: MEDIUM + Visibility: VISIBLE + ParameterControlId: e93bf3e8-31a7-4ff5-8f3a-f57287f2c218 + SelectableValues: + LinkToDataSetColumn: + ColumnName: linkedaccountname + DataSetIdentifier: ca_summary_view + SourceParameterName: AccountName + Title: Account Name + Type: SINGLE_SELECT + - List: + DisplayOptions: + SearchOptions: + Visibility: HIDDEN + SelectAllOptions: + Visibility: VISIBLE + TitleOptions: + FontConfiguration: + FontSize: + Relative: MEDIUM + Visibility: VISIBLE + ParameterControlId: e9616d68-4a5b-4757-8cd7-4b45ae118a2f + SelectableValues: + Values: + - Account ID + - Account Name + - All + - Payer Account ID + SourceParameterName: GroupBy + Title: Group By + Type: SINGLE_SELECT + - Dropdown: + DisplayOptions: + SelectAllOptions: + Visibility: VISIBLE + TitleOptions: + FontConfiguration: + FontSize: + Relative: MEDIUM + Visibility: VISIBLE + ParameterControlId: f9ead3c8-57c6-4416-b2f6-717b9c5f678e + SelectableValues: + LinkToDataSetColumn: + ColumnName: linkedaccount + DataSetIdentifier: ca_summary_view + SourceParameterName: linkedaccount + Title: Account ID + Type: MULTI_SELECT + - Dropdown: + DisplayOptions: + SelectAllOptions: + Visibility: VISIBLE + TitleOptions: + FontConfiguration: + FontSize: + Relative: MEDIUM + Visibility: VISIBLE + ParameterControlId: df8dcea1-b182-43ac-86d0-ec07024eb0a6 + SelectableValues: + LinkToDataSetColumn: + ColumnName: payer_id + DataSetIdentifier: ca_summary_view + SourceParameterName: payeraccount + Title: Payer Account ID + Type: SINGLE_SELECT + - Dropdown: + DisplayOptions: + SelectAllOptions: + Visibility: VISIBLE + TitleOptions: + FontConfiguration: + FontSize: + Relative: MEDIUM + Visibility: VISIBLE + ParameterControlId: bcda09e9-b05d-4d2f-9838-dc65ebbfdab7 + SelectableValues: + Values: + - Closed + - Open + SourceParameterName: status + Title: Status + Type: MULTI_SELECT + - Slider: + DisplayOptions: + TitleOptions: + FontConfiguration: + FontSize: + Relative: MEDIUM + Visibility: VISIBLE + MaximumValue: 1000000.0 + MinimumValue: 0.0 + ParameterControlId: 0242b4bf-9667-4886-afc8-156564e737d9 + SourceParameterName: totalimpact + StepSize: 2000.0 + Title: Filter by Total Impact Greater then by input value + - TextField: + DisplayOptions: + PlaceholderOptions: + Visibility: VISIBLE + TitleOptions: + FontConfiguration: + FontSize: + Relative: MEDIUM + Visibility: VISIBLE + ParameterControlId: daed692c-42a2-4974-816e-2ea370479e97 + SourceParameterName: numberofdays + Title: Number of X days to consider Closed Anomalies + SheetControlLayouts: + - Configuration: + GridLayout: + Elements: + - ColumnIndex: 0 + ColumnSpan: 2 + ElementId: e9616d68-4a5b-4757-8cd7-4b45ae118a2f + ElementType: PARAMETER_CONTROL + RowIndex: 0 + RowSpan: 1 + - ColumnIndex: 2 + ColumnSpan: 3 + ElementId: df8dcea1-b182-43ac-86d0-ec07024eb0a6 + ElementType: PARAMETER_CONTROL + RowIndex: 0 + RowSpan: 1 + - ColumnIndex: 5 + ColumnSpan: 3 + ElementId: f9ead3c8-57c6-4416-b2f6-717b9c5f678e + ElementType: PARAMETER_CONTROL + RowIndex: 0 + RowSpan: 1 + - ColumnIndex: 8 + ColumnSpan: 3 + ElementId: e93bf3e8-31a7-4ff5-8f3a-f57287f2c218 + ElementType: PARAMETER_CONTROL + RowIndex: 0 + RowSpan: 1 + SheetId: 71953f59-5361-4610-be0d-088c270b9666 + TextBoxes: + - Content: |- + + + + By default, you can see the anomalies that are detected in the last 90 days. + + + + SheetTextBoxId: a10b6e7d-f99d-45ec-80fa-cde786f87d2a + Visuals: + - InsightVisual: + Actions: [] + DataSetIdentifier: ca_summary_view + InsightConfiguration: + Computations: + - TopBottomRanked: + Category: + CategoricalDimensionField: + Column: + ColumnName: service + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.service.2.1686744080024 + ComputationId: f34fa7c9-8b4b-473a-af43-8f9131c695af + Name: Top + ResultSize: 5 + Type: TOP + Value: + NumericalMeasureField: + AggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: tota_impact + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.tota_impact.2.1688557465691 + CustomNarrative: + Narrative: "\n Top\n \n Top.itemsCount\n \n\ + \ Top.categoryField.name\n \_for total\n Top.metricField.name\n\ + \ \_are:\n\ + \ \_is:\n\ + \
\n \n
    \n\ + \
  • \n \n \n \ + \ Top.items[index].categoryValue.formattedValue\n\ + \ \n \n \_with\n Top.items[index].metricValue.formattedValue\n\ + \
  • \n
\n
\n
\n
" + Subtitle: + Visibility: VISIBLE + Title: + FormatText: + RichText: Top 5 Movers (Total Cost Impact) + Visibility: VISIBLE + VisualId: 75496596-ec3b-4932-9723-3a11e50c0474 + - WordCloudVisual: + Actions: [] + ChartConfiguration: + FieldWells: + WordCloudAggregatedFieldWells: + GroupBy: + - CategoricalDimensionField: + Column: + ColumnName: service + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.service.0.1688557193615 + Size: [] + SortConfiguration: + CategoryItemsLimit: + OtherCategories: INCLUDE + WordCloudOptions: {} + ColumnHierarchies: [] + Subtitle: + Visibility: VISIBLE + Title: + FormatText: + RichText: |- + + Top Service with high-cost anomaly impact + + Visibility: VISIBLE + VisualId: d21a3831-b55a-439d-b59a-7c674bdc4fbb + - WordCloudVisual: + Actions: [] + ChartConfiguration: + FieldWells: + WordCloudAggregatedFieldWells: + GroupBy: + - CategoricalDimensionField: + Column: + ColumnName: linkedaccountname + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.linkedaccountname.0.1686761251548 + Size: [] + SortConfiguration: + CategoryItemsLimit: + OtherCategories: INCLUDE + WordCloudOptions: {} + ColumnHierarchies: [] + Subtitle: + Visibility: VISIBLE + Title: + FormatText: + RichText: Top Account with high-cost anomaly impact + Visibility: VISIBLE + VisualId: 120d7e67-9cc0-4777-ae7f-5e1af2e2bb25 + - TableVisual: + Actions: [] + ChartConfiguration: + FieldOptions: + Order: [] + SelectedFieldOptions: + - CustomLabel: Service + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.service.2.1686744080024 + - CustomLabel: Account ID + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.linkedaccount.5.1686759391866 + - CustomLabel: Account Name + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.linkedaccountname.7.1686760943959 + - CustomLabel: Region + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.region.10.1688542840413 + - CustomLabel: Usage Type + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.usagetype.8.1686760952029 + - CustomLabel: Anomaly Start Date + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.anomaly_start_date.0.1686744069603 + - CustomLabel: Status + FieldId: 29d6de89-a42f-47b9-a077-d9c421e79def.9.1688530165960 + - CustomLabel: Total Actual Spend + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.totalactualspend.1.1686744072810 + - CustomLabel: Total Expected Spend + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.totalexpectedspend.6.1686759551680 + - CustomLabel: Total Impact + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.tota_impact.3.1686751650024 + Width: 91px + - CustomLabel: Total Impact % + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.totalimpactpercentage.6.1686760175338 + Width: 115px + FieldWells: + TableAggregatedFieldWells: + GroupBy: + - CategoricalDimensionField: + Column: + ColumnName: service + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.service.2.1686744080024 + - CategoricalDimensionField: + Column: + ColumnName: linkedaccount + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.linkedaccount.5.1686759391866 + - CategoricalDimensionField: + Column: + ColumnName: linkedaccountname + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.linkedaccountname.7.1686760943959 + - CategoricalDimensionField: + Column: + ColumnName: region + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.region.10.1688542840413 + - CategoricalDimensionField: + Column: + ColumnName: usagetype + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.usagetype.8.1686760952029 + - DateDimensionField: + Column: + ColumnName: anomaly_start_date + DataSetIdentifier: ca_summary_view + DateGranularity: MONTH + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.anomaly_start_date.0.1686744069603 + - CategoricalDimensionField: + Column: + ColumnName: status + DataSetIdentifier: ca_summary_view + FieldId: 29d6de89-a42f-47b9-a077-d9c421e79def.9.1688530165960 + Values: + - NumericalMeasureField: + AggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: totalactualspend + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.totalactualspend.1.1686744072810 + FormatConfiguration: + FormatConfiguration: + CurrencyDisplayFormatConfiguration: + DecimalPlacesConfiguration: + DecimalPlaces: 2 + NegativeValueConfiguration: + DisplayMode: POSITIVE + SeparatorConfiguration: + DecimalSeparator: DOT + ThousandsSeparator: + Symbol: COMMA + Visibility: VISIBLE + Symbol: USD + - NumericalMeasureField: + AggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: totalexpectedspend + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.totalexpectedspend.6.1686759551680 + FormatConfiguration: + FormatConfiguration: + CurrencyDisplayFormatConfiguration: + DecimalPlacesConfiguration: + DecimalPlaces: 2 + NegativeValueConfiguration: + DisplayMode: POSITIVE + SeparatorConfiguration: + DecimalSeparator: DOT + ThousandsSeparator: + Symbol: COMMA + Visibility: VISIBLE + Symbol: USD + - NumericalMeasureField: + AggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: tota_impact + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.tota_impact.3.1686751650024 + - NumericalMeasureField: + AggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: totalimpactpercentage + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.totalimpactpercentage.6.1686760175338 + FormatConfiguration: + FormatConfiguration: + CurrencyDisplayFormatConfiguration: + DecimalPlacesConfiguration: + DecimalPlaces: 0 + NegativeValueConfiguration: + DisplayMode: POSITIVE + NumberScale: NONE + SeparatorConfiguration: + DecimalSeparator: DOT + ThousandsSeparator: + Symbol: COMMA + Visibility: HIDDEN + Suffix: '%' + Symbol: USD + SortConfiguration: {} + TableOptions: + CellStyle: + Height: 25 + TextWrap: WRAP + HeaderStyle: + Height: 44 + TextWrap: WRAP + Visibility: VISIBLE + RowAlternateColorOptions: + Status: ENABLED + TotalOptions: + Placement: END + ScrollStatus: PINNED + TotalsVisibility: VISIBLE + Subtitle: + Visibility: VISIBLE + Title: + FormatText: + RichText: AWS Cost Anomalies Details + Visibility: VISIBLE + VisualId: 24488232-a887-4a68-b5d2-bc818d94a7d3 + - ComboChartVisual: + Actions: [] + ChartConfiguration: + BarDataLabels: + Overlap: DISABLE_OVERLAP + Visibility: HIDDEN + BarsArrangement: STACKED + FieldWells: + ComboChartAggregatedFieldWells: + BarValues: + - NumericalMeasureField: + AggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: tota_impact + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.tota_impact.2.1688556543198 + Category: + - DateDimensionField: + Column: + ColumnName: anomaly_start_date + DataSetIdentifier: ca_summary_view + DateGranularity: MONTH + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.anomaly_start_date.1.1686733460628 + HierarchyId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.anomaly_start_date.1.1686733460628 + Colors: + - CategoricalDimensionField: + Column: + ColumnName: service + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.service.2.1688539250062 + LineValues: [] + Legend: + Height: 64px + Position: AUTO + Title: + Visibility: HIDDEN + Visibility: VISIBLE + Width: 181px + PrimaryYAxisDisplayOptions: + GridLineVisibility: HIDDEN + TickLabelOptions: + LabelOptions: + Visibility: VISIBLE + SortConfiguration: + CategoryItemsLimit: + OtherCategories: INCLUDE + CategorySort: + - FieldSort: + Direction: ASC + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.anomaly_start_date.1.1686733460628 + ColorItemsLimit: + OtherCategories: INCLUDE + ColumnHierarchies: + - DateTimeHierarchy: + HierarchyId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.anomaly_start_date.1.1686733460628 + Subtitle: + FormatText: + RichText: Total Impact + Visibility: HIDDEN + Title: + FormatText: + RichText: AWS Cost Anomalies - Service (Total Cost Impact) + Visibility: VISIBLE + VisualId: 3d702097-ea7a-4a34-b4fb-435e3ae2005c + - FunnelChartVisual: + Actions: [] + ChartConfiguration: + CategoryLabelOptions: + AxisLabelOptions: + - ApplyTo: + Column: + ColumnName: status + DataSetIdentifier: ca_summary_view + FieldId: 29d6de89-a42f-47b9-a077-d9c421e79def.0.1688537174069 + CustomLabel: <<$GroupBy>> + DataLabelOptions: + CategoryLabelVisibility: VISIBLE + LabelFontConfiguration: + FontSize: + Relative: EXTRA_LARGE + MeasureLabelVisibility: VISIBLE + Position: INSIDE + Visibility: VISIBLE + FieldWells: + FunnelChartAggregatedFieldWells: + Category: + - CategoricalDimensionField: + Column: + ColumnName: status + DataSetIdentifier: ca_summary_view + FieldId: 29d6de89-a42f-47b9-a077-d9c421e79def.0.1688537174069 + Values: [] + SortConfiguration: + CategoryItemsLimit: + OtherCategories: INCLUDE + CategorySort: [] + Tooltip: + FieldBasedTooltip: + AggregationVisibility: HIDDEN + TooltipFields: + - FieldTooltipItem: + FieldId: 29d6de89-a42f-47b9-a077-d9c421e79def.0.1688537174069 + Visibility: VISIBLE + TooltipTitleType: PRIMARY_VALUE + SelectedTooltipType: BASIC + TooltipVisibility: VISIBLE + VisualPalette: + ColorMap: + - Color: '#2CAD00' + Element: + FieldId: 29d6de89-a42f-47b9-a077-d9c421e79def.0.1688537174069 + FieldValue: Closed + ColumnHierarchies: [] + Subtitle: + Visibility: HIDDEN + Title: + FormatText: + RichText: Anomalies Status by days input from left side control + Visibility: VISIBLE + VisualId: 643a37ff-5efa-46c2-ab0b-7a892205b5e4 + - WordCloudVisual: + Actions: [] + ChartConfiguration: + FieldWells: + WordCloudAggregatedFieldWells: + GroupBy: + - CategoricalDimensionField: + Column: + ColumnName: region + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.region.0.1686761488609 + Size: [] + SortConfiguration: + CategoryItemsLimit: + OtherCategories: INCLUDE + WordCloudOptions: {} + ColumnHierarchies: [] + Subtitle: + Visibility: VISIBLE + Title: + FormatText: + RichText: |- + + Top Region with high-cost anomaly impact + + Visibility: VISIBLE + VisualId: 92cc7652-7613-4d12-b595-b50c322a2181 + - ComboChartVisual: + Actions: [] + ChartConfiguration: + BarDataLabels: + Overlap: DISABLE_OVERLAP + Visibility: HIDDEN + BarsArrangement: STACKED + FieldWells: + ComboChartAggregatedFieldWells: + BarValues: + - NumericalMeasureField: + AggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: totalactualspend + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.totalactualspend.0.1688540208376 + FormatConfiguration: + FormatConfiguration: + CurrencyDisplayFormatConfiguration: + DecimalPlacesConfiguration: + DecimalPlaces: 2 + NegativeValueConfiguration: + DisplayMode: POSITIVE + SeparatorConfiguration: + DecimalSeparator: DOT + ThousandsSeparator: + Symbol: COMMA + Visibility: VISIBLE + Symbol: USD + - NumericalMeasureField: + AggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: totalexpectedspend + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.totalexpectedspend.2.1688540235516 + FormatConfiguration: + FormatConfiguration: + CurrencyDisplayFormatConfiguration: + DecimalPlacesConfiguration: + DecimalPlaces: 2 + NegativeValueConfiguration: + DisplayMode: POSITIVE + SeparatorConfiguration: + DecimalSeparator: DOT + ThousandsSeparator: + Symbol: COMMA + Visibility: VISIBLE + Symbol: USD + Category: + - CategoricalDimensionField: + Column: + ColumnName: service + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.service.1.1688540227659 + Colors: [] + LineValues: + - NumericalMeasureField: + AggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: tota_impact + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.tota_impact.3.1688540293625 + FormatConfiguration: + FormatConfiguration: + CurrencyDisplayFormatConfiguration: + DecimalPlacesConfiguration: + DecimalPlaces: 2 + NegativeValueConfiguration: + DisplayMode: POSITIVE + SeparatorConfiguration: + DecimalSeparator: DOT + ThousandsSeparator: + Symbol: COMMA + Visibility: VISIBLE + Symbol: USD + Legend: + Title: + CustomLabel: Total Spend (Actual Vs Expected) + Visibility: HIDDEN + Visibility: VISIBLE + Width: 137px + PrimaryYAxisDisplayOptions: + GridLineVisibility: HIDDEN + TickLabelOptions: + LabelOptions: + Visibility: VISIBLE + SortConfiguration: + CategoryItemsLimit: + OtherCategories: INCLUDE + CategorySort: + - FieldSort: + Direction: DESC + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.totalactualspend.0.1688540208376 + ColorItemsLimit: + OtherCategories: INCLUDE + ColumnHierarchies: [] + Subtitle: + Visibility: HIDDEN + Title: + FormatText: + RichText: Total 10 Services Spend (Actual Vs Expected) + Visibility: VISIBLE + VisualId: a98ae705-0b31-4a15-b1ba-eafdf37da041 + - KPIVisual: + Actions: [] + ChartConfiguration: + FieldWells: + TargetValues: [] + TrendGroups: + - DateDimensionField: + Column: + ColumnName: anomaly_start_date + DataSetIdentifier: ca_summary_view + DateGranularity: MONTH + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.anomaly_start_date.1.1688556821520 + HierarchyId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.anomaly_start_date.1.1688556821520 + Values: + - NumericalMeasureField: + AggregationFunction: + SimpleNumericalAggregation: SUM + Column: + ColumnName: tota_impact + DataSetIdentifier: ca_summary_view + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.tota_impact.1.1688556235551 + KPIOptions: + PrimaryValueDisplayType: COMPARISON + ProgressBar: + Visibility: HIDDEN + SecondaryValue: + Visibility: VISIBLE + SortConfiguration: + TrendGroupSort: + - FieldSort: + Direction: DESC + FieldId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.anomaly_start_date.1.1688556821520 + ColumnHierarchies: + - DateTimeHierarchy: + HierarchyId: 0e0f5f65-03fe-4bbd-b40d-34a68730292e.anomaly_start_date.1.1688556821520 + ConditionalFormatting: + ConditionalFormattingOptions: + - PrimaryValue: + Icon: + CustomCondition: + Color: '#DE3B00' + Expression: difference(SUM({tota_impact}),[SUM({tota_impact}) DESC],1,[]) + > 0 + IconOptions: + Icon: ARROW_UP + - PrimaryValue: + Icon: + CustomCondition: + Color: '#F6AA54' + Expression: difference(SUM({tota_impact}),[SUM({tota_impact}) DESC],1,[]) + = 0 + IconOptions: + Icon: ARROW_RIGHT + - PrimaryValue: + Icon: + CustomCondition: + Color: '#2CAD00' + Expression: difference(SUM({tota_impact}),[SUM({tota_impact}) DESC],1,[]) + < 0 + IconOptions: + Icon: ARROW_DOWN + Subtitle: + Visibility: HIDDEN + Title: + FormatText: + RichText: Trend of Total cost Impact from previous month + Visibility: VISIBLE + VisualId: 1ffd5f49-7d1b-448e-8f65-88650bb12191 + - ContentType: INTERACTIVE + Layouts: + - Configuration: + GridLayout: + CanvasSizeOptions: + ScreenCanvasSizeOptions: + OptimizedViewPortWidth: 1600px + ResizeOption: FIXED + Elements: + - ColumnIndex: 0 + ColumnSpan: 36 + ElementId: 384699fa-14d8-4399-af5d-4e4fa2b09d98 + ElementType: TEXT_BOX + RowIndex: 0 + RowSpan: 6 + - ColumnIndex: 0 + ColumnSpan: 36 + ElementId: d9a12df7-0c6e-4942-9403-b82db7d5f754 + ElementType: TEXT_BOX + RowIndex: 6 + RowSpan: 14 + Name: About + SheetId: aa07bedc-348c-4fe1-ba03-57a8e3dff04f + TextBoxes: + - Content: "\n
\n \n \n \n AWS Cost Anomaly Insights Dashboard\n \ + \ \n \n \n
\n \n\ + \ \n v1.0\n \n \n\ + \
\n \n
\n \n\ + \
\n \n
\n \n\ + \
\n \n
\n \n\ + \
\n \n
\n Built\ + \ by: Yash Bindlish, Yuriy Prykhodko, Iakov Gan\n
\n \n Learn more about AWS Cloud Intelligence Dashboards (CID)\ + \ on\n \n AWS Well-Architected Labs\n \n \_and\n\ + \ \n CID YouTube Channel\n \n
\n
\n\ + \ \n \n \n \n If you like Cloud Intelligence Dashboards\ + \ we kindly ask you to fill out this\_\n \n \n \n\ + \ \n customer survey\n \n \n
\n\ + \ \n \n \n \n Powered by: Amazon QuickSight, Amazon Athena,\ + \ Amazon S3\n \n \n \n \n
\n\ + \ \n \n \n \n If you wish to provide feedback or report\ + \ an error please email:\n \n \n \n \_\n \n cloud-intelligence-dashboards@amazon.com\n \n\ + \ \n
" + SheetTextBoxId: d9a12df7-0c6e-4942-9403-b82db7d5f754 + - Content: "\n \n Notices\n \n\ + \
\n
\n Customers are responsible for\ + \ making their own independent assessment of\_this dashboard and its content.\_\ + This dashboard and its content: (a) is for informational purposes only, (b)\ + \ represents current AWS product offerings and practices, which are subject\ + \ to change without notice, and (c) does not create any commitments or assurances\ + \ from AWS and its affiliates, suppliers or licensors. AWS\_content, products\ + \ or services are provided \u201Cas is\u201D without warranties, representations,\ + \ or conditions of any kind, whether express or implied. The responsibilities\ + \ and liabilities of AWS to its customers are controlled by AWS agreements,\ + \ and this document is not part of, nor does it modify, any agreement between\ + \ AWS and its customers.\n
" + SheetTextBoxId: 384699fa-14d8-4399-af5d-4e4fa2b09d98 + Visuals: [] + datasets: ca_summary_view: data: @@ -55,6 +1368,8 @@ datasets: Type: STRING - Name: linkedaccountname Type: STRING + - Name: payer_id + Type: STRING - Name: usagetype Type: STRING LogicalTableMap: @@ -84,6 +1399,7 @@ datasets: - service - linkedaccount - linkedaccountname + - payer_id - usagetype Source: PhysicalTableId: 3cec5d27-cc1d-4503-9b2e-c2bf0ce7b389 @@ -118,4 +1434,3 @@ views: FROM "optimization_data"."cost_anomaly_data" WHERE ("date_parse"("concat"("year", "month", "day"), '%Y%m%d') >= (current_timestamp - INTERVAL '1' MONTH)) - GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 \ No newline at end of file From dac5e03a1a842ae82ae13c604ca064fc7c336d96 Mon Sep 17 00:00:00 2001 From: Voicu Chirtes Date: Mon, 26 Feb 2024 12:56:05 -0800 Subject: [PATCH 04/11] Refresh datasets in status command --- cid/common.py | 60 +++++++++++++++++++++-------- cid/helpers/quicksight/__init__.py | 33 +++++++++++++++- cid/helpers/quicksight/dashboard.py | 14 +++++-- 3 files changed, 85 insertions(+), 22 deletions(-) diff --git a/cid/common.py b/cid/common.py index 6c3d82f7..87294b1d 100644 --- a/cid/common.py +++ b/cid/common.py @@ -631,24 +631,50 @@ def open(self, dashboard_id, **kwargs): @command def status(self, dashboard_id, **kwargs): """Check QuickSight dashboard status""" - - if not dashboard_id: - if not self.qs.dashboards: - print('No deployed dashboards found') - return - dashboard_id = self.qs.select_dashboard(force=True) + next_selection = None + while next_selection != 'exit': if not dashboard_id: - print('No dashboard selected') - return - dashboard = self.qs.discover_dashboard(dashboardId=dashboard_id) - else: - dashboard = self.qs.discover_dashboard(dashboardId=dashboard_id) - - if dashboard is not None: - dashboard.display_status() - dashboard.display_url(self.qs_url, **self.qs_url_params) - else: - click.echo('not deployed.') + if not self.qs.dashboards: + print('No deployed dashboards found') + return + dashboard_id = self.qs.select_dashboard(force=True) + if not dashboard_id: + print('No dashboard selected') + return + dashboard = self.qs.discover_dashboard(dashboardId=dashboard_id) + else: + dashboard = self.qs.discover_dashboard(dashboardId=dashboard_id) + + if dashboard is not None: + dashboard.display_status() + dashboard.display_url(self.qs_url, **self.qs_url_params) + next_selections = { + 'Refresh all datasets of this dashboard': 'refresh', + 'Update dashboard': 'update', + 'Re-deploy this dashboard': 'redeploy', + 'Go back to dashboard selection': 'goback', + 'Exit': 'exit' + } + next_selection = get_parameter( + param_name='next-selection', + message="Please make a selection", + choices=next_selections, + ) + if next_selection == 'refresh': + dashboard.refresh_datasets() + + if next_selection == 'update': + print(f'Updating dashboard: {dashboard}') + ## TODO dashboard update here + + if next_selection == 'redeploy': + print(f'Re-deploying dashboard: {dashboard_id}') + ## TODO dashboard deployment here + self.qs.clear_dashboard_selection() + unset_parameter('next_selection') + dashboard_id = None + else: + click.echo('not deployed.') @command def delete(self, dashboard_id, **kwargs): diff --git a/cid/helpers/quicksight/__init__.py b/cid/helpers/quicksight/__init__.py index 663779a2..44e1b77f 100644 --- a/cid/helpers/quicksight/__init__.py +++ b/cid/helpers/quicksight/__init__.py @@ -2,6 +2,7 @@ import json import uuid import time +import datetime import logging from string import Template from typing import Dict, List, Union @@ -15,7 +16,7 @@ from cid.helpers.quicksight.dataset import Dataset from cid.helpers.quicksight.datasource import Datasource from cid.helpers.quicksight.template import Template as CidQsTemplate -from cid.utils import get_parameter, get_parameters, exec_env, cid_print, ago +from cid.utils import get_parameter, get_parameters, exec_env, cid_print, ago, unset_parameter from cid.exceptions import CidCritical, CidError logger = logging.getLogger(__name__) @@ -604,6 +605,10 @@ def list_data_sources(self) -> list: logger.debug(exc, exc_info=True) return list() + def clear_dashboard_selection (self): + """ Clears the current dashboard selection. """ + unset_parameter('dashboard-id') + def select_dashboard(self, force=False) -> str: """ Select from a list of discovered dashboards """ dashboard_id = get_parameters().get('dashboard-id') @@ -914,7 +919,7 @@ def get_dataset_last_ingestion(self, dataset_id) -> str: except self.client.exceptions.AccessDeniedException: return 'AccessDenied' if not ingestions: - return None + return None #todo fix this using dataset import type. None could be if refresh never ran too, no ? last_ingestion = ingestions[0] # Suppose it is the latest status = last_ingestion.get('IngestionStatus') time_ago = ago(last_ingestion.get('CreatedTime')) @@ -950,6 +955,30 @@ def discover_datasets(self, _datasets: list=None): logger.debug(exc, exc_info=True) logger.info('No datasets found') + def refresh_dataset(self, dataset_id): + """ Refresh the dataset """ + + logger.info(f'Starting refresh for dataset: {dataset_id}') + status = 'FAILED' + try: + response = self.client.describe_data_set( + AwsAccountId=self.account_id, + DataSetId=dataset_id) + mode = response.get('DataSet').get('ImportMode') + if mode == 'DIRECT_QUERY': + return mode, 'DIRECT' + response = self.client.create_ingestion( + DataSetId=dataset_id, + IngestionId=datetime.datetime.now().strftime("%d%m%y-%H%M%S-%f"), + AwsAccountId=self.account_id) + status = response.get('IngestionStatus') + except self.client.exceptions.AccessDeniedException: + logger.info(f'Access denied refreshing dataset: {dataset_id}') + except Exception as exc: + logger.debug(exc, exc_info=True) + logger.info(f'Unable to list refresh dataset {dataset_id}: {str(exc)}') + raise CidError(f'Unable to list refresh dataset {dataset_id}: {str(exc)}') from exc + return mode, status def describe_data_source(self, id: str, update: bool=False) -> Datasource: """ Describes an AWS QuickSight DataSource """ diff --git a/cid/helpers/quicksight/dashboard.py b/cid/helpers/quicksight/dashboard.py index 5e6fd13f..643de11d 100644 --- a/cid/helpers/quicksight/dashboard.py +++ b/cid/helpers/quicksight/dashboard.py @@ -147,13 +147,21 @@ def display_status(self) -> None: if self.datasets: cid_print(f" Datasets:") for dataset_name, dataset_id in sorted(self.datasets.items()): - status = self.qs.get_dataset_last_ingestion(dataset_id) or 'DIRECT' + status = self.qs.get_dataset_last_ingestion(dataset_id) or 'DIRECT' #todo fix this Blue using dataset import type. cid_print(f' {dataset_name: <36} ({dataset_id: <36}) {status}') - print('\n') + """print('\n') if get_yesno_parameter('display-raw', 'Display dashboard raw data?', default='yes'): - print(json.dumps(self.raw, indent=4, sort_keys=True, default=str)) + print(json.dumps(self.raw, indent=4, sort_keys=True, default=str))""" def display_url(self, url_template: str, launch: bool = False, **kwargs) -> None: url = url_template.format(dashboard_id=self.id, **kwargs) print(f"#######\n####### {self.name} is available at: " + url + "\n#######") + + def refresh_datasets(self) -> None: + """Refresh datasets of dashboard""" + if self.datasets: + cid_print(f" Refreshing Datasets:") + for dataset_name, dataset_id in sorted(self.datasets.items()): + mode, status = self.qs.refresh_dataset(dataset_id) + cid_print(f' {dataset_name: <36} ({dataset_id: <36}) Refresh Status: {status} Mode: {mode}') From 3a8a7a0041454c38c82bf6cb15903e08dbdd2f5e Mon Sep 17 00:00:00 2001 From: Voicu Chirtes Date: Sun, 10 Mar 2024 22:32:28 -0700 Subject: [PATCH 05/11] Adding update functionality to status command --- cid/common.py | 63 +++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/cid/common.py b/cid/common.py index 87294b1d..279ab00b 100644 --- a/cid/common.py +++ b/cid/common.py @@ -23,7 +23,7 @@ from cid import utils from cid.base import CidBase from cid.plugin import Plugin -from cid.utils import get_parameter, get_parameters, set_parameters, unset_parameter, get_yesno_parameter, cid_print, isatty, merge_objects +from cid.utils import get_parameter, get_parameters, set_parameters, unset_parameter, get_yesno_parameter, cid_print, isatty, merge_objects, IsolatedParameters from cid.helpers.account_map import AccountMap from cid.helpers import Athena, CUR, Glue, QuickSight, Dashboard, Dataset, Datasource, csv2view, Organizations from cid.helpers.quicksight.template import Template as CidQsTemplate @@ -641,37 +641,46 @@ def status(self, dashboard_id, **kwargs): if not dashboard_id: print('No dashboard selected') return - dashboard = self.qs.discover_dashboard(dashboardId=dashboard_id) - else: - dashboard = self.qs.discover_dashboard(dashboardId=dashboard_id) + dashboard = self.qs.discover_dashboard(dashboardId=dashboard_id) if dashboard is not None: dashboard.display_status() dashboard.display_url(self.qs_url, **self.qs_url_params) - next_selections = { - 'Refresh all datasets of this dashboard': 'refresh', - 'Update dashboard': 'update', - 'Re-deploy this dashboard': 'redeploy', - 'Go back to dashboard selection': 'goback', - 'Exit': 'exit' - } - next_selection = get_parameter( - param_name='next-selection', - message="Please make a selection", - choices=next_selections, - ) - if next_selection == 'refresh': - dashboard.refresh_datasets() - - if next_selection == 'update': - print(f'Updating dashboard: {dashboard}') - ## TODO dashboard update here - - if next_selection == 'redeploy': - print(f'Re-deploying dashboard: {dashboard_id}') - ## TODO dashboard deployment here + with IsolatedParameters(): + next_selections = { + 'Refresh all datasets of this dashboard': 'refresh', + 'Update dashboard': 'update', + 'Go back to dashboard selection': 'goback', + 'Exit': 'exit' + } + next_selection = get_parameter( + param_name='next-selection', + message="Please make a selection", + choices=next_selections, + ) + if next_selection == 'refresh': + dashboard.refresh_datasets() + + if next_selection == 'update': + if dashboard.latest: + if not get_yesno_parameter( + param_name=f'redeploy-{dashboard.id}', + message=f'\nThe selected dashboard {dashboard.id} is already on the latest version.\nDo you want to re-deploy it?', + default='no'): + logger.info(f'Not re-deploying {dashboard.id} as it is on latest version.\n') + continue + recursive = False + if get_yesno_parameter( + param_name='recursive', + message=f'\nRecursive update the Datasets and Views in addition to the Dashboard update?\nATTENTION: This could lead to the loss of dataset customization.\nRecursive update?', + default='no'): + logger.info("Recursive update selected") + recursive = True + logger.info(f'Updating dashboard: {dashboard.id} wiht Recursive = {recursive}') + self._deploy(dashboard_id, recursive=recursive, update=True) + logger.info('Rediscover dashboards after update') + self.qs.discover_dashboards() self.qs.clear_dashboard_selection() - unset_parameter('next_selection') dashboard_id = None else: click.echo('not deployed.') From b14bd3776c4d74060c35b033eeda1ed26b456dc6 Mon Sep 17 00:00:00 2001 From: Voicu Chirtes Date: Mon, 11 Mar 2024 22:22:34 -0700 Subject: [PATCH 06/11] update status selections --- cid/common.py | 16 ++++++++++------ cid/helpers/quicksight/__init__.py | 5 ++--- cid/helpers/quicksight/dashboard.py | 4 ---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/cid/common.py b/cid/common.py index 279ab00b..27a33130 100644 --- a/cid/common.py +++ b/cid/common.py @@ -648,16 +648,20 @@ def status(self, dashboard_id, **kwargs): dashboard.display_url(self.qs_url, **self.qs_url_params) with IsolatedParameters(): next_selections = { - 'Refresh all datasets of this dashboard': 'refresh', - 'Update dashboard': 'update', - 'Go back to dashboard selection': 'goback', - 'Exit': 'exit' + '[◀] Back': 'back', + '[↗] Open': 'open', + '[◴] Refresh datasets': 'refresh', + '[↺] Update dashboard': 'update', + '[✕] Exit': 'exit', } next_selection = get_parameter( - param_name='next-selection', + param_name=f'{dashboard.id}', message="Please make a selection", - choices=next_selections, + choices=next_selections ) + if next_selection == 'open': + self.open(dashboard.id, **kwargs) + if next_selection == 'refresh': dashboard.refresh_datasets() diff --git a/cid/helpers/quicksight/__init__.py b/cid/helpers/quicksight/__init__.py index 44e1b77f..fcc82bc6 100644 --- a/cid/helpers/quicksight/__init__.py +++ b/cid/helpers/quicksight/__init__.py @@ -919,7 +919,7 @@ def get_dataset_last_ingestion(self, dataset_id) -> str: except self.client.exceptions.AccessDeniedException: return 'AccessDenied' if not ingestions: - return None #todo fix this using dataset import type. None could be if refresh never ran too, no ? + return None last_ingestion = ingestions[0] # Suppose it is the latest status = last_ingestion.get('IngestionStatus') time_ago = ago(last_ingestion.get('CreatedTime')) @@ -973,10 +973,9 @@ def refresh_dataset(self, dataset_id): AwsAccountId=self.account_id) status = response.get('IngestionStatus') except self.client.exceptions.AccessDeniedException: - logger.info(f'Access denied refreshing dataset: {dataset_id}') + logger.error(f'Access denied refreshing dataset: {dataset_id}') except Exception as exc: logger.debug(exc, exc_info=True) - logger.info(f'Unable to list refresh dataset {dataset_id}: {str(exc)}') raise CidError(f'Unable to list refresh dataset {dataset_id}: {str(exc)}') from exc return mode, status diff --git a/cid/helpers/quicksight/dashboard.py b/cid/helpers/quicksight/dashboard.py index 643de11d..a2c4e270 100644 --- a/cid/helpers/quicksight/dashboard.py +++ b/cid/helpers/quicksight/dashboard.py @@ -150,10 +150,6 @@ def display_status(self) -> None: status = self.qs.get_dataset_last_ingestion(dataset_id) or 'DIRECT' #todo fix this Blue using dataset import type. cid_print(f' {dataset_name: <36} ({dataset_id: <36}) {status}') - """print('\n') - if get_yesno_parameter('display-raw', 'Display dashboard raw data?', default='yes'): - print(json.dumps(self.raw, indent=4, sort_keys=True, default=str))""" - def display_url(self, url_template: str, launch: bool = False, **kwargs) -> None: url = url_template.format(dashboard_id=self.id, **kwargs) print(f"#######\n####### {self.name} is available at: " + url + "\n#######") From 14dac2ef0fcc69fd8a0efba8ec67bb8471fbbbbd Mon Sep 17 00:00:00 2001 From: Iakov Gan Date: Tue, 12 Mar 2024 08:13:01 +0100 Subject: [PATCH 07/11] some enhancements --- cid/common.py | 39 +++++++++++++++-------------- cid/helpers/quicksight/__init__.py | 5 ++-- cid/helpers/quicksight/dashboard.py | 2 +- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/cid/common.py b/cid/common.py index 27a33130..2c0d4c4e 100644 --- a/cid/common.py +++ b/cid/common.py @@ -647,25 +647,24 @@ def status(self, dashboard_id, **kwargs): dashboard.display_status() dashboard.display_url(self.qs_url, **self.qs_url_params) with IsolatedParameters(): - next_selections = { - '[◀] Back': 'back', - '[↗] Open': 'open', - '[◴] Refresh datasets': 'refresh', - '[↺] Update dashboard': 'update', - '[✕] Exit': 'exit', - } next_selection = get_parameter( param_name=f'{dashboard.id}', message="Please make a selection", - choices=next_selections + choices={ + '[◀] Back': 'back', + '[↗] Open': 'open', + '[◴] Refresh datasets': 'refresh', + '[↺] Update dashboard': 'update', + '[✕] Exit': 'exit', + } ) if next_selection == 'open': self.open(dashboard.id, **kwargs) - if next_selection == 'refresh': + elif next_selection == 'refresh': dashboard.refresh_datasets() - if next_selection == 'update': + elif next_selection == 'update': if dashboard.latest: if not get_yesno_parameter( param_name=f'redeploy-{dashboard.id}', @@ -673,21 +672,23 @@ def status(self, dashboard_id, **kwargs): default='no'): logger.info(f'Not re-deploying {dashboard.id} as it is on latest version.\n') continue - recursive = False - if get_yesno_parameter( - param_name='recursive', - message=f'\nRecursive update the Datasets and Views in addition to the Dashboard update?\nATTENTION: This could lead to the loss of dataset customization.\nRecursive update?', - default='no'): - logger.info("Recursive update selected") - recursive = True - logger.info(f'Updating dashboard: {dashboard.id} wiht Recursive = {recursive}') + recursive = get_parameter( + param_name='recursive', + message=f'\nRecursive update the Datasets and Views in addition to the Dashboard update?\nATTENTION: This could lead to the loss of dataset customization.\nRecursive update?', + choices={ + '[→] Simple Update (only dashboard)': 'simple', + '[⇶] Recursive Update (dashboard and all dependencies)': 'recursive', + }, + default='simple' + ) == 'recursive' + logger.info(f'Updating dashboard: {dashboard.id} with Recursive = {recursive}') self._deploy(dashboard_id, recursive=recursive, update=True) logger.info('Rediscover dashboards after update') self.qs.discover_dashboards() self.qs.clear_dashboard_selection() dashboard_id = None else: - click.echo('not deployed.') + cid_print('not deployed.') @command def delete(self, dashboard_id, **kwargs): diff --git a/cid/helpers/quicksight/__init__.py b/cid/helpers/quicksight/__init__.py index fcc82bc6..1a4ab36b 100644 --- a/cid/helpers/quicksight/__init__.py +++ b/cid/helpers/quicksight/__init__.py @@ -619,8 +619,9 @@ def select_dashboard(self, force=False) -> str: return None choices = {} for dashboard in self.dashboards.values(): - health = 'healthy' if dashboard.health else 'unhealthy' - key = f'{dashboard.name} ({dashboard.arn}, {health}, {dashboard.status})' + health = '' if dashboard.health else ' UNHEALTHY' + status = '' if dashboard.status == 'up to date' else ' ' + dashboard.status.upper() + key = f'{dashboard.name} ({dashboard.arn.split("/")[-1]}){health}{status}' notice = dashboard.definition.get('deprecationNotice', '') if notice: key = f'{key} {notice}' diff --git a/cid/helpers/quicksight/dashboard.py b/cid/helpers/quicksight/dashboard.py index a2c4e270..74787c19 100644 --- a/cid/helpers/quicksight/dashboard.py +++ b/cid/helpers/quicksight/dashboard.py @@ -81,7 +81,7 @@ def status(self) -> str: if self.version.get('Status') not in ['CREATION_SUCCESSFUL']: self._status = 'broken' self.status_detail = f"{self.version.get('Status')}: {self.version.get('Errors')}" - # Not dicovered yet + # Not discovered yet elif not self.definition: self._status = 'undiscovered' # Missing dataset From 28037a2d76fbf9ea52d3ebd05245170d9d9afd60 Mon Sep 17 00:00:00 2001 From: Iakov Gan Date: Thu, 14 Mar 2024 19:01:13 +0100 Subject: [PATCH 08/11] enable colors for windows --- cid/cli.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cid/cli.py b/cid/cli.py index 4b996cca..b996249d 100644 --- a/cid/cli.py +++ b/cid/cli.py @@ -1,4 +1,6 @@ +import os import logging +import platform import click @@ -14,7 +16,6 @@ print(f'{prog_name} {version}\n') if __version__ != latest_version and latest_version != 'UNDEFINED': - print('\033[93mUPDATE AVAILABLE\033[0m') print(f'\033[93mA new version {latest_version} is available, please consider update cid-cmd package via pip\033[0m\n\n') logger.info(f'A new version {latest_version} is available, please consider update cid-cmd package via pip') @@ -67,6 +68,11 @@ def wrapper(ctx, **kwargs): @click.option('-y', '--yes', help='confirm all', is_flag=True, default=False) @click.pass_context def main(ctx, **kwargs): + + # enable color for windows terminal + if platform.system() == "Windows": + os.system('color') + ctx.obj = Cid(**kwargs) From 91a2b75bd13a353404f33faeaa80453e9cf48cd3 Mon Sep 17 00:00:00 2001 From: Iakov Gan Date: Thu, 14 Mar 2024 19:06:00 +0100 Subject: [PATCH 09/11] enable colors for windows --- cid/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cid/cli.py b/cid/cli.py index b996249d..399f16cc 100644 --- a/cid/cli.py +++ b/cid/cli.py @@ -70,7 +70,7 @@ def wrapper(ctx, **kwargs): def main(ctx, **kwargs): # enable color for windows terminal - if platform.system() == "Windows": + if platform.system() == "Windows": #nosec B605,B607 os.system('color') ctx.obj = Cid(**kwargs) From f5d155baa1583c4b9d4839e31ca12b59f91cd997 Mon Sep 17 00:00:00 2001 From: Iakov Gan Date: Thu, 14 Mar 2024 21:05:27 +0100 Subject: [PATCH 10/11] enable colors for windows --- cid/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cid/cli.py b/cid/cli.py index 399f16cc..b7220552 100644 --- a/cid/cli.py +++ b/cid/cli.py @@ -70,8 +70,8 @@ def wrapper(ctx, **kwargs): def main(ctx, **kwargs): # enable color for windows terminal - if platform.system() == "Windows": #nosec B605,B607 - os.system('color') + if platform.system() == "Windows": + os.system('color') #nosec B605,B607 ctx.obj = Cid(**kwargs) From 10ea3184019193cbe30505f6f7f6b0d059ae559c Mon Sep 17 00:00:00 2001 From: Iakov Gan Date: Thu, 14 Mar 2024 21:18:53 +0100 Subject: [PATCH 11/11] enable colors for windows --- cid/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cid/cli.py b/cid/cli.py index b7220552..ab773118 100644 --- a/cid/cli.py +++ b/cid/cli.py @@ -71,7 +71,7 @@ def main(ctx, **kwargs): # enable color for windows terminal if platform.system() == "Windows": - os.system('color') #nosec B605,B607 + os.system('color') #nosec B605, B607 ctx.obj = Cid(**kwargs)