Parcourir la source

handle prunplanner backend v2

raylu il y a 1 semaine
Parent
commit
6492f0ebf6
4 fichiers modifiés avec 81 ajouts et 87 suppressions
  1. 4 4
      fx.py
  2. 15 22
      integration.py
  3. 1 0
      prunplanner/share.py
  4. 61 61
      roi.py

+ 4 - 4
fx.py

@@ -10,7 +10,7 @@ import roi
 def main() -> None:
 	from_cx, to_cx = sys.argv[1:]
 	raw_prices: list[RawPrice] = cache.get('https://refined-prun.github.io/refined-prices/all.json')
-	materials: dict[str, roi.Material] = {m['Ticker']: m for m in cache.get('https://api.prunplanner.org/data/materials')}
+	materials: dict[str, roi.Material] = {m['ticker']: m for m in cache.get('https://api.prunplanner.org/data/materials/')}
 
 	bids: dict[str, float] = {}
 	for price in raw_prices:
@@ -31,12 +31,12 @@ def main() -> None:
 	print('mat  rate     per shipping  max converted')
 	for rate in rates:
 		mat = materials[rate.ticker]
-		if mat['Weight'] >= mat['Volume']:
-			if (per_t := rate.to_price / mat['Weight']) < 1000:
+		if mat['weight'] >= mat['volume']:
+			if (per_t := rate.to_price / mat['weight']) < 1000:
 				continue
 			per_shipping = f'{per_t:9,.0f}/t '
 		else:
-			if (per_m3 := rate.to_price / mat['Volume']) < 1000:
+			if (per_m3 := rate.to_price / mat['volume']) < 1000:
 				continue
 			per_shipping = f'{per_m3:9,.0f}/m³'
 		print(f'{rate.ticker:4} {rate.rate:8.5f} {per_shipping} {rate.depth * rate.to_price:12,.0f}')

+ 15 - 22
integration.py

@@ -7,21 +7,25 @@ import typing
 
 import cache
 
+if typing.TYPE_CHECKING:
+	import roi
+
 def main() -> None:
 	mat = sys.argv[1]
 
-	recipes: list[Recipe] = cache.get('https://api.prunplanner.org/data/recipes')
+	recipes: list[roi.Recipe] = cache.get('https://api.prunplanner.org/data/recipes/')
 	companies = pmmg_monthly_report()
 
 	print(mat, '→')
-	wrought = (recipe for recipe in recipes if mat in (i['Ticker'] for i in recipe['Inputs']) and len(recipe['Outputs']) == 1)
+	wrought = (recipe for recipe in recipes
+			if mat in (i['material_ticker'] for i in recipe['inputs']) and len(recipe['outputs']) == 1)
 	output_mats = {}
-	for recipe in sorted(wrought, key=lambda r: r['Outputs'][0]['Ticker']):
-		(output,) = recipe['Outputs']
-		(input,) = (i for i in recipe['Inputs'] if i['Ticker'] == mat)
-		ratio = input['Amount'] / output['Amount']
-		output_mats[output['Ticker']] = ratio
-		print(f'\t{output["Ticker"]:3}:', ratio)
+	for recipe in sorted(wrought, key=lambda r: r['outputs'][0]['material_ticker']):
+		(output,) = recipe['outputs']
+		(input,) = (i for i in recipe['inputs'] if i['material_ticker'] == mat)
+		ratio = input['material_amount'] / output['material_amount']
+		output_mats[output['material_ticker']] = ratio
+		print(f'\t{output["material_ticker"]:3}:', ratio)
 
 	companies_produced = companies_consumed = companies_consumed_80 = 0
 	into = dict.fromkeys(output_mats.keys(), 0)
@@ -45,9 +49,9 @@ def main() -> None:
 
 	recipes_per_output = collections.defaultdict(int)
 	for recipe in recipes:
-		for output in recipe['Outputs']:
-			if output['Ticker'] in into:
-				recipes_per_output[output['Ticker']] += 1
+		for output in recipe['outputs']:
+			if output['material_ticker'] in into:
+				recipes_per_output[output['material_ticker']] += 1
 	for output_mat, total in sorted(into.items(), key=lambda kv: kv[1], reverse=True):
 		alt = ''
 		if (recipe_count := recipes_per_output[output_mat]) != 1:
@@ -69,16 +73,5 @@ def pmmg_monthly_report() -> dict[str, dict[str, CompanyOutput]]:
 class CompanyOutput(typing.TypedDict):
 	amount: int
 
-class Recipe(typing.TypedDict):
-	RecipeName: str
-	BuildingTicker: str
-	Inputs: list[RecipeMat]
-	Outputs: list[RecipeMat]
-	TimeMs: int
-
-class RecipeMat(typing.TypedDict):
-	Ticker: str
-	Amount: int
-
 if __name__ == '__main__':
 	main()

+ 1 - 0
prunplanner/share.py

@@ -3,6 +3,7 @@
 from __future__ import annotations
 import collections
 import csv
+import math
 import sys
 import typing
 

+ 61 - 61
roi.py

@@ -7,9 +7,9 @@ import typing
 import cache
 
 def main() -> None:
-	recipes: list[Recipe] = cache.get('https://api.prunplanner.org/data/recipes')
-	buildings: dict[str, Building] = {m['Ticker']: m for m in cache.get('https://api.prunplanner.org/data/buildings')}
-	materials: dict[str, Material] = {m['Ticker']: m for m in cache.get('https://api.prunplanner.org/data/materials')}
+	recipes: list[Recipe] = cache.get('https://api.prunplanner.org/data/recipes/')
+	buildings: dict[str, Building] = {m['building_ticker']: m for m in cache.get('https://api.prunplanner.org/data/buildings/')}
+	materials: dict[str, Material] = {m['ticker']: m for m in cache.get('https://api.prunplanner.org/data/materials/')}
 	raw_prices: list[RawPrice] = cache.get('https://refined-prun.github.io/refined-prices/all.json')
 	for cx in ['AI1', 'CI1', 'IC1', 'NC1']:
 		profits = calc_for_cx(cx, recipes, buildings, materials, raw_prices)
@@ -23,16 +23,16 @@ def calc_for_cx(cx: str, recipes: typing.Collection[Recipe], buildings: typing.M
 		if p['ExchangeCode'] == cx
 	}
 	habitation: typing.Mapping[Worker, str] = {
-		'Pioneers': 'HB1',
-		'Settlers': 'HB2',
-		'Technicians': 'HB3',
-		'Engineers': 'HB4',
-		'Scientists': 'HB5',
+		'pioneers': 'HB1',
+		'settlers': 'HB2',
+		'technicians': 'HB3',
+		'engineers': 'HB4',
+		'scientists': 'HB5',
 	}
 	hab_area_cost: dict[Worker, float] = {}
 	hab_capex: dict[Worker, float] = {}
 	for worker, hab in habitation.items():
-		hab_area_cost[worker] = buildings[hab]['AreaCost'] / 100
+		hab_area_cost[worker] = buildings[hab]['area_cost'] / 100
 		hab_capex[worker] = building_construction_cost(buildings[hab], prices) / 100
 
 	profits: list[Profit] = []
@@ -45,51 +45,51 @@ def calc_for_cx(cx: str, recipes: typing.Collection[Recipe], buildings: typing.M
 def calc_profit(recipe: Recipe, buildings: typing.Mapping[str, Building], hab_area_cost: typing.Mapping[Worker, float],
 		hab_capex: typing.Mapping[Worker, float], materials: typing.Mapping[str, Material],
 		prices: typing.Mapping[str, Price]) -> Profit | None:
-	if len(recipe['Outputs']) == 0:
+	if len(recipe['outputs']) == 0:
 		return
 
 	outputs: list[MatPrice] = []
 	revenue = 0
 	output_prices: dict[str, PriceNonNull] = {}
-	for output in recipe['Outputs']:
-		price = prices[output['Ticker']]
+	for output in recipe['outputs']:
+		price = prices[output['material_ticker']]
 		if price.vwap_7d is None or price.average_traded_7d is None:
 			return # skip recipes with thinly traded outputs
-		output_prices[output['Ticker']] = typing.cast(PriceNonNull, price)
-		outputs.append(MatPrice(output['Ticker'], output['Amount'], price.vwap_7d))
-		revenue += price.vwap_7d * output['Amount']
+		output_prices[output['material_ticker']] = typing.cast(PriceNonNull, price)
+		outputs.append(MatPrice(output['material_ticker'], output['material_amount'], price.vwap_7d))
+		revenue += price.vwap_7d * output['material_amount']
 
 	input_costs: list[MatPrice] = []
 	cost = 0
-	for input in recipe['Inputs']:
-		if (input_cost := prices[input['Ticker']].vwap_7d) is None:
+	for input in recipe['inputs']:
+		if (input_cost := prices[input['material_ticker']].vwap_7d) is None:
 			return # skip recipes with thinly traded inputs
-		input_costs.append(MatPrice(input['Ticker'], input['Amount'], input_cost))
-		cost += input_cost * input['Amount']
+		input_costs.append(MatPrice(input['material_ticker'], input['material_amount'], input_cost))
+		cost += input_cost * input['material_amount']
 	profit_per_run = revenue - cost
 
-	building = buildings[recipe['BuildingTicker']]
-	area = building['AreaCost'] + sum(hab_area_cost[worker] * building[worker] for worker in hab_area_cost)
+	building = buildings[recipe['building_ticker']]
+	area = building['area_cost'] + sum(hab_area_cost[worker] * building[worker] for worker in hab_area_cost)
 	capex = building_construction_cost(building, prices) + \
 		sum(hab_capex[worker] * building[worker] for worker in hab_capex)
-	runs_per_day = 24 * 60 * 60 * 1000 / recipe['TimeMs']
-	if building['Ticker'] in ('FRM', 'ORC'):
+	runs_per_day = 24 * 60 * 60 * 1000 / recipe['time_ms']
+	if building['building_ticker'] in ('FRM', 'ORC'):
 		runs_per_day *= 1.1212 # promitor's fertility
 	worker_consumable_daily_cost = building_daily_cost(building, prices)
 	cost_per_day = cost * runs_per_day + worker_consumable_daily_cost
 
-	lowest_liquidity = min(recipe['Outputs'],
-			key=lambda output: output['Amount'] / output_prices[output['Ticker']].average_traded_7d)
-	output_per_day = lowest_liquidity['Amount'] * runs_per_day
+	lowest_liquidity = min(recipe['outputs'],
+			key=lambda output: output['material_amount'] / output_prices[output['material_ticker']].average_traded_7d)
+	output_per_day = lowest_liquidity['material_amount'] * runs_per_day
 
 	logistics_per_area = max(
-		sum(materials[input['Ticker']]['Weight'] * input['Amount'] for input in recipe['Inputs']),
-		sum(materials[input['Ticker']]['Volume'] * input['Amount'] for input in recipe['Inputs']),
-		sum(materials[output['Ticker']]['Weight'] * output['Amount'] for output in recipe['Outputs']),
-		sum(materials[output['Ticker']]['Volume'] * output['Amount'] for output in recipe['Outputs']),
+		sum(materials[input['material_ticker']]['weight'] * input['material_amount'] for input in recipe['inputs']),
+		sum(materials[input['material_ticker']]['volume'] * input['material_amount'] for input in recipe['inputs']),
+		sum(materials[output['material_ticker']]['weight'] * output['material_amount'] for output in recipe['outputs']),
+		sum(materials[output['material_ticker']]['volume'] * output['material_amount'] for output in recipe['outputs']),
 	) * runs_per_day / area
-	return Profit(outputs, recipe['RecipeName'],
-			expertise=building['Expertise'],
+	return Profit(outputs, recipe['recipe_name'],
+			expertise=building['expertise'],
 			profit_per_day=(profit_per_run * runs_per_day - worker_consumable_daily_cost),
 			area=area,
 			capex=capex,
@@ -99,18 +99,18 @@ def calc_profit(recipe: Recipe, buildings: typing.Mapping[str, Building], hab_ar
 			runs_per_day=runs_per_day,
 			logistics_per_area=logistics_per_area,
 			output_per_day=output_per_day,
-			average_traded_7d=output_prices[lowest_liquidity['Ticker']].average_traded_7d)
+			average_traded_7d=output_prices[lowest_liquidity['material_ticker']].average_traded_7d)
 
 def building_construction_cost(building: Building, prices: typing.Mapping[str, Price]) -> float:
-	return sum(bc['Amount'] * prices[bc['CommodityTicker']].vwap_7d for bc in building['BuildingCosts']) # pyright: ignore[reportOperatorIssue]
+	return sum(bc['material_amount'] * prices[bc['material_ticker']].vwap_7d for bc in building['costs']) # pyright: ignore[reportOperatorIssue]
 
 def building_daily_cost(building: Building, prices: typing.Mapping[str, Price]) -> float:
 	consumption = {
-		'Pioneers': [('COF', 0.5), ('DW', 4), ('RAT', 4), ('OVE', 0.5), ('PWO', 0.2)],
-		'Settlers': [('DW', 5), ('RAT', 6), ('KOM', 1), ('EXO', 0.5), ('REP', 0.2), ('PT', 0.5)],
-		'Technicians': [('DW', 7.5), ('RAT', 7), ('ALE', 1), ('MED', 0.5), ('SC', 0.1), ('HMS', 0.5), ('SCN', 0.1)],
-		'Engineers': [('DW', 10), ('MED', 0.5), ('GIN', 1), ('FIM', 7), ('VG', 0.2), ('HSS', 0.2), ('PDA', 0.1)],
-		'Scientists': [('DW', 10), ('MED', 0.5), ('WIN', 1), ('MEA', 7), ('NST', 0.1), ('LC', 0.2), ('WS', 0.1)],
+		'pioneers': [('COF', 0.5), ('DW', 4), ('RAT', 4), ('OVE', 0.5), ('PWO', 0.2)],
+		'settlers': [('DW', 5), ('RAT', 6), ('KOM', 1), ('EXO', 0.5), ('REP', 0.2), ('PT', 0.5)],
+		'technicians': [('DW', 7.5), ('RAT', 7), ('ALE', 1), ('MED', 0.5), ('SC', 0.1), ('HMS', 0.5), ('SCN', 0.1)],
+		'engineers': [('DW', 10), ('MED', 0.5), ('GIN', 1), ('FIM', 7), ('VG', 0.2), ('HSS', 0.2), ('PDA', 0.1)],
+		'scientists': [('DW', 10), ('MED', 0.5), ('WIN', 1), ('MEA', 7), ('NST', 0.1), ('LC', 0.2), ('WS', 0.1)],
 	}
 	cost = 0
 	for worker, mats in consumption.items():
@@ -120,38 +120,38 @@ def building_daily_cost(building: Building, prices: typing.Mapping[str, Price])
 			cost += (mat_price.vwap_7d or mat_price.vwap_30d) * workers * per_100 / 100
 	return cost
 
-Worker = typing.Literal['Pioneers', 'Settlers', 'Technicians', 'Engineers', 'Scientists']
+Worker = typing.Literal['pioneers', 'settlers', 'technicians', 'engineers', 'scientists']
 
 class Recipe(typing.TypedDict):
-	RecipeName: str
-	BuildingTicker: str
-	Inputs: list[RecipeMat]
-	Outputs: list[RecipeMat]
-	TimeMs: int
+	recipe_name: str
+	building_ticker: str
+	inputs: list[RecipeMat]
+	outputs: list[RecipeMat]
+	time_ms: int
 
 class RecipeMat(typing.TypedDict):
-	Ticker: str
-	Amount: int
+	material_ticker: str
+	material_amount: int
 
 class Building(typing.TypedDict):
-	Ticker: str
-	Expertise: str
-	AreaCost: int
-	BuildingCosts: list[BuildingMat]
-	Pioneers: int
-	Settlers: int
-	Technicians: int
-	Engineers: int
-	Scientists: int
+	building_ticker: str
+	expertise: str
+	area_cost: int
+	costs: list[BuildingMat]
+	pioneers: int
+	settlers: int
+	technicians: int
+	engineers: int
+	scientists: int
 
 class BuildingMat(typing.TypedDict):
-	CommodityTicker: str
-	Amount: int
+	material_ticker: str
+	material_amount: int
 
 class Material(typing.TypedDict):
-	Ticker: str
-	Weight: float
-	Volume: float
+	ticker: str
+	weight: float
+	volume: float
 
 class RawPrice(typing.TypedDict):
 	MaterialTicker: str