Skip to content

Commit da47339

Browse files
authored
chore(back): add wue value to saved emissions data in db (#952)
* chore(back): add wue value to saved emissions data in db
1 parent 0fd0e61 commit da47339

File tree

6 files changed

+159
-0
lines changed

6 files changed

+159
-0
lines changed

carbonserver/carbonserver/api/infra/database/sql_models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class Emission(Base):
2121
gpu_energy = Column(Float)
2222
ram_energy = Column(Float)
2323
energy_consumed = Column(Float)
24+
wue = Column(Float, nullable=False, default=0)
2425
run_id = Column(UUID(as_uuid=True), ForeignKey("runs.id", ondelete="CASCADE"))
2526
run = relationship("Run", back_populates="emissions")
2627

carbonserver/carbonserver/api/infra/repositories/repository_emissions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def add_emission(self, emission: EmissionCreate) -> UUID:
4141
gpu_energy=emission.gpu_energy,
4242
ram_energy=emission.ram_energy,
4343
energy_consumed=emission.energy_consumed,
44+
wue=emission.wue,
4445
run_id=emission.run_id,
4546
)
4647
session.add(db_emission)
@@ -104,5 +105,6 @@ def map_sql_to_schema(emission: sql_models.Emission) -> Emission:
104105
gpu_energy=emission.gpu_energy,
105106
ram_energy=emission.ram_energy,
106107
energy_consumed=emission.energy_consumed,
108+
wue=emission.wue,
107109
run_id=emission.run_id,
108110
)

carbonserver/carbonserver/api/schemas.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ class EmissionBase(BaseModel):
8787
ram_energy: Optional[float] = Field(
8888
..., ge=0, description="The ram_energy must be greater than zero"
8989
)
90+
wue: Optional[float] = Field(
91+
default=0,
92+
ge=0,
93+
description="The WUE (Water Usage Effectiveness) must be greater than or equal to zero",
94+
)
9095

9196
class Config:
9297
schema_extra = {
@@ -103,6 +108,7 @@ class Config:
103108
"gpu_energy": 0.0,
104109
"ram_energy": 2.0,
105110
"energy_consumed": 57.21874,
111+
"wue": 0,
106112
}
107113
}
108114

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""add_wue_to_emissions
2+
3+
Revision ID: 3212895acafd
4+
Revises: 2a898cf81c3e
5+
Create Date: 2025-10-19 21:29:36.800401
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from alembic import op
11+
12+
# revision identifiers, used by Alembic.
13+
revision = "3212895acafd"
14+
down_revision = "2a898cf81c3e"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade():
20+
"""
21+
Add WUE (Water Usage Effectiveness) field to emissions table.
22+
Default value is 0 (no water usage).
23+
"""
24+
op.add_column(
25+
"emissions",
26+
sa.Column("wue", sa.Float, nullable=False, server_default="0"),
27+
)
28+
29+
30+
def downgrade():
31+
"""
32+
Remove WUE field from emissions table.
33+
"""
34+
op.drop_column("emissions", "wue")

carbonserver/tests/api/integration/test_api_black_box.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ def test_api24_runs_for_experiment_list(user_session):
279279
"gpu_energy": 105.50,
280280
"ram_energy": 60.50,
281281
"energy_consumed": 65.50,
282+
"wue": 0,
282283
}
283284

284285

@@ -297,6 +298,7 @@ def add_emission(run_id: str):
297298
"gpu_energy": default_emission["gpu_energy"],
298299
"ram_energy": default_emission["ram_energy"],
299300
"energy_consumed": default_emission["energy_consumed"],
301+
"wue": default_emission["wue"],
300302
}
301303
r = requests.post(
302304
url=URL + "/emissions/",

carbonserver/tests/api/routers/test_emissions.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"gpu_energy": 0.0,
3838
"ram_energy": 2.0,
3939
"energy_consumed": 57.21874,
40+
"wue": 0,
4041
}
4142

4243
EMISSION_1 = {
@@ -53,6 +54,7 @@
5354
"gpu_energy": 0.0,
5455
"ram_energy": 2.0,
5556
"energy_consumed": 57.21874,
57+
"wue": 0,
5658
}
5759

5860
EMISSION_2 = {
@@ -69,6 +71,7 @@
6971
"gpu_energy": 0.0,
7072
"ram_energy": 2.0,
7173
"energy_consumed": 57.21874,
74+
"wue": 0,
7275
}
7376

7477

@@ -86,6 +89,7 @@
8689
"gpu_energy": 0.0,
8790
"ram_energy": 2.0,
8891
"energy_consumed": 57.21874,
92+
"wue": 0,
8993
}
9094

9195

@@ -196,3 +200,113 @@ def test_get_emissions_from_run_retreives_all_emissions_from_run(
196200
assert not diff
197201
assert len(actual_emission_ids_list) == len(set(actual_emission_ids_list))
198202
assert EMISSION_3["id"] not in actual_emission_ids_list
203+
204+
205+
def test_add_emission_with_default_wue_value(client, custom_test_server):
206+
"""Test that WUE defaults to 0 when not provided"""
207+
# Prepare the test - create emission without WUE field
208+
emission_without_wue = {
209+
"timestamp": "2021-04-04T08:43:00+02:00",
210+
"run_id": "40088f1a-d28e-4980-8d80-bf5600056a14",
211+
"duration": 98745,
212+
"emissions_sum": 206.548444,
213+
"emissions_rate": 89.548444,
214+
"cpu_power": 0.3,
215+
"gpu_power": 0.0,
216+
"ram_power": 0.15,
217+
"cpu_energy": 55.21874,
218+
"gpu_energy": 0.0,
219+
"ram_energy": 2.0,
220+
"energy_consumed": 57.21874,
221+
# Note: wue is not provided, should default to 0
222+
}
223+
224+
repository_mock = mock.Mock(spec=EmissionRepository)
225+
repository_mock.add_emission.return_value = UUID(EMISSION_ID)
226+
227+
# Setup the project token repository
228+
project_tokens_repository_mock = mock.Mock(spec=ProjectTokenRepository)
229+
PROJECT_ID = UUID("f52fe339-164d-4c2b-a8c0-f562dfce066d")
230+
PROJECT_TOKEN_ID = UUID("e60afb92-17b7-4720-91a0-1ae91e409ba7")
231+
PROJECT_TOKEN = ProjectToken(
232+
id=PROJECT_TOKEN_ID,
233+
project_id=PROJECT_ID,
234+
name="Project",
235+
token="token",
236+
access=AccessLevel.WRITE.value,
237+
)
238+
project_tokens_repository_mock.get_project_token_by_run_id_and_token.return_value = (
239+
PROJECT_TOKEN
240+
)
241+
242+
# Call the endpoint
243+
with custom_test_server.container.emission_repository.override(
244+
repository_mock
245+
) and custom_test_server.container.project_token_repository.override(
246+
project_tokens_repository_mock
247+
):
248+
response = client.post(
249+
"/emissions", json=emission_without_wue, headers={"x-api-token": "token"}
250+
)
251+
252+
# Asserts
253+
assert response.status_code == status.HTTP_201_CREATED
254+
255+
# Verify that the repository was called with WUE defaulting to 0
256+
called_emission = repository_mock.add_emission.call_args[0][0]
257+
assert called_emission.wue == 0, "WUE should default to 0 when not provided"
258+
259+
260+
def test_add_emission_with_custom_wue_value(client, custom_test_server):
261+
"""Test that custom WUE value is properly saved"""
262+
# Prepare the test - create emission with custom WUE value
263+
emission_with_wue = {
264+
"timestamp": "2021-04-04T08:43:00+02:00",
265+
"run_id": "40088f1a-d28e-4980-8d80-bf5600056a14",
266+
"duration": 98745,
267+
"emissions_sum": 206.548444,
268+
"emissions_rate": 89.548444,
269+
"cpu_power": 0.3,
270+
"gpu_power": 0.0,
271+
"ram_power": 0.15,
272+
"cpu_energy": 55.21874,
273+
"gpu_energy": 0.0,
274+
"ram_energy": 2.0,
275+
"energy_consumed": 57.21874,
276+
"wue": 1.5,
277+
}
278+
279+
repository_mock = mock.Mock(spec=EmissionRepository)
280+
repository_mock.add_emission.return_value = UUID(EMISSION_ID)
281+
282+
# Setup the project token repository
283+
project_tokens_repository_mock = mock.Mock(spec=ProjectTokenRepository)
284+
PROJECT_ID = UUID("f52fe339-164d-4c2b-a8c0-f562dfce066d")
285+
PROJECT_TOKEN_ID = UUID("e60afb92-17b7-4720-91a0-1ae91e409ba7")
286+
PROJECT_TOKEN = ProjectToken(
287+
id=PROJECT_TOKEN_ID,
288+
project_id=PROJECT_ID,
289+
name="Project",
290+
token="token",
291+
access=AccessLevel.WRITE.value,
292+
)
293+
project_tokens_repository_mock.get_project_token_by_run_id_and_token.return_value = (
294+
PROJECT_TOKEN
295+
)
296+
297+
# Call the endpoint
298+
with custom_test_server.container.emission_repository.override(
299+
repository_mock
300+
) and custom_test_server.container.project_token_repository.override(
301+
project_tokens_repository_mock
302+
):
303+
response = client.post(
304+
"/emissions", json=emission_with_wue, headers={"x-api-token": "token"}
305+
)
306+
307+
# Asserts
308+
assert response.status_code == status.HTTP_201_CREATED
309+
310+
# Verify that the repository was called with the correct WUE value
311+
called_emission = repository_mock.add_emission.call_args[0][0]
312+
assert called_emission.wue == 1.5, "WUE should be set to the provided value"

0 commit comments

Comments
 (0)