Compare commits
8 Commits
e66a20a473
...
f5b185505e
| Author | SHA1 | Date | |
|---|---|---|---|
| f5b185505e | |||
| 07b8f955da | |||
| 4e2af55b7c | |||
| de4c6e3b4a | |||
| c16afc6c11 | |||
| 68a7315044 | |||
| 6e7f39de2a | |||
| 89d19ee9d6 |
@ -9,7 +9,7 @@ from typing import TypeVar, Type, Optional, ClassVar, Any
|
|||||||
from .message import Message, MessageFilter, MessageError
|
from .message import Message, MessageFilter, MessageError
|
||||||
|
|
||||||
ChatInst = TypeVar('ChatInst', bound='Chat')
|
ChatInst = TypeVar('ChatInst', bound='Chat')
|
||||||
ChatDBInst = TypeVar('ChatDBInst', bound='ChatDB')
|
ChatDirInst = TypeVar('ChatDirInst', bound='ChatDir')
|
||||||
|
|
||||||
|
|
||||||
class ChatError(Exception):
|
class ChatError(Exception):
|
||||||
@ -39,29 +39,6 @@ class Chat:
|
|||||||
"""
|
"""
|
||||||
self.messages = [m for m in self.messages if m.match(mfilter)]
|
self.messages = [m for m in self.messages if m.match(mfilter)]
|
||||||
|
|
||||||
def sort(self, reverse: bool = False) -> None:
|
|
||||||
"""
|
|
||||||
Sort the messages according to 'Message.msg_id()'.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# the message may not have an ID if it doesn't have a file_path
|
|
||||||
self.messages.sort(key=lambda m: m.msg_id(), reverse=reverse)
|
|
||||||
except MessageError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def clear(self) -> None:
|
|
||||||
"""
|
|
||||||
Delete all messages.
|
|
||||||
"""
|
|
||||||
self.messages = []
|
|
||||||
|
|
||||||
def add_msgs(self, msgs: list[Message]) -> None:
|
|
||||||
"""
|
|
||||||
Add new messages and sort them if possible.
|
|
||||||
"""
|
|
||||||
self.messages += msgs
|
|
||||||
self.sort()
|
|
||||||
|
|
||||||
def print(self, dump: bool = False) -> None:
|
def print(self, dump: bool = False) -> None:
|
||||||
if dump:
|
if dump:
|
||||||
pp(self)
|
pp(self)
|
||||||
@ -81,7 +58,7 @@ class Chat:
|
|||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ChatDB(Chat):
|
class ChatDir(Chat):
|
||||||
"""
|
"""
|
||||||
A 'Chat' class that is bound to a given directory structure. Supports reading
|
A 'Chat' class that is bound to a given directory structure. Supports reading
|
||||||
and writing messages from / to that structure. Such a structure consists of
|
and writing messages from / to that structure. Such a structure consists of
|
||||||
@ -97,19 +74,17 @@ class ChatDB(Chat):
|
|||||||
# a MessageFilter that all messages must match (if given)
|
# a MessageFilter that all messages must match (if given)
|
||||||
mfilter: Optional[MessageFilter] = None
|
mfilter: Optional[MessageFilter] = None
|
||||||
file_suffix: str = default_file_suffix
|
file_suffix: str = default_file_suffix
|
||||||
# the glob pattern for all messages
|
|
||||||
glob: Optional[str] = None
|
|
||||||
# set containing all file names of the current messages
|
# set containing all file names of the current messages
|
||||||
message_files: set[str] = field(default_factory=set, repr=False)
|
message_files: set[str] = field(default_factory=set)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dir(cls: Type[ChatDBInst],
|
def from_dir(cls: Type[ChatDirInst],
|
||||||
cache_path: pathlib.Path,
|
cache_path: pathlib.Path,
|
||||||
db_path: pathlib.Path,
|
db_path: pathlib.Path,
|
||||||
glob: Optional[str] = None,
|
glob: Optional[str] = None,
|
||||||
mfilter: Optional[MessageFilter] = None) -> ChatDBInst:
|
mfilter: Optional[MessageFilter] = None) -> ChatDirInst:
|
||||||
"""
|
"""
|
||||||
Create a 'ChatDB' instance from the given directory structure.
|
Create a 'ChatDir' instance from the given directory structure.
|
||||||
Reads all messages from 'db_path' into the local message list.
|
Reads all messages from 'db_path' into the local message list.
|
||||||
Parameters:
|
Parameters:
|
||||||
* 'cache_path': path to the directory for temporary messages
|
* 'cache_path': path to the directory for temporary messages
|
||||||
@ -131,20 +106,19 @@ class ChatDB(Chat):
|
|||||||
message_files.add(file_path.name)
|
message_files.add(file_path.name)
|
||||||
except MessageError as e:
|
except MessageError as e:
|
||||||
print(f"Error processing message in '{file_path}': {str(e)}")
|
print(f"Error processing message in '{file_path}': {str(e)}")
|
||||||
return cls(messages, cache_path, db_path, mfilter,
|
return cls(messages, cache_path, db_path, mfilter, cls.default_file_suffix, message_files)
|
||||||
cls.default_file_suffix, glob, message_files)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_messages(cls: Type[ChatDBInst],
|
def from_messages(cls: Type[ChatDirInst],
|
||||||
cache_path: pathlib.Path,
|
cache_path: pathlib.Path,
|
||||||
db_path: pathlib.Path,
|
db_path: pathlib.Path,
|
||||||
messages: list[Message],
|
messages: list[Message],
|
||||||
mfilter: Optional[MessageFilter]) -> ChatDBInst:
|
mfilter: Optional[MessageFilter]) -> ChatDirInst:
|
||||||
"""
|
"""
|
||||||
Create a ChatDB instance from the given message list. Note that the next
|
Create a ChatDir instance from the given message list.
|
||||||
call to 'dump()' will write all files in order to synchronize the messages.
|
Note that the next call to 'dump()' will write all files
|
||||||
Similarly, 'update()' will read all messages, so you may end up with a lot
|
in order to synchronize the messages. 'update()' is not
|
||||||
of duplicates when using 'update()' first.
|
supported until after the first 'dump()'.
|
||||||
"""
|
"""
|
||||||
return cls(messages, cache_path, db_path, mfilter)
|
return cls(messages, cache_path, db_path, mfilter)
|
||||||
|
|
||||||
@ -163,10 +137,9 @@ class ChatDB(Chat):
|
|||||||
|
|
||||||
def dump(self, to_db: bool = False, force_all: bool = False) -> None:
|
def dump(self, to_db: bool = False, force_all: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
Write all messages to 'cache_path' (or 'db_path' if 'to_db' is True). If a message
|
Writes all messages to the 'cache_path' or 'db_path'. If a message has no file_path,
|
||||||
has no file_path, a new one will be created. By default, only messages that have
|
it will create a new one. By default, only messages that have not been written
|
||||||
not been written (or read) before will be dumped. Use 'force_all' to force writing
|
(or read) before will be dumped. Use 'force_all' to force writing all message files.
|
||||||
all message files.
|
|
||||||
"""
|
"""
|
||||||
for message in self.messages:
|
for message in self.messages:
|
||||||
# skip messages that we have already written (or read)
|
# skip messages that we have already written (or read)
|
||||||
@ -179,26 +152,3 @@ class ChatDB(Chat):
|
|||||||
file_path = self.db_path / fname if to_db else self.cache_path / fname
|
file_path = self.db_path / fname if to_db else self.cache_path / fname
|
||||||
self.set_next_fid(fid)
|
self.set_next_fid(fid)
|
||||||
message.to_file(file_path)
|
message.to_file(file_path)
|
||||||
|
|
||||||
def update(self, from_cache: bool = False, force_all: bool = False) -> None:
|
|
||||||
"""
|
|
||||||
Read new messages from 'db_path' (or 'cache_path' if 'from_cache' is true).
|
|
||||||
By default, only messages that have not been read (or written) before will
|
|
||||||
be read. Use 'force_all' to force reading all messages.
|
|
||||||
"""
|
|
||||||
if from_cache:
|
|
||||||
file_iter = self.cache_path.glob(self.glob) if self.glob else self.cache_path.iterdir()
|
|
||||||
else:
|
|
||||||
file_iter = self.cache_path.glob(self.glob) if self.glob else self.cache_path.iterdir()
|
|
||||||
for file_path in sorted(file_iter):
|
|
||||||
if file_path.is_file():
|
|
||||||
if file_path.name in self.message_files and not force_all:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
message = Message.from_file(file_path, self.mfilter)
|
|
||||||
if message:
|
|
||||||
self.messages.append(message)
|
|
||||||
self.message_files.add(file_path.name)
|
|
||||||
except MessageError as e:
|
|
||||||
print(f"Error processing message in '{file_path}': {str(e)}")
|
|
||||||
self.sort()
|
|
||||||
|
|||||||
@ -409,10 +409,10 @@ class Message():
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def msg_id(self) -> str:
|
def file_id(self) -> str:
|
||||||
"""
|
"""
|
||||||
Returns an ID that is unique throughout all messages in the same (DB) directory.
|
Returns an ID that is unique within the directory of this message.
|
||||||
Currently this is the file name. The ID is also used for sorting messages.
|
Currently this is simply the file name.
|
||||||
"""
|
"""
|
||||||
if self.file_path:
|
if self.file_path:
|
||||||
return self.file_path.name
|
return self.file_path.name
|
||||||
|
|||||||
@ -577,7 +577,7 @@ This is an answer.
|
|||||||
self.assertSetEqual(tags, {Tag('tag1'), Tag('tag2')})
|
self.assertSetEqual(tags, {Tag('tag1'), Tag('tag2')})
|
||||||
|
|
||||||
|
|
||||||
class MessageIDTestCase(CmmTestCase):
|
class MessageFileIDTxtTestCase(CmmTestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.txt')
|
self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.txt')
|
||||||
self.file_path = pathlib.Path(self.file.name)
|
self.file_path = pathlib.Path(self.file.name)
|
||||||
@ -589,9 +589,9 @@ class MessageIDTestCase(CmmTestCase):
|
|||||||
self.file.close()
|
self.file.close()
|
||||||
self.file_path.unlink()
|
self.file_path.unlink()
|
||||||
|
|
||||||
def test_msg_id_txt(self) -> None:
|
def test_file_id_txt(self) -> None:
|
||||||
self.assertEqual(self.message.msg_id(), self.file_path.name)
|
self.assertEqual(self.message.file_id(), self.file_path.name)
|
||||||
|
|
||||||
def test_msg_id_txt_exception(self) -> None:
|
def test_file_id_txt_exception(self) -> None:
|
||||||
with self.assertRaises(MessageError):
|
with self.assertRaises(MessageError):
|
||||||
self.message_no_file_path.msg_id()
|
self.message_no_file_path.file_id()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user