diff --git a/arcee/arcee_receiver/requirements.txt b/arcee/arcee_receiver/requirements.txt
index bd5a00998..090ba3f86 100644
--- a/arcee/arcee_receiver/requirements.txt
+++ b/arcee/arcee_receiver/requirements.txt
@@ -1,7 +1,8 @@
aiohttp==3.10.2
sanic==23.12.1
sanic-ext==23.12.0
-motor==3.4.0
+motor==3.6.0
+pymongo==4.9.1
python-etcd==0.4.5
mongodb-migrations==1.2.1
pydantic==2.4.2
diff --git a/bulldozer/bulldozer_api/requirements.txt b/bulldozer/bulldozer_api/requirements.txt
index 5aed6609f..1feeacc9e 100644
--- a/bulldozer/bulldozer_api/requirements.txt
+++ b/bulldozer/bulldozer_api/requirements.txt
@@ -1,6 +1,7 @@
aiohttp==3.10.2
sanic==23.12.1
-motor==3.4.0
+motor==3.6.0
+pymongo==4.9.1
websockets==12.0
kombu==5.3.4
mongodb-migrations==1.2.1
diff --git a/diworker/diworker/importers/aws.py b/diworker/diworker/importers/aws.py
index 08fbf33db..6f6ec42e9 100644
--- a/diworker/diworker/importers/aws.py
+++ b/diworker/diworker/importers/aws.py
@@ -47,6 +47,7 @@
AWS_CUR_PREFIX_MAP = {
'identity': (False, []),
'bill': (False, []),
+ 'discount': (False, []),
'line_item': (False, []),
'product': (True, ['product_name', 'purchase_option', 'size_flex']),
'pricing': (True, ['rate_code', 'rate_id', 'purchase_option',
diff --git a/diworker/diworker/migrations/202409171620000_fix_discount_fields_names.py b/diworker/diworker/migrations/202409171620000_fix_discount_fields_names.py
new file mode 100644
index 000000000..a4054516b
--- /dev/null
+++ b/diworker/diworker/migrations/202409171620000_fix_discount_fields_names.py
@@ -0,0 +1,48 @@
+import logging
+from optscale_client.rest_api_client.client_v2 import Client as RestClient
+from diworker.diworker.migrations.base import BaseMigration
+"""
+Fixed names for discount fields in AWS expenses
+"""
+LOG = logging.getLogger(__name__)
+
+
+class Migration(BaseMigration):
+ @property
+ def rest_cl(self):
+ if self._rest_cl is None:
+ self._rest_cl = RestClient(
+ url=self.config_cl.restapi_url(),
+ secret=self.config_cl.cluster_secret())
+ return self._rest_cl
+
+ @property
+ def mongo_raw(self):
+ return self.db.raw_expenses
+
+ def get_cloud_accs(self):
+ cloud_accounts_ids = set()
+ _, organizations = self.rest_cl.organization_list({
+ 'with_connected_accounts': True, 'is_demo': False})
+ for org in organizations['organizations']:
+ _, accounts = self.rest_cl.cloud_account_list(
+ org['id'], type='aws_cnr')
+ for cloud_account in accounts['cloud_accounts']:
+ if cloud_account['auto_import']:
+ cloud_accounts_ids.add(cloud_account['id'])
+ return cloud_accounts_ids
+
+ def upgrade(self):
+ cloud_accs = self.get_cloud_accs()
+ for i, cloud_acc_id in enumerate(cloud_accs):
+ LOG.info('Starting processing for cloud account %s (%s/%s)' % (
+ cloud_acc_id, i+1, len(cloud_accs)))
+ self.db.raw_expenses.update_many(
+ {'bill/BillingPeriodStartDate': {'$exists': True},
+ 'cloud_account_id': cloud_acc_id},
+ {'$rename': {
+ 'discount_total_discount': 'discount/TotalDiscount',
+ 'discount_bundled_discount': 'discount/BundledDiscount'}})
+
+ def downgrade(self):
+ pass
diff --git a/ngui/ui/src/components/TagKey/TagKey.test.tsx b/ngui/ui/src/components/TagKey/TagKey.test.tsx
deleted file mode 100644
index 63f644b6c..000000000
--- a/ngui/ui/src/components/TagKey/TagKey.test.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { createRoot } from "react-dom/client";
-import TestProvider from "tests/TestProvider";
-import TagKey from "./TagKey";
-
-it("renders without crashing", () => {
- const div = document.createElement("div");
- const root = createRoot(div);
- root.render(
-
-
-
- );
- root.unmount();
-});
diff --git a/ngui/ui/src/components/TagKey/TagKey.tsx b/ngui/ui/src/components/TagKey/TagKey.tsx
deleted file mode 100644
index d17057a01..000000000
--- a/ngui/ui/src/components/TagKey/TagKey.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { FormattedMessage } from "react-intl";
-
-const TagKey = ({ tagKey }) => (tagKey === null ? : tagKey);
-
-export default TagKey;
diff --git a/ngui/ui/src/components/TagKey/index.ts b/ngui/ui/src/components/TagKey/index.ts
deleted file mode 100644
index d625648ce..000000000
--- a/ngui/ui/src/components/TagKey/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import TagKey from "./TagKey";
-
-export default TagKey;
diff --git a/ngui/ui/src/components/TagsBreakdown/TagsBreakdownTable/TagsBreakdownTable.tsx b/ngui/ui/src/components/TagsBreakdown/TagsBreakdownTable/TagsBreakdownTable.tsx
index 75d967b12..70b3c0884 100644
--- a/ngui/ui/src/components/TagsBreakdown/TagsBreakdownTable/TagsBreakdownTable.tsx
+++ b/ngui/ui/src/components/TagsBreakdown/TagsBreakdownTable/TagsBreakdownTable.tsx
@@ -1,24 +1,18 @@
import { useMemo } from "react";
import BarChartOutlinedIcon from "@mui/icons-material/BarChartOutlined";
-import { FormattedMessage } from "react-intl";
+import { FormattedMessage, useIntl } from "react-intl";
import FormattedMoney from "components/FormattedMoney";
import Table from "components/Table";
import TableCellActions from "components/TableCellActions";
import TableLoader from "components/TableLoader";
-import TagKey from "components/TagKey";
import TextWithDataTestId from "components/TextWithDataTestId";
import TextWithDate from "components/TextWithDate";
import { FORMATTED_MONEY_TYPES } from "utils/constants";
-const getTotalBreakdownTableData = (counts) =>
- Object.entries(counts).map(([id, { name = "", ...details }]) => ({
- id: id ?? name,
- name,
- ...details
- }));
-
const TagsBreakdownTable = ({ data, appliedRange, isLoading, selectedTag, onShowOnChartClick }) => {
- const tableData = useMemo(() => getTotalBreakdownTableData(data), [data]);
+ const tableData = useMemo(() => data, [data]);
+
+ const intl = useIntl();
const columns = useMemo(
() => [
@@ -28,8 +22,12 @@ const TagsBreakdownTable = ({ data, appliedRange, isLoading, selectedTag, onShow
),
- accessorKey: "tag",
- cell: ({ cell }) =>
+ id: "tagKey",
+ accessorFn: ({ tag }) =>
+ tag ??
+ intl.formatMessage({
+ id: "(untagged)"
+ })
},
{
header: (
@@ -81,7 +79,7 @@ const TagsBreakdownTable = ({ data, appliedRange, isLoading, selectedTag, onShow
)
}
],
- [appliedRange.startSecondsTimestamp, appliedRange.endSecondsTimestamp, onShowOnChartClick, selectedTag]
+ [appliedRange.startSecondsTimestamp, appliedRange.endSecondsTimestamp, intl, selectedTag, onShowOnChartClick]
);
return isLoading ? (
diff --git a/rest_api/live_demo.tar.xz b/rest_api/live_demo.tar.xz
index 5480e7bfc..8e681ca41 100644
Binary files a/rest_api/live_demo.tar.xz and b/rest_api/live_demo.tar.xz differ
diff --git a/rest_api/rest_api_server/controllers/live_demo.py b/rest_api/rest_api_server/controllers/live_demo.py
index 98a2882ea..182de8cc1 100644
--- a/rest_api/rest_api_server/controllers/live_demo.py
+++ b/rest_api/rest_api_server/controllers/live_demo.py
@@ -50,8 +50,8 @@
TOP_NO_DUPLICATE_RESOURCES = 10
DUPLICATION_FORMAT = '-x{ending}'
WITH_SUBPOOLS_SIGN = '+'
-MIN_RUN_DURATION = 7 * 60 * 60
-MAX_RUN_DURATION = 24 * 60 * 60
+MIN_RUN_DURATION = 14 * 60 * 60
+MAX_RUN_DURATION = 48 * 60 * 60
RECOMMENDATION_MULTIPLIED_FIELDS = ['saving', 'annually_monthly_saving',
'monthly_saving']
PREPARED_DEMO_LIFETIME_DAYS = 3