mirror of https://github.com/DJ2LS/FreeDATA.git
266 lines
9.7 KiB
Python
266 lines
9.7 KiB
Python
from sqlalchemy import Index, Boolean, Column, String, Integer, JSON, ForeignKey, DateTime, Float
|
|
from sqlalchemy.orm import declarative_base, relationship
|
|
from datetime import datetime, timezone
|
|
|
|
Base = declarative_base()
|
|
|
|
|
|
class MessageAttachment(Base):
|
|
"""Represents the association between a message and an attachment.
|
|
|
|
This association object links P2PMessage and Attachment records in
|
|
the database, representing a many-to-many relationship between
|
|
messages and attachments. It uses foreign keys and cascading deletes
|
|
to maintain data integrity.
|
|
"""
|
|
|
|
__tablename__ = "message_attachment"
|
|
message_id = Column(String, ForeignKey("p2p_message.id", ondelete="CASCADE"), primary_key=True)
|
|
attachment_id = Column(Integer, ForeignKey("attachment.id", ondelete="CASCADE"), primary_key=True)
|
|
|
|
message = relationship("P2PMessage", back_populates="message_attachments")
|
|
attachment = relationship("Attachment", back_populates="message_attachments")
|
|
|
|
|
|
class Config(Base):
|
|
"""Represents a configuration setting in the database.
|
|
|
|
This class maps to the 'config' table in the database and stores
|
|
configuration settings. It currently only stores the database
|
|
version.
|
|
"""
|
|
|
|
__tablename__ = "config"
|
|
db_variable = Column(String, primary_key=True) # Unique identifier for the configuration setting
|
|
db_version = Column(String)
|
|
|
|
def to_dict(self):
|
|
"""Converts the Config object to a dictionary.
|
|
|
|
Returns:
|
|
dict: A dictionary representation of the Config object.
|
|
"""
|
|
return {"db_variable": self.db_variable, "db_version": self.db_version}
|
|
|
|
|
|
class Beacon(Base):
|
|
"""Represents a beacon signal received by the station.
|
|
|
|
This class maps to the 'beacon' table and stores information about
|
|
received beacon signals, including the timestamp, signal-to-noise
|
|
ratio (SNR), and the callsign of the transmitting station. It is
|
|
linked to the Station table via a foreign key.
|
|
"""
|
|
|
|
__tablename__ = "beacon"
|
|
id = Column(Integer, primary_key=True)
|
|
timestamp = Column(DateTime)
|
|
snr = Column(Integer)
|
|
callsign = Column(String, ForeignKey("station.callsign"))
|
|
station = relationship("Station", back_populates="beacons")
|
|
|
|
Index("idx_beacon_callsign", "callsign")
|
|
|
|
|
|
class Station(Base):
|
|
"""Represents a station in the network.
|
|
|
|
This class maps to the 'station' table and stores information about
|
|
stations, including their callsign, checksum, location (as JSON),
|
|
and additional info (as JSON). It has a relationship with the Beacon
|
|
table, representing the beacons received from this station.
|
|
"""
|
|
|
|
__tablename__ = "station"
|
|
callsign = Column(String, primary_key=True)
|
|
checksum = Column(String, nullable=True)
|
|
location = Column(JSON, nullable=True)
|
|
info = Column(JSON, nullable=True)
|
|
beacons = relationship("Beacon", order_by="Beacon.id", back_populates="station")
|
|
|
|
Index("idx_station_callsign_checksum", "callsign", "checksum")
|
|
|
|
def to_dict(self):
|
|
"""Converts the Station object to a dictionary.
|
|
|
|
Returns:
|
|
dict: A dictionary representation of the Station object.
|
|
"""
|
|
return {
|
|
"callsign": self.callsign,
|
|
"checksum": self.checksum,
|
|
"location": self.location,
|
|
"info": self.info,
|
|
}
|
|
|
|
|
|
class Status(Base):
|
|
"""Represents the status of a P2P message.
|
|
|
|
This class maps to the 'status' table and stores the possible
|
|
statuses a message can have (e.g., 'queued', 'transmitted',
|
|
'received', 'failed'). The `name` field is unique to prevent
|
|
duplicate status entries.
|
|
"""
|
|
|
|
__tablename__ = "status"
|
|
id = Column(Integer, primary_key=True)
|
|
name = Column(String, unique=True)
|
|
|
|
|
|
class P2PMessage(Base):
|
|
"""Represents a peer-to-peer (P2P) message.
|
|
|
|
This class maps to the 'p2p_message' table and stores information
|
|
about P2P messages, including sender, recipient, message body,
|
|
attachments, transmission attempts, timestamp, status, priority,
|
|
direction, statistics (JSON), and read status. It has relationships
|
|
with the Station, Status, and Attachment tables.
|
|
"""
|
|
|
|
__tablename__ = "p2p_message"
|
|
id = Column(String, primary_key=True)
|
|
origin_callsign = Column(String, ForeignKey("station.callsign"))
|
|
via_callsign = Column(String, ForeignKey("station.callsign"), nullable=True)
|
|
destination_callsign = Column(String, ForeignKey("station.callsign"))
|
|
body = Column(String, nullable=True)
|
|
message_attachments = relationship("MessageAttachment", back_populates="message", cascade="all, delete-orphan")
|
|
attempt = Column(Integer, default=0)
|
|
timestamp = Column(DateTime)
|
|
status_id = Column(Integer, ForeignKey("status.id"), nullable=True)
|
|
status = relationship("Status", backref="p2p_messages")
|
|
priority = Column(Integer, default=10)
|
|
direction = Column(String)
|
|
statistics = Column(JSON, nullable=True)
|
|
is_read = Column(Boolean, default=True)
|
|
|
|
Index(
|
|
"idx_p2p_message_origin_timestamp",
|
|
"origin_callsign",
|
|
"via_callsign",
|
|
"destination_callsign",
|
|
"timestamp",
|
|
)
|
|
|
|
def to_dict(self):
|
|
"""Converts the P2PMessage object to a dictionary.
|
|
|
|
This method converts the P2PMessage object and its associated
|
|
attachments to a dictionary format, suitable for serialization or
|
|
other data processing. It retrieves the attachment data using
|
|
the to_dict method of each attachment object. It formats the
|
|
timestamp as an ISO 8601 string.
|
|
|
|
Returns:
|
|
dict: A dictionary representation of the P2PMessage object,
|
|
including its attachments.
|
|
"""
|
|
attachments_list = [ma.attachment.to_dict() for ma in self.message_attachments]
|
|
|
|
return {
|
|
"id": self.id,
|
|
"timestamp": self.timestamp.isoformat() if self.timestamp else None,
|
|
"attempt": self.attempt,
|
|
"origin": self.origin_callsign,
|
|
"via": self.via_callsign,
|
|
"destination": self.destination_callsign,
|
|
"direction": self.direction,
|
|
"body": self.body,
|
|
"attachments": attachments_list,
|
|
"status": self.status.name if self.status else None,
|
|
"priority": self.priority,
|
|
"is_read": self.is_read,
|
|
"statistics": self.statistics,
|
|
}
|
|
|
|
|
|
class Attachment(Base):
|
|
"""Represents a file attachment associated with a message.
|
|
|
|
This class maps to the 'attachment' table and stores information
|
|
about attachments, including their name, data type, data content,
|
|
CRC32 checksum, and SHA-512 hash. It has a relationship with the
|
|
MessageAttachment association table.
|
|
"""
|
|
|
|
__tablename__ = "attachment"
|
|
id = Column(Integer, primary_key=True)
|
|
name = Column(String)
|
|
data_type = Column(String)
|
|
data = Column(String)
|
|
checksum_crc32 = Column(String)
|
|
hash_sha512 = Column(String)
|
|
message_attachments = relationship("MessageAttachment", back_populates="attachment")
|
|
|
|
Index("idx_attachments_id_message_id", "id", "hash_sha512")
|
|
|
|
def to_dict(self):
|
|
"""Converts the Attachment object to a dictionary.
|
|
|
|
Returns:
|
|
dict: A dictionary representation of the Attachment object.
|
|
"""
|
|
return {
|
|
"id": self.id,
|
|
"name": self.name,
|
|
"type": self.data_type,
|
|
"data": self.data,
|
|
"checksum_crc32": self.checksum_crc32,
|
|
"hash_sha512": self.hash_sha512,
|
|
}
|
|
|
|
|
|
class BroadcastMessage(Base):
|
|
__tablename__ = "broadcast_messages"
|
|
|
|
id = Column(String, primary_key=True)
|
|
origin = Column(String, ForeignKey("station.callsign"))
|
|
timestamp = Column(Float, default=lambda: datetime.now(timezone.utc))
|
|
repairing_callsigns = Column(JSON, nullable=True)
|
|
domain = Column(String)
|
|
gridsquare = Column(String)
|
|
frequency = Column(Integer, default=0)
|
|
priority = Column(Integer, default=1)
|
|
is_read = Column(Boolean, default=True)
|
|
direction = Column(String, nullable=True)
|
|
payload_size = Column(Integer, default=0)
|
|
payload_data = Column(JSON, nullable=True)
|
|
msg_type = Column(String)
|
|
total_bursts = Column(Integer, default=0)
|
|
checksum = Column(String)
|
|
received_at = Column(Float, default=lambda: datetime.now(timezone.utc))
|
|
nexttransmission_at = Column(Float, default=lambda: datetime.now(timezone.utc))
|
|
expires_at = Column(Float, default=lambda: datetime.now(timezone.utc))
|
|
|
|
status_id = Column(Integer, ForeignKey("status.id"), nullable=True)
|
|
status = relationship("Status", backref="broadcast_messages")
|
|
error_reason = Column(String, nullable=True)
|
|
attempts = Column(Integer, default=0)
|
|
|
|
__table_args__ = (Index("idx_broadcast_domain_received", "domain", "received_at"),)
|
|
|
|
def to_dict(self):
|
|
return {
|
|
"id": self.id,
|
|
"origin": self.origin,
|
|
"timestamp": self.timestamp,
|
|
"repairing_callsigns": self.repairing_callsigns,
|
|
"domain": self.domain,
|
|
"gridsquare": self.gridsquare,
|
|
"frequency": self.frequency,
|
|
"priority": self.priority,
|
|
"is_read": self.is_read,
|
|
"direction": self.direction,
|
|
"payload_size": self.payload_size,
|
|
"payload_data": self.payload_data,
|
|
"msg_type": self.msg_type,
|
|
"total_bursts": self.total_bursts,
|
|
"checksum": self.checksum,
|
|
"received_at": self.received_at if self.received_at else None,
|
|
"expires_at": self.expires_at if self.expires_at else None,
|
|
"nexttransmission_at": self.nexttransmission_at if self.nexttransmission_at else None,
|
|
"status": self.status.name if self.status else None,
|
|
"error_reason": self.error_reason,
|
|
"attempts": self.attempts,
|
|
}
|