comparison sat/memory/sqla_mapping.py @ 3796:24c1c06c865b

core (memory/mapping): add `origin_id` column to History + constraints update: - `origin_id` is added as a column instead of being just in extra, as it an important data to filter on. - Add some constraints. - Add `serialise` method to Message and Subject.
author Goffi <goffi@goffi.org>
date Fri, 17 Jun 2022 14:15:23 +0200
parents 658ddbabaf36
children fe4725bf42fb
comparison
equal deleted inserted replaced
3795:967a8e109cda 3796:24c1c06c865b
14 # GNU Affero General Public License for more details. 14 # GNU Affero General Public License for more details.
15 15
16 # You should have received a copy of the GNU Affero General Public License 16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. 17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 18
19 from typing import Dict, Any
20 from datetime import datetime
21 import enum
22 import json
19 import pickle 23 import pickle
20 import json
21 from datetime import datetime
22 import time 24 import time
23 import enum 25
24 from sqlalchemy import ( 26 from sqlalchemy import (
25 MetaData, Column, Integer, Text, Float, Boolean, DateTime, Enum, JSON, ForeignKey, 27 Boolean,
26 UniqueConstraint, Index, DDL, event 28 Column,
29 DDL,
30 DateTime,
31 Enum,
32 Float,
33 ForeignKey,
34 Index,
35 Integer,
36 JSON,
37 MetaData,
38 Text,
39 UniqueConstraint,
40 event,
27 ) 41 )
28
29 from sqlalchemy.orm import declarative_base, relationship 42 from sqlalchemy.orm import declarative_base, relationship
43 from sqlalchemy.sql.functions import now
30 from sqlalchemy.types import TypeDecorator 44 from sqlalchemy.types import TypeDecorator
31 from sqlalchemy.sql.functions import now
32 from twisted.words.protocols.jabber import jid 45 from twisted.words.protocols.jabber import jid
33 from wokkel import generic 46 from wokkel import generic
34 47
35 48
36 Base = declarative_base( 49 Base = declarative_base(
44 } 57 }
45 ) 58 )
46 ) 59 )
47 # keys which are in message data extra but not stored in extra field this is 60 # keys which are in message data extra but not stored in extra field this is
48 # because those values are stored in separate fields 61 # because those values are stored in separate fields
49 NOT_IN_EXTRA = ('stanza_id', 'received_timestamp', 'update_uid') 62 NOT_IN_EXTRA = ('origin_id', 'stanza_id', 'received_timestamp', 'update_uid')
50 63
51 64
52 class SyncState(enum.Enum): 65 class SyncState(enum.Enum):
53 #: synchronisation is currently in progress 66 #: synchronisation is currently in progress
54 IN_PROGRESS = 1 67 IN_PROGRESS = 1
182 195
183 class History(Base): 196 class History(Base):
184 __tablename__ = "history" 197 __tablename__ = "history"
185 __table_args__ = ( 198 __table_args__ = (
186 UniqueConstraint("profile_id", "stanza_id", "source", "dest"), 199 UniqueConstraint("profile_id", "stanza_id", "source", "dest"),
200 UniqueConstraint("profile_id", "origin_id", "source", name="uq_origin_id"),
187 Index("history__profile_id_timestamp", "profile_id", "timestamp"), 201 Index("history__profile_id_timestamp", "profile_id", "timestamp"),
188 Index( 202 Index(
189 "history__profile_id_received_timestamp", "profile_id", "received_timestamp" 203 "history__profile_id_received_timestamp", "profile_id", "received_timestamp"
190 ) 204 )
191 ) 205 )
192 206
193 uid = Column(Text, primary_key=True) 207 uid = Column(Text, primary_key=True)
208 origin_id = Column(Text)
194 stanza_id = Column(Text) 209 stanza_id = Column(Text)
195 update_uid = Column(Text) 210 update_uid = Column(Text)
196 profile_id = Column(ForeignKey("profiles.id", ondelete="CASCADE")) 211 profile_id = Column(ForeignKey("profiles.id", ondelete="CASCADE"))
197 source = Column(Text) 212 source = Column(Text)
198 dest = Column(Text) 213 dest = Column(Text)
257 dt = datetime.fromtimestamp(self.timestamp) 272 dt = datetime.fromtimestamp(self.timestamp)
258 return f"History<{self.source_jid.full()}->{self.dest_jid.full()} [{dt}]>" 273 return f"History<{self.source_jid.full()}->{self.dest_jid.full()} [{dt}]>"
259 274
260 def serialise(self): 275 def serialise(self):
261 extra = self.extra 276 extra = self.extra
277 if self.origin_id is not None:
278 extra["origin_id"] = self.origin_id
262 if self.stanza_id is not None: 279 if self.stanza_id is not None:
263 extra["stanza_id"] = self.stanza_id 280 extra["stanza_id"] = self.stanza_id
264 if self.update_uid is not None: 281 if self.update_uid is not None:
265 extra["update_uid"] = self.update_uid 282 extra["update_uid"] = self.update_uid
266 if self.received_timestamp is not None: 283 if self.received_timestamp is not None:
317 334
318 id = Column( 335 id = Column(
319 Integer, 336 Integer,
320 primary_key=True, 337 primary_key=True,
321 ) 338 )
322 history_uid = Column(ForeignKey("history.uid", ondelete="CASCADE")) 339 history_uid = Column(ForeignKey("history.uid", ondelete="CASCADE"), nullable=False)
323 message = Column(Text) 340 message = Column(Text, nullable=False)
324 language = Column(Text) 341 language = Column(Text)
342
343 def serialise(self) -> Dict[str, Any]:
344 s = {}
345 if self.message:
346 s["message"] = str(self.message)
347 if self.language:
348 s["language"] = str(self.language)
349 return s
325 350
326 def __repr__(self): 351 def __repr__(self):
327 lang_str = f"[{self.language}]" if self.language else "" 352 lang_str = f"[{self.language}]" if self.language else ""
328 msg = f"{self.message[:20]}…" if len(self.message)>20 else self.message 353 msg = f"{self.message[:20]}…" if len(self.message)>20 else self.message
329 content = f"{lang_str}{msg}" 354 content = f"{lang_str}{msg}"
338 363
339 id = Column( 364 id = Column(
340 Integer, 365 Integer,
341 primary_key=True, 366 primary_key=True,
342 ) 367 )
343 history_uid = Column(ForeignKey("history.uid", ondelete="CASCADE")) 368 history_uid = Column(ForeignKey("history.uid", ondelete="CASCADE"), nullable=False)
344 subject = Column(Text) 369 subject = Column(Text, nullable=False)
345 language = Column(Text) 370 language = Column(Text)
371
372 def serialise(self) -> Dict[str, Any]:
373 s = {}
374 if self.subject:
375 s["subject"] = str(self.subject)
376 if self.language:
377 s["language"] = str(self.language)
378 return s
346 379
347 def __repr__(self): 380 def __repr__(self):
348 lang_str = f"[{self.language}]" if self.language else "" 381 lang_str = f"[{self.language}]" if self.language else ""
349 msg = f"{self.subject[:20]}…" if len(self.subject)>20 else self.subject 382 msg = f"{self.subject[:20]}…" if len(self.subject)>20 else self.subject
350 content = f"{lang_str}{msg}" 383 content = f"{lang_str}{msg}"