2022-04-16 18:40:04 +03:00
|
|
|
from peewee import (
|
2025-07-07 17:03:57 +03:00
|
|
|
BlobField,
|
2023-05-29 13:31:17 +03:00
|
|
|
BooleanField,
|
2022-04-16 18:40:04 +03:00
|
|
|
CharField,
|
2025-07-07 17:03:57 +03:00
|
|
|
CompositeKey,
|
2022-04-16 18:40:04 +03:00
|
|
|
DateTimeField,
|
|
|
|
|
FloatField,
|
2025-03-13 23:20:09 +03:00
|
|
|
ForeignKeyField,
|
2022-04-16 18:40:04 +03:00
|
|
|
IntegerField,
|
2023-05-29 13:31:17 +03:00
|
|
|
Model,
|
|
|
|
|
TextField,
|
2022-04-16 18:40:04 +03:00
|
|
|
)
|
2022-04-18 14:52:13 +03:00
|
|
|
from playhouse.sqlite_ext import JSONField
|
2020-11-01 17:06:15 +03:00
|
|
|
|
2020-11-04 15:31:25 +03:00
|
|
|
|
2025-08-08 20:25:39 +03:00
|
|
|
class Event(Model):
|
2020-11-01 17:06:15 +03:00
|
|
|
id = CharField(null=False, primary_key=True, max_length=30)
|
|
|
|
|
label = CharField(index=True, max_length=20)
|
2023-05-05 01:59:44 +03:00
|
|
|
sub_label = CharField(max_length=100, null=True)
|
2020-11-01 17:06:15 +03:00
|
|
|
camera = CharField(index=True, max_length=20)
|
|
|
|
|
start_time = DateTimeField()
|
|
|
|
|
end_time = DateTimeField()
|
2023-04-30 20:07:14 +03:00
|
|
|
top_score = (
|
|
|
|
|
FloatField()
|
|
|
|
|
) # TODO remove when columns can be dropped without rebuilding table
|
|
|
|
|
score = (
|
|
|
|
|
FloatField()
|
|
|
|
|
) # TODO remove when columns can be dropped without rebuilding table
|
2020-11-01 17:06:15 +03:00
|
|
|
false_positive = BooleanField()
|
2020-11-04 15:31:25 +03:00
|
|
|
zones = JSONField()
|
2020-11-15 17:50:49 +03:00
|
|
|
thumbnail = TextField()
|
2020-12-24 16:47:27 +03:00
|
|
|
has_clip = BooleanField(default=True)
|
|
|
|
|
has_snapshot = BooleanField(default=True)
|
2023-04-30 20:07:14 +03:00
|
|
|
region = (
|
|
|
|
|
JSONField()
|
|
|
|
|
) # TODO remove when columns can be dropped without rebuilding table
|
|
|
|
|
box = (
|
|
|
|
|
JSONField()
|
|
|
|
|
) # TODO remove when columns can be dropped without rebuilding table
|
|
|
|
|
area = (
|
|
|
|
|
IntegerField()
|
|
|
|
|
) # TODO remove when columns can be dropped without rebuilding table
|
2022-02-22 07:03:01 +03:00
|
|
|
retain_indefinitely = BooleanField(default=False)
|
2023-07-09 19:40:39 +03:00
|
|
|
ratio = FloatField(
|
|
|
|
|
default=1.0
|
|
|
|
|
) # TODO remove when columns can be dropped without rebuilding table
|
2022-04-03 23:00:11 +03:00
|
|
|
plus_id = CharField(max_length=30)
|
2023-04-24 15:24:28 +03:00
|
|
|
model_hash = CharField(max_length=32)
|
|
|
|
|
detector_type = CharField(max_length=32)
|
|
|
|
|
model_type = CharField(max_length=32)
|
2023-04-30 20:07:14 +03:00
|
|
|
data = JSONField() # ex: tracked object box, region, etc.
|
2021-06-07 04:24:36 +03:00
|
|
|
|
|
|
|
|
|
2025-08-08 20:25:39 +03:00
|
|
|
class Timeline(Model):
|
2023-04-23 18:45:19 +03:00
|
|
|
timestamp = DateTimeField()
|
|
|
|
|
camera = CharField(index=True, max_length=20)
|
|
|
|
|
source = CharField(index=True, max_length=20) # ex: tracked object, audio, external
|
|
|
|
|
source_id = CharField(index=True, max_length=30)
|
|
|
|
|
class_type = CharField(max_length=50) # ex: entered_zone, audio_heard
|
|
|
|
|
data = JSONField() # ex: tracked object id, region, box, etc.
|
|
|
|
|
|
|
|
|
|
|
2025-08-08 20:25:39 +03:00
|
|
|
class Regions(Model):
|
2023-10-19 02:21:52 +03:00
|
|
|
camera = CharField(null=False, primary_key=True, max_length=20)
|
|
|
|
|
grid = JSONField() # json blob of grid
|
|
|
|
|
last_update = DateTimeField()
|
|
|
|
|
|
|
|
|
|
|
2025-08-08 20:25:39 +03:00
|
|
|
class Recordings(Model):
|
2021-06-07 04:24:36 +03:00
|
|
|
id = CharField(null=False, primary_key=True, max_length=30)
|
|
|
|
|
camera = CharField(index=True, max_length=20)
|
|
|
|
|
path = CharField(unique=True)
|
|
|
|
|
start_time = DateTimeField()
|
|
|
|
|
end_time = DateTimeField()
|
|
|
|
|
duration = FloatField()
|
2021-12-11 22:47:59 +03:00
|
|
|
motion = IntegerField(null=True)
|
|
|
|
|
objects = IntegerField(null=True)
|
2023-07-15 03:05:14 +03:00
|
|
|
dBFS = IntegerField(null=True)
|
2022-10-09 14:28:26 +03:00
|
|
|
segment_size = FloatField(default=0) # this should be stored as MB
|
2024-03-15 18:29:22 +03:00
|
|
|
regions = IntegerField(null=True)
|
2023-06-11 16:01:50 +03:00
|
|
|
|
|
|
|
|
|
2025-08-08 20:25:39 +03:00
|
|
|
class Export(Model):
|
2024-04-20 01:11:41 +03:00
|
|
|
id = CharField(null=False, primary_key=True, max_length=30)
|
|
|
|
|
camera = CharField(index=True, max_length=20)
|
|
|
|
|
name = CharField(index=True, max_length=100)
|
|
|
|
|
date = DateTimeField()
|
|
|
|
|
video_path = CharField(unique=True)
|
|
|
|
|
thumb_path = CharField(unique=True)
|
|
|
|
|
in_progress = BooleanField()
|
|
|
|
|
|
|
|
|
|
|
2025-08-08 20:25:39 +03:00
|
|
|
class ReviewSegment(Model):
|
2024-02-21 02:26:09 +03:00
|
|
|
id = CharField(null=False, primary_key=True, max_length=30)
|
|
|
|
|
camera = CharField(index=True, max_length=20)
|
|
|
|
|
start_time = DateTimeField()
|
|
|
|
|
end_time = DateTimeField()
|
2024-10-23 16:35:49 +03:00
|
|
|
severity = CharField(max_length=30) # alert, detection
|
2024-02-21 02:26:09 +03:00
|
|
|
thumb_path = CharField(unique=True)
|
|
|
|
|
data = JSONField() # additional data about detection like list of labels, zone, areas of significant motion
|
|
|
|
|
|
|
|
|
|
|
2025-08-08 20:25:39 +03:00
|
|
|
class UserReviewStatus(Model):
|
2025-03-13 23:20:09 +03:00
|
|
|
user_id = CharField(max_length=30)
|
|
|
|
|
review_segment = ForeignKeyField(ReviewSegment, backref="user_reviews")
|
|
|
|
|
has_been_reviewed = BooleanField(default=False)
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
indexes = ((("user_id", "review_segment"), True),)
|
|
|
|
|
|
|
|
|
|
|
2025-08-08 20:25:39 +03:00
|
|
|
class Previews(Model):
|
2023-12-03 17:16:01 +03:00
|
|
|
id = CharField(null=False, primary_key=True, max_length=30)
|
|
|
|
|
camera = CharField(index=True, max_length=20)
|
|
|
|
|
path = CharField(unique=True)
|
|
|
|
|
start_time = DateTimeField()
|
|
|
|
|
end_time = DateTimeField()
|
|
|
|
|
duration = FloatField()
|
|
|
|
|
|
|
|
|
|
|
2023-06-11 16:01:50 +03:00
|
|
|
# Used for temporary table in record/cleanup.py
|
2025-08-08 20:25:39 +03:00
|
|
|
class RecordingsToDelete(Model):
|
2023-06-11 16:01:50 +03:00
|
|
|
id = CharField(null=False, primary_key=False, max_length=30)
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
temporary = True
|
2024-05-18 19:36:13 +03:00
|
|
|
|
|
|
|
|
|
2025-08-08 20:25:39 +03:00
|
|
|
class User(Model):
|
2024-05-18 19:36:13 +03:00
|
|
|
username = CharField(null=False, primary_key=True, max_length=30)
|
2025-03-08 19:01:08 +03:00
|
|
|
role = CharField(
|
|
|
|
|
max_length=20,
|
2025-03-10 06:59:07 +03:00
|
|
|
default="admin",
|
2025-03-08 19:01:08 +03:00
|
|
|
)
|
2024-05-18 19:36:13 +03:00
|
|
|
password_hash = CharField(null=False, max_length=120)
|
2025-12-08 19:02:28 +03:00
|
|
|
password_changed_at = DateTimeField(null=True)
|
2024-07-22 23:39:15 +03:00
|
|
|
notification_tokens = JSONField()
|
2025-07-07 17:03:57 +03:00
|
|
|
|
2025-09-12 14:19:29 +03:00
|
|
|
@classmethod
|
|
|
|
|
def get_allowed_cameras(
|
|
|
|
|
cls, role: str, roles_dict: dict[str, list[str]], all_camera_names: set[str]
|
|
|
|
|
) -> list[str]:
|
|
|
|
|
if role not in roles_dict:
|
|
|
|
|
return [] # Invalid role grants no access
|
|
|
|
|
allowed = roles_dict[role]
|
|
|
|
|
if not allowed: # Empty list means all cameras
|
|
|
|
|
return list(all_camera_names)
|
|
|
|
|
|
|
|
|
|
return [cam for cam in allowed if cam in all_camera_names]
|
|
|
|
|
|
2025-07-07 17:03:57 +03:00
|
|
|
|
2025-08-08 20:25:39 +03:00
|
|
|
class Trigger(Model):
|
2025-07-07 17:03:57 +03:00
|
|
|
camera = CharField(max_length=20)
|
|
|
|
|
name = CharField()
|
|
|
|
|
type = CharField(max_length=10)
|
|
|
|
|
data = TextField()
|
|
|
|
|
threshold = FloatField()
|
|
|
|
|
model = CharField(max_length=30)
|
|
|
|
|
embedding = BlobField()
|
|
|
|
|
triggering_event_id = CharField(max_length=30)
|
|
|
|
|
last_triggered = DateTimeField()
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
primary_key = CompositeKey("camera", "name")
|