Skip to content

Commit 53a31cb

Browse files
improbemtns
1 parent e18149e commit 53a31cb

File tree

9 files changed

+102
-30
lines changed

9 files changed

+102
-30
lines changed

packages/models-library/src/models_library/resource_tracker.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,27 @@ class CreditTransactionStatus(StrAutoEnum):
6161

6262

6363
class CreditClassification(StrAutoEnum):
64-
ADD_WALLET_TOP_UP = auto() # user top up credits
65-
DEDUCT_SERVICE_RUN = auto() # computational/dynamic service run costs)
64+
# Represents the different types of credit classifications.
65+
66+
ADD_WALLET_TOP_UP = auto()
67+
# Indicates that credits have been added to the user's wallet through a top-up.
68+
# Example: The user adds funds to their wallet to increase their available credits.
69+
70+
DEDUCT_SERVICE_RUN = auto()
71+
# Represents a deduction from the user's wallet due to the costs of running a computational or dynamic service.
72+
# Example: Credits are deducted when the user runs a simulation.
73+
6674
DEDUCT_LICENSE_PURCHASE = auto()
75+
# Represents a deduction from the user's wallet for purchasing a license.
76+
# Example: The user purchases a license to access premium features such as VIP models.
77+
6778
ADD_WALLET_EXCHANGE = auto()
79+
# Represents the addition of credits to the user's wallet through an exchange.
80+
# Example: Credits are added due to credit exchange between wallets.
81+
6882
DEDUCT_WALLET_EXCHANGE = auto()
83+
# Represents a deduction of credits from the user's wallet through an exchange.
84+
# Example: Credits are deducted due to credit exchange between wallets.
6985

7086

7187
class PricingPlanClassification(StrAutoEnum):

packages/postgres-database/src/simcore_postgres_database/models/resource_tracker_credit_transactions.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
class CreditTransactionStatus(str, enum.Enum):
1919
PENDING = "PENDING"
2020
BILLED = "BILLED"
21+
IN_DEBT = "IN_DEBT"
2122
NOT_BILLED = "NOT_BILLED"
2223
REQUIRES_MANUAL_REVIEW = "REQUIRES_MANUAL_REVIEW"
2324

@@ -28,6 +29,8 @@ class CreditTransactionClassification(str, enum.Enum):
2829
"DEDUCT_SERVICE_RUN" # computational/dynamic service run costs)
2930
)
3031
DEDUCT_LICENSE_PURCHASE = "DEDUCT_LICENSE_PURCHASE"
32+
ADD_WALLET_EXCHANGE = "ADD_WALLET_EXCHANGE"
33+
DEDUCT_WALLET_EXCHANGE = "DEDUCT_WALLET_EXCHANGE"
3134

3235

3336
resource_tracker_credit_transactions = sa.Table(

packages/service-library/src/servicelib/rabbitmq/rpc_interfaces/resource_usage_tracker/service_runs.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
ServiceRunPage,
1010
)
1111
from models_library.products import ProductName
12+
from models_library.projects import ProjectID
1213
from models_library.rabbitmq_basic_types import RPCMethodName
1314
from models_library.resource_tracker import (
15+
CreditTransactionStatus,
1416
ServiceResourceUsagesFilters,
1517
ServicesAggregatedUsagesTimePeriod,
1618
ServicesAggregatedUsagesType,
@@ -37,24 +39,30 @@ async def get_service_run_page(
3739
*,
3840
user_id: UserID,
3941
product_name: ProductName,
40-
limit: int = 20,
41-
offset: int = 0,
4242
wallet_id: WalletID | None = None,
4343
access_all_wallet_usage: bool = False,
44-
order_by: OrderBy | None = None,
4544
filters: ServiceResourceUsagesFilters | None = None,
45+
transaction_status: CreditTransactionStatus | None = None,
46+
project_id: ProjectID | None = None,
47+
# pagination
48+
offset: int = 0,
49+
limit: int = 20,
50+
# ordering
51+
order_by: OrderBy | None = None,
4652
) -> ServiceRunPage:
4753
result = await rabbitmq_rpc_client.request(
4854
RESOURCE_USAGE_TRACKER_RPC_NAMESPACE,
4955
_RPC_METHOD_NAME_ADAPTER.validate_python("get_service_run_page"),
5056
user_id=user_id,
5157
product_name=product_name,
52-
limit=limit,
53-
offset=offset,
5458
wallet_id=wallet_id,
5559
access_all_wallet_usage=access_all_wallet_usage,
56-
order_by=order_by,
5760
filters=filters,
61+
transaction_status=transaction_status,
62+
project_id=project_id,
63+
offset=offset,
64+
limit=limit,
65+
order_by=order_by,
5866
timeout_s=_DEFAULT_TIMEOUT_S,
5967
)
6068
assert isinstance(result, ServiceRunPage) # nosec

services/resource-usage-tracker/src/simcore_service_resource_usage_tracker/api/rpc/_service_runs.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
ServiceRunPage,
55
)
66
from models_library.products import ProductName
7+
from models_library.projects import ProjectID
78
from models_library.resource_tracker import (
9+
CreditTransactionStatus,
810
ServiceResourceUsagesFilters,
911
ServicesAggregatedUsagesTimePeriod,
1012
ServicesAggregatedUsagesType,
@@ -34,9 +36,11 @@ async def get_service_run_page(
3436
wallet_id: WalletID | None = None,
3537
access_all_wallet_usage: bool = False,
3638
filters: ServiceResourceUsagesFilters | None = None,
39+
transaction_status: CreditTransactionStatus | None = None,
40+
project_id: ProjectID | None = None,
3741
# pagination
38-
limit: int = 20,
3942
offset: int = 0,
43+
limit: int = 20,
4044
# ordering
4145
order_by: OrderBy | None = None,
4246
) -> ServiceRunPage:
@@ -47,8 +51,10 @@ async def get_service_run_page(
4751
wallet_id=wallet_id,
4852
access_all_wallet_usage=access_all_wallet_usage,
4953
filters=filters,
50-
limit=limit,
54+
transaction_status=transaction_status,
55+
project_id=project_id,
5156
offset=offset,
57+
limit=limit,
5258
order_by=order_by,
5359
)
5460

services/resource-usage-tracker/src/simcore_service_resource_usage_tracker/services/credit_transactions.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,14 @@ async def pay_project_debt(
116116
total_project_debt_amount.available_osparc_credits
117117
!= new_wallet_transaction.osparc_credits
118118
):
119-
raise ValueError("wrong")
119+
msg = f"Project DEBT of {total_project_debt_amount.available_osparc_credits} does not equal to payment: new_wallet {new_wallet_transaction.osparc_credits}, current wallet {current_wallet_transaction.osparc_credits}"
120+
raise ValueError(msg)
120121
if (
121122
make_negative(total_project_debt_amount.available_osparc_credits)
122123
!= current_wallet_transaction.osparc_credits
123124
):
124-
raise ValueError("wrong")
125+
msg = f"Project DEBT of {total_project_debt_amount.available_osparc_credits} does not equal to payment: new_wallet {new_wallet_transaction.osparc_credits}, current wallet {current_wallet_transaction.osparc_credits}"
126+
raise ValueError(msg)
125127

126128
new_wallet_transaction_create = CreditTransactionCreate(
127129
product_name=new_wallet_transaction.product_name,

services/resource-usage-tracker/src/simcore_service_resource_usage_tracker/services/modules/db/service_runs_db.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import sqlalchemy as sa
77
from models_library.api_schemas_storage import S3BucketName
88
from models_library.products import ProductName
9+
from models_library.projects import ProjectID
910
from models_library.resource_tracker import (
1011
CreditClassification,
1112
CreditTransactionStatus,
@@ -197,6 +198,8 @@ async def list_service_runs_by_product_and_user_and_wallet(
197198
service_run_status: ServiceRunStatus | None = None,
198199
started_from: datetime | None = None,
199200
started_until: datetime | None = None,
201+
transaction_status: CreditTransactionStatus | None = None,
202+
project_id: ProjectID | None = None,
200203
# pagination
201204
offset: int,
202205
limit: int,
@@ -287,6 +290,15 @@ async def list_service_runs_by_product_and_user_and_wallet(
287290
sa.func.DATE(resource_tracker_service_runs.c.started_at)
288291
<= started_until.date()
289292
)
293+
if project_id:
294+
base_query = base_query.where(
295+
resource_tracker_service_runs.c.project_id == project_id
296+
)
297+
if transaction_status:
298+
base_query = base_query.where(
299+
resource_tracker_credit_transactions.c.transaction_status
300+
== transaction_status
301+
)
290302

291303
# Select total count from base_query
292304
subquery = base_query.subquery()

services/resource-usage-tracker/src/simcore_service_resource_usage_tracker/services/service_runs.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
)
1111
from models_library.api_schemas_storage import S3BucketName
1212
from models_library.products import ProductName
13+
from models_library.projects import ProjectID
1314
from models_library.resource_tracker import (
15+
CreditTransactionStatus,
1416
ServiceResourceUsagesFilters,
1517
ServicesAggregatedUsagesTimePeriod,
1618
ServicesAggregatedUsagesType,
@@ -34,8 +36,10 @@ async def list_service_runs(
3436
wallet_id: WalletID | None = None,
3537
access_all_wallet_usage: bool = False,
3638
filters: ServiceResourceUsagesFilters | None = None,
37-
limit: int = 20,
39+
transaction_status: CreditTransactionStatus | None = None,
40+
project_id: ProjectID | None = None,
3841
offset: int = 0,
42+
limit: int = 20,
3943
order_by: OrderBy | None = None,
4044
) -> ServiceRunPage:
4145
started_from = None
@@ -56,6 +60,8 @@ async def list_service_runs(
5660
wallet_id=None,
5761
started_from=started_from,
5862
started_until=started_until,
63+
transaction_status=transaction_status,
64+
project_id=project_id,
5965
offset=offset,
6066
limit=limit,
6167
order_by=order_by,
@@ -72,6 +78,8 @@ async def list_service_runs(
7278
wallet_id=wallet_id,
7379
started_from=started_from,
7480
started_until=started_until,
81+
transaction_status=transaction_status,
82+
project_id=project_id,
7583
offset=offset,
7684
limit=limit,
7785
order_by=order_by,
@@ -88,6 +96,8 @@ async def list_service_runs(
8896
wallet_id=wallet_id,
8997
started_from=started_from,
9098
started_until=started_until,
99+
transaction_status=transaction_status,
100+
project_id=project_id,
91101
offset=offset,
92102
limit=limit,
93103
order_by=order_by,

services/web/server/src/simcore_service_webserver/projects/_wallets_api.py

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from models_library.wallets import WalletDB, WalletID
1313
from servicelib.rabbitmq.rpc_interfaces.resource_usage_tracker import (
1414
credit_transactions,
15+
service_runs,
1516
)
1617

1718
from ..rabbitmq import get_rabbitmq_rpc_client
@@ -40,32 +41,47 @@ async def connect_wallet_to_project(
4041
) -> WalletGet:
4142
db: ProjectDBAPI = ProjectDBAPI.get_from_app_context(app)
4243

43-
project_wallet = await db.get_project_wallet(project_uuid=project_id)
44+
# ensure the wallet can be used by the user
45+
wallet: WalletGet = await wallets_api.get_wallet_by_user(
46+
app,
47+
user_id=user_id,
48+
wallet_id=wallet_id,
49+
product_name=product_name,
50+
)
51+
52+
current_project_wallet = await db.get_project_wallet(project_uuid=project_id)
53+
rpc_client = get_rabbitmq_rpc_client(app)
4454

45-
if project_wallet:
46-
# NOTE: Do not allow to change wallet if the project is in DEBT!
47-
rpc_client = get_rabbitmq_rpc_client(app)
55+
if current_project_wallet:
56+
# Do not allow to change wallet if the project connected wallet is in DEBT!
4857
project_wallet_credits = await credit_transactions.get_wallet_total_credits(
4958
rpc_client,
5059
product_name=product_name,
51-
wallet_id=project_wallet.wallet_id,
60+
wallet_id=current_project_wallet.wallet_id,
5261
project_id=project_id,
5362
transaction_status=CreditTransactionStatus.IN_DEBT,
5463
)
5564
if project_wallet_credits.available_osparc_credits > 0:
56-
msg = f"Current Project Wallet {project_wallet.wallet_id} is in DEBT"
65+
msg = (
66+
f"Current Project Wallet {current_project_wallet.wallet_id} is in DEBT"
67+
)
5768
raise ValueError(msg)
5869

59-
# ensure the wallet can be used by the user
60-
wallet: WalletGet = await wallets_api.get_wallet_by_user(
61-
app,
62-
user_id=user_id,
63-
wallet_id=wallet_id,
64-
product_name=product_name,
65-
)
66-
67-
# Allow changing the wallet only if there are no pending transactions within the project.
68-
# TODO: MATUS: check pending transactions
70+
# Do not allow to change wallet if the project has transaction in PENDING!
71+
project_service_runs_in_progress = await service_runs.get_service_run_page(
72+
rpc_client,
73+
user_id=user_id,
74+
product_name=product_name,
75+
wallet_id=wallet_id,
76+
access_all_wallet_usage=True,
77+
transaction_status=CreditTransactionStatus.PENDING,
78+
project_id=project_id,
79+
offset=0,
80+
limit=1,
81+
)
82+
if project_service_runs_in_progress.total > 0:
83+
msg = "Can not change the wallet, as project has currently pending transaction"
84+
raise ValueError(msg)
6985

7086
await db.connect_wallet_to_project(project_uuid=project_id, wallet_id=wallet_id)
7187
return wallet

services/web/server/src/simcore_service_webserver/projects/_wallets_handlers.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ async def pay_project_debt(request: web.Request):
140140
request.app, path_params.project_id
141141
)
142142
if not current_wallet:
143-
_logger.warning("This should not happen?")
144143
raise web.HTTPNotFound(
145144
reason="Project doesn't have any wallet associated to the project"
146145
)

0 commit comments

Comments
 (0)