Begin implementing better DB layer

This commit is contained in:
XANTRONIX Development 2024-11-08 23:11:09 -05:00
parent 0878e830e4
commit 2766286a7e
3 changed files with 173 additions and 30 deletions

View file

@ -1,28 +1,141 @@
import enum
import sqlite3
from nntp.tiny.message import Message
class DatabaseOrder(enum.Enum):
DEFAULT = 0
ASC = 1
DESC = 2
class DatabaseTable():
pass
class DatabaseTableCursor():
__slots__ = 'cr', 'table',
def __init__(self, table, cr):
self.cr = cr
self.table = table
def __getattr__(self, name):
return getattr(self.cr, name)
def __map__(self, row):
obj = self.table()
for name in self.table.columns:
try:
setattr(obj, name, row[name])
except IndexError:
setattr(obj, name, None)
return obj
def fetchone(self):
return self.__map__(self.cr.fetchone())
def fetchall(self):
return map(self.__map__, self.cr.fetchall())
class Database():
__slots__ = 'db',
def __init__(self, path: str):
self.db = sqlite3.connect(path)
def __init__(self, db):
self.db = db
def message_add(self, newsgroup_id: int, message: Message):
sql = """
insert into
newsgroup_message
(newsgroup_id, message_id, created_on, sender, subject, content) values (?, ?, ?, ?, ?, ?)
"""
def __getattr__(self, name):
return getattr(self.db, name)
self.db.execute(sql, (
newsgroup_id,
message.id(),
message.date().isoformat(),
message.sender(),
message.subject(),
message.content
))
@staticmethod
def connect(path):
db = sqlite3.connect(path)
db.row_factory = sqlite3.Row
def commit(self):
self.db.commit()
return Database(db)
def add(self, obj):
table = type(obj)
sql = f"insert into {table.name} ("
first = True
for column in table.columns:
if column == table.key:
continue
if first:
sql += column
first = False
else:
sql += ', ' + column
sql += ') values ('
first = True
for column in table.columns:
if column == table.key:
continue
if first:
first = False
else:
sql += ', '
sql += '?'
sql += ')'
fn = getattr(obj, '__values__')
if fn is not None:
values = fn()
else:
values = list()
for column in table.columns:
if column != table.key:
values.append(getattr(obj, column))
self.db.execute(sql, values)
def query(self, table, values=dict(), order_by=list()):
sql = f"select * from {table.name}"
params = list()
first = True
for key in values.keys():
if first:
sql += f" where {table.name}.{key} = ?"
first = False
else:
sql += f" and {table.name}.{key} = ?"
params.append(values[key])
first = True
if len(order_by) > 0:
sql += " order by"
for column, order in order_by:
if first:
first = False
else:
sql += ", "
if order is None or order is DatabaseOrder.DEFAULT:
sql += f" {column}"
elif order is DatabaseOrder.ASC:
sql += f" {column} asc"
elif order is DatabaseOrder.DESC:
sql += f" {column} desc"
cr = DatabaseTableCursor(table, self.db.cursor())
cr.execute(sql, params)
return cr
def get(self, table, values=dict()):
return self.query(table, values).fetchone()

View file

@ -5,6 +5,8 @@ import datetime
from dateparser.search import search_dates
from email.header import decode_header
from nntp.tiny.db import DatabaseTable
def decode(text: str):
decoded = decode_header(text)[0]
@ -21,19 +23,41 @@ class MessageState(enum.Enum):
HEADER = 1
BODY = 2
class Message():
__slots__ = 'state', 'headers', 'line', 'content', 'body', 'key',
class Message(DatabaseTable):
__slots__ = 'newsgroup_id', 'state', 'headers', 'line', 'content', 'body', '_key',
name = 'newsgroup_message'
key = 'id'
columns = (
'newsgroup_id',
'message_id',
'created_on',
'sender',
'subject',
'content'
)
RE_HEADER = re.compile(r'^([A-Za-z0-9\-]+): (.*)$')
RE_MESSAGE_ID = re.compile(r'^<([^<>]+)>$')
def __init__(self):
self.state = MessageState.EMPTY
self.headers = dict()
self.line = None
self.content = ''
self.body = None
self.key = None
self.newsgroup_id = None
self.state = MessageState.EMPTY
self.headers = dict()
self.line = None
self.content = ''
self.body = None
self._key = None
def __values__(self):
return (
self.newsgroup_id,
self.id(),
self.date(),
self.sender(),
self.subject(),
self.content
)
def add(self, line: str):
if self.line is not None:
@ -46,14 +70,14 @@ class Message():
if line == '\n' or line == '\r\n':
self.state = MessageState.BODY
elif line[0] == ' ' or line[0] == '\t':
self.headers[self.key] += ' ' + decode(line.strip())
self.headers[self._key] += ' ' + decode(line.strip())
else:
match = self.RE_HEADER.match(line)
if match:
self.key = match[1].lower()
self._key = match[1].lower()
self.headers[self.key] = decode(match[2].rstrip())
self.headers[self._key] = decode(match[2].rstrip())
elif self.state is MessageState.BODY:
if self.body is None:
self.body = ''

View file

@ -0,0 +1,6 @@
from nntp.tiny.db import DatabaseTable
class Newsgroup(DatabaseTable):
name = 'newsgroup'
key = 'id'
columns = 'id', 'name', 'description',