Okay, let’s have a look at plotting the data we were looking at last post.

Temperature Gauge

I am going to start with a rotary gauge that will show the current day’s most recently available temperature. Comparing that to the minimum, average and maximum for the current month. Though I am thinking perhaps I should be comparing it to those values for the same day across the years in the database. Will see.

This code is going to be very similar to the rainfall gauge. Maybe some adjustments for title and such. So, I think I am just going to code a function to generate the gauge. Then test that in an if block before I look at adding it to the dashboard.

I copied the rainfall gauge method, renamed it, refactored and wrote a quick test. And, of course, I wasted a bunch of time playing with the colours.

When coding the test I realized I would need to query the database to get the latest temperature for the currently selected/specified date. Typically, the current day. But, I am thinking I may allow for the option to review past dates. Hence, my query for a past date—roughly a month ago as I work on the draft of this post.

def temp_gauge(dttm, c_temp, t_avg, t_min, t_max):
  """Generate and return figure for gauge showing today's temperature compared to
     the historical minimum, average and maximum for the current month

    Params:
      dttm: datetime for current temperature
      c_temp: current day's temperature
      t_avg: historical average temperature for current month
      t_min: historical minimum rainfall for period
      t_max: historical maximum rainfall for period
  """
  g_ttl = f"Temperature: {dttm}"
  g_rng = int(((t_max * 1.2) // 10) * 10)

  fig = go.Figure(go.Indicator(
    domain = {'x': [0, 1], 'y': [0, 1]},
    value = c_temp,
    mode = "gauge+number+delta",
    title = {'text': g_ttl, 'font': {'size': 36}},
    delta = {'reference': t_avg, 'increasing': {'color': "#FA8100"}},
    number = {'font': {'size': 54}},
    gauge = {
      'axis': {'range': [None, g_rng], 'tickwidth': 1, 'tickcolor': "maroon"},
      'bar': {'color': "#FBA94B"},
      'bgcolor': "white",
      'borderwidth': 2,
      'bordercolor': "gray",
      'steps' : [
          {'range': [0, t_min], 'color': "#FEEFDE"},
          {'range': [t_min, t_max], 'color': "#FBC587"},
          {'range': [t_max, g_rng], 'color': "#FEEFDE"}
          ],
      'threshold' : {'line': {'color': "#FA8100", 'width': 4}, 'thickness': 0.75, 'value': t_avg}}))
  fig.update_layout(autosize=False, width=600, height=400,
      margin=dict(l=25, r=15, b=0, t=20, pad=0),
      paper_bgcolor = "#FFF9F5", font = {'color': "maroon", 'family': "Arial"})
  return fig
... ...
    if do_tp_gauge:
      # get temp for selected date
      n_tbl = rfall.tnms["tp_tnm"]
      q_dtmp = f"""SELECT datetime, temperature
        FROM {n_tbl}
        WHERE SUBSTR(datetime, 1, 10)='{c_dttm}' AND temperature!='' AND temperature IS NOT Null;"""
      t_day = rfall.qry_pd(q_dtmp)
      print(t_day)
      ix_lrw = len(t_day) - 1
      c_dttm, c_tmp = t_day.loc[ix_lrw, :].values.tolist()

      mn_anx = rfall.get_mn_anx(c_dttm[:7])
      t_av, t_mn, t_mx = mn_anx.loc[0, :].values.tolist()
      print(mn_anx, "\n", t_av, t_mn, t_mx)
      fig = temp_gauge(c_dttm, c_tmp, t_av, t_mn, t_mx)

In the terminal I got the following. As you will see, the selected datetime and temperature is the second one.

(dbd-3.13) PS R:\learn\dashboard\utils> python db_charts.py
           datetime  temperature
0  2025.09.24 07:24         12.0
1  2025.09.24 14:55         21.0
          av   mn    mx
0  13.935106  7.0  26.0
 13.935106382978724 7.0 26.0

And in a browser tab I got the following. Not including the HTML in an iframe, just a png.

Current State of Daily Temperature Gauge
image of a gauge chart showing the current temperature vs historical values

The darker center portion of the gauge marks the historical minimum and maximum temperatures for the current month. I think, doing it that way more clearly identifies where the current day (the darker, narrower curve) fits in that range.

Historical Yearly Values for the Current Month

Not quite sure how I am going to do this. But I think I will once again (as with the rainfall data) start by looking at a grouped bar chart. One bar for minimum, average and maximum for the month for each year. I am going to stick to a maximum of the 10 most recent years.

As above, rather than coding the chart in a test block, I am going to just work on the function. Testing that in an if block in __main__. I am doing it this way as I already have working code that all I may need to do is refactor. I did think about refactoring the existing function, but don’t want to also test its previous functionality along with the new. So, a new function, copy over existing code, refactor and test. Updating as needed to get what I want displayed. Well, and how I want it displayed.

def hist_tmp_mon(c_mn, mn_tmp, sv_pth=""):
  """Generate and return plotly grouped bar chart showing the the monthly 
    minimum, average and maximum temperatures for a given month for some
    number of years.

    params:
      c_mn: current month, mm, str
      mn_tmp: dataframe containing the data to plot
             one row for each year, columns [yr, av, mn, mx]
      sv_pth: path to which to save chart html, do not save if falsy
    
    returns: plotly figure
  """
  c_nms = ["Monthly Minimum", "Monthly Average", "Monthly Maximum"]
  yr_vals = mn_tmp.loc[:, 'yr']
  grid_clr = "blue"
  grid_wd = 1

  mx_y = mn_tmp['mx'].max()
  print(mx_y)
  fig = go.Figure()
  # add trace for each data type to the plotly figure
  for c_ndx, c_col in enumerate(["mn", "av", "mx"]):
    y_vals = list(mn_tmp.loc[:, c_col])
    x_vals = yr_vals
    fig.add_trace(go.Bar(
      x=x_vals,
      y=y_vals,
      cliponaxis=False,
      name=c_nms[c_ndx]
    ))
  # generate a group bar chart
  fig.update_layout(scattermode="group", xaxis=dict(type="category"))
  fig.update_yaxes(showgrid=True, gridwidth=grid_wd, gridcolor=grid_clr)
  fig.update_layout(width=640, height=480, plot_bgcolor = "#ffffff",
    margin=dict(l=0, r=12, b=5, t=40, pad=12),
    title_text=f"Historical {l_mons[int(c_mn)-1]} Temperatures", title_font_size=25, title_x=0.5, title_y=0.97,
    xaxis=dict(title=dict(text="Year")), yaxis=dict(title=dict(text="Temperature")),
  )
  fig.update_layout(legend={'itemsizing': 'constant', 'orientation': 'h', 'y': -0.15, 'xanchor': 'right', 'x': 1})
  if sv_pth:
    fig.write_html(sv_pth)
  return fig
... ...
    if do_tp_yr_hist:
      f_pth = d_fig/f"yr_tmpr_{mn_dy}.html"
      t_hist = rfall.get_yr_t_manx(c_dttm[:7])
      print(t_hist)
      fig = hist_tmp_mon(mn_dy[:2], t_hist, sv_pth=f_pth)

    fig.show()

And, in a browser tab I got the following.

And for now I think that works for me. Not sure I need anything fancier.

Now to sort out what goes where on the dashboard.

Update Dashboard

We have 4 things to add to the dashboard for this selection: daily temperature gauge, weather condition pie chart, table of temperatures for the month, and the yearly history chart. The latter is the widest item and the table the potentially longest (as the month progresses). So I am thinking the table will span all rows of the dashboard. The historical grouped bar chart will be on its own row. Not yet sure about the other two.

But, let’s try something and see how it looks/works.

Took me awhile, but I think I have something reasonable working for the temperature and weather condition selection in the dashboard. In the various iterations and bug fixes, I eventually settled on using tabs to select between the temperature gauge and the weather condition pie chart in the top row of the left column. But, no way to center in the column without messing with adding markdown to write HTML to the dashboard. Didn’t feel like messing with that at the moment.

And, there were a number of bugs that got fixed. In some cases all too slowly—I didn’t always read the message in full, not very bright some days. I may show the corrected code in this post or I may just update the post in which the code was first covered/discussed.

Some new variables with respect to the current date being displayed in the dashboard.

... ...
c_dtm = time.localtime()
c_dt = time.strftime("%Y.%m.%d", c_dtm)
# year and month as "yyyy.mm"
c_ym = time.strftime("%Y.%m", c_dtm)
# month and day as "mm.dd"
c_md = time.strftime("%m.%d", c_dtm)
# month name as string
s_mon = time.strftime("%B", c_dtm)

# for testing
c_dt = "2025.10.16"
c_ym = "2025.10"
c_md = "10.16"
s_mon = "October"

Then I needed to account for the different configuration for the temperature/weather versus rainfall. I was originally going to use row2 for both cases. So, even though I no longer use it for the temperature display, it still remains outside the else block.

... ...
if w_item == "Current month's temperatures":
  grid2 = [col.container(height=1000) for col in st.columns([4, 1])]
else:
  row1 = st.columns([3, 2])
  grid1 = [col.container(height=500) for col in row1]

row2 = st.container(height=500)

And finally the code to display the temperature/weather charts and data.

elif w_item == "Current month's temperatures":
  c_dttm, c_tmp, c_cnd = rfall.get_c_t_wc(c_dt)
  mn_anx = rfall.get_mn_anx(c_ym)
  t_av, t_mn, t_mx = mn_anx.loc[0, :].values.tolist()
  t_gg = dbc.temp_gauge(c_dttm, c_tmp, t_av, t_mn, t_mx)
  wc_cnt = rfall.get_wc_count(c_md)
  wc_pie = dbc.wc_hist_pie(c_md, wc_cnt, c_cnd)
  df_temps = rfall.get_mon_temps(c_ym)
  t_hist = rfall.get_yr_t_manx(c_md[:2])
  fig_th = dbc.hist_tmp_mon(c_md[:2], t_hist)
  with grid2[0]:
    cntr_1 = st.container()
    cntr_2 = st.container()
    with cntr_1:
      tab1, tab2 = st.tabs(["Temperature Gauge", "Weather Condition Pie Chart"])
      with tab1:
        st.plotly_chart(t_gg, use_container_width=False)
      with tab2:
        st.plotly_chart(wc_pie, use_container_width=False)
    with cntr_2:
      st.plotly_chart(fig_th)
  with grid2[1]:
    st.dataframe(df_temps, height=1000)

And here’s a screen capture of the dashboard when temperature data was selected in the sidebar. And the temperature gauge tab was selected. For now won’t bother showing the equivalent image when the weather condition pie chart is selected.

Dashboard Displaying Temperature Charts and Data
image of dashboard displaying temperature charts and data

Done

And, I believe that’s it for this post. I think I am going to look at adding an option in the sidebar to allow the viewer (i.e. me) to select the date to be displayed in the dashboard. Never know, might want to look at past dates.

Until next time, do enjoy your successes. Even if arrived at painfully.