AccruedInnovation commited on
Commit
03070aa
·
verified ·
1 Parent(s): 6e15d13

Update app.py

Browse files

adding batch costs, fixing indicators

Files changed (1) hide show
  1. app.py +105 -26
app.py CHANGED
@@ -135,6 +135,9 @@ class CannabinoidEstimator(param.Parameterized):
135
  shifts_per_week = param.Number(
136
  default=21.0, bounds=(1, 28), step=1.0, label="Shifts per week"
137
  )
 
 
 
138
 
139
  kg_processed_per_shift = 0.0
140
  labour_cost_per_shift = 0.0
@@ -215,6 +218,20 @@ class CannabinoidEstimator(param.Parameterized):
215
  "Value": "center",
216
  },
217
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  self._update_calculations()
219
 
220
  def _create_sliders(self):
@@ -456,6 +473,13 @@ class CannabinoidEstimator(param.Parameterized):
456
  format="0",
457
  )
458
 
 
 
 
 
 
 
 
459
  @param.depends(
460
  "kg_processed_per_hour",
461
  "finished_product_yield_pct",
@@ -470,6 +494,7 @@ class CannabinoidEstimator(param.Parameterized):
470
  "wholesale_cbx_price",
471
  "wholesale_cbx_pct",
472
  "batch_test_cost",
 
473
  "fixed_overhead_per_week",
474
  "workers_per_shift",
475
  "worker_hourly_rate",
@@ -555,10 +580,34 @@ class CannabinoidEstimator(param.Parameterized):
555
  else 0.0
556
  )
557
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  shift_cogs_before_output_specific = (
559
  self.labour_cost_per_shift
560
  + self.variable_cost_per_shift
561
  + self.overhead_cost_per_shift
 
562
  )
563
  shift_output_specific_cogs = (
564
  self.consumables_per_kg_output * self.saleable_kg_per_shift
@@ -698,6 +747,14 @@ class CannabinoidEstimator(param.Parameterized):
698
  self.processing_data_df = pd.DataFrame(processing_data_dict)
699
  if hasattr(self, "processing_table"):
700
  self.processing_table.value = self.processing_data_df
 
 
 
 
 
 
 
 
701
 
702
  def _get_money_formatters(self):
703
  return {
@@ -718,14 +775,14 @@ class CannabinoidEstimator(param.Parameterized):
718
  max_width=input_col_max_width,
719
  )
720
  col2 = pn.Column(
721
- pn.pane.Markdown("### Biomass parameters"),
722
  self.bio_cbx_pct_slider,
723
  self.bio_cost_slider,
724
  sizing_mode="stretch_width",
725
  max_width=input_col_max_width,
726
  )
727
  col3 = pn.Column(
728
- pn.pane.Markdown("### Consumable rates"),
729
  self.kwh_rate_slider,
730
  self.water_cost_per_1000l_slider,
731
  self.consumables_per_kg_bio_rate_slider,
@@ -733,14 +790,14 @@ class CannabinoidEstimator(param.Parameterized):
733
  max_width=input_col_max_width,
734
  )
735
  col4 = pn.Column(
736
- pn.pane.Markdown("### Wholesale details"),
737
  self.wholesale_cbx_price_slider,
738
  self.wholesale_cbx_pct_slider,
739
  sizing_mode="stretch_width",
740
  max_width=input_col_max_width,
741
  )
742
  col5 = pn.Column(
743
- pn.pane.Markdown("### Variable costs"),
744
  self.kwh_per_kg_bio_slider,
745
  self.water_liters_consumed_per_kg_bio_slider,
746
  self.consumables_per_kg_output_slider,
@@ -748,15 +805,17 @@ class CannabinoidEstimator(param.Parameterized):
748
  max_width=input_col_max_width,
749
  )
750
  col6 = pn.Column(
751
- pn.pane.Markdown("### Compliance"),
752
  self.batch_test_cost_slider,
753
- pn.pane.Markdown("### Overhead"),
 
 
754
  self.fixed_overhead_per_week_slider,
755
  sizing_mode="stretch_width",
756
  max_width=input_col_max_width,
757
  )
758
  col8 = pn.Column(
759
- pn.pane.Markdown("### Worker Details"),
760
  self.workers_per_shift_slider,
761
  self.worker_hourly_rate_slider,
762
  self.managers_per_shift_slider,
@@ -765,7 +824,7 @@ class CannabinoidEstimator(param.Parameterized):
765
  max_width=input_col_max_width,
766
  )
767
  col9 = pn.Column(
768
- pn.pane.Markdown("### Shift details"),
769
  self.labour_hours_per_shift_slider,
770
  self.processing_hours_per_shift_slider,
771
  self.shifts_per_day_slider,
@@ -773,7 +832,12 @@ class CannabinoidEstimator(param.Parameterized):
773
  sizing_mode="stretch_width",
774
  max_width=input_col_max_width,
775
  )
776
-
 
 
 
 
 
777
  input_grid = pn.FlexBox(
778
  col1, col2, col3, col4, col5, col8, col9, col6, align_content="normal"
779
  )
@@ -799,25 +863,30 @@ class CannabinoidEstimator(param.Parameterized):
799
  max_width=input_col_max_width,
800
  )
801
 
802
- profit_weekly = pn.indicators.Number(
803
- name="Weekly Profit",
804
- value=self.net_rev_per_week,
805
- format=f"${self.net_rev_per_week / 1000:.0f} k",
806
- default_color="green",
807
- align="center",
808
- )
809
 
810
- profit_pct = pn.indicators.Number(
811
- name="Operating Profit",
812
- value=self.operating_profit_pct,
813
- format=f"{self.operating_profit_pct * 100.0:.2f}%",
814
- default_color="green",
815
- align="center",
816
- )
817
-
 
 
 
 
 
818
  table_grid = pn.FlexBox(
819
- profit_weekly,
820
- profit_pct,
821
  processing_table_display,
822
  profit_table_display,
823
  money_table_display,
@@ -839,6 +908,16 @@ estimator_app = CannabinoidEstimator()
839
  # pn.config.raw_css = custom_themes.get_base_css(custom_themes.DARK_THEME_VARS)
840
  estimator_app.view().servable(title="CBx Revenue Estimator")
841
 
 
 
 
 
 
 
 
 
 
 
842
  if __name__ == "__main__":
843
  pn.serve(
844
  estimator_app.view(),
 
135
  shifts_per_week = param.Number(
136
  default=21.0, bounds=(1, 28), step=1.0, label="Shifts per week"
137
  )
138
+ batch_frequency = param.String(
139
+ default = "Day", label="New batch frequency"
140
+ )
141
 
142
  kg_processed_per_shift = 0.0
143
  labour_cost_per_shift = 0.0
 
218
  "Value": "center",
219
  },
220
  )
221
+ self.profit_weekly = pn.indicators.Number(
222
+ name="Weekly Profit",
223
+ value=self.net_rev_per_week,
224
+ format=f"${self.net_rev_per_week / 1000:.0f} k",
225
+ default_color="green",
226
+ align="center",
227
+ )
228
+ self.profit_pct = pn.indicators.Number(
229
+ name="Operating Profit",
230
+ value=self.operating_profit_pct,
231
+ format=f"{self.operating_profit_pct * 100.0:.2f}%",
232
+ default_color="green",
233
+ align="center",
234
+ )
235
  self._update_calculations()
236
 
237
  def _create_sliders(self):
 
473
  format="0",
474
  )
475
 
476
+ self.batch_frequency_radio = pn.widgets.RadioButtonGroup.from_param(
477
+ self.param.batch_frequency,
478
+ name=self.param.batch_frequency.label,
479
+ options=["Shift", "Day", "Week"],
480
+ button_type="primary",
481
+ )
482
+
483
  @param.depends(
484
  "kg_processed_per_hour",
485
  "finished_product_yield_pct",
 
494
  "wholesale_cbx_price",
495
  "wholesale_cbx_pct",
496
  "batch_test_cost",
497
+ "batch_frequency",
498
  "fixed_overhead_per_week",
499
  "workers_per_shift",
500
  "worker_hourly_rate",
 
580
  else 0.0
581
  )
582
 
583
+ # Calculate batch_test_cost_per_shift based on batch_frequency
584
+ self.batch_test_cost_per_shift = 0.0
585
+ if self.batch_frequency == "Shift":
586
+ self.batch_test_cost_per_shift = self.batch_test_cost
587
+ elif self.batch_frequency == "Day":
588
+ # Ensure self.shifts_per_day is defined and positive
589
+ if hasattr(self, 'shifts_per_day') and self.shifts_per_day > 0:
590
+ self.batch_test_cost_per_shift = self.batch_test_cost / self.shifts_per_day
591
+ else:
592
+ # If no shifts per day, or attribute not defined, cost per shift is 0
593
+ # Or, this could be an error condition if shifts_per_day is expected
594
+ self.batch_test_cost_per_shift = 0.0
595
+ elif self.batch_frequency == "Week":
596
+ # self.shifts_per_week is used above, so it should be available
597
+ if self.shifts_per_week > 0:
598
+ self.batch_test_cost_per_shift = self.batch_test_cost / self.shifts_per_week
599
+ else:
600
+ # If no shifts per week, cost per shift is 0
601
+ self.batch_test_cost_per_shift = 0.0
602
+ # else: # Optional: handle invalid self.batch_frequency value
603
+ # For example, raise ValueError or log a warning
604
+ # print(f"Warning: Unknown batch_frequency: {self.batch_frequency}")
605
+
606
  shift_cogs_before_output_specific = (
607
  self.labour_cost_per_shift
608
  + self.variable_cost_per_shift
609
  + self.overhead_cost_per_shift
610
+ + self.batch_test_cost_per_shift # Added batch test cost
611
  )
612
  shift_output_specific_cogs = (
613
  self.consumables_per_kg_output * self.saleable_kg_per_shift
 
747
  self.processing_data_df = pd.DataFrame(processing_data_dict)
748
  if hasattr(self, "processing_table"):
749
  self.processing_table.value = self.processing_data_df
750
+
751
+ if hasattr(self, "profit_weekly"):
752
+ self.profit_weekly.value = self.net_rev_per_week
753
+ self.profit_weekly.format = f"${self.net_rev_per_week / 1000:.0f} k"
754
+
755
+ if hasattr(self, "profit_pct"):
756
+ self.profit_pct.value = self.operating_profit_pct
757
+ self.profit_pct.format=f"{self.operating_profit_pct * 100.0:.2f}%"
758
 
759
  def _get_money_formatters(self):
760
  return {
 
775
  max_width=input_col_max_width,
776
  )
777
  col2 = pn.Column(
778
+ pn.pane.Markdown("### Biomass parameters", margin=0),
779
  self.bio_cbx_pct_slider,
780
  self.bio_cost_slider,
781
  sizing_mode="stretch_width",
782
  max_width=input_col_max_width,
783
  )
784
  col3 = pn.Column(
785
+ pn.pane.Markdown("### Consumable rates", margin=0),
786
  self.kwh_rate_slider,
787
  self.water_cost_per_1000l_slider,
788
  self.consumables_per_kg_bio_rate_slider,
 
790
  max_width=input_col_max_width,
791
  )
792
  col4 = pn.Column(
793
+ pn.pane.Markdown("### Wholesale details", margin=0),
794
  self.wholesale_cbx_price_slider,
795
  self.wholesale_cbx_pct_slider,
796
  sizing_mode="stretch_width",
797
  max_width=input_col_max_width,
798
  )
799
  col5 = pn.Column(
800
+ pn.pane.Markdown("### Variable costs", margin=0),
801
  self.kwh_per_kg_bio_slider,
802
  self.water_liters_consumed_per_kg_bio_slider,
803
  self.consumables_per_kg_output_slider,
 
805
  max_width=input_col_max_width,
806
  )
807
  col6 = pn.Column(
808
+ pn.pane.Markdown("### Compliance", margin=0),
809
  self.batch_test_cost_slider,
810
+ pn.pane.Markdown("New Batch Every:", margin=0),
811
+ self.batch_frequency_radio,
812
+ pn.pane.Markdown("### Overhead", margin=0),
813
  self.fixed_overhead_per_week_slider,
814
  sizing_mode="stretch_width",
815
  max_width=input_col_max_width,
816
  )
817
  col8 = pn.Column(
818
+ pn.pane.Markdown("### Worker Details", margin=0),
819
  self.workers_per_shift_slider,
820
  self.worker_hourly_rate_slider,
821
  self.managers_per_shift_slider,
 
824
  max_width=input_col_max_width,
825
  )
826
  col9 = pn.Column(
827
+ pn.pane.Markdown("### Shift details", margin=0),
828
  self.labour_hours_per_shift_slider,
829
  self.processing_hours_per_shift_slider,
830
  self.shifts_per_day_slider,
 
832
  sizing_mode="stretch_width",
833
  max_width=input_col_max_width,
834
  )
835
+
836
+ # input_grid = pn.GridSpec(sizing_mode="stretch_width", max_width=1800, margin=10)
837
+ # input_grid[0, 0] = col1
838
+ # input_grid[0, 1] = col2
839
+ # input_grid[0, 2] = col3
840
+ # input_grid[0, 3] = col4
841
  input_grid = pn.FlexBox(
842
  col1, col2, col3, col4, col5, col8, col9, col6, align_content="normal"
843
  )
 
863
  max_width=input_col_max_width,
864
  )
865
 
866
+ # profit_weekly = pn.indicators.Number(
867
+ # name="Weekly Profit",
868
+ # value=self.net_rev_per_week,
869
+ # format=f"${self.net_rev_per_week / 1000:.0f} k",
870
+ # default_color="green",
871
+ # align="center",
872
+ # )
873
 
874
+ # profit_pct = pn.indicators.Number(
875
+ # name="Operating Profit",
876
+ # value=self.operating_profit_pct,
877
+ # format=f"{self.operating_profit_pct * 100.0:.2f}%",
878
+ # default_color="green",
879
+ # align="center",
880
+ # )
881
+
882
+ # indicator_layout = pn.Column(profit_pct, profit_weekly, align="center")
883
+
884
+ # table_grid = pn.GridSpec(sizing_mode="stretch_width", max_width=1800, margin=10)
885
+ # table_grid[:, 0:2] = tables_layout
886
+ # table_grid[:, 2] = indicator_layout
887
  table_grid = pn.FlexBox(
888
+ self.profit_weekly,
889
+ self.profit_pct,
890
  processing_table_display,
891
  profit_table_display,
892
  money_table_display,
 
908
  # pn.config.raw_css = custom_themes.get_base_css(custom_themes.DARK_THEME_VARS)
909
  estimator_app.view().servable(title="CBx Revenue Estimator")
910
 
911
+ # Instantiate the template with widgets displayed in the sidebar
912
+ # template = pn.template.FastListTemplate(
913
+ # title="CBx Revenue Estimator (FastList Panel)",
914
+ # #theme = custom_themes.DarkTheme,
915
+ # #sidebar=[freq, phase],
916
+ # )
917
+
918
+ # template.main.append(estimator_app.view())
919
+ # template.servable()
920
+
921
  if __name__ == "__main__":
922
  pn.serve(
923
  estimator_app.view(),