aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Nanis2026-10-11 19:17:02 -0700
committerMax Nanis2026-10-11 19:17:02 -0700
commit590b57d0a495f2cfbbb2e4187b59c9ac25b8896b (patch)
tree906a33bcef2515b484264b36fbb186a282cf0c8f
parent64a93f0874381d19212e1af4c0beacaeb36323d9 (diff)
downloadgeneralresearch-ee7e998d5c1d73c205827a7f3314067faf2de9fb.tar.gz
generalresearch-ee7e998d5c1d73c205827a7f3314067faf2de9fb.zip
Moving away from assigning objects into redis explicitly.HEADv3.7.7master
-rw-r--r--generalresearch/managers/gr/business.py49
-rw-r--r--generalresearch/managers/gr/team.py29
-rw-r--r--generalresearch/managers/thl/product.py49
-rw-r--r--generalresearch/models/gr/authentication.py175
-rw-r--r--pyproject.toml2
5 files changed, 164 insertions, 140 deletions
diff --git a/generalresearch/managers/gr/business.py b/generalresearch/managers/gr/business.py
index 9338f03..bb7944d 100644
--- a/generalresearch/managers/gr/business.py
+++ b/generalresearch/managers/gr/business.py
@@ -314,48 +314,33 @@ class BusinessManager(PostgresManagerWithRedis):
return response
- def get_ids_by_user_id(self, user_id: PositiveInt) -> list[PositiveInt]:
+ def get_uuids(self, user_id: PositiveInt | None) -> list[UUIDStr]:
"""
- :return: Every Business UUIDStr that this GRUser has permission to view
+ :param user_id: If given, restrict to Businesses this GRUser has permission to view.
+ If None, return every Business.
+ :return: Business UUIDStrs
"""
- with self.pg_config.make_connection() as conn, conn.cursor() as c:
- c.execute(
- query=sql.SQL("""
- SELECT b.id
+ if user_id is None:
+ query = sql.SQL("""
+ SELECT b.uuid
FROM common_business AS b
- INNER JOIN common_team_businesses AS tb
- ON tb.business_id = b.id
- INNER JOIN common_membership AS cm
- ON tb.team_id = cm.team_id
- WHERE cm.user_id = %s
- """),
- params=(user_id,),
- )
-
- res = c.fetchall()
-
- return [i["id"] for i in res]
-
- def get_uuids_by_user_id(self, user_id: PositiveInt) -> list[UUIDStr]:
- """
- :return: Every Business UUIDStr that this GRUser has permission to view
- """
-
- with self.pg_config.make_connection() as conn, conn.cursor() as c:
- c.execute(
- query=sql.SQL("""
+ """)
+ params: tuple[int, ...] = ()
+ else:
+ query = sql.SQL("""
SELECT b.uuid
FROM common_business AS b
- INNER JOIN common_team_businesses AS tb
+ INNER JOIN common_team_businesses AS tb
ON tb.business_id = b.id
- INNER JOIN common_membership AS cm
+ INNER JOIN common_membership AS cm
ON tb.team_id = cm.team_id
WHERE cm.user_id = %s
- """),
- params=(user_id,),
- )
+ """)
+ params = (user_id,)
+ with self.pg_config.make_connection() as conn, conn.cursor() as c:
+ c.execute(query=query, params=params)
res = c.fetchall()
return [i["uuid"] for i in res]
diff --git a/generalresearch/managers/gr/team.py b/generalresearch/managers/gr/team.py
index 41af709..968f3b5 100644
--- a/generalresearch/managers/gr/team.py
+++ b/generalresearch/managers/gr/team.py
@@ -278,3 +278,32 @@ class TeamManager(PostgresManagerWithRedis):
res = c.fetchall()
return [Team.model_validate(item) for item in res]
+
+ def get_uuids(self, user_id: PositiveInt | None) -> list[UUIDStr]:
+ """
+ :param user_id: If given, restrict to Teams this GRUser has permission to view.
+ If None, return every Team.
+ :return: Team UUIDStrs
+ """
+
+ if user_id is None:
+ query = sql.SQL("""
+ SELECT team.uuid
+ FROM common_team AS team
+ """)
+ params: tuple[int, ...] = ()
+ else:
+ query = sql.SQL("""
+ SELECT team.uuid
+ FROM common_team AS team
+ INNER JOIN common_membership AS mem
+ ON mem.team_id = team.id
+ WHERE mem.user_id = %s
+ """)
+ params = (user_id,)
+
+ with self.pg_config.make_connection() as conn, conn.cursor() as c:
+ c.execute(query=query, params=params)
+ res = c.fetchall()
+
+ return [i["uuid"] for i in res]
diff --git a/generalresearch/managers/thl/product.py b/generalresearch/managers/thl/product.py
index aadb30b..78b0b7a 100644
--- a/generalresearch/managers/thl/product.py
+++ b/generalresearch/managers/thl/product.py
@@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, Any
from cachetools import TTLCache, cachedmethod, keys
from psycopg import Connection, sql
-from pydantic import NonNegativeInt
+from pydantic import NonNegativeInt, PositiveInt
from sentry_sdk import capture_exception
from generalresearch.managers.base import (
@@ -128,7 +128,7 @@ class ProductManager(PostgresManager):
product_uuids = self.get_all_uuids(rand_limit=rand_limit)
return self.filter_paginated(product_uuids=product_uuids)
- def get_all_uuids(self, rand_limit: int | None) -> list[UUIDStr]:
+ def get_all_uuids(self, rand_limit: PositiveInt | None) -> list[UUIDStr]:
if rand_limit:
res = self.pg_config.execute_sql_query(
@@ -142,12 +142,10 @@ class ProductManager(PostgresManager):
)
else:
- res = self.pg_config.execute_sql_query(
- query="""
+ res = self.pg_config.execute_sql_query(query="""
SELECT p.id::uuid
FROM userprofile_brokerageproduct AS p
- """
- )
+ """)
return [i["id"] for i in res]
def filter_paginated(
@@ -163,7 +161,7 @@ class ProductManager(PostgresManager):
order_field: str = "created",
descending: bool = False,
conn: Connection | None = None,
- ):
+ ) -> list[Product]:
"""
Automatically paginate the results of a filter query.
"""
@@ -390,6 +388,27 @@ class ProductManager(PostgresManager):
res = c.fetchone()
return int(res["cnt"])
+ def get_uuids(
+ self,
+ business_uuids: list[UUIDStr] | None = None,
+ team_uuids: list[UUIDStr] | None = None,
+ conn: Connection | None = None,
+ ) -> list[UUIDStr]:
+ filter_str, params = self.make_filter_str(
+ business_uuids=business_uuids,
+ team_uuids=team_uuids,
+ )
+ query = f"""
+ SELECT bp.uuid
+ FROM userprofile_brokerageproduct AS bp
+ {filter_str}
+ """
+ with self.connection(conn) as conn, conn.cursor() as c:
+ c.execute(query=query, params=params)
+ res = c.fetchall()
+
+ return [i["uuid"] for i in res]
+
@staticmethod
def make_filter_str(
product_uuids: list[UUIDStr] | None = None,
@@ -404,9 +423,9 @@ class ProductManager(PostgresManager):
params = {}
filters = []
identifiers = [product_uuids, business_uuids, team_uuids]
- assert sum([bool(x) for x in identifiers]) == 1, (
- "Must provide exactly one set of identifiers"
- )
+ assert (
+ sum([bool(x) for x in identifiers]) == 1
+ ), "Must provide exactly one set of identifiers"
identifier = next(x for x in identifiers if x)
assert all(is_valid_uuid(v) for v in identifier), "invalid uuid"
@@ -557,16 +576,10 @@ class ProductManager(PostgresManager):
insert_data["payments_enabled"] = instance.payments_enabled
try:
- insert_data["id_int"] = next(
- iter(
- self.pg_config.execute_sql_query(
- query="""
+ insert_data["id_int"] = next(iter(self.pg_config.execute_sql_query(query="""
SELECT COALESCE(MAX(id_int), 0) + 1 as id_int
FROM userprofile_brokerageproduct
- """
- )
- )
- )["id_int"]
+ """)))["id_int"]
instance.id_int = insert_data["id_int"]
query = """
diff --git a/generalresearch/models/gr/authentication.py b/generalresearch/models/gr/authentication.py
index 142f842..fa0bcae 100644
--- a/generalresearch/models/gr/authentication.py
+++ b/generalresearch/models/gr/authentication.py
@@ -16,10 +16,12 @@ from pydantic import (
field_validator,
)
-from generalresearch.decorators import LOG
from generalresearch.models.custom_types import AwareDatetimeISO, UUIDStr
if TYPE_CHECKING:
+ from generalresearch.managers.gr.business import BusinessManager
+ from generalresearch.managers.gr.team import TeamManager
+ from generalresearch.managers.thl.product import ProductManager
from generalresearch.models.gr.business import Business
from generalresearch.models.gr.team import Team
from generalresearch.models.thl.product import Product
@@ -164,8 +166,8 @@ class GRUser(BaseModel):
self.prefetch_businesses(pg_config=pg_config, redis_config=redis_config)
self.prefetch_teams(pg_config=pg_config, redis_config=redis_config)
- business_uuids = self.business_uuids or []
- team_uuids = self.team_uuids or []
+ business_uuids = [b.uuid for b in self.businesses or []]
+ team_uuids = [t.uuid for t in self.teams or []]
if len(business_uuids + team_uuids) == 0:
self.products = []
@@ -205,83 +207,78 @@ class GRUser(BaseModel):
def cache_key(self) -> str:
return f"gr_user:{self.id}"
- @property
- def business_uuids(self) -> list[UUIDStr] | None:
- if self.businesses is None:
- LOG.warning("prefetch not run")
- return None
-
- return [b.uuid for b in self.businesses]
-
- @property
- def business_ids(self) -> list[PositiveInt] | None:
- if self.businesses is None:
- LOG.warning("prefetch not run")
- return None
-
- return [b.id for b in self.businesses if b.id is not None]
-
- @property
- def team_uuids(self) -> list[UUIDStr] | None:
- if self.teams is None:
- LOG.warning("prefetch not run")
- return None
-
- return [t.uuid for t in self.teams]
-
- @property
- def team_ids(self) -> list[PositiveInt] | None:
- if self.teams is None:
- LOG.warning("prefetch not run")
- return None
-
- return [t.id for t in self.teams if t.id is not None]
-
- @property
- def product_uuids(self) -> list[UUIDStr] | None:
- if self.products is None:
- LOG.warning("prefetch not run")
- return None
+ # --- Methods ---
- return [p.uuid for p in self.products]
+ def business_uuids(self, business_manager: BusinessManager) -> list[UUIDStr]:
+ if self.is_superuser:
+ return business_manager.get_uuids(user_id=None)
+ else:
+ return business_manager.get_uuids(user_id=self.id)
- # --- Methods ---
+ def team_uuids(self, team_manager: TeamManager) -> list[UUIDStr]:
+ if self.is_superuser:
+ return team_manager.get_uuids(user_id=None)
+ else:
+ return team_manager.get_uuids(user_id=self.id)
- def set_cache(
+ def product_uuids(
self,
- pg_config: PostgresConfig,
- thl_web_rr: PostgresConfig,
- redis_config: RedisConfig,
- ) -> None:
- ex_secs = 60 * 60 * 24 * 3 # 3 days
-
- self.prefetch_teams(pg_config=pg_config, redis_config=redis_config)
- self.prefetch_businesses(pg_config=pg_config, redis_config=redis_config)
- self.prefetch_products(
- pg_config=pg_config,
- thl_pg_config=thl_web_rr,
- redis_config=redis_config,
+ business_manager: BusinessManager | None,
+ team_manager: TeamManager | None,
+ product_manager: ProductManager,
+ ) -> list[UUIDStr]:
+ assert (
+ business_manager is not None or team_manager is not None
+ ), "At least one of business_manager or team_manager is required"
+
+ return product_manager.get_uuids(
+ business_uuids=(
+ self.business_uuids(business_manager=business_manager)
+ if business_manager is not None
+ else None
+ ),
+ team_uuids=(
+ self.team_uuids(team_manager=team_manager)
+ if team_manager is not None
+ else None
+ ),
)
- self.prefetch_token(pg_config=pg_config)
- rc = redis_config.create_redis_client()
-
- rc.set(name=self.cache_key, value=self.to_redis(), ex=ex_secs)
- rc.set(
- name=f"{self.cache_key}:team_uuids",
- value=json.dumps(self.team_uuids),
- ex=ex_secs,
- )
- rc.set(
- name=f"{self.cache_key}:business_uuids",
- value=json.dumps(self.business_uuids),
- ex=ex_secs,
- )
- rc.set(
- name=f"{self.cache_key}:product_uuids",
- value=json.dumps(self.product_uuids),
- ex=ex_secs,
- )
+ # def set_cache(
+ # self,
+ # pg_config: PostgresConfig,
+ # thl_web_rr: PostgresConfig,
+ # redis_config: RedisConfig,
+ # ) -> None:
+ # ex_secs = 60 * 60 * 24 * 3 # 3 days
+
+ # self.prefetch_teams(pg_config=pg_config, redis_config=redis_config)
+ # self.prefetch_businesses(pg_config=pg_config, redis_config=redis_config)
+ # self.prefetch_products(
+ # pg_config=pg_config,
+ # thl_pg_config=thl_web_rr,
+ # redis_config=redis_config,
+ # )
+ # self.prefetch_token(pg_config=pg_config)
+
+ # rc = redis_config.create_redis_client()
+
+ # rc.set(name=self.cache_key, value=self.to_redis(), ex=ex_secs)
+ # rc.set(
+ # name=f"{self.cache_key}:team_uuids",
+ # value=json.dumps(self.team_uuids),
+ # ex=ex_secs,
+ # )
+ # rc.set(
+ # name=f"{self.cache_key}:business_uuids",
+ # value=json.dumps(self.business_uuids),
+ # ex=ex_secs,
+ # )
+ # rc.set(
+ # name=f"{self.cache_key}:product_uuids",
+ # value=json.dumps(self.product_uuids),
+ # ex=ex_secs,
+ # )
# --- ORM ---
@@ -290,26 +287,26 @@ class GRUser(BaseModel):
d["date_joined"] = d["date_joined"].replace(tzinfo=UTC)
return GRUser.model_validate(d)
- @classmethod
- def from_redis(cls, d: str | dict[str, Any]) -> GRUser:
- if isinstance(d, str):
- d = json.loads(d)
- assert isinstance(d, dict)
+ # @classmethod
+ # def from_redis(cls, d: str | dict[str, Any]) -> GRUser:
+ # if isinstance(d, str):
+ # d = json.loads(d)
+ # assert isinstance(d, dict)
- d["date_joined"] = datetime.fromisoformat(d["date_joined"])
+ # d["date_joined"] = datetime.fromisoformat(d["date_joined"])
- if d.get("token"):
- d["token"] = GRToken.from_redis(d["token"])
+ # if d.get("token"):
+ # d["token"] = GRToken.from_redis(d["token"])
- return GRUser.model_validate(d)
+ # return GRUser.model_validate(d)
- def to_redis(self) -> str:
- d = self.model_dump(mode="json", exclude={"businesses", "teams", "products"})
- d["business_uuids"] = self.business_uuids
- d["team_uuids"] = self.team_uuids
- d["product_uuids"] = self.product_uuids
+ # def to_redis(self) -> str:
+ # d = self.model_dump(mode="json", exclude={"businesses", "teams", "products"})
+ # d["business_uuids"] = self.business_uuids
+ # d["team_uuids"] = self.team_uuids
+ # d["product_uuids"] = self.product_uuids
- return json.dumps(d)
+ # return json.dumps(d)
class GRToken(BaseModel):
diff --git a/pyproject.toml b/pyproject.toml
index 5839d26..bf52137 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "generalresearch"
-version = "3.7.6"
+version = "3.7.7"
description = "Python Utilities for General Research"
readme = "README.md"
requires-python = ">=3.14"