oh-my-dear-ai commited on
Commit
5787f24
·
1 Parent(s): 671b8d8

[feat](app): support more efficient plant filtering

Browse files
Files changed (1) hide show
  1. app.py +146 -71
app.py CHANGED
@@ -73,21 +73,43 @@ INTERFACE_TEXTS = {
73
  },
74
  }
75
 
76
- # Import and process plant data
77
- df = pd.read_csv("plants.csv")
78
-
79
- # Convert columns to Categorical type and remove rows with NaN in 'gold' column
80
- df["species"] = pd.Categorical(df["species"])
81
- df["tier"] = pd.Categorical(df["tier"])
82
- df = df.dropna(subset=["gold"])
83
- df = df.astype(
84
- {
85
- "gold": int,
86
- "gems": int,
87
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  )
89
 
90
 
 
 
 
 
 
 
 
91
  def calculator(currency, budget, strategy, extra_rate, *amount):
92
  """
93
  Calculate the optimal solution of plant sales based on the given budget
@@ -95,10 +117,11 @@ def calculator(currency, budget, strategy, extra_rate, *amount):
95
 
96
  Args:
97
  *args (tuple): A tuple containing:
 
98
  - budget (int): Gabby's gold budget.
99
  - strategy (str): The selected strategy for selling plants ("MaximizeStock" or "MinimizeStock").
100
  - extra_rate (int): The premium rate for selling plants.
101
- - stocks (list of int): Stock levels of each plant type.
102
 
103
  Returns:
104
  str: A description of the optimal solution, including which plants to sell,
@@ -122,7 +145,7 @@ def calculator(currency, budget, strategy, extra_rate, *amount):
122
  sold_prices = np.array(price * (1 + extra_rate))
123
 
124
  # Initialize the master problem
125
- model = Model("BewilderingBlossom")
126
 
127
  # Decision variables in master problem
128
  x = [
@@ -157,7 +180,7 @@ def calculator(currency, budget, strategy, extra_rate, *amount):
157
  # Final solution processing
158
  solution = []
159
  total_price = 0
160
- # total_count = 0
161
 
162
  if model.getStatus() == "optimal":
163
  for i, var in enumerate(x):
@@ -166,12 +189,12 @@ def calculator(currency, budget, strategy, extra_rate, *amount):
166
  f"{plants_names[i]} ({sold_prices[i]} {currency}): {v}\n"
167
  )
168
  total_price += v * sold_prices[i]
169
- # total_count += v
170
 
171
  if optimal_total_value == budget:
172
- return f"Great! Found a combination of items with a total value equal to the budget ({budget} {currency}).😃\n\n{''.join(solution)}\nTotal value: {int(total_price)} {currency}\n" # Count: {int(model.getObjVal())}
173
 
174
- return f"Oops! {int(budget - optimal_total_value)} {currency} short of the target value ({budget} {currency}).😣\n\n{''.join(solution)}\nTotal value: {int(total_price)} {currency}\n" # Count: {int(model.getObjVal())}
175
 
176
  return "No solution found for the second optimization!"
177
 
@@ -180,14 +203,87 @@ def calculator(currency, budget, strategy, extra_rate, *amount):
180
 
181
  # 高亮每种植物的最高品质
182
  css = """
183
- .first-gold-box {background-color: #fafad2}
184
- .first-gems-box {background-color: #fed9b4}
185
  """
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  with gr.Blocks(css=css) as demo:
188
  gr.Markdown(
189
  """
190
- <center><font size=8>HP-Magic-Awakened Herbologist Toolkit👾</font></center>
191
 
192
  This program is essentially a solver for a variant of the knapsack problem.
193
  Another more versatile [application](https://huggingface.co/spaces/oh-my-dear-ai/easy-knapsack-problem).
@@ -244,65 +340,30 @@ with gr.Blocks(css=css) as demo:
244
  info="Select a strategy:",
245
  )
246
 
247
- # TODO: Add a checkbox group for selecting plants
248
- # selected_plants = gr.CheckboxGroup(
249
- # choices=list(PLANTS_LABLES.values()),
250
- # type="index",
251
- # label="Plants",
252
- # info="Select plants:",
253
- # value=list(PLANTS_LABLES.values()),
254
- # interactive=True,
255
- # )
256
-
257
- def show_plant_boxes(currency):
258
- inventory = {}
259
- species_set = set()
260
- species_count = 0
261
- new_species = False
262
-
263
- for _, row in df.iterrows():
264
- # Check if the plant should be shown based on the selected currency
265
- if row[currency] != 0 and row["tier"] != "feeble":
266
- species_set.add(row["species"])
267
- new_species = len(species_set) > species_count
268
- # Create the Number component for the plant inventory
269
-
270
- inventory[f"{row['species']}_{row['tier']}"] = gr.Number(
271
- label=PLANTS_LABLES[row["species"]],
272
- info=f"{PLANTS_TIERS[row['tier']]} ${row[currency]}",
273
- value=0,
274
- precision=0,
275
- minimum=0,
276
- maximum=500,
277
- step=10,
278
- visible=True,
279
- elem_classes=(f"first-{currency}-box" if new_species else None),
280
- )
281
- species_count = len(species_set)
282
- else:
283
- # If not shown, create a dummy invisible component
284
- inventory[f"{row['species']}_{row['tier']}"] = gr.Number(visible=False)
285
-
286
- # Return the updated inventory components
287
- return list(inventory.values())
288
 
289
  # Create the dynamic plant inventory inputs
290
  with gr.Row() as inventory_row:
291
- inventory = show_plant_boxes(currency_radio.value)
292
 
293
  # Add a row for the Clear and Calculate buttons
294
  with gr.Row():
295
- clear_btn = gr.ClearButton(inventory, size="sm", value="❌Clear")
296
 
297
- # Add a button to trigger the calculation
298
- submit_btn = gr.Button(value="🛠Calculate")
299
 
300
  # Add a row for the result textbox
301
  with gr.Row():
302
  result = gr.Textbox(label="Output")
303
 
304
  # Set up the button click event to call the calculator function
305
- submit_btn.click(
306
  calculator,
307
  inputs=[currency_radio, budget, selected_strategy, acquisition_rate]
308
  + inventory,
@@ -312,11 +373,25 @@ with gr.Blocks(css=css) as demo:
312
 
313
  # Update the inventory when the currency changes
314
  currency_radio.change(
315
- fn=lambda selected_currency: show_plant_boxes(
316
- selected_currency
317
- ), # Adjusted function to return only the components
318
  inputs=[currency_radio],
319
- outputs=inventory, # Update each child in the inventory_row
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  )
321
 
322
  # Launch the Gradio application
 
73
  },
74
  }
75
 
76
+
77
+ def process_csv(file_path):
78
+ """import and process plants data"""
79
+ df = pd.read_csv(file_path)
80
+ df["species"] = pd.Categorical(df["species"])
81
+ df["tier"] = pd.Categorical(df["tier"])
82
+ # df = df.dropna(subset=["gold"])
83
+ df = df.astype(
84
+ {
85
+ "gold": int,
86
+ "gems": int,
87
+ }
88
+ )
89
+ return df
90
+
91
+
92
+ df = process_csv("plants.csv")
93
+
94
+ GOLD_PLANTS = set(
95
+ row["species"]
96
+ for _, row in df.iterrows()
97
+ if row["gold"] != 0 and row["tier"] != "feeble"
98
+ )
99
+ GEMS_PLANTS = set(
100
+ row["species"]
101
+ for _, row in df.iterrows()
102
+ if row["gems"] != 0 and row["tier"] != "feeble"
103
  )
104
 
105
 
106
+ def check_currency(plant, currency):
107
+ if currency == "gold":
108
+ return plant in GOLD_PLANTS
109
+ elif currency == "gems":
110
+ return plant in GEMS_PLANTS
111
+
112
+
113
  def calculator(currency, budget, strategy, extra_rate, *amount):
114
  """
115
  Calculate the optimal solution of plant sales based on the given budget
 
117
 
118
  Args:
119
  *args (tuple): A tuple containing:
120
+ - currency (str): The currency used for purchasing plants ("gold" or "gems").
121
  - budget (int): Gabby's gold budget.
122
  - strategy (str): The selected strategy for selling plants ("MaximizeStock" or "MinimizeStock").
123
  - extra_rate (int): The premium rate for selling plants.
124
+ - amount (list of int): Stock levels of each plant type.
125
 
126
  Returns:
127
  str: A description of the optimal solution, including which plants to sell,
 
145
  sold_prices = np.array(price * (1 + extra_rate))
146
 
147
  # Initialize the master problem
148
+ model = Model("BewilderingBlooms")
149
 
150
  # Decision variables in master problem
151
  x = [
 
180
  # Final solution processing
181
  solution = []
182
  total_price = 0
183
+ total_count = 0
184
 
185
  if model.getStatus() == "optimal":
186
  for i, var in enumerate(x):
 
189
  f"{plants_names[i]} ({sold_prices[i]} {currency}): {v}\n"
190
  )
191
  total_price += v * sold_prices[i]
192
+ total_count += v
193
 
194
  if optimal_total_value == budget:
195
+ return f"Great! Found a combination of items with a total value equal to the budget ({budget} {currency}).😃\n\n{''.join(solution)}\nTotal value: {int(total_price)} {currency}\nTotal count: {total_count}" # Count: {int(model.getObjVal())}
196
 
197
+ return f"Oops! {int(budget - optimal_total_value)} {currency} short of the target value ({budget} {currency}).😣\n\n{''.join(solution)}\nTotal value: {int(total_price)} {currency}\nTotal count: {total_count}" # Count: {int(model.getObjVal())}
198
 
199
  return "No solution found for the second optimization!"
200
 
 
203
 
204
  # 高亮每种植物的最高品质
205
  css = """
206
+ .highlight-first-gold {background-color: #fafad2}
207
+ .highlight-first-gems {background-color: #fed9b4}
208
  """
209
 
210
+
211
+ def show_checkboxgroup(currency, select_all=False):
212
+ """
213
+ 根据选定的货币显示选择框。
214
+ """
215
+ plants_tuples: list[tuple[str, str]] = [
216
+ (PLANTS_LABLES[pl], pl)
217
+ for pl in PLANTS_LABLES.keys()
218
+ if check_currency(pl, currency)
219
+ ]
220
+
221
+ if select_all:
222
+ default_value = [v for (n, v) in plants_tuples if select_all]
223
+ else:
224
+ default_value = None
225
+
226
+ checkbox_group_component = gr.CheckboxGroup(
227
+ choices=plants_tuples,
228
+ value=default_value,
229
+ type="value",
230
+ label="Plants",
231
+ info="Select plants",
232
+ interactive=True,
233
+ )
234
+ return checkbox_group_component
235
+
236
+
237
+ def show_plant_boxes(currency, plants=None):
238
+ _inventory = {}
239
+ species_set = set()
240
+ species_count = 0
241
+ new_species = False
242
+
243
+ for _, row in df.iterrows():
244
+ if (
245
+ row[currency] != 0
246
+ and row["tier"] != "feeble"
247
+ and (not plants or row["species"] in plants)
248
+ ):
249
+ species_set.add(row["species"])
250
+ inventory_key = f"{row['species']}_{row['tier']}"
251
+ _inventory[inventory_key] = gr.Number(
252
+ label=PLANTS_LABLES[row["species"]],
253
+ info=f"{PLANTS_TIERS[row['tier']]} ${row[currency]}",
254
+ value=0,
255
+ precision=0,
256
+ minimum=0,
257
+ maximum=500,
258
+ step=10,
259
+ visible=True,
260
+ elem_classes=(
261
+ f"highlight-first-{currency}"
262
+ if len(species_set) > species_count
263
+ else None
264
+ ),
265
+ )
266
+ species_count = len(species_set)
267
+ else:
268
+ _inventory[f"{row['species']}_{row['tier']}"] = gr.Number(visible=False)
269
+
270
+ return list(_inventory.values())
271
+
272
+
273
+ def handle_currency(currency):
274
+ """
275
+ 根据选定的货币类型更新库存组件"""
276
+ return [False, show_checkboxgroup(currency)] + show_plant_boxes(currency, None)
277
+
278
+
279
+ def handle_select_all(initial_state, currency):
280
+ return [(not initial_state)] + [show_checkboxgroup(currency, not initial_state)]
281
+
282
+
283
  with gr.Blocks(css=css) as demo:
284
  gr.Markdown(
285
  """
286
+ <center><font size=8>HPMA Bewildering Blooms Calculator👨🏻‍🌾</font></center>
287
 
288
  This program is essentially a solver for a variant of the knapsack problem.
289
  Another more versatile [application](https://huggingface.co/spaces/oh-my-dear-ai/easy-knapsack-problem).
 
340
  info="Select a strategy:",
341
  )
342
 
343
+ plants_filter = show_checkboxgroup(currency_radio.value)
344
+
345
+ with gr.Row():
346
+ select_all_state = gr.State(False)
347
+ select_all_button = gr.Button(value="Select All⭕", size="sm")
348
+ filter_button = gr.Button(value="Filter Plants🔍", size="lg")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
  # Create the dynamic plant inventory inputs
351
  with gr.Row() as inventory_row:
352
+ inventory = show_plant_boxes(currency_radio.value, plants_filter.value)
353
 
354
  # Add a row for the Clear and Calculate buttons
355
  with gr.Row():
356
+ inventory_clear_btn = gr.ClearButton(inventory, size="sm", value="❌Clear")
357
 
358
+ # Add a button to trigger the calculation
359
+ inventory_submit_btn = gr.Button(value="🛠Calculate")
360
 
361
  # Add a row for the result textbox
362
  with gr.Row():
363
  result = gr.Textbox(label="Output")
364
 
365
  # Set up the button click event to call the calculator function
366
+ inventory_submit_btn.click(
367
  calculator,
368
  inputs=[currency_radio, budget, selected_strategy, acquisition_rate]
369
  + inventory,
 
373
 
374
  # Update the inventory when the currency changes
375
  currency_radio.change(
376
+ fn=handle_currency, # Adjusted function to return only the components
 
 
377
  inputs=[currency_radio],
378
+ outputs=[
379
+ select_all_state,
380
+ plants_filter,
381
+ ]
382
+ + inventory, # Update each child in the inventory_row
383
+ )
384
+
385
+ filter_button.click(
386
+ fn=show_plant_boxes,
387
+ inputs=[currency_radio, plants_filter],
388
+ outputs=inventory,
389
+ )
390
+
391
+ select_all_button.click(
392
+ fn=handle_select_all,
393
+ inputs=[select_all_state, currency_radio],
394
+ outputs=[select_all_state, plants_filter],
395
  )
396
 
397
  # Launch the Gradio application