Server side implementation for interactive entry edits

This commit is contained in:
2024-11-23 15:34:33 +01:00
parent bb74fd3621
commit dbc0068a82
28 changed files with 749 additions and 553 deletions

View File

@@ -1,21 +1,22 @@
from datetime import datetime
import uuid
import xml.etree.ElementTree as ET
from typing import Union
from .util import format_timestamp
from .command import Command
from .delete_command import DeleteCommand
from .stop_command import StopCommand
from .background_entry import BackgroundEntry
from .entry.background_entry import BackgroundEntry
from .entry import Entry
from .io_buffer import IOBuffer
from .parse_error_entry import ParseErrorEntry
from .read_entry import ReadEntry
from .reasoning_entry import ReasoningEntry
from .repeat_entry import RepeatEntry
from .single_entry import SingleEntry
from .write_entry import WriteEntry
from .entry.parse_error_entry import ParseErrorEntry
from .entry.read_entry import ReadEntry
from .entry.reasoning_entry import ReasoningEntry
from .entry.repeat_entry import RepeatEntry
from .entry.single_entry import SingleEntry
from .entry.write_entry import WriteEntry
class ResponseParser:
"""
@@ -48,7 +49,7 @@ class ResponseParser:
"""
return self._io_buffer
def parse(self, xml: str) -> Union[Command, Entry]:
def parse(self, timestamp: datetime, xml: str) -> Union[Command, Entry]:
"""
Parse XML response into a Command or Entry.
@@ -59,8 +60,7 @@ class ResponseParser:
Command or Entry based on the XML content
ParseErrorEntry for any parsing or validation errors
"""
entry_id = str(uuid.uuid4())
timestamp = datetime.now()
entry_id = format_timestamp(timestamp)
parser = ET.XMLPullParser(events=("start", "end"))
parser.feed(xml)
@@ -70,15 +70,15 @@ class ResponseParser:
if event == "start":
break
except ET.ParseError as e:
return ParseErrorEntry(entry_id, timestamp, xml, f"Invalid XML: {str(e)}")
return ParseErrorEntry(entry_id, xml, f"Invalid XML: {str(e)}")
try:
if root.tag == 'delete':
target_id = root.get('id')
if not target_id:
return ParseErrorEntry(entry_id, timestamp, xml, "Delete command missing required 'id' attribute")
return ParseErrorEntry(entry_id, xml, "Delete command missing required 'id' attribute")
if len(root.attrib) > 1:
return ParseErrorEntry(entry_id, timestamp, xml, "Delete command should only have 'id' attribute")
return ParseErrorEntry(entry_id, xml, "Delete command should only have 'id' attribute")
return DeleteCommand(target_id)
elif root.tag == 'stop':
@@ -86,46 +86,46 @@ class ResponseParser:
elif root.tag == 'background':
if len(root) != 0 or root.attrib or root.text is None or root.text.strip() == '':
return ParseErrorEntry(entry_id, timestamp, xml, "Background entry requires (only) script content")
return BackgroundEntry(entry_id, timestamp, root.text)
return ParseErrorEntry(entry_id, xml, "Background entry requires (only) script content")
return BackgroundEntry(entry_id, root.text)
elif root.tag == 'repeat':
if len(root) != 0 or root.text is None or root.text.strip() == '':
return ParseErrorEntry(entry_id, timestamp, xml, "Repeat entry requires (only) script content")
return ParseErrorEntry(entry_id, xml, "Repeat entry requires (only) script content")
if len(root.attrib) > 2 or any(k not in ('timeout', 'limit') for k in root.attrib):
return ParseErrorEntry(entry_id, timestamp, xml, "Repeat entry only accepts 'timeout' and 'limit' attributes")
return ParseErrorEntry(entry_id, xml, "Repeat entry only accepts 'timeout' and 'limit' attributes")
timeout = root.get('timeout')
limit = root.get('limit')
timeout = float(timeout) if timeout is not None else None
limit = int(limit) if limit is not None else None
return RepeatEntry(entry_id, timestamp, root.text, timeout, limit)
return RepeatEntry(entry_id, root.text, timeout, limit)
elif root.tag == 'single':
if len(root) != 0 or root.text is None or root.text.strip() == '':
return ParseErrorEntry(entry_id, timestamp, xml, "Single entry requires (only) script content")
return ParseErrorEntry(entry_id, xml, "Single entry requires (only) script content")
if len(root.attrib) > 2 or any(k not in ('timeout', 'limit') for k in root.attrib):
return ParseErrorEntry(entry_id, timestamp, xml, "Single entry only accepts 'timeout' and 'limit' attributes")
return ParseErrorEntry(entry_id, xml, "Single entry only accepts 'timeout' and 'limit' attributes")
timeout = root.get('timeout')
limit = root.get('limit')
timeout = float(timeout) if timeout is not None else None
limit = int(limit) if limit is not None else None
return SingleEntry(entry_id, timestamp, root.text, timeout, limit)
return SingleEntry(entry_id, root.text, timeout, limit)
elif root.tag == 'reasoning':
if len(root) != 0 or root.attrib or root.text is None or root.text.strip() == '':
return ParseErrorEntry(entry_id, timestamp, xml, "Reasoning entry requires (only) text content")
return ReasoningEntry(entry_id, timestamp, root.text)
return ParseErrorEntry(entry_id, xml, "Reasoning entry requires (only) text content")
return ReasoningEntry(entry_id, root.text)
elif root.tag == 'read_stdin':
return ReadEntry(entry_id, timestamp, self._io_buffer)
return ReadEntry(entry_id, self._io_buffer)
elif root.tag == 'write_stdout':
if len(root) != 0 or root.attrib or root.text is None or root.text.strip() == '':
return ParseErrorEntry(entry_id, timestamp, xml, "Write stdout entry requires (only) text content")
return WriteEntry(entry_id, timestamp, root.text, self._io_buffer)
return ParseErrorEntry(entry_id, xml, "Write stdout entry requires (only) text content")
return WriteEntry(entry_id, root.text, self._io_buffer)
else:
return ParseErrorEntry(entry_id, timestamp, xml, f"Unknown root element: {root.tag}")
return ParseErrorEntry(entry_id, xml, f"Unknown root element: {root.tag}")
except Exception as e:
return ParseErrorEntry(entry_id, timestamp, xml, f"Error parsing response: {str(e)}")
return ParseErrorEntry(entry_id, xml, f"Error parsing response: {str(e)}")