feat: add RSS data models (SQLAlchemy + Pydantic)#857
feat: add RSS data models (SQLAlchemy + Pydantic)#857HeloiseJoffe wants to merge 4 commits intoDIRACGrid:mainfrom
Conversation
dee80c6 to
bf5abcb
Compare
59514d8 to
036cb9c
Compare
There was a problem hiding this comment.
Right now db.py does three things:
- queries the database
- dispatches based on resource type
- converts DB strings into Pydantic models via
map_status().
In diracx's architecture (routers -> logic -> db -> core), we try to follow a n-layer architecture approach, where each layer has a single responsibility. I think the DB layer should only do the first one and should be generic and simple.
See https://diracx.diracgrid.org/en/latest/dev/explanations/components/#python-modules
I would likely have 2 methods here: one to query the sites and one to query the resources.
Then the logic would call one of these 2 methods, and would return the right pydantic model based on the result (so it would likely have 4 functions: site, storage, compute, fts).
What do you think?
There was a problem hiding this comment.
You're right, I think that's actually better. I've organized the features according to the diracx's architecture.
cb53ac5 to
87c7b68
Compare
87c7b68 to
5c3f59d
Compare
There was a problem hiding this comment.
What about calling that file query.py?
| def map_status(db_status: str, reason: str | None = None) -> ResourceStatus: | ||
| if db_status in ALLOWED: | ||
| return AllowedStatus(allowed=True) | ||
|
|
||
| if db_status in BANNED: | ||
| return BannedStatus( | ||
| allowed=False, | ||
| reason=reason or db_status, | ||
| ) | ||
|
|
||
| return BannedStatus( | ||
| allowed=False, | ||
| reason=f"Unknown status: {db_status}", | ||
| ) |
There was a problem hiding this comment.
What about moving map_status to diracx-logic?
There was a problem hiding this comment.
And therefore you can potentially move that test to diracx-logic too
|
|
||
|
|
||
| class ElementStatusBase: | ||
| __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"} |
There was a problem hiding this comment.
Do you really need that?
| __table_args__ = {"mysql_engine": "InnoDB", "mysql_charset": "utf8mb4"} |
| vo: Mapped[str64] = mapped_column("VO", primary_key=True, server_default="all") | ||
| status: Mapped[str] = mapped_column("Status", String(8), server_default="") | ||
| reason: Mapped[str512] = mapped_column("Reason", server_default="Unspecified") | ||
| dateeffective: Mapped[datetime] = mapped_column("DateEffective", DateTime) |
There was a problem hiding this comment.
Don't you need to pass SmarterDateTime here? Same for the other dates.
| dateeffective: Mapped[datetime] = mapped_column("DateEffective", DateTime) | |
| date_effective: Mapped[datetime] = mapped_column("DateEffective", SmarterDateTime()) |
| "ID", BigInteger, autoincrement=True, primary_key=True | ||
| ) | ||
| name: Mapped[str64] = mapped_column("Name") | ||
| statustype: Mapped[str128] = mapped_column("StatusType", server_default="all") |
There was a problem hiding this comment.
Any reason for not using snake case as in the other table definitions? Same for all the attribute names
| statustype: Mapped[str128] = mapped_column("StatusType", server_default="all") | |
| status_type: Mapped[str128] = mapped_column("StatusType", server_default="all") |
|
|
||
| def map_status(db_status: str, reason: str | None = None) -> ResourceStatus: | ||
| if db_status in ALLOWED: | ||
| return AllowedStatus(allowed=True) |
There was a problem hiding this comment.
The should be what is in reason, at least for the case when db_status is "Degraded".
| ) | ||
|
|
||
| # Test with the test Site (should be found) | ||
| async with rss_db as rss_db: |
There was a problem hiding this comment.
Minor: to avoid name shadowing
| async with rss_db as rss_db: | |
| async with rss_db as db: |
|
|
||
| metadata = RSSBase.metadata | ||
|
|
||
| async def get_site_status(self, name: str, vo: str = "all") -> tuple[str, str]: |
There was a problem hiding this comment.
@fstagni out of curiosity, shouldn't we (not now but later) transition from this magic all to NULL in the DB?
If not VO is set, then we assume it's for all of them.
Unless I miss something?
| async def get_resource_status( | ||
| self, | ||
| name: str, | ||
| statustypes: list[str] = ["all"], |
There was a problem hiding this comment.
Beware of https://docs.astral.sh/ruff/rules/mutable-argument-default/
It's fine here because we don't mutate status_types but we should be careful.
@fstagni same here, should we transition from all for VO and StatusType to NULL at some point?
There was a problem hiding this comment.
I would agree to this one too.
|
|
||
| def map_status(db_status: str, reason: str | None = None) -> ResourceStatus: | ||
| if db_status in ALLOWED: | ||
| return AllowedStatus(allowed=True) |
There was a problem hiding this comment.
The should be what is in reason, at least for the case when db_status is "Degraded".
|
|
||
| metadata = RSSBase.metadata | ||
|
|
||
| async def get_site_status(self, name: str, vo: str = "all") -> tuple[str, str]: |
| async def get_resource_status( | ||
| self, | ||
| name: str, | ||
| statustypes: list[str] = ["all"], |
There was a problem hiding this comment.
I would agree to this one too.
| ) | ||
|
|
||
|
|
||
| class ResourceNotFoundError(DiracError): |
There was a problem hiding this comment.
I am really wondering if we need this one at all.
Closes #837
Changes :