Skip to content

Commit

Permalink
expand fe
Browse files Browse the repository at this point in the history
  • Loading branch information
szvsw committed Feb 9, 2024
1 parent 0325203 commit 23afb01
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 20 deletions.
8 changes: 8 additions & 0 deletions .streamlit/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# this is needed for local development with docker
[server]
# if you don't want to start the default browser:
headless = true
# you will need this for local development:
runOnSave = true
# you will need this if running docker on windows host:
fileWatcherType = "poll"
12 changes: 1 addition & 11 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,6 @@
}
},
"isort.args": ["--profile", "black"],
"terminal.integrated.env.linux": {
"PYTHONPATH": "${workspaceFolder}/campus-decarb"
},
"terminal.integrated.env.osx": {
"PYTHONPATH": "${workspaceFolder}/campus-decarb"
},
"terminal.integrated.env.windows": {
"PYTHONPATH": "${workspaceFolder}/campus-decarb"
},
"python.analysis.extraPaths": ["campus-decarb"],
"jupyter.notebookFileRoot": "${workspaceFolder}/campus-decarb",
"jupyter.notebookFileRoot": "${workspaceFolder}",
"python.envFile": "${workspaceFolder}/.env"
}
Empty file removed campus-decarb/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions docker-compose.override.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: '3.4'
services:
frontend:
volumes:
- ./frontend:/app/frontend
- ./lib:/app/lib
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: '3.4'
services:
frontend: # offloads api callbacks to a scalable service
build:
context: .
dockerfile: frontend/Dockerfile
env_file:
- .env
ports:
- "8501:8501"
16 changes: 16 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.9-slim

WORKDIR /app

COPY requirements/ requirements/

RUN pip install --no-cache-dir -r requirements/base-requirements.txt -r requirements/fe-requirements.txt

COPY .streamlit/ .streamlit/
COPY frontend/ frontend/
COPY lib/ lib/

ENV PYTHONPATH=/app

CMD ["streamlit", "run", "frontend/app.py", "--server.port", "8501", "--server.address", "0.0.0.0"]

19 changes: 19 additions & 0 deletions frontend/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Literal, Optional

from pydantic import Field
from pydantic_settings import BaseSettings

FrontendEnvs = Literal["dev", "prod"]


class FrontendSettings(BaseSettings):

env: str = Field("dev", env="ENV")
password: str = Field(..., env="PASSWORD")

class Config:
env_prefix = "FRONTEND_"
env_file = ".env"


frontend_settings = FrontendSettings()
222 changes: 214 additions & 8 deletions frontend/app.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,239 @@
import json
import os

import pandas as pd
import plotly.express as px
import streamlit as st

from frontend import frontend_settings as settings
from lib.client import client
from lib.supa import Building

st.set_page_config(layout="wide", page_title="MIT Decarbonization")


@st.cache_data
def get_all_buildings():
df = pd.DataFrame(client.table("Building").select("*").execute().data).set_index('id')
df = pd.DataFrame(client.table("Building").select("*").execute().data).set_index(
"id"
)
# create csv bytes
csv = df.to_csv().encode()
return df, csv


@st.cache_data
def get_building_scenarios(building_id: int):
ids = (
client.table("DemandScenarioBuilding")
.select("id, demand_scenario_id")
.eq("building_id", building_id)
.execute()
.data
)
scenario_ids = [d["demand_scenario_id"] for d in ids]
results_ids = [d["id"] for d in ids]
return scenario_ids, results_ids


@st.cache_data
def get_scenarios():
return client.table("DemandScenario").select("*").execute().data


@st.cache_data
def get_scenario_results(scenario_id: int):
ids = (
client.table("DemandScenarioBuilding")
.select("id")
.eq("demand_scenario_id", scenario_id)
.execute()
.data
)
results_ids = [d["id"] for d in ids]
results = (
client.table("BuildingSimulationResult")
.select("*")
.in_("id", results_ids)
.execute()
.data
)
dfs = []
for result in results:
result["heating"] = json.loads(result["heating"])
result["cooling"] = json.loads(result["cooling"])
result["lighting"] = json.loads(result["lighting"])
result["equipment"] = json.loads(result["equipment"])
df = pd.DataFrame(
{
"heating": result["heating"],
"cooling": result["cooling"],
"lighting": result["lighting"],
"equipment": result["equipment"],
}
)
df["Timestamp"] = pd.date_range(start="2024-01-01", periods=len(df), freq="h")
df = df.set_index("Timestamp")
df["result_id"] = result["id"]
df = df.set_index("result_id", append=True)
dfs.append(df)
df_buildings = pd.concat(dfs)
df = df_buildings.groupby("Timestamp").sum()
df.columns = [x.capitalize() for x in df.columns]
df = df.reset_index("Timestamp")
df_melted = df.melt(
id_vars=["Timestamp"], var_name="End Use", value_name="Energy [J]"
)
return df, df_melted, df_buildings


@st.cache_data
def get_scenario_building_result(scenario_id: int, building_id: int):
ids = (
client.table("DemandScenarioBuilding")
.select("id")
.eq("demand_scenario_id", scenario_id)
.eq("building_id", building_id)
.execute()
.data
)
results_ids = [d["id"] for d in ids]
results = (
client.table("BuildingSimulationResult")
.select("*")
.in_("id", results_ids)
.execute()
.data
)
df = pd.DataFrame(
{
"heating": json.loads(results[0]["heating"]),
"cooling": json.loads(results[0]["cooling"]),
"lighting": json.loads(results[0]["lighting"]),
"equipment": json.loads(results[0]["equipment"]),
}
)
df["Timestamp"] = pd.date_range(start="2024-01-01", periods=len(df), freq="h")
df.columns = [x.capitalize() for x in df.columns]
df_melted = df.melt(
id_vars=["Timestamp"], var_name="End Use", value_name="Energy [J]"
)

return df, df_melted


ENDUSE_PASTEL_COLORS = {
"Heating": "#FF7671",
"Cooling": "#6D68E6",
"Lighting": "#FFD700",
"Equipment": "#90EE90",
}


def render_title():
st.title("MIT Decarbonization")


def render_buildings():
st.header("Buildings")
all_buildings, all_buildings_csv = get_all_buildings()
st.download_button("Download all buildings", all_buildings_csv, "buildings_metadata.csv", "Download all buildings", use_container_width=True, type="primary")
building_id = st.selectbox('Building', all_buildings.index, format_func=lambda x: all_buildings.loc[x, 'name'], help='Select a building to view its data')
st.download_button(
"Download all building metadata",
all_buildings_csv,
"buildings_metadata.csv",
"Download all buildings",
use_container_width=True,
type="primary",
)
building_id = st.selectbox(
"Building",
all_buildings.index,
format_func=lambda x: all_buildings.loc[x, "name"],
help="Select a building to view its data",
)
building = all_buildings.loc[building_id]
st.dataframe(building)
scenario_ids, results_ids = get_building_scenarios(building_id)
all_scenarios = get_scenarios()
filtered_scenarios = [s for s in all_scenarios if s["id"] in scenario_ids]
l, r = st.columns(2)
with l:
scenario_id = st.selectbox(
"Building Demand Scenario",
scenario_ids,
format_func=lambda x: [
s["name"] for s in filtered_scenarios if s["id"] == x
][0],
help="Select a demand scenario to view its data",
)
result, result_melted = get_scenario_building_result(scenario_id, building_id)
fig = px.line(
result_melted,
x="Timestamp",
y="Energy [J]",
color="End Use",
title=f"Building {building['name']} Energy Use",
color_discrete_map=ENDUSE_PASTEL_COLORS,
)
st.plotly_chart(fig, use_container_width=True)

with r:
st.dataframe(building)

render_title()
st.divider()
render_buildings()

def render_building_scenarios():
all_scenarios = get_scenarios()
scenario = st.selectbox(
"Demand Scenario",
all_scenarios,
format_func=lambda x: x["name"],
help="Select a demand scenario to view its data",
)
df, df_melted, df_buildings = get_scenario_results(scenario["id"])
l, r = st.columns(2)
with l:
st.download_button(
"Download scenario results",
df.to_csv().encode(),
f"{scenario['name']}_results.csv",
"Download scenario results",
use_container_width=True,
type="primary",
)
with r:
st.download_button(
"Download scenario buildings results",
df_buildings.to_csv().encode(),
f"{scenario['name']}_buildings_results.csv",
"Download scenario buildings results",
use_container_width=True,
type="primary",
)
fig = px.line(
df_melted,
x="Timestamp",
y="Energy [J]",
color="End Use",
title=f"{scenario['name']} Demand Scenario",
color_discrete_map=ENDUSE_PASTEL_COLORS,
)
st.plotly_chart(fig, use_container_width=True)


def password_protect():
if "password" not in st.session_state:
st.session_state.password = None
if st.session_state.password == settings.password:
return True
else:
password = st.text_input("Password", type="password")
st.session_state.password = password
return st.session_state.password == settings.password


render_title()
logged_in = password_protect()
if logged_in:
buildings_tab, scenarios_tab = st.tabs(["Buildings", "Scenarios"])
with buildings_tab:
render_buildings()
with scenarios_tab:
render_building_scenarios()
3 changes: 2 additions & 1 deletion requirements/fe-requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
streamlit
streamlit
plotly

0 comments on commit 23afb01

Please sign in to comment.