perfact-api-app 0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- perfact_api_app-0.6/PKG-INFO +45 -0
- perfact_api_app-0.6/README.md +31 -0
- perfact_api_app-0.6/pyproject.toml +40 -0
- perfact_api_app-0.6/setup.cfg +4 -0
- perfact_api_app-0.6/src/perfact/api/app/model/__init__.py +28 -0
- perfact_api_app-0.6/src/perfact/api/app/model/appgroup.py +5 -0
- perfact_api_app-0.6/src/perfact/api/app/model/appperm.py +5 -0
- perfact_api_app-0.6/src/perfact/api/app/model/apppermxgroup.py +11 -0
- perfact_api_app-0.6/src/perfact/api/app/model/apppermxstc.py +12 -0
- perfact_api_app-0.6/src/perfact/api/app/model/appstc.py +16 -0
- perfact_api_app-0.6/src/perfact/api/app/model/apptblcleanup.py +99 -0
- perfact_api_app-0.6/src/perfact/api/app/model/appuser.py +6 -0
- perfact_api_app-0.6/src/perfact/api/app/model/appuserkey.py +9 -0
- perfact_api_app-0.6/src/perfact/api/app/model/appuserlogin.py +11 -0
- perfact_api_app-0.6/src/perfact/api/app/model/appuserxperm.py +12 -0
- perfact_api_app-0.6/src/perfact/api/app/model/appuserxstc.py +12 -0
- perfact_api_app-0.6/src/perfact/api/app/model/authinfo.py +13 -0
- perfact_api_app-0.6/src/perfact/api/app/model/selfilterable.py +137 -0
- perfact_api_app-0.6/src/perfact_api_app.egg-info/PKG-INFO +45 -0
- perfact_api_app-0.6/src/perfact_api_app.egg-info/SOURCES.txt +22 -0
- perfact_api_app-0.6/src/perfact_api_app.egg-info/dependency_links.txt +1 -0
- perfact_api_app-0.6/src/perfact_api_app.egg-info/requires.txt +2 -0
- perfact_api_app-0.6/src/perfact_api_app.egg-info/top_level.txt +1 -0
- perfact_api_app-0.6/tox.ini +31 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: perfact-api-app
|
|
3
|
+
Version: 0.6
|
|
4
|
+
Summary: PerFact API - SQLAlchemy models for the app namespace
|
|
5
|
+
Author-email: Viktor Dick <viktor.dick@perfact.de>
|
|
6
|
+
License: GPL-2.0-or-later
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: SQL
|
|
9
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: sqlalchemy
|
|
13
|
+
Requires-Dist: perfact-api-base-model
|
|
14
|
+
|
|
15
|
+
# perfact-api-app
|
|
16
|
+
|
|
17
|
+
SQLAlchemy models for the `app` namespace of the PerFact API.
|
|
18
|
+
|
|
19
|
+
## Models
|
|
20
|
+
|
|
21
|
+
Provides ORM models under `perfact.api.app.model`:
|
|
22
|
+
|
|
23
|
+
- `AppUser` / `AppUserKey` / `AppUserLogin` — user accounts, API keys, login sessions
|
|
24
|
+
- `AppStc` — org areas (Strukturknoten), hierarchical
|
|
25
|
+
- `AppPerm` / `AppPermXGroup` / `AppPermXStc` — permissions and their assignments
|
|
26
|
+
- `AppGroup` / `AppUserXPerm` / `AppUserXStc` — group and user-permission mappings
|
|
27
|
+
- `AppTblCleanup` — table cleanup job model
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
pip install perfact-api-app
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Development
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
pip install -e ../perfact-api-base-model/
|
|
39
|
+
pip install -e .
|
|
40
|
+
tox
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Maintainers
|
|
44
|
+
|
|
45
|
+
- Viktor Dick <viktor.dick@perfact.de>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# perfact-api-app
|
|
2
|
+
|
|
3
|
+
SQLAlchemy models for the `app` namespace of the PerFact API.
|
|
4
|
+
|
|
5
|
+
## Models
|
|
6
|
+
|
|
7
|
+
Provides ORM models under `perfact.api.app.model`:
|
|
8
|
+
|
|
9
|
+
- `AppUser` / `AppUserKey` / `AppUserLogin` — user accounts, API keys, login sessions
|
|
10
|
+
- `AppStc` — org areas (Strukturknoten), hierarchical
|
|
11
|
+
- `AppPerm` / `AppPermXGroup` / `AppPermXStc` — permissions and their assignments
|
|
12
|
+
- `AppGroup` / `AppUserXPerm` / `AppUserXStc` — group and user-permission mappings
|
|
13
|
+
- `AppTblCleanup` — table cleanup job model
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pip install perfact-api-app
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Development
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
pip install -e ../perfact-api-base-model/
|
|
25
|
+
pip install -e .
|
|
26
|
+
tox
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Maintainers
|
|
30
|
+
|
|
31
|
+
- Viktor Dick <viktor.dick@perfact.de>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.2", "setuptools-scm>=8.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "perfact-api-app"
|
|
7
|
+
authors = [
|
|
8
|
+
{name="Viktor Dick", email="viktor.dick@perfact.de"},
|
|
9
|
+
]
|
|
10
|
+
description = "PerFact API - SQLAlchemy models for the app namespace"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
license = {text = "GPL-2.0-or-later"}
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: SQL",
|
|
16
|
+
"Operating System :: POSIX :: Linux",
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"sqlalchemy",
|
|
20
|
+
"perfact-api-base-model",
|
|
21
|
+
]
|
|
22
|
+
dynamic = ["version"]
|
|
23
|
+
requires-python = ">=3.10"
|
|
24
|
+
|
|
25
|
+
[tool.distutils.bdist_wheel]
|
|
26
|
+
universal = 1
|
|
27
|
+
|
|
28
|
+
[tool.setuptools]
|
|
29
|
+
include-package-data = true
|
|
30
|
+
|
|
31
|
+
[tool.setuptools.packages.find]
|
|
32
|
+
where = ["src"]
|
|
33
|
+
|
|
34
|
+
[tool.setuptools_scm]
|
|
35
|
+
root = ".."
|
|
36
|
+
fallback_version = "0.0.0"
|
|
37
|
+
|
|
38
|
+
[tool.ruff]
|
|
39
|
+
[tool.ruff.lint]
|
|
40
|
+
select = ["E", "F", "W", "I"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from .appgroup import AppGroup
|
|
2
|
+
from .appperm import AppPerm
|
|
3
|
+
from .apppermxgroup import AppPermXGroup
|
|
4
|
+
from .apppermxstc import AppPermXStc
|
|
5
|
+
from .appstc import AppStc, AppStc_Paths
|
|
6
|
+
from .appuser import AppUser
|
|
7
|
+
from .appuserkey import AppUserKey
|
|
8
|
+
from .appuserlogin import AppUserLogin
|
|
9
|
+
from .appuserxperm import AppUserXPerm
|
|
10
|
+
from .appuserxstc import AppUserXStc
|
|
11
|
+
from .authinfo import AuthInfo
|
|
12
|
+
from .selfilterable import Selfilterable
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"AppGroup",
|
|
16
|
+
"AppPerm",
|
|
17
|
+
"AppPermXGroup",
|
|
18
|
+
"AppPermXStc",
|
|
19
|
+
"AppStc",
|
|
20
|
+
"AppStc_Paths",
|
|
21
|
+
"AppUser",
|
|
22
|
+
"AppUserKey",
|
|
23
|
+
"AppUserLogin",
|
|
24
|
+
"AppUserXPerm",
|
|
25
|
+
"AppUserXStc",
|
|
26
|
+
"Selfilterable",
|
|
27
|
+
"AuthInfo",
|
|
28
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from perfact.api.base.model import Base, ForeignKey, Mapped, mapped_column, relationship
|
|
2
|
+
|
|
3
|
+
from .appgroup import AppGroup
|
|
4
|
+
from .appperm import AppPerm
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AppPermXGroup(Base):
|
|
8
|
+
appgroup_id: Mapped[int] = mapped_column(ForeignKey(AppGroup.id))
|
|
9
|
+
appperm_id: Mapped[int] = mapped_column(ForeignKey(AppPerm.id))
|
|
10
|
+
perm: Mapped[AppPerm] = relationship()
|
|
11
|
+
group: Mapped[AppGroup] = relationship()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from perfact.api.base.model import Base, ForeignKey, Mapped, mapped_column, relationship
|
|
2
|
+
|
|
3
|
+
from .appperm import AppPerm
|
|
4
|
+
from .appstc import AppStc
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AppPermXStc(Base):
|
|
8
|
+
appperm_id: Mapped[int] = mapped_column(ForeignKey(AppPerm.id))
|
|
9
|
+
appstc_id: Mapped[int] = mapped_column(ForeignKey(AppStc.id))
|
|
10
|
+
|
|
11
|
+
perm: Mapped[AppPerm] = relationship()
|
|
12
|
+
stc: Mapped[AppStc] = relationship()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from perfact.api.base.model import Base, ForeignKey, Mapped, View, mapped_column
|
|
2
|
+
from sqlalchemy.types import ARRAY, Integer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AppStc(Base):
|
|
6
|
+
name: Mapped[str]
|
|
7
|
+
parent_appstc_id: Mapped[int | None] = mapped_column(ForeignKey("appstc.id"))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AppStc_Paths(View):
|
|
11
|
+
__tablename__ = "appstc_paths"
|
|
12
|
+
id: Mapped[int] = mapped_column("id", primary_key=True)
|
|
13
|
+
id_path: Mapped[list[int]] = mapped_column(
|
|
14
|
+
"id_path", ARRAY(Integer, as_tuple=True, zero_indexes=True)
|
|
15
|
+
)
|
|
16
|
+
depth: Mapped[int] = mapped_column("depth")
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from perfact.api.base.model import Base
|
|
6
|
+
from sqlalchemy import (
|
|
7
|
+
Column,
|
|
8
|
+
DateTime,
|
|
9
|
+
Integer,
|
|
10
|
+
MetaData,
|
|
11
|
+
Table,
|
|
12
|
+
delete,
|
|
13
|
+
select,
|
|
14
|
+
text,
|
|
15
|
+
)
|
|
16
|
+
from sqlalchemy.orm import Mapped, Session
|
|
17
|
+
|
|
18
|
+
metadata_obj = MetaData()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AppTblCleanup(Base):
|
|
22
|
+
tablename: Mapped[str]
|
|
23
|
+
range: Mapped[Optional[str]]
|
|
24
|
+
filter: Mapped[Optional[str]]
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class RunResult:
|
|
28
|
+
count: int # number of deleted records
|
|
29
|
+
elapsed: float # time in seconds it took
|
|
30
|
+
limit_reached: bool # if the limit was reached
|
|
31
|
+
|
|
32
|
+
def run(self, session: Session) -> RunResult:
|
|
33
|
+
"""
|
|
34
|
+
Execute cleanup for the selected table.
|
|
35
|
+
"""
|
|
36
|
+
start = time.monotonic()
|
|
37
|
+
LIMIT = 100_000
|
|
38
|
+
table = Table(
|
|
39
|
+
self.tablename,
|
|
40
|
+
metadata_obj,
|
|
41
|
+
Column(f"{self.tablename}_id", Integer, primary_key=True, key="id"),
|
|
42
|
+
Column(f"{self.tablename}_modtime", DateTime(timezone=True), key="modtime"),
|
|
43
|
+
extend_existing=True,
|
|
44
|
+
)
|
|
45
|
+
ids_select = (
|
|
46
|
+
select(table.c.id)
|
|
47
|
+
.where(table.c.modtime < text("now() - (:ival)::interval"))
|
|
48
|
+
.where(text(self.filter or "true"))
|
|
49
|
+
.params(ival=self.range)
|
|
50
|
+
.order_by(table.c.id)
|
|
51
|
+
.limit(LIMIT)
|
|
52
|
+
)
|
|
53
|
+
# Use connection().execute() (Core layer) instead of session.execute()
|
|
54
|
+
# (ORM layer): return type is CursorResult, which exposes rowcount.
|
|
55
|
+
# Also skips autoflush — intentional, this worker runs in its own
|
|
56
|
+
# session with no pending state.
|
|
57
|
+
result = session.connection().execute(
|
|
58
|
+
delete(table).where(table.c.id.in_(ids_select))
|
|
59
|
+
)
|
|
60
|
+
count = result.rowcount
|
|
61
|
+
elapsed = time.monotonic() - start
|
|
62
|
+
return AppTblCleanup.RunResult(
|
|
63
|
+
count=count,
|
|
64
|
+
limit_reached=(count == LIMIT),
|
|
65
|
+
elapsed=elapsed,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def run_cleanup_batches(session: Session):
|
|
70
|
+
"""
|
|
71
|
+
Clean up data according to given rules in apptblcleanup.
|
|
72
|
+
Commits after each processed batch. Returns a mapping from table name to
|
|
73
|
+
number of entries deleted.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
result = session.execute(select(AppTblCleanup))
|
|
77
|
+
rows = list(result.scalars().all())
|
|
78
|
+
stats = {
|
|
79
|
+
row.tablename: AppTblCleanup.RunResult(
|
|
80
|
+
count=0,
|
|
81
|
+
elapsed=0.0,
|
|
82
|
+
limit_reached=False,
|
|
83
|
+
)
|
|
84
|
+
for row in rows
|
|
85
|
+
}
|
|
86
|
+
while rows:
|
|
87
|
+
new_rows = []
|
|
88
|
+
for row in rows:
|
|
89
|
+
res = row.run(session)
|
|
90
|
+
tgt = stats[row.tablename]
|
|
91
|
+
tgt.count += res.count
|
|
92
|
+
tgt.elapsed += res.elapsed
|
|
93
|
+
tgt.limit_reached = tgt.limit_reached or res.limit_reached
|
|
94
|
+
if res.limit_reached:
|
|
95
|
+
new_rows.append(row)
|
|
96
|
+
|
|
97
|
+
yield stats
|
|
98
|
+
|
|
99
|
+
rows = new_rows
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from perfact.api.base.model import Base, ForeignKey, Mapped, mapped_column, relationship
|
|
2
|
+
|
|
3
|
+
from .appuser import AppUser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AppUserKey(Base):
|
|
7
|
+
appuser_id: Mapped[int] = mapped_column(ForeignKey(AppUser.id))
|
|
8
|
+
key: Mapped[str]
|
|
9
|
+
appuser: Mapped[AppUser] = relationship()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from perfact.api.base.model import Base, ForeignKey, Mapped, mapped_column, relationship
|
|
2
|
+
|
|
3
|
+
from .appuser import AppUser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AppUserLogin(Base):
|
|
7
|
+
appuser_id: Mapped[int] = mapped_column(ForeignKey(AppUser.id))
|
|
8
|
+
cookie: Mapped[str | None]
|
|
9
|
+
nextcookie: Mapped[str | None]
|
|
10
|
+
done: Mapped[bool] = mapped_column(default=False)
|
|
11
|
+
user: Mapped[AppUser] = relationship()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from perfact.api.base.model import Base, ForeignKey, Mapped, mapped_column, relationship
|
|
2
|
+
|
|
3
|
+
from .appperm import AppPerm
|
|
4
|
+
from .appuser import AppUser
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AppUserXPerm(Base):
|
|
8
|
+
appuser_id: Mapped[int] = mapped_column(ForeignKey(AppUser.id))
|
|
9
|
+
appperm_id: Mapped[int] = mapped_column(ForeignKey(AppPerm.id))
|
|
10
|
+
needsgrant: Mapped[bool] = mapped_column(server_default="false")
|
|
11
|
+
user: Mapped[AppUser] = relationship()
|
|
12
|
+
perm: Mapped[AppPerm] = relationship()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from perfact.api.base.model import Base, ForeignKey, Mapped, mapped_column, relationship
|
|
2
|
+
|
|
3
|
+
from .appstc import AppStc
|
|
4
|
+
from .appuser import AppUser
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AppUserXStc(Base):
|
|
8
|
+
appuser_id: Mapped[int] = mapped_column(ForeignKey(AppUser.id))
|
|
9
|
+
appstc_id: Mapped[int] = mapped_column(ForeignKey(AppStc.id))
|
|
10
|
+
|
|
11
|
+
user: Mapped[AppUser] = relationship()
|
|
12
|
+
stc: Mapped[AppStc] = relationship()
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from typing import List, Optional, Sequence, Type
|
|
2
|
+
|
|
3
|
+
from perfact.api.base.model import Base
|
|
4
|
+
from sqlalchemy import (
|
|
5
|
+
ARRAY,
|
|
6
|
+
BigInteger,
|
|
7
|
+
ColumnElement,
|
|
8
|
+
ScalarSelect,
|
|
9
|
+
Select,
|
|
10
|
+
and_,
|
|
11
|
+
cast,
|
|
12
|
+
func,
|
|
13
|
+
join,
|
|
14
|
+
literal,
|
|
15
|
+
or_,
|
|
16
|
+
select,
|
|
17
|
+
)
|
|
18
|
+
from sqlalchemy.dialects.postgresql import array
|
|
19
|
+
|
|
20
|
+
from .appstc import AppStc_Paths
|
|
21
|
+
from .authinfo import AuthInfo
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Selfilterable:
|
|
25
|
+
"""
|
|
26
|
+
selfilters is a mechanism which creates visibility rules for a table.
|
|
27
|
+
The rule is defined by a SQL WHERE statement.
|
|
28
|
+
This statement should be added to all queries to the database.
|
|
29
|
+
The selfilter can be retrived by calling the classmethod `get_selfilter`.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def get_selfilter(cls, auth: Optional[AuthInfo]) -> ColumnElement[bool]:
|
|
34
|
+
"""
|
|
35
|
+
returns the selfilter as a ColumnElement which can be used in
|
|
36
|
+
a WHERE/filter statement.
|
|
37
|
+
|
|
38
|
+
usage:
|
|
39
|
+
select(MtArt).filter(
|
|
40
|
+
MtArt.get_selfilter(...)
|
|
41
|
+
)
|
|
42
|
+
"""
|
|
43
|
+
raise NotImplementedError(f"No selfilter defined for {cls.__name__}")
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def generate_selfilter_stc_query(
|
|
47
|
+
entity: Type[Base],
|
|
48
|
+
stcrefcol: Optional[str] = None,
|
|
49
|
+
stcmxntable: Optional[Type[Base]] = None,
|
|
50
|
+
mxnflags: Sequence[str] = [],
|
|
51
|
+
appstc_ids=None,
|
|
52
|
+
inherit=False,
|
|
53
|
+
) -> Select:
|
|
54
|
+
"""
|
|
55
|
+
this implements
|
|
56
|
+
PerFact.WebApp.db_selfilter_sql_d.methods.appstc_query_iq
|
|
57
|
+
the sqlalchemy way.
|
|
58
|
+
|
|
59
|
+
The stcrefcol-column (e.g. <entity>_appstc_id) is used via AppStc_Paths to
|
|
60
|
+
find object ids linked to the given appstc.
|
|
61
|
+
If `stcmxntable` is set, the mxn-table is used to find
|
|
62
|
+
the object ids linked to the given appstc.
|
|
63
|
+
|
|
64
|
+
mxnflags:
|
|
65
|
+
Optional list of flags that must exist as additional boolean
|
|
66
|
+
columns of the MxN table. If set, only entries with this flag are
|
|
67
|
+
considered.
|
|
68
|
+
|
|
69
|
+
returns:
|
|
70
|
+
filter statement returning all object ids that are
|
|
71
|
+
mapped to the given appstc and allowed to present to the user.
|
|
72
|
+
|
|
73
|
+
usage:
|
|
74
|
+
conditions = [
|
|
75
|
+
MtArt.generate_selfilter_stc_query(appstc_ids=[stc.id]),
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
if (stcmxntable is None) == (stcrefcol is None):
|
|
80
|
+
raise RuntimeError("Either mxn or refcol need to be supplied")
|
|
81
|
+
|
|
82
|
+
main_table = entity
|
|
83
|
+
tbl = main_table.__tablename__
|
|
84
|
+
|
|
85
|
+
# build up where statement
|
|
86
|
+
conditions: List[ColumnElement[bool]] = [
|
|
87
|
+
AppStc_Paths.id_path.bool_op("&&")(
|
|
88
|
+
cast(array(appstc_ids), ARRAY(BigInteger))
|
|
89
|
+
),
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
if inherit:
|
|
93
|
+
subquery: ScalarSelect[bool] = (
|
|
94
|
+
select(
|
|
95
|
+
func.bool_or(
|
|
96
|
+
AppStc_Paths.id_path.op("&&")(func.array(AppStc_Paths.id))
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
.filter(AppStc_Paths.id.in_(appstc_ids))
|
|
100
|
+
.scalar_subquery()
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
conditions.append(subquery)
|
|
104
|
+
|
|
105
|
+
if stcmxntable:
|
|
106
|
+
mxn_table = entity.metadata.tables[stcmxntable.__tablename__]
|
|
107
|
+
|
|
108
|
+
column_names = mxn_table.c[f"{tbl}_id"]
|
|
109
|
+
|
|
110
|
+
join_on = [or_(AppStc_Paths.id == mxn_table.c["appstc_id"])]
|
|
111
|
+
|
|
112
|
+
for flag in mxnflags:
|
|
113
|
+
join_on.append(mxn_table.c[flag])
|
|
114
|
+
|
|
115
|
+
j = join(
|
|
116
|
+
mxn_table,
|
|
117
|
+
AppStc_Paths,
|
|
118
|
+
and_(*join_on),
|
|
119
|
+
)
|
|
120
|
+
elif stcrefcol:
|
|
121
|
+
column_names = getattr(main_table, "id")
|
|
122
|
+
j = join(
|
|
123
|
+
tbl,
|
|
124
|
+
AppStc_Paths,
|
|
125
|
+
AppStc_Paths.id == getattr(main_table, stcrefcol),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
query = select(column_names).select_from(j).where(or_(*conditions))
|
|
129
|
+
return query
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def generate_selfilter_open():
|
|
133
|
+
"""
|
|
134
|
+
this implements a open selfilter (everything is accessible)
|
|
135
|
+
the sqlalchemy way.
|
|
136
|
+
"""
|
|
137
|
+
return literal(True)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: perfact-api-app
|
|
3
|
+
Version: 0.6
|
|
4
|
+
Summary: PerFact API - SQLAlchemy models for the app namespace
|
|
5
|
+
Author-email: Viktor Dick <viktor.dick@perfact.de>
|
|
6
|
+
License: GPL-2.0-or-later
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: SQL
|
|
9
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: sqlalchemy
|
|
13
|
+
Requires-Dist: perfact-api-base-model
|
|
14
|
+
|
|
15
|
+
# perfact-api-app
|
|
16
|
+
|
|
17
|
+
SQLAlchemy models for the `app` namespace of the PerFact API.
|
|
18
|
+
|
|
19
|
+
## Models
|
|
20
|
+
|
|
21
|
+
Provides ORM models under `perfact.api.app.model`:
|
|
22
|
+
|
|
23
|
+
- `AppUser` / `AppUserKey` / `AppUserLogin` — user accounts, API keys, login sessions
|
|
24
|
+
- `AppStc` — org areas (Strukturknoten), hierarchical
|
|
25
|
+
- `AppPerm` / `AppPermXGroup` / `AppPermXStc` — permissions and their assignments
|
|
26
|
+
- `AppGroup` / `AppUserXPerm` / `AppUserXStc` — group and user-permission mappings
|
|
27
|
+
- `AppTblCleanup` — table cleanup job model
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
pip install perfact-api-app
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Development
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
pip install -e ../perfact-api-base-model/
|
|
39
|
+
pip install -e .
|
|
40
|
+
tox
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Maintainers
|
|
44
|
+
|
|
45
|
+
- Viktor Dick <viktor.dick@perfact.de>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
tox.ini
|
|
4
|
+
src/perfact/api/app/model/__init__.py
|
|
5
|
+
src/perfact/api/app/model/appgroup.py
|
|
6
|
+
src/perfact/api/app/model/appperm.py
|
|
7
|
+
src/perfact/api/app/model/apppermxgroup.py
|
|
8
|
+
src/perfact/api/app/model/apppermxstc.py
|
|
9
|
+
src/perfact/api/app/model/appstc.py
|
|
10
|
+
src/perfact/api/app/model/apptblcleanup.py
|
|
11
|
+
src/perfact/api/app/model/appuser.py
|
|
12
|
+
src/perfact/api/app/model/appuserkey.py
|
|
13
|
+
src/perfact/api/app/model/appuserlogin.py
|
|
14
|
+
src/perfact/api/app/model/appuserxperm.py
|
|
15
|
+
src/perfact/api/app/model/appuserxstc.py
|
|
16
|
+
src/perfact/api/app/model/authinfo.py
|
|
17
|
+
src/perfact/api/app/model/selfilterable.py
|
|
18
|
+
src/perfact_api_app.egg-info/PKG-INFO
|
|
19
|
+
src/perfact_api_app.egg-info/SOURCES.txt
|
|
20
|
+
src/perfact_api_app.egg-info/dependency_links.txt
|
|
21
|
+
src/perfact_api_app.egg-info/requires.txt
|
|
22
|
+
src/perfact_api_app.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
perfact
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[tox]
|
|
2
|
+
envlist = py3
|
|
3
|
+
isolated_build = true
|
|
4
|
+
|
|
5
|
+
[pytest]
|
|
6
|
+
|
|
7
|
+
[testenv]
|
|
8
|
+
passenv = SSH_AUTH_SOCK, PYTHONPATH, HTTP_PROXY, HTTPS_PROXY
|
|
9
|
+
setenv =
|
|
10
|
+
GIT_SSH_VARIANT=ssh
|
|
11
|
+
GIT_SSH_COMMAND=ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
|
|
12
|
+
LOCALDBMODE=pgctl
|
|
13
|
+
|
|
14
|
+
deps =
|
|
15
|
+
ruff
|
|
16
|
+
pytest
|
|
17
|
+
coverage
|
|
18
|
+
psycopg[binary]
|
|
19
|
+
pytest-postgresql
|
|
20
|
+
pytest-cov
|
|
21
|
+
pytest-typing
|
|
22
|
+
bandit
|
|
23
|
+
mypy
|
|
24
|
+
perfact-api-base-model
|
|
25
|
+
|
|
26
|
+
commands =
|
|
27
|
+
ruff format --check
|
|
28
|
+
ruff check
|
|
29
|
+
bandit --configfile {toxinidir}/../bandit.yml -r src
|
|
30
|
+
mypy src
|
|
31
|
+
# pytest --doctest-modules --cov-branch --cov=src --cov-report=term-missing {posargs:src}
|