Skip to content

Commit

Permalink
create supabase client
Browse files Browse the repository at this point in the history
  • Loading branch information
szvsw committed Feb 8, 2024
1 parent c7cd4ca commit d63aaa5
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 0 deletions.
22 changes: 22 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
},
"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",
"python.envFile": "${workspaceFolder}/.env"
}
28 changes: 28 additions & 0 deletions campus-decarb/lib/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Optional

from pydantic import Field, computed_field
from pydantic_settings import BaseSettings


class SupaSettings(BaseSettings):
url: str = Field(..., env="URL")
service_role_key: str = Field(..., env="SERVICE_ROLE_KEY")
anon_key: Optional[str] = Field(..., env="ANON_KEY")
host: Optional[str] = Field(..., env="HOST")
port: Optional[int] = Field(..., env="PORT")
user: Optional[str] = Field(..., env="USER")
database: Optional[str] = Field(..., env="DATABASE")
password: Optional[str] = Field(..., env="PASSWORD")
echo: Optional[bool] = Field(False, env="ECHO")

@computed_field
@property
def connection_string(self) -> str:
return f"postgresql://{self.user}:{self.password}@{self.host}:{self.port}/{self.database}"

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


supa_settings = SupaSettings()
12 changes: 12 additions & 0 deletions campus-decarb/lib/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import os

from lib import supa_settings
from supabase import create_client
from supabase.client import ClientOptions

client_options = ClientOptions(postgrest_client_timeout=60)
client = create_client(
supabase_url=supa_settings.url,
supabase_key=supa_settings.service_role_key,
options=client_options,
)
155 changes: 155 additions & 0 deletions campus-decarb/lib/supa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import json
import os
from datetime import datetime
from typing import Optional

import numpy as np
import pandas as pd
from lib.client import client
from pydantic import BaseModel, Field, model_validator, validate_call


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")


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):
building = client.table("Building").select("*").eq("id", id).execute()
if len(building.data) == 0:
raise ValueError(f"Building with id {id} not found")

return cls(**building.data[0])

@classmethod
@validate_call()
def create(cls, building: BuildingBase):
building = client.table("Building").insert(building.model_dump()).execute()
return cls(**building.data[0])

def commit(self):
client.table("Building").upsert(self.model_dump()).execute()


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
equipment: np.ndarray
pumps: np.ndarray
fans: np.ndarray
water: np.ndarray
misc: np.ndarray

# set up all np.ndarrays to serialize to list
class Config:
json_encoders = {np.ndarray: lambda v: v.tolist()}

@property
def building_id(self):
res = (
client.table("DemandScenarioBuilding")
.select("building_id")
.eq("id", self.id)
.execute()
)
return res.data[0].get("building_id")

@property
def demand_scenario_id(self):
res = (
client.table("DemandScenarioBuilding")
.select("demand_scenario_id")
.eq("id", self.id)
.execute()
)
return res.data[0].get("demand_scenario_id")

@property
def design_vector_id(self):
res = (
client.table("DemandScenarioBuilding")
.select("design_vector_id")
.eq("id", self.id)
.execute()
)
return res.data[0].get("design_vector_id")

@model_validator(mode="before")
def cast_fields_to_numpy(cls, v):
for key in v:
if cls.model_fields[key].annotation == np.ndarray:
if type(v[key]) == list:
v[key] = np.array(v[key])
elif type(v[key]) == str:
v[key] = np.array(json.loads(v[key]))
elif type(v[key]) == pd.Series:
v[key] = v[key].values
else:
pass

assert v[key].shape == (
8760,
), f"Field {key} must have shape (8760,) but has shape {v[key].shape}"
return v

@classmethod
def get(cls, id: int):
res = (
client.table("BuildingSimulationResult").select("*").eq("id", id).execute()
)
if len(res.data) == 0:
raise ValueError(f"BuildingSimulationResult with id {id} not found")

return cls(**res.data[0])

def to_df(self) -> pd.DataFrame:
df = pd.DataFrame(
{
"heating": self.heating,
"cooling": self.cooling,
"lighting": self.lighting,
"equipment": self.equipment,
"pumps": self.pumps,
"fans": self.fans,
"water": self.water,
"misc": self.misc,
},
index=pd.date_range(
start="2024-01-01 00:00:00", periods=8760, freq="H", name="timestep"
),
)
df = df.set_index(
pd.Series([self.id] * 8760, name="building_id"),
append=True,
)
raise ValueError("Not finished implementing; decide on multiindex!")
df = df.unstack(level="timestep")
return df

# def from_df(self, df: pd.DataFrame):
# series = df.loc[self.id]
# self.heating = series.heating.values
# self.cooling = series.cooling.values
# self.lighting = series.lighting.values
# self.equipment = series.equipment.values
# self.pumps = series.pumps.values
# self.fans = series.fans.values
# self.water = series.water.values
# self.misc = series.misc.values

def commit(self):
client.table("BuildingSimulationResult").upsert(
self.model_dump(mode="json", exclude=["created_at"])
).execute()

0 comments on commit d63aaa5

Please sign in to comment.