@@ -1236,39 +1236,58 @@ def total_compute_time(self):
12361236 return self .phase_1_compute_time + self .phase_2_compute_time
12371237
12381238 @property
1239- def phase_1_storage_size_bytes (self ):
1239+ def phase_1_data_storage_size_bytes (self ):
12401240 return (
12411241 self .phase_1_number_of_test_images
12421242 * (self .average_size_of_test_image_in_mb * settings .MEGABYTE )
12431243 * self .number_of_tasks
12441244 )
12451245
12461246 @property
1247- def phase_2_storage_size_bytes (self ):
1247+ def phase_2_data_storage_size_bytes (self ):
12481248 return (
12491249 self .phase_2_number_of_test_images
12501250 * (self .average_size_of_test_image_in_mb * settings .MEGABYTE )
12511251 * self .number_of_tasks
12521252 )
12531253
12541254 @property
1255- def docker_s3_storage_size_bytes (self ):
1255+ def num_unique_docker_images_per_team (self ):
1256+ # A docker for a later phase should also be submitted to an earlier one.
1257+ return max (
1258+ self .phase_1_number_of_submissions_per_team ,
1259+ self .phase_2_number_of_submissions_per_team ,
1260+ )
1261+
1262+ @property
1263+ def num_unique_docker_images (self ):
1264+ return (
1265+ self .num_unique_docker_images_per_team
1266+ * self .expected_number_of_teams
1267+ * self .number_of_tasks
1268+ )
1269+
1270+ @property
1271+ def docker_storage_size_gb (self ):
12561272 return (
1257- # 1 unique container image per submission
1258- (self .average_algorithm_container_size_in_gb * settings .GIGABYTE )
1259- * self .total_num_submissions
1273+ self .average_algorithm_container_size_in_gb
1274+ * self .num_unique_docker_images
12601275 )
12611276
1277+ @property
1278+ def docker_storage_size_bytes (self ):
1279+ return self .docker_storage_size_gb * settings .GIGABYTE
1280+
12621281 @property
12631282 def total_data_and_docker_storage_bytes (self ):
12641283 return (
1265- self .docker_s3_storage_size_bytes
1266- + self .phase_1_storage_size_bytes
1267- + self .phase_2_storage_size_bytes
1284+ self .docker_storage_size_bytes
1285+ + self .phase_1_data_storage_size_bytes
1286+ + self .phase_2_data_storage_size_bytes
12681287 )
12691288
12701289 @cached_property
1271- def compute_euro_cents_per_hour (self ):
1290+ def compute_costs_euro_cents_per_hour (self ):
12721291 executors = [
12731292 import_string (settings .COMPONENTS_DEFAULT_BACKEND )(
12741293 job_id = "" ,
@@ -1285,40 +1304,54 @@ def compute_euro_cents_per_hour(self):
12851304 )
12861305 return usd_cents_per_hour * settings .COMPONENTS_USD_TO_EUR
12871306
1307+ @property
1308+ def compute_costs_euros_per_hour (self ):
1309+ return self .compute_costs_euro_cents_per_hour / 100
1310+
12881311 def get_compute_costs_euros (self , duration ):
1289- return self .round_to_10_euros (
1290- duration .total_seconds ()
1291- * (1 + settings .COMPONENTS_TAX_RATE_PERCENT )
1292- * self .compute_euro_cents_per_hour
1293- / 3600
1312+ return (
1313+ math .ceil (
1314+ self .compute_costs_euro_cents_per_hour
1315+ * duration .total_seconds ()
1316+ / 3600
1317+ )
1318+ / 100
12941319 )
12951320
1296- @classmethod
1297- def get_data_storage_costs_euros ( cls , size_bytes ):
1298- return cls . round_to_10_euros (
1299- size_bytes
1300- * settings .CHALLENGE_NUM_SUPPORT_YEARS
1301- * (1 + settings .COMPONENTS_TAX_RATE_PERCENT )
1321+ @property
1322+ def storage_costs_euros_per_gb ( self ):
1323+ return (
1324+ settings . CHALLENGE_NUM_SUPPORT_YEARS
1325+ * settings .COMPONENTS_S3_USD_MILLICENTS_PER_YEAR_PER_TB_EXCLUDING_TAX
1326+ * (1 + settings .COMPONENTS_TAX_RATE )
13021327 * settings .COMPONENTS_USD_TO_EUR
1303- * settings .COMPONENTS_S3_USD_MILLICENTS_PER_YEAR_PER_TB
13041328 / 1000
1329+ / 100
13051330 / settings .TERABYTE
1331+ * settings .GIGABYTE
13061332 )
13071333
1308- @classmethod
1309- def round_to_10_euros (cls , cents ):
1310- return 10 * math .ceil (cents / 100 / 10 )
1334+ def get_storage_costs_euros (self , size_bytes ):
1335+ return (
1336+ math .ceil (
1337+ self .storage_costs_euros_per_gb
1338+ * 100
1339+ * size_bytes
1340+ / settings .GIGABYTE
1341+ )
1342+ / 100
1343+ )
13111344
13121345 @property
1313- def phase_1_data_storage_euros (self ):
1314- return self .get_data_storage_costs_euros (
1315- self .phase_1_storage_size_bytes
1346+ def phase_1_data_storage_costs_euros (self ):
1347+ return self .get_storage_costs_euros (
1348+ self .phase_1_data_storage_size_bytes
13161349 )
13171350
13181351 @property
1319- def phase_2_data_storage_euros (self ):
1320- return self .get_data_storage_costs_euros (
1321- self .phase_2_storage_size_bytes
1352+ def phase_2_data_storage_costs_euros (self ):
1353+ return self .get_storage_costs_euros (
1354+ self .phase_2_data_storage_size_bytes
13221355 )
13231356
13241357 @property
@@ -1332,20 +1365,20 @@ def phase_2_compute_costs_euros(self):
13321365 @property
13331366 def phase_1_total_euros (self ):
13341367 return (
1335- self .phase_1_data_storage_euros + self .phase_1_compute_costs_euros
1368+ self .phase_1_data_storage_costs_euros
1369+ + self .phase_1_compute_costs_euros
13361370 )
13371371
13381372 @property
13391373 def phase_2_total_euros (self ):
13401374 return (
1341- self .phase_2_data_storage_euros + self .phase_2_compute_costs_euros
1375+ self .phase_2_data_storage_costs_euros
1376+ + self .phase_2_compute_costs_euros
13421377 )
13431378
13441379 @property
13451380 def docker_storage_costs_euros (self ):
1346- return self .get_data_storage_costs_euros (
1347- self .docker_s3_storage_size_bytes
1348- )
1381+ return self .get_storage_costs_euros (self .docker_storage_size_bytes )
13491382
13501383 @cached_property
13511384 def base_cost_euros (self ):
@@ -1359,42 +1392,11 @@ def base_cost_euros(self):
13591392 else :
13601393 return settings .CHALLENGE_BASE_COST_IN_EURO
13611394
1362- @property
1363- def total_euros_storage_and_compute (self ):
1364- return (
1365- self .phase_1_total_euros
1366- + self .phase_2_total_euros
1367- + self .docker_storage_costs_euros
1368- )
1369-
1370- @cached_property
1371- def budget (self ):
1372- try :
1373- return {
1374- "Data storage cost for phase 1" : self .phase_1_data_storage_euros ,
1375- "Compute costs for phase 1" : self .phase_1_compute_costs_euros ,
1376- "Total phase 1" : self .phase_1_total_euros ,
1377- "Data storage cost for phase 2" : self .phase_2_data_storage_euros ,
1378- "Compute costs for phase 2" : self .phase_2_compute_costs_euros ,
1379- "Total phase 2" : self .phase_2_total_euros ,
1380- "Docker storage cost" : self .docker_storage_costs_euros ,
1381- "Total across phases" : self .total_euros_storage_and_compute ,
1382- }
1383- except TypeError :
1384- return None
1385-
1386- @property
1387- def storage_and_compute_cost_surplus (self ):
1388- return (
1389- self .total_euros_storage_and_compute
1390- - settings .CHALLENGE_MINIMAL_COMPUTE_AND_STORAGE_IN_EURO
1391- )
1392-
13931395 @property
13941396 def total_storage_costs_euros (self ):
13951397 return (
1396- self .phase_1_data_storage_euros
1397- + self .phase_2_data_storage_euros
1398+ self .phase_1_data_storage_costs_euros
1399+ + self .phase_2_data_storage_costs_euros
13981400 + self .docker_storage_costs_euros
13991401 )
14001402
@@ -1404,69 +1406,73 @@ def total_compute_costs_euros(self):
14041406 self .phase_1_compute_costs_euros + self .phase_2_compute_costs_euros
14051407 )
14061408
1407- def calculate_invoiced_amount (self , * , cost , ratio ):
1408- if self .storage_and_compute_cost_surplus <= 0 :
1409- # Below minimum price, add proportional shortfall
1410- return round (
1411- cost + ratio * abs (self .storage_and_compute_cost_surplus )
1412- )
1413- else :
1414- # Above minimum price, allocate surplus proportionally
1415- return round (
1416- ratio
1417- * (
1418- self .additional_compute_and_storage_costs
1419- + settings .CHALLENGE_MINIMAL_COMPUTE_AND_STORAGE_IN_EURO
1420- )
1421- )
1422-
14231409 @property
1424- def compute_cost_ratio (self ):
1425- return (
1426- self .total_compute_costs_euros
1427- / self .total_euros_storage_and_compute
1428- )
1410+ def total_compute_and_storage_costs_euros (self ):
1411+ return self .total_storage_costs_euros + self .total_compute_costs_euros
14291412
14301413 @property
1431- def total_storage_to_be_invoiced (self ):
1432- return self .calculate_invoiced_amount (
1433- cost = self .total_storage_costs_euros ,
1434- ratio = 1 - self .compute_cost_ratio ,
1414+ def capacity_reservation_units (self ):
1415+ return math .ceil (
1416+ max (
1417+ settings .CHALLENGE_MINIMAL_COMPUTE_AND_STORAGE_IN_EURO ,
1418+ self .total_compute_and_storage_costs_euros ,
1419+ )
1420+ / settings .CHALLENGE_CAPACITY_RESERVATION_PACK_SIZE_IN_EURO
14351421 )
14361422
14371423 @property
1438- def total_compute_to_be_invoiced (self ):
1439- return self . calculate_invoiced_amount (
1440- cost = self .total_compute_costs_euros ,
1441- ratio = self . compute_cost_ratio ,
1424+ def capacity_reservation_euros (self ):
1425+ return (
1426+ self .capacity_reservation_units
1427+ * settings . CHALLENGE_CAPACITY_RESERVATION_PACK_SIZE_IN_EURO
14421428 )
14431429
14441430 @property
1445- def minimal_challenge_cost (self ):
1431+ def capacity_reservation_compute_euros (self ):
14461432 return (
1447- self .base_cost_euros
1448- + settings .CHALLENGE_MINIMAL_COMPUTE_AND_STORAGE_IN_EURO
1433+ self .total_compute_costs_euros
1434+ / self .total_compute_and_storage_costs_euros
1435+ * self .capacity_reservation_euros
14491436 )
14501437
14511438 @property
1452- def additional_compute_and_storage_costs (self ):
1453- if self .storage_and_compute_cost_surplus <= 0 :
1454- return 0
1455- else :
1456- return (
1457- math .ceil (
1458- self .storage_and_compute_cost_surplus
1459- / settings .CHALLENGE_ADDITIONAL_COMPUTE_AND_STORAGE_PACK_SIZE_IN_EURO
1460- )
1461- * settings .CHALLENGE_ADDITIONAL_COMPUTE_AND_STORAGE_PACK_SIZE_IN_EURO
1462- )
1439+ def capacity_reservation_storage_euros (self ):
1440+ return (
1441+ self .total_storage_costs_euros
1442+ / self .total_compute_and_storage_costs_euros
1443+ * self .capacity_reservation_euros
1444+ )
14631445
14641446 @property
14651447 def total_challenge_cost (self ):
1466- return (
1467- self .minimal_challenge_cost
1468- + self .additional_compute_and_storage_costs
1469- )
1448+ return self .base_cost_euros + self .capacity_reservation_euros
1449+
1450+ @cached_property
1451+ def costs_for_phases (self ):
1452+ return [
1453+ {
1454+ "name" : "Phase 1" ,
1455+ "number_of_submissions_per_team" : self .phase_1_number_of_submissions_per_team ,
1456+ "number_of_test_images" : self .phase_1_number_of_test_images ,
1457+ "compute_time" : self .phase_1_compute_time ,
1458+ "compute_costs_euros" : self .phase_1_compute_costs_euros ,
1459+ "data_storage_size_gb" : self .phase_1_data_storage_size_bytes
1460+ / settings .GIGABYTE ,
1461+ "data_storage_costs_euros" : self .phase_1_data_storage_costs_euros ,
1462+ "total_euros" : self .phase_1_total_euros ,
1463+ },
1464+ {
1465+ "name" : "Phase 2" ,
1466+ "number_of_submissions_per_team" : self .phase_2_number_of_submissions_per_team ,
1467+ "number_of_test_images" : self .phase_2_number_of_test_images ,
1468+ "compute_time" : self .phase_2_compute_time ,
1469+ "compute_costs_euros" : self .phase_2_compute_costs_euros ,
1470+ "data_storage_size_gb" : self .phase_2_data_storage_size_bytes
1471+ / settings .GIGABYTE ,
1472+ "data_storage_costs_euros" : self .phase_2_data_storage_costs_euros ,
1473+ "total_euros" : self .phase_2_total_euros ,
1474+ },
1475+ ]
14701476
14711477
14721478class ChallengeRequestUserObjectPermission (UserObjectPermissionBase ):
0 commit comments