corvic-engine 0.3.0rc77__cp38-abi3-win_amd64.whl → 0.3.0rc79__cp38-abi3-win_amd64.whl
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.
- corvic/engine/_native.pyd +0 -0
- corvic/eorm/__init__.py +404 -0
- corvic/model/_base_model.py +24 -24
- corvic/model/_completion_model.py +9 -9
- corvic/model/_defaults.py +5 -5
- corvic/model/_feature_view.py +17 -17
- corvic/model/_pipeline.py +12 -12
- corvic/model/_proto_orm_convert.py +102 -100
- corvic/model/_resource.py +24 -24
- corvic/model/_room.py +10 -14
- corvic/model/_source.py +18 -18
- corvic/model/_space.py +21 -21
- corvic/op_graph/_schema.py +3 -3
- corvic/op_graph/feature_types.py +4 -4
- corvic/op_graph/ops.py +2 -2
- corvic/orm/__init__.py +202 -307
- corvic/orm/_soft_delete.py +218 -0
- corvic/orm/ids.py +0 -69
- corvic/system/__init__.py +0 -2
- corvic/system/_embedder.py +2 -2
- corvic/system/in_memory_executor.py +0 -13
- corvic/system/op_graph_executor.py +2 -2
- corvic/system/storage.py +10 -8
- corvic/system_sqlite/client.py +10 -10
- corvic/table/table.py +3 -3
- {corvic_engine-0.3.0rc77.dist-info → corvic_engine-0.3.0rc79.dist-info}/METADATA +1 -1
- {corvic_engine-0.3.0rc77.dist-info → corvic_engine-0.3.0rc79.dist-info}/RECORD +29 -29
- corvic/orm/base.py +0 -256
- corvic/orm/mixins.py +0 -480
- {corvic_engine-0.3.0rc77.dist-info → corvic_engine-0.3.0rc79.dist-info}/WHEEL +0 -0
- {corvic_engine-0.3.0rc77.dist-info → corvic_engine-0.3.0rc79.dist-info}/licenses/LICENSE +0 -0
corvic/orm/mixins.py
DELETED
@@ -1,480 +0,0 @@
|
|
1
|
-
"""Mixin models for corvic orm tables."""
|
2
|
-
|
3
|
-
from __future__ import annotations
|
4
|
-
|
5
|
-
from collections.abc import Callable, Sequence
|
6
|
-
from datetime import UTC, datetime
|
7
|
-
from typing import Any, LiteralString, cast
|
8
|
-
|
9
|
-
import sqlalchemy as sa
|
10
|
-
from google.protobuf import timestamp_pb2
|
11
|
-
from sqlalchemy import event, exc
|
12
|
-
from sqlalchemy import orm as sa_orm
|
13
|
-
from sqlalchemy.ext import hybrid
|
14
|
-
from sqlalchemy.ext.hybrid import hybrid_property
|
15
|
-
|
16
|
-
import corvic.context
|
17
|
-
from corvic.orm.base import EventBase, EventKey, OrgBase
|
18
|
-
from corvic.orm.errors import (
|
19
|
-
DeletedObjectError,
|
20
|
-
RequestedObjectsForNobodyError,
|
21
|
-
)
|
22
|
-
from corvic.orm.func import gen_uuid
|
23
|
-
from corvic.orm.ids import OrgID
|
24
|
-
from corvic.orm.keys import ForeignKey
|
25
|
-
from corvic.result import InvalidArgumentError
|
26
|
-
from corvic_generated.status.v1 import event_pb2
|
27
|
-
|
28
|
-
|
29
|
-
def _filter_org_objects(orm_execute_state: sa_orm.ORMExecuteState):
|
30
|
-
if all(
|
31
|
-
not issubclass(mapper.class_, BelongsToOrgMixin | OrgBase)
|
32
|
-
for mapper in orm_execute_state.all_mappers
|
33
|
-
):
|
34
|
-
# operation has nothing to do with models owned by org
|
35
|
-
return
|
36
|
-
if orm_execute_state.is_select:
|
37
|
-
requester = corvic.context.get_requester()
|
38
|
-
org_id = OrgID(requester.org_id)
|
39
|
-
if org_id.is_super_user:
|
40
|
-
return
|
41
|
-
|
42
|
-
if org_id.is_nobody:
|
43
|
-
raise RequestedObjectsForNobodyError(
|
44
|
-
"requester org from context was nobody"
|
45
|
-
)
|
46
|
-
# we need the real value in in expression world and
|
47
|
-
# because of sqlalchemys weird runtime parsing of this it
|
48
|
-
# needs to be a real local with a name
|
49
|
-
db_id = org_id.to_db().unwrap_or_raise()
|
50
|
-
|
51
|
-
# this goofy syntax doesn't typecheck well, but is the documented way to apply
|
52
|
-
# these operations to all subclasses (recursive). Sqlalchemy is inspecting the
|
53
|
-
# lambda rather than just executing it so a function won't work.
|
54
|
-
# https://docs.sqlalchemy.org/en/20/orm/queryguide/api.html#sqlalchemy.orm.with_loader_criteria
|
55
|
-
check_org_id_lambda: Callable[ # noqa: E731
|
56
|
-
[type[BelongsToOrgMixin]], sa.ColumnElement[bool]
|
57
|
-
] = lambda cls: cls.org_id == db_id
|
58
|
-
orm_execute_state.statement = orm_execute_state.statement.options(
|
59
|
-
sa_orm.with_loader_criteria(
|
60
|
-
BelongsToOrgMixin,
|
61
|
-
cast(Any, check_org_id_lambda),
|
62
|
-
include_aliases=True,
|
63
|
-
track_closure_variables=False,
|
64
|
-
),
|
65
|
-
sa_orm.with_loader_criteria(
|
66
|
-
OrgBase,
|
67
|
-
OrgBase.id == org_id,
|
68
|
-
include_aliases=True,
|
69
|
-
track_closure_variables=False,
|
70
|
-
),
|
71
|
-
)
|
72
|
-
|
73
|
-
|
74
|
-
class BadDeleteError(DeletedObjectError):
|
75
|
-
"""Raised when deleting deleted objects."""
|
76
|
-
|
77
|
-
def __init__(self):
|
78
|
-
super().__init__(message="deleting an object that is already deleted")
|
79
|
-
|
80
|
-
|
81
|
-
def _filter_deleted_objects_when_orm_loading(
|
82
|
-
execute_state: sa_orm.session.ORMExecuteState,
|
83
|
-
):
|
84
|
-
# check if the orm operation was submitted with an option to force load despite
|
85
|
-
# soft-load status and if so just skip this event
|
86
|
-
if any(
|
87
|
-
isinstance(opt, SoftDeleteMixin.ForceLoadOption)
|
88
|
-
for opt in execute_state.user_defined_options
|
89
|
-
) or any(
|
90
|
-
isinstance(opt, SoftDeleteMixin.ForceLoadOption)
|
91
|
-
for opt in execute_state.local_execution_options.values()
|
92
|
-
):
|
93
|
-
return
|
94
|
-
|
95
|
-
def where_criteria(cls: type[SoftDeleteMixin]) -> sa.ColumnElement[bool]:
|
96
|
-
return ~cls.is_deleted
|
97
|
-
|
98
|
-
execute_state.statement = execute_state.statement.options(
|
99
|
-
sa_orm.with_loader_criteria(
|
100
|
-
entity_or_base=SoftDeleteMixin,
|
101
|
-
# suppressing pyright is unfortunately required as there seems to be a
|
102
|
-
# problem with sqlalchemy.orm.util::LoaderCriteriaOption which will
|
103
|
-
# construct a 'DeferredLambdaElement' when `where_criteria` is callable.
|
104
|
-
# However, the type annotations are not consistent with the implementation.
|
105
|
-
# The implementation, on callables criteria, passes to the lambda the
|
106
|
-
# mapping class for using in constructing the `ColumnElement[bool]` result
|
107
|
-
# needed. For this reason we ignore the argument type.
|
108
|
-
where_criteria=where_criteria,
|
109
|
-
include_aliases=True,
|
110
|
-
)
|
111
|
-
)
|
112
|
-
|
113
|
-
|
114
|
-
class SoftDeleteMixin(sa_orm.MappedAsDataclass):
|
115
|
-
"""Mixin to make corvic orm models use soft-delete.
|
116
|
-
|
117
|
-
Modifications to objects which are marked as deleted will result in
|
118
|
-
an error.
|
119
|
-
"""
|
120
|
-
|
121
|
-
class ForceLoadOption(sa_orm.UserDefinedOption):
|
122
|
-
"""Option for ignoring soft delete status when loading."""
|
123
|
-
|
124
|
-
_deleted_at: sa_orm.Mapped[datetime | None] = sa_orm.mapped_column(
|
125
|
-
"deleted_at",
|
126
|
-
sa.DateTime(timezone=True),
|
127
|
-
server_default=None,
|
128
|
-
default=None,
|
129
|
-
)
|
130
|
-
is_live: sa_orm.Mapped[bool | None] = sa_orm.mapped_column(
|
131
|
-
init=False,
|
132
|
-
default=True,
|
133
|
-
)
|
134
|
-
|
135
|
-
@hybrid.hybrid_property
|
136
|
-
def deleted_at(self) -> datetime | None:
|
137
|
-
if not self._deleted_at:
|
138
|
-
return None
|
139
|
-
return self._deleted_at.replace(tzinfo=UTC)
|
140
|
-
|
141
|
-
def reset_delete(self):
|
142
|
-
self._deleted_at = None
|
143
|
-
|
144
|
-
@classmethod
|
145
|
-
def _force_load_option(cls):
|
146
|
-
return cls.ForceLoadOption()
|
147
|
-
|
148
|
-
@classmethod
|
149
|
-
def force_load_options(cls):
|
150
|
-
"""Options to force load soft-deleted objects when using session.get."""
|
151
|
-
return [cls._force_load_option()]
|
152
|
-
|
153
|
-
@classmethod
|
154
|
-
def force_load_execution_options(cls):
|
155
|
-
"""Options to force load soft-deleted objects when using session.execute.
|
156
|
-
|
157
|
-
Also works with session.scalars.
|
158
|
-
"""
|
159
|
-
return {"ignored_option_name": cls._force_load_option()}
|
160
|
-
|
161
|
-
def mark_deleted(self):
|
162
|
-
"""Updates soft-delete object.
|
163
|
-
|
164
|
-
Note: users should not use this directly and instead should use
|
165
|
-
`session.delete(obj)`.
|
166
|
-
"""
|
167
|
-
if self.is_deleted:
|
168
|
-
raise BadDeleteError()
|
169
|
-
# set is_live to None instead of False so that orm objects can use it to
|
170
|
-
# build uniqueness constraints that are only enforced on non-deleted objects
|
171
|
-
self.is_live = None
|
172
|
-
self._deleted_at = datetime.now(tz=UTC)
|
173
|
-
|
174
|
-
@hybrid_property
|
175
|
-
def is_deleted(self) -> bool:
|
176
|
-
"""Useful when constructing queries for direct use (e.g via `session.execute`).
|
177
|
-
|
178
|
-
ORM users can rely on the typical session interfaces for checking object
|
179
|
-
persistence.
|
180
|
-
"""
|
181
|
-
return not self.is_live
|
182
|
-
|
183
|
-
@is_deleted.inplace.expression
|
184
|
-
@classmethod
|
185
|
-
def _is_deleted_expression(cls):
|
186
|
-
return cls.is_live.is_not(True)
|
187
|
-
|
188
|
-
@staticmethod
|
189
|
-
def register_session_event_listeners(session: type[sa_orm.Session]):
|
190
|
-
event.listen(
|
191
|
-
session, "do_orm_execute", _filter_deleted_objects_when_orm_loading
|
192
|
-
)
|
193
|
-
|
194
|
-
|
195
|
-
def live_unique_constraint(
|
196
|
-
column_name: LiteralString, *other_column_names: LiteralString
|
197
|
-
) -> sa.UniqueConstraint:
|
198
|
-
"""Construct a unique constraint that only applies to live objects.
|
199
|
-
|
200
|
-
Live objects are those that support soft deletion and have not been soft deleted.
|
201
|
-
"""
|
202
|
-
return sa.UniqueConstraint(column_name, *other_column_names, "is_live")
|
203
|
-
|
204
|
-
|
205
|
-
class BelongsToOrgMixin(sa_orm.MappedAsDataclass):
|
206
|
-
"""Mark models that should be subject to org level access control."""
|
207
|
-
|
208
|
-
@staticmethod
|
209
|
-
def _current_org_id_from_context():
|
210
|
-
requester = corvic.context.get_requester()
|
211
|
-
return OrgID(requester.org_id)
|
212
|
-
|
213
|
-
@staticmethod
|
214
|
-
def _make_org_id_default() -> OrgID | None:
|
215
|
-
org_id = BelongsToOrgMixin._current_org_id_from_context()
|
216
|
-
|
217
|
-
if org_id.is_nobody:
|
218
|
-
raise RequestedObjectsForNobodyError(
|
219
|
-
"the nobody org cannot change orm objects"
|
220
|
-
)
|
221
|
-
|
222
|
-
if org_id.is_super_user:
|
223
|
-
return None
|
224
|
-
|
225
|
-
return org_id
|
226
|
-
|
227
|
-
org_id: sa_orm.Mapped[OrgID | None] = sa_orm.mapped_column(
|
228
|
-
ForeignKey(OrgBase).make(ondelete="CASCADE"),
|
229
|
-
nullable=False,
|
230
|
-
default_factory=_make_org_id_default,
|
231
|
-
init=False,
|
232
|
-
)
|
233
|
-
|
234
|
-
@sa_orm.validates("org_id")
|
235
|
-
def validate_org_id(self, _key: str, orm_id: OrgID | None):
|
236
|
-
expected_org_id = self._current_org_id_from_context()
|
237
|
-
if expected_org_id.is_nobody:
|
238
|
-
raise RequestedObjectsForNobodyError(
|
239
|
-
"the nobody org cannot change orm objects"
|
240
|
-
)
|
241
|
-
|
242
|
-
if expected_org_id.is_super_user:
|
243
|
-
return orm_id
|
244
|
-
|
245
|
-
if orm_id != expected_org_id:
|
246
|
-
raise InvalidArgumentError(
|
247
|
-
"provided org_id must match the current org",
|
248
|
-
provided=orm_id,
|
249
|
-
expected=expected_org_id,
|
250
|
-
)
|
251
|
-
|
252
|
-
return orm_id
|
253
|
-
|
254
|
-
@staticmethod
|
255
|
-
def register_session_event_listeners(session: type[sa_orm.Session]):
|
256
|
-
event.listen(session, "do_orm_execute", _filter_org_objects)
|
257
|
-
|
258
|
-
|
259
|
-
class Session(sa_orm.Session):
|
260
|
-
"""Wrapper around sqlalchemy.orm.Session."""
|
261
|
-
|
262
|
-
_soft_deleted: dict[sa_orm.InstanceState[Any], Any] | None = None
|
263
|
-
|
264
|
-
def _track_soft_deleted(self, instance: object):
|
265
|
-
if self._soft_deleted is None:
|
266
|
-
self._soft_deleted = {}
|
267
|
-
self._soft_deleted[sa_orm.attributes.instance_state(instance)] = instance
|
268
|
-
|
269
|
-
def _reset_soft_deleted(self):
|
270
|
-
self._soft_deleted = {}
|
271
|
-
|
272
|
-
def _ensure_persistence(self, instance: object):
|
273
|
-
instance_state = sa_orm.attributes.instance_state(instance)
|
274
|
-
if instance_state.key is None:
|
275
|
-
raise exc.InvalidRequestError("Instance is not persisted")
|
276
|
-
|
277
|
-
def _delete_soft_deleted(self, instance: SoftDeleteMixin):
|
278
|
-
self._ensure_persistence(instance)
|
279
|
-
|
280
|
-
instance.mark_deleted()
|
281
|
-
|
282
|
-
# Soft deleted should be tracked so that way a deleted soft-delete instance is
|
283
|
-
# correctly identified as being "deleted"
|
284
|
-
self._track_soft_deleted(instance)
|
285
|
-
|
286
|
-
# Flushing the objects being deleted is needed to ensure the 'soft-delete'
|
287
|
-
# impact is spread. This is because sqlalchemy flush implementation is doing
|
288
|
-
# the heavy lifting of updating deleted/modified state across dependencies
|
289
|
-
# after flushing. Ensuring this is done necessary to ensure relationships with
|
290
|
-
# cascades have valid state after a soft-delete. Otherwise divergence between
|
291
|
-
# hard-delete and soft-delete will be seen here (and surprise the user).
|
292
|
-
# Note: the cost is reduced by limiting the flush to the soft-delete instance.
|
293
|
-
self.flush([instance])
|
294
|
-
|
295
|
-
# Invalidate existing session references for expected get-after-delete behavior.
|
296
|
-
if sa_orm.attributes.instance_state(instance).session_id is self.hash_key:
|
297
|
-
self.expunge(instance)
|
298
|
-
|
299
|
-
def commit(self):
|
300
|
-
super().commit()
|
301
|
-
if self._soft_deleted:
|
302
|
-
self._reset_soft_deleted()
|
303
|
-
|
304
|
-
def rollback(self):
|
305
|
-
super().rollback()
|
306
|
-
if self._soft_deleted:
|
307
|
-
for obj in self._soft_deleted.values():
|
308
|
-
if isinstance(obj, SoftDeleteMixin):
|
309
|
-
obj.reset_delete()
|
310
|
-
obj.is_live = True
|
311
|
-
continue
|
312
|
-
raise RuntimeError("non-soft delete object in soft deleted set")
|
313
|
-
self._reset_soft_deleted()
|
314
|
-
|
315
|
-
@property
|
316
|
-
def deleted(self):
|
317
|
-
deleted = super().deleted
|
318
|
-
if self._soft_deleted:
|
319
|
-
deleted.update(self._soft_deleted.values())
|
320
|
-
return deleted
|
321
|
-
|
322
|
-
def delete(self, instance: object, *, force_hard_delete=False):
|
323
|
-
if isinstance(instance, SoftDeleteMixin) and not force_hard_delete:
|
324
|
-
self._delete_soft_deleted(instance)
|
325
|
-
return
|
326
|
-
super().delete(instance)
|
327
|
-
|
328
|
-
def recent_object_events(
|
329
|
-
self,
|
330
|
-
key_provider: EventKey.Provider,
|
331
|
-
max_events: int | None = None,
|
332
|
-
) -> list[event_pb2.Event]:
|
333
|
-
"""Returns max_events (default=10) most recent events from the event log."""
|
334
|
-
recent_events: Sequence[EventBase] = self.scalars(
|
335
|
-
EventBase.select_latest_by_event_key(
|
336
|
-
event_key=key_provider.event_key, limit=max_events or None
|
337
|
-
)
|
338
|
-
).all()
|
339
|
-
return [ev.as_event() for ev in recent_events]
|
340
|
-
|
341
|
-
|
342
|
-
def _timestamp_or_utc_now(timestamp: datetime | None = None):
|
343
|
-
if timestamp is not None:
|
344
|
-
return timestamp
|
345
|
-
return datetime.now(tz=UTC)
|
346
|
-
|
347
|
-
|
348
|
-
class EventLoggerMixin(sa_orm.MappedAsDataclass):
|
349
|
-
"""Mixin to add status event logging features to corvic orm models.
|
350
|
-
|
351
|
-
This mixin will add a `log_src_id` uuid value to the ORM model. This value is set
|
352
|
-
by the DB on initial object persistence. ORM users can then use `orm.log_X` method
|
353
|
-
to add a status event to the corvic event log which will be associated with the
|
354
|
-
`log_src_id` value for the object. Supported events include done, error,
|
355
|
-
pending_system, and pending_user. The `latest_event` property can be used to read
|
356
|
-
the latest event for the orm object from the event log.
|
357
|
-
"""
|
358
|
-
|
359
|
-
def _get_latest_event(self, _: Any = None) -> event_pb2.Event:
|
360
|
-
obj_session = sa_orm.object_session(self)
|
361
|
-
if obj_session:
|
362
|
-
query = EventBase.select_latest_by_event_key(
|
363
|
-
event_key=self.event_key, limit=1
|
364
|
-
)
|
365
|
-
last_log_entry = obj_session.scalars(query).first()
|
366
|
-
if last_log_entry is not None:
|
367
|
-
timestamp = timestamp_pb2.Timestamp()
|
368
|
-
timestamp.FromDatetime(dt=last_log_entry.timestamp)
|
369
|
-
return event_pb2.Event(
|
370
|
-
timestamp=timestamp,
|
371
|
-
reason=last_log_entry.reason,
|
372
|
-
event_type=event_pb2.EventType.Name(last_log_entry.event),
|
373
|
-
regarding=last_log_entry.regarding,
|
374
|
-
)
|
375
|
-
return event_pb2.Event()
|
376
|
-
|
377
|
-
def _set_latest_event(self, event: event_pb2.Event, _: Any = None):
|
378
|
-
if event.SerializeToString(): # initially the event is b''
|
379
|
-
obj_session = sa_orm.object_session(self)
|
380
|
-
|
381
|
-
if obj_session is not None:
|
382
|
-
# this can occur when an event is set on a new object
|
383
|
-
if not self._event_src_id:
|
384
|
-
obj_session.flush()
|
385
|
-
|
386
|
-
obj_session.add(
|
387
|
-
EventBase(
|
388
|
-
event=event.event_type,
|
389
|
-
timestamp=event.timestamp.ToDatetime(tzinfo=UTC),
|
390
|
-
regarding=event.regarding,
|
391
|
-
reason=event.reason,
|
392
|
-
event_key=str(self.event_key),
|
393
|
-
)
|
394
|
-
)
|
395
|
-
else:
|
396
|
-
raise sa_orm.exc.UnmappedInstanceError(
|
397
|
-
self, msg="cannot add event to unmapped instance"
|
398
|
-
)
|
399
|
-
|
400
|
-
@sa_orm.declared_attr
|
401
|
-
def _latest_event(self):
|
402
|
-
"""Get or set the latest event for this orm object."""
|
403
|
-
return sa_orm.synonym(
|
404
|
-
"_latest_event",
|
405
|
-
default_factory=event_pb2.Event,
|
406
|
-
descriptor=property(self._get_latest_event, self._set_latest_event),
|
407
|
-
)
|
408
|
-
|
409
|
-
@property
|
410
|
-
def latest_event(self):
|
411
|
-
"""Returns the latest event for this entity from the event log."""
|
412
|
-
return self._get_latest_event()
|
413
|
-
|
414
|
-
_event_src_id: sa_orm.Mapped[str] = sa_orm.mapped_column(
|
415
|
-
sa.Text, init=False, server_default=gen_uuid()
|
416
|
-
)
|
417
|
-
|
418
|
-
@property
|
419
|
-
def event_key(self) -> EventKey:
|
420
|
-
return EventKey.from_str(id=self._event_src_id)
|
421
|
-
|
422
|
-
def _log_event(
|
423
|
-
self, event_type: event_pb2.EventType, reason: str, regarding: str, dt: datetime
|
424
|
-
):
|
425
|
-
timestamp = timestamp_pb2.Timestamp()
|
426
|
-
timestamp.FromDatetime(dt=dt)
|
427
|
-
return event_pb2.Event(
|
428
|
-
event_type=event_type,
|
429
|
-
reason=reason,
|
430
|
-
regarding=regarding,
|
431
|
-
timestamp=timestamp,
|
432
|
-
)
|
433
|
-
|
434
|
-
def notify_done(
|
435
|
-
self, reason: str = "", regarding: str = "", timestamp: datetime | None = None
|
436
|
-
):
|
437
|
-
"""Add a finished event to the event log for this object."""
|
438
|
-
self._latest_event = self._log_event(
|
439
|
-
event_type=event_pb2.EVENT_TYPE_FINISHED,
|
440
|
-
reason=reason,
|
441
|
-
regarding=regarding,
|
442
|
-
dt=_timestamp_or_utc_now(timestamp=timestamp),
|
443
|
-
)
|
444
|
-
|
445
|
-
def notify_error(
|
446
|
-
self, reason: str = "", regarding: str = "", timestamp: datetime | None = None
|
447
|
-
):
|
448
|
-
"""Add an error event to the event log for this object."""
|
449
|
-
self._latest_event = self._log_event(
|
450
|
-
event_type=event_pb2.EVENT_TYPE_ERROR,
|
451
|
-
reason=reason,
|
452
|
-
regarding=regarding,
|
453
|
-
dt=_timestamp_or_utc_now(timestamp=timestamp),
|
454
|
-
)
|
455
|
-
|
456
|
-
def notify_pending_system(
|
457
|
-
self, reason: str = "", regarding: str = "", timestamp: datetime | None = None
|
458
|
-
):
|
459
|
-
"""Add a pending system event to the event log for this object."""
|
460
|
-
self._latest_event = self._log_event(
|
461
|
-
event_type=event_pb2.EVENT_TYPE_SYSTEM_PENDING,
|
462
|
-
reason=reason,
|
463
|
-
regarding=regarding,
|
464
|
-
dt=_timestamp_or_utc_now(timestamp=timestamp),
|
465
|
-
)
|
466
|
-
|
467
|
-
def notify_pending_user(
|
468
|
-
self, reason: str = "", regarding: str = "", timestamp: datetime | None = None
|
469
|
-
):
|
470
|
-
"""Add a pending user event to the event log for this object."""
|
471
|
-
self._latest_event = self._log_event(
|
472
|
-
event_type=event_pb2.EVENT_TYPE_USER_PENDING,
|
473
|
-
reason=reason,
|
474
|
-
regarding=regarding,
|
475
|
-
dt=_timestamp_or_utc_now(timestamp=timestamp),
|
476
|
-
)
|
477
|
-
|
478
|
-
|
479
|
-
SoftDeleteMixin.register_session_event_listeners(Session)
|
480
|
-
BelongsToOrgMixin.register_session_event_listeners(Session)
|
File without changes
|
File without changes
|