Skip to content

Commit

Permalink
fixed models
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiog1901 committed Jul 15, 2023
1 parent f0edfed commit 86cad01
Show file tree
Hide file tree
Showing 15 changed files with 384 additions and 185 deletions.
17 changes: 13 additions & 4 deletions storage/worst_crm.ddl.sql
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,21 @@ INSERT INTO task_status (name) VALUES ('NEW'), ('OPEN'), ('ON HOLD'), ('PENDING'
CREATE TABLE models (
-- pk
name STRING NOT NULL,
-- audit info
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
created_by STRING NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ON UPDATE now(),
updated_by STRING NULL,
-- fields
model_def JSONB,
CONSTRAINT pk PRIMARY KEY (name)
skema JSONB,
CONSTRAINT pk PRIMARY KEY (name),
CONSTRAINT created_by_in_users FOREIGN KEY (created_by)
REFERENCES users(user_id) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT updated_by_in_users FOREIGN KEY (updated_by)
REFERENCES users(user_id) ON DELETE SET NULL ON UPDATE CASCADE
);
INSERT INTO models VALUES
('account', '{}'),
INSERT INTO models (name, skema) VALUES
('account', '{"properties": {}}'),
('opportunity', '{}'),
('artifact', '{}'),
('project', '{}'),
Expand Down
114 changes: 72 additions & 42 deletions worst_crm/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
ProjectOverview,
ProjectOverviewWithAccountName,
ProjectOverviewWithOpportunityName,
Model,
ModelInDB,
Status,
Task,
TaskFilters,
Expand Down Expand Up @@ -103,47 +105,59 @@ def get_all_account_status() -> list[Status]:
return execute_stmt("SELECT name FROM account_status", model=Status, is_list=True)


def create_account_status(status: str):
execute_stmt(
"UPSERT INTO account_status(name) VALUES (%s)", (status,), returning_rs=False
def create_account_status(status: str) -> Status | None:
return execute_stmt(
"INSERT INTO account_status(name) VALUES (%s) RETURNING name",
(status,),
model=Status,
)


def delete_account_status(status: str):
execute_stmt(
"DELETE FROM account_status WHERE name = %s", (status,), returning_rs=False
def delete_account_status(status: str) -> Status | None:
return execute_stmt(
"DELETE FROM account_status WHERE name = %s RETURNING name",
(status,),
model=Status,
)


def get_all_project_status() -> list[Status]:
return execute_stmt("SELECT name FROM project_status", model=Status, is_list=True)


def create_project_status(status: str):
execute_stmt(
"UPSERT INTO project_status(name) VALUES (%s)", (status,), returning_rs=False
def create_project_status(status: str) -> Status | None:
return execute_stmt(
"INSERT INTO project_status(name) VALUES (%s) RETURNING name",
(status,),
model=Status,
)


def delete_project_status(status: str):
execute_stmt(
"DELETE FROM project_status WHERE name = %s", (status,), returning_rs=False
def delete_project_status(status: str) -> Status | None:
return execute_stmt(
"DELETE FROM project_status WHERE name = %s RETURNING name",
(status,),
model=Status,
)


def get_all_task_status() -> list[Status]:
return execute_stmt("SELECT name FROM task_status", model=Status, is_list=True)


def create_task_status(status: str):
execute_stmt(
"UPSERT INTO task_status(name) VALUES (%s)", (status,), returning_rs=False
def create_task_status(status: str) -> Status | None:
return execute_stmt(
"INSERT INTO task_status(name) VALUES (%s) RETURNING name",
(status,),
model=Status,
)


def delete_task_status(status: str):
execute_stmt(
"DELETE FROM task_status WHERE name = %s", (status,), returning_rs=False
def delete_task_status(status: str) -> Status | None:
return execute_stmt(
"DELETE FROM task_status WHERE name = %s RETURNING name",
(status,),
model=Status,
)


Expand Down Expand Up @@ -295,6 +309,14 @@ def __get_where_clause(


# ADMIN/MODELS


# ACCOUNTS
MODEL_IN_DB_COLS = get_fields(ModelInDB)
MODEL_IN_DB_PLACEHOLDERS = get_placeholders(ModelInDB)
MODEL_COLS = get_fields(Model)


def get_type(json_data_type: str) -> str:
"""
Maps a python type to a column data type
Expand All @@ -307,17 +329,18 @@ def get_type(json_data_type: str) -> str:
}[json_data_type]


def get_model(name: str) -> dict:
def get_model(name: str) -> Model:
return execute_stmt(
"""
SELECT model_def
f"""
SELECT {MODEL_COLS}
FROM models
WHERE name = %s""",
(name,),
)[0]
Model,
)


def update_model(name: str, model: dict) -> dict:
def update_model(model_in_db: ModelInDB) -> Model | None:
def get_table_name(name: str) -> str:
return {
"account": "accounts",
Expand All @@ -331,42 +354,44 @@ def get_table_name(name: str) -> str:
"contact": "contacts",
}[name]

old_model = get_model(name)
old_model = get_model(model_in_db.name)

additions: dict[str, str] = {}
removals: list[str] = []

for k in old_model.get("properties", {}).keys():
if k not in model.get("properties", {}).keys():
for k in old_model.skema.properties.keys():
if k not in model_in_db.skema.properties.keys():
removals.append(k)

for k, v in model.get("properties", {}).items():
if k not in old_model.get("properties", {}).keys():
for k, v in model_in_db.skema.properties.items():
if k not in old_model.skema.properties.keys():
additions[k] = v["type"]

# drop column stmts have to be executed in their own transaction
for x in removals:
execute_stmt(
f"""SET sql_safe_updates = false;
ALTER TABLE {get_table_name(name)} DROP COLUMN {x};
ALTER TABLE {get_table_name(model_in_db.name)} DROP COLUMN {x};
SET sql_safe_updates = true;
""",
returning_rs=False,
)

for x, y in additions.items():
execute_stmt(
f"ALTER TABLE {get_table_name(name)} ADD COLUMN {x} {get_type(y)};",
f"ALTER TABLE {get_table_name(model_in_db.name)} ADD COLUMN {x} {get_type(y)};",
returning_rs=False,
)

new_model = execute_stmt(
"""UPDATE models
SET model_def = %s
f"""
UPDATE models SET
({MODEL_IN_DB_COLS}) = ({MODEL_IN_DB_PLACEHOLDERS})
WHERE name = %s
RETURNING model_def""",
(model, name),
)[0]
RETURNING {MODEL_COLS}""",
(*tuple(model_in_db.model_dump().values()), model_in_db.name),
Model,
)

# trigger async App restart
update_watch()
Expand Down Expand Up @@ -493,7 +518,7 @@ def remove_account_attachment(account_id: UUID, s3_object_name: str) -> None:


def get_all_contacts() -> list[ContactWithAccountName]:
fully_qualified = ", ".join([f"contacts.{x}" for x in Contact.__fields__.keys()])
fully_qualified = ", ".join([f"contacts.{x}" for x in Contact.model_fields.keys()])

return execute_stmt(
f"""
Expand Down Expand Up @@ -602,7 +627,7 @@ def get_all_opportunities(
)

fully_qualified = ", ".join(
[f"opportunities.{x}" for x in OpportunityOverview.__fields__.keys()]
[f"opportunities.{x}" for x in OpportunityOverview.model_fields.keys()]
)
return execute_stmt(
f"""
Expand Down Expand Up @@ -829,7 +854,7 @@ def get_all_artifacts(
)

fully_qualified = ", ".join(
[f"artifacts.{x}" for x in ArtifactOverview.__fields__.keys()]
[f"artifacts.{x}" for x in ArtifactOverview.model_fields.keys()]
)

return execute_stmt(
Expand Down Expand Up @@ -861,7 +886,7 @@ def get_all_artifacts_for_account_id(
)

fully_qualified = ", ".join(
[f"artifacts.{x}" for x in ArtifactOverview.__fields__.keys()]
[f"artifacts.{x}" for x in ArtifactOverview.model_fields.keys()]
)

return execute_stmt(
Expand Down Expand Up @@ -983,7 +1008,7 @@ def get_all_projects(
)

fully_qualified = ", ".join(
[f"projects.{x}" for x in ProjectOverview.__fields__.keys()]
[f"projects.{x}" for x in ProjectOverview.model_fields.keys()]
)

return execute_stmt(
Expand Down Expand Up @@ -1015,7 +1040,7 @@ def get_all_projects_for_account_id(
)

fully_qualified = ", ".join(
[f"projects.{x}" for x in ProjectOverview.__fields__.keys()]
[f"projects.{x}" for x in ProjectOverview.model_fields.keys()]
)

return execute_stmt(
Expand Down Expand Up @@ -1164,7 +1189,9 @@ def get_all_tasks_for_opportunity_id(
task_filters, table_name="tasks", include_where=False
)

fully_qualified = ", ".join([f"tasks.{x}" for x in TaskOverview.__fields__.keys()])
fully_qualified = ", ".join(
[f"tasks.{x}" for x in TaskOverview.model_fields.keys()]
)

return execute_stmt(
f"""
Expand Down Expand Up @@ -1738,6 +1765,9 @@ def execute_stmt(
rs = cur.fetchone()
if rs:
if model:
print(model.__name__, "here")

print(rs)
return model(**{k: rs[i] for i, k in enumerate(col_names)})
else:
return rs
Expand Down
37 changes: 35 additions & 2 deletions worst_crm/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from enum import Enum
from pydantic import create_model, BaseModel, EmailStr, Field
from pydantic.fields import FieldInfo
from uuid import UUID
Expand All @@ -24,7 +25,7 @@ def to_snake_case(string):
with conn.cursor() as cur:
cur.execute(
"""
SELECT model_def
SELECT skema
FROM models
WHERE name = %s""",
(to_snake_case(model_name),),
Expand Down Expand Up @@ -192,6 +193,38 @@ class Status(BaseModel):
name: str = Field(min_length=3, max_length=20)


# MODELS


class ModelName(str, Enum):
account = "account"
artifact = "artifact"
contact = "contact"
opportunity = "opportunity"
project = "project"
task = "task"


class PydanticModel(BaseModel):
properties: dict
required: list[str] | None = None
title: str | None = None
type: str | None = None


class UpdatedModel(BaseModel):
name: ModelName
skema: PydanticModel


class ModelInDB(UpdatedModel, CommonInDB):
pass


class Model(DBComputed, ModelInDB):
pass


# ACCOUNT
class UpdatedAccount(Basic1, Text):
account_id: UUID | None = None
Expand Down Expand Up @@ -308,7 +341,7 @@ class UpdatedArtifact(Name):
opportunity_id: UUID
artifact_id: UUID | None = None
artifact_schema_id: str
payload: dict | None = None
payload: dict
tags: set[str] | None = None


Expand Down
6 changes: 3 additions & 3 deletions worst_crm/routers/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async def create_account(
db.log_event,
NAME,
current_user.user_id,
inspect.currentframe().f_code.co_name,
inspect.currentframe().f_code.co_name, # type: ignore
x.model_dump_json(),
)

Expand All @@ -91,7 +91,7 @@ async def update_account(
db.log_event,
NAME,
current_user.user_id,
inspect.currentframe().f_code.co_name,
inspect.currentframe().f_code.co_name, # type: ignore
x.model_dump_json(),
)

Expand All @@ -113,7 +113,7 @@ async def delete_account(
db.log_event,
NAME,
current_user.user_id,
inspect.currentframe().f_code.co_name,
inspect.currentframe().f_code.co_name, # type: ignore
x.model_dump_json(),
)

Expand Down
8 changes: 2 additions & 6 deletions worst_crm/routers/admin/admin.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
from fastapi import APIRouter, Security
from fastapi import APIRouter

from worst_crm import dependencies as dep
from . import users, status, models

router = APIRouter(
prefix="/admin",
dependencies=[Security(dep.get_current_user, scopes=["admin"])],
)
router = APIRouter(prefix="/admin")


router.include_router(users.router)
Expand Down
Loading

0 comments on commit 86cad01

Please sign in to comment.