-
Notifications
You must be signed in to change notification settings - Fork 2
/
models.py
106 lines (73 loc) · 3.53 KB
/
models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
"""Classes used by the application. This module contains only their definitions.
To actually map these classes to their database equivalent, database.py is used,
which creates a database and initialises these definitions used here (via
initiate_data_definitions)"""
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Table, UniqueConstraint, Integer, String, LargeBinary, ForeignKey, Enum, DateTime
from sqlalchemy.orm import relationship
import config
Base = declarative_base()
class Value(Base):
__tablename__ = 'values'
id = Column(Integer, primary_key=True, unique=True)
hash = Column(LargeBinary(length=config.DIGEST_SIZE), unique=True)
blob = Column(LargeBinary)
@property
def size_in_bytes(self):
return len(self.blob)
def __repr__(self):
return '<Value: hash=%s length=%d>' % (self.hash.hex(), len(self.blob))
class Entry(Base):
__tablename__ = 'entries'
id = Column(Integer, primary_key=True, unique=True)
# Key sizes are not enforced in the database. This is not necessary,
# because the application itself enforces only legal keys are ever
# saved in the database and because this would complicate the data
# definitions here significantly and would create duplicate lots of
# code.
key = Column(String(255), unique=True)
value_id = Column(Integer, ForeignKey('values.id'))
value = relationship('Value')
def __repr__(self):
return '<Entry(key=%s, len(value)=%d>' % (self.key, self.value.size_in_bytes)
# Metadata for many-to-many association between HttpRequest and HttpHeader
association_table = Table('association', Base.metadata,
Column('request_id', Integer, ForeignKey('http_requests.id')),
Column('header_id', Integer, ForeignKey('http_headers.id'))
)
class HttpHeader(Base):
__tablename__ = 'http_headers'
id = Column(Integer, primary_key=True, unique=True)
key = Column(String(255))
value = Column(String)
__table_args__ = (UniqueConstraint('key', 'value', name = 'header_pair_unique'), )
def __repr__(self):
return '<HTTPHeader(key=%s, value=%s)>' % (self.key, self.value)
class HttpRequest(Base):
__tablename__ = 'http_requests'
id = Column(Integer, primary_key = True, unique = True)
timestamp = Column(DateTime)
sender = Column(String(255))
headers = relationship('HttpHeader', secondary = association_table)
def __repr__(self):
return '<HTTPRequest(timestamp=%s, sender=%s, headers=%s>' % (
self.timestamp,
self.sender,
self.headers)
class Event(Base):
__tablename__ = 'events'
id = Column(Integer, primary_key=True, unique=True)
action = Column(Enum('Update', 'Retrieve', 'Delete',
name = 'action_enum', nullable = False))
request_id = Column(Integer, ForeignKey('http_requests.id'))
request = relationship('HttpRequest')
# See above, correct key format is enforced by the application.
key = Column(String(255))
value_before_id = Column(Integer, ForeignKey('values.id'), nullable=True)
value_before = relationship('Value', foreign_keys=value_before_id)
value_after_id = Column(Integer, ForeignKey('values.id'), nullable=True)
value_after = relationship('Value', foreign_keys=value_after_id)
def initiate_data_definitions(engine):
"""Initiates the database.
To be called from database.py to finish initialisation"""
Base.metadata.create_all(engine)