File indexing completed on 2026-04-27 07:41:45
0001 """Django models for swf-remote.
0002
0003 Central to this repo: a generic tjai-style `Entry` table that holds any
0004 kind of customization data — alarm configs, alarm firings, engine run
0005 records, and whatever we add next (dashboards, annotations, ad-hoc notes).
0006 One flexible document-DB on top of swf-remote's existing Postgres.
0007
0008 Shape follows tjai (tjai_app/models.py): content + kind + context +
0009 name + JSONField data + priority/status + timestamps + soft-delete,
0010 with `data.entry_id` as a non-unique human-readable slug (a single
0011 entry_id can appear across many rows — e.g. all events for one alarm
0012 share `data.entry_id = 'event_<alarm_name>'`).
0013
0014 Archive policy: `status='archive'` entries are filtered out of live
0015 dashboard lists. Hard-delete via `deleted_at`.
0016 """
0017 from __future__ import annotations
0018
0019 import time
0020 import uuid
0021
0022 from django.db import models
0023
0024
0025 VALID_KINDS = (
0026 'alarm',
0027 'event',
0028 'engine_run',
0029 'team',
0030
0031 'memory',
0032 'list',
0033 'action',
0034 )
0035
0036 VALID_STATUSES = ('active', 'done', 'blocked', 'failed')
0037
0038
0039 def _new_entry_id() -> str:
0040 """UUID4 default for Entry.id. Module-level so migrations can serialize it."""
0041 return str(uuid.uuid4())
0042
0043
0044 class EntryContext(models.Model):
0045 """Project/topic grouping for entries. Matches tjai Context model."""
0046 name = models.CharField(max_length=255, primary_key=True)
0047 title = models.CharField(max_length=255, blank=True, default='')
0048 description = models.TextField(blank=True, default='')
0049 timestamp_created = models.FloatField(default=time.time)
0050 timestamp_modified = models.FloatField(default=time.time)
0051 data = models.JSONField(default=dict, blank=True)
0052
0053 class Meta:
0054 db_table = 'entry_context'
0055
0056 def __str__(self):
0057 return self.name
0058
0059
0060 class Entry(models.Model):
0061 """Generic document-store row. See module docstring for the model."""
0062
0063 id = models.CharField(max_length=36, primary_key=True,
0064 default=_new_entry_id)
0065
0066
0067
0068
0069 title = models.CharField(max_length=255, blank=True, default='')
0070 content = models.TextField(blank=True, default='')
0071 kind = models.CharField(max_length=50)
0072 context = models.ForeignKey(EntryContext, on_delete=models.PROTECT,
0073 null=True, blank=True, related_name='entries')
0074
0075
0076 name = models.CharField(max_length=255, null=True, blank=True)
0077 data = models.JSONField(null=True, blank=True)
0078 priority = models.IntegerField(null=True, blank=True)
0079 status = models.CharField(max_length=50, null=True, blank=True)
0080
0081
0082 archived = models.BooleanField(default=False)
0083 parent = models.ForeignKey('self', on_delete=models.SET_NULL,
0084 null=True, blank=True, related_name='children')
0085 timestamp_created = models.FloatField(default=time.time)
0086 timestamp_modified = models.FloatField(default=time.time)
0087 deleted_at = models.FloatField(null=True, blank=True)
0088
0089 class Meta:
0090 db_table = 'entry'
0091 constraints = [
0092 models.UniqueConstraint(
0093 fields=['context', 'name'],
0094 condition=models.Q(name__isnull=False),
0095 name='uniq_context_name',
0096 ),
0097 ]
0098 indexes = [
0099 models.Index(fields=['kind', '-timestamp_created']),
0100 models.Index(fields=['context', 'kind', '-timestamp_created']),
0101 models.Index(fields=['archived']),
0102 models.Index(fields=['status']),
0103 ]
0104
0105 def __str__(self):
0106 slug = (self.data or {}).get('entry_id') or self.name or self.id[:8]
0107 return f'{self.kind}:{slug}'
0108
0109 @property
0110 def entry_id(self) -> str | None:
0111 """Human-readable slug from data.entry_id (non-unique by design)."""
0112 return (self.data or {}).get('entry_id')
0113
0114
0115 class EntryVersion(models.Model):
0116 """Immutable snapshot of an Entry's content + data at a point in time.
0117
0118 Written by a pre_save signal on Entry whenever content or substantive
0119 data changes. Matches tjai's versioning pattern — UI can render a
0120 history table and load a prior version for re-editing.
0121 """
0122 entry = models.ForeignKey(Entry, on_delete=models.CASCADE,
0123 related_name='versions')
0124 version_num = models.IntegerField()
0125 title = models.CharField(max_length=255, blank=True, default='')
0126 content = models.TextField(blank=True, default='')
0127 data = models.JSONField(null=True, blank=True)
0128 changed_by = models.CharField(max_length=100, default='unknown')
0129 timestamp = models.FloatField(default=time.time)
0130
0131 class Meta:
0132 db_table = 'entry_version'
0133 constraints = [
0134 models.UniqueConstraint(fields=['entry', 'version_num'],
0135 name='uniq_entry_version_num'),
0136 ]
0137 indexes = [models.Index(fields=['entry', '-timestamp'])]