diff --git a/campus-decarb/frontend/__init__.py b/campus-decarb/frontend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/campus-decarb/frontend/app.py b/campus-decarb/frontend/app.py new file mode 100644 index 0000000..f3ef07b --- /dev/null +++ b/campus-decarb/frontend/app.py @@ -0,0 +1,33 @@ +import pandas as pd +import streamlit as st +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') + # create csv bytes + csv = df.to_csv().encode() + return df, csv + +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') + building = all_buildings.loc[building_id] + st.dataframe(building) + + +render_title() +st.divider() +render_buildings() + + + + diff --git a/campus-decarb/lib/supa.py b/campus-decarb/lib/supa.py index b6a3f87..6243002 100644 --- a/campus-decarb/lib/supa.py +++ b/campus-decarb/lib/supa.py @@ -1,24 +1,28 @@ import json import os from datetime import datetime -from typing import Optional +from typing import Literal, Optional import numpy as np import pandas as pd from lib.client import client from pydantic import BaseModel, Field, model_validator, validate_call +BuildingUsage = Literal['Lab & Mixed Use', 'Office & Mixed Use', 'Lab Dominant', 'Mechanical', 'Support Areas & Parking', 'Residential', 'Special', 'Office'] +BuildingGroupLevel1 = Literal['CUP', 'Non-CUP', 'Leased Buildings', 'FSILGs', 'Off Campus Buildings'] class BuildingBase(BaseModel, extra="forbid"): name: str = Field(..., description="Name of the building") - gfa: float = Field(..., gt=100, description="Gross Floor Area of the building, m2") + gfa: Optional[float] = Field(..., ge=0, description="Gross Floor Area of the building, m2") + building_number: Optional[str] = Field(None, description="MIT Building number") + usage: Optional[BuildingUsage] = Field(None, description="Building usage") + group_level_1: Optional[BuildingGroupLevel1] = Field(None, description="Non-CUP, CUP, etc") + height: Optional[float] = Field(None, description="Height of the building, m") + year: Optional[int] = Field(None, description="Year of construction") class Building(BuildingBase): id: int = Field(..., description="Unique identifier for the building") - created_at: str = Field( - ..., description="Timestamp of the creation of the building" - ) @classmethod def get(cls, id: int): @@ -31,7 +35,7 @@ def get(cls, id: int): @classmethod @validate_call() def create(cls, building: BuildingBase): - building = client.table("Building").insert(building.model_dump()).execute() + building = client.table("Building").upsert(building.model_dump()).execute() return cls(**building.data[0]) def commit(self): @@ -40,9 +44,6 @@ def commit(self): class BuildingSimulationResult(BaseModel, arbitrary_types_allowed=True, extra="forbid"): id: int - created_at: Optional[str] = Field( - None, description="Timestamp of the creation of the record" - ) heating: np.ndarray cooling: np.ndarray lighting: np.ndarray @@ -151,5 +152,30 @@ def to_df(self) -> pd.DataFrame: def commit(self): client.table("BuildingSimulationResult").upsert( - self.model_dump(mode="json", exclude=["created_at"]) + self.model_dump(mode="json") ).execute() + + +if __name__ == "__main__": + import math + + import numpy as np + + # TODO: + # add in better area breakdowns + # add in metering types etc + df = pd.read_csv("data/mit_buildings_info.csv") + for i, row in df.iterrows(): + building = BuildingBase( + name=row["BUILDING_NAME_LONG"], + building_number=row["BUILDING_NUMBER"], + group_level_1=row["BUILDING_GROUP_LEVEL1"], + usage=row["CLUSTER_NUM"], + gfa=( + row["EXT_GROSS_AREA"] if not math.isnan(row["EXT_GROSS_AREA"]) else None + ), + height=(row["BUILDING_HEIGHT"] if not math.isnan(row["BUILDING_HEIGHT"]) else None), + year=row["YEAR_CONST_BEGAN"] if not math.isnan(row["YEAR_CONST_BEGAN"]) else None, + + ) + Building.create(building) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3f1eca1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +-r requirements/base-requirements.txt +-r requirements/fe-requirements.txt \ No newline at end of file diff --git a/requirements/base-requirements.txt b/requirements/base-requirements.txt index ba65203..544cb65 100644 --- a/requirements/base-requirements.txt +++ b/requirements/base-requirements.txt @@ -1,2 +1,4 @@ pydantic -pandera \ No newline at end of file +pydantic_settings +pandera +supabase \ No newline at end of file diff --git a/requirements/fe-requirements.txt b/requirements/fe-requirements.txt new file mode 100644 index 0000000..e251330 --- /dev/null +++ b/requirements/fe-requirements.txt @@ -0,0 +1 @@ +streamlit \ No newline at end of file