mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-06-15 23:02:16 +03:00
EDDN: Open & create sqlite3 db for replay
* sqlite3 open, and creation of table. * Change `load_journal_replay()` to `load_journal_replay_file()` and change the semantics to just return the `list[str]` loaded from it. It also now catches no exceptions. * Remove the "lock the journal cache" on init as it's not necessary. There's still a lot more changes to come on this.
This commit is contained in:
parent
4c3ea5fd27
commit
9faae8b9bc
@ -26,16 +26,16 @@ import itertools
|
|||||||
import json
|
import json
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
import sqlite3
|
||||||
import sys
|
import sys
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from os import SEEK_SET
|
from os import SEEK_SET
|
||||||
from os.path import join
|
|
||||||
from platform import system
|
from platform import system
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Mapping, MutableMapping, Optional
|
from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Mapping, MutableMapping, Optional
|
||||||
from typing import OrderedDict as OrderedDictT
|
from typing import OrderedDict as OrderedDictT
|
||||||
from typing import TextIO, Tuple, Union
|
from typing import Tuple, Union
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@ -52,10 +52,6 @@ from prefs import prefsVersion
|
|||||||
from ttkHyperlinkLabel import HyperlinkLabel
|
from ttkHyperlinkLabel import HyperlinkLabel
|
||||||
from util import text
|
from util import text
|
||||||
|
|
||||||
if sys.platform != 'win32':
|
|
||||||
from fcntl import LOCK_EX, LOCK_NB, lockf
|
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
def _(x: str) -> str:
|
def _(x: str) -> str:
|
||||||
return x
|
return x
|
||||||
@ -155,8 +151,25 @@ class EDDN:
|
|||||||
self.parent: tk.Tk = parent
|
self.parent: tk.Tk = parent
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self.session.headers['User-Agent'] = user_agent
|
self.session.headers['User-Agent'] = user_agent
|
||||||
self.replayfile: Optional[TextIO] = None # For delayed messages
|
|
||||||
self.replaylog: List[str] = []
|
#######################################################################
|
||||||
|
# EDDN delayed sending/retry
|
||||||
|
#######################################################################
|
||||||
|
self.replaydb = self.journal_replay_sqlite_init()
|
||||||
|
# Kept only for converting legacy file to sqlite3
|
||||||
|
try:
|
||||||
|
replaylog = self.load_journal_replay_file()
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# TODO: Convert `replaylog` into the database.
|
||||||
|
# Remove the file.
|
||||||
|
...
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
self.fss_signals: List[Mapping[str, Any]] = []
|
self.fss_signals: List[Mapping[str, Any]] = []
|
||||||
|
|
||||||
if config.eddn_url is not None:
|
if config.eddn_url is not None:
|
||||||
@ -165,36 +178,49 @@ class EDDN:
|
|||||||
else:
|
else:
|
||||||
self.eddn_url = self.DEFAULT_URL
|
self.eddn_url = self.DEFAULT_URL
|
||||||
|
|
||||||
def load_journal_replay(self) -> bool:
|
def journal_replay_sqlite_init(self) -> sqlite3.Cursor:
|
||||||
|
"""
|
||||||
|
Ensure the sqlite3 database for EDDN replays exists and has schema.
|
||||||
|
|
||||||
|
:return: sqlite3 cursor for the database.
|
||||||
|
"""
|
||||||
|
self.replaydb_conn = sqlite3.connect(config.app_dir_path / 'eddn_replay.db')
|
||||||
|
replaydb = self.replaydb_conn.cursor()
|
||||||
|
try:
|
||||||
|
replaydb.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE messages
|
||||||
|
(
|
||||||
|
id INT PRIMARY KEY NOT NULL,
|
||||||
|
created TEXT NOT NULL,
|
||||||
|
cmdr TEXT NOT NULL,
|
||||||
|
edmc_version TEXT,
|
||||||
|
game_version TEXT,
|
||||||
|
game_build TEXT,
|
||||||
|
message TEXT NOT NULL
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
except sqlite3.OperationalError as e:
|
||||||
|
if str(e) != "table messages already exists":
|
||||||
|
raise e
|
||||||
|
|
||||||
|
return replaydb
|
||||||
|
|
||||||
|
def load_journal_replay_file(self) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Load cached journal entries from disk.
|
Load cached journal entries from disk.
|
||||||
|
|
||||||
:return: a bool indicating success
|
Simply let any exceptions propagate up if there's an error.
|
||||||
|
|
||||||
|
:return: Contents of the file as a list.
|
||||||
"""
|
"""
|
||||||
# Try to obtain exclusive access to the journal cache
|
# Try to obtain exclusive access to the journal cache
|
||||||
filename = join(config.app_dir, 'replay.jsonl')
|
filename = config.app_dir_path / 'replay.jsonl'
|
||||||
try:
|
# Try to open existing file
|
||||||
try:
|
with open(filename, 'r+', buffering=1) as replay_file:
|
||||||
# Try to open existing file
|
return [line.strip() for line in replay_file]
|
||||||
self.replayfile = open(filename, 'r+', buffering=1)
|
|
||||||
|
|
||||||
except FileNotFoundError:
|
|
||||||
self.replayfile = open(filename, 'w+', buffering=1) # Create file
|
|
||||||
|
|
||||||
if sys.platform != 'win32': # open for writing is automatically exclusive on Windows
|
|
||||||
lockf(self.replayfile, LOCK_EX | LOCK_NB)
|
|
||||||
|
|
||||||
except OSError:
|
|
||||||
logger.exception('Failed opening "replay.jsonl"')
|
|
||||||
if self.replayfile:
|
|
||||||
self.replayfile.close()
|
|
||||||
|
|
||||||
self.replayfile = None
|
|
||||||
return False
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.replaylog = [line.strip() for line in self.replayfile]
|
|
||||||
return True
|
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
"""Flush the replay file, clearing any data currently there that is not in the replaylog list."""
|
"""Flush the replay file, clearing any data currently there that is not in the replaylog list."""
|
||||||
@ -762,7 +788,7 @@ class EDDN:
|
|||||||
:param entry: The full journal event dictionary (due to checks in this function).
|
:param entry: The full journal event dictionary (due to checks in this function).
|
||||||
:param msg: The EDDN message body to be sent.
|
:param msg: The EDDN message body to be sent.
|
||||||
"""
|
"""
|
||||||
if self.replayfile or self.load_journal_replay():
|
if self.replayfile or self.load_journal_replay_file():
|
||||||
# Store the entry
|
# Store the entry
|
||||||
self.replaylog.append(json.dumps([cmdr, msg]))
|
self.replaylog.append(json.dumps([cmdr, msg]))
|
||||||
self.replayfile.write(f'{self.replaylog[-1]}\n') # type: ignore
|
self.replayfile.write(f'{self.replaylog[-1]}\n') # type: ignore
|
||||||
@ -1621,10 +1647,6 @@ def plugin_app(parent: tk.Tk) -> Optional[tk.Frame]:
|
|||||||
"""
|
"""
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
this.eddn = EDDN(parent)
|
this.eddn = EDDN(parent)
|
||||||
# Try to obtain exclusive lock on journal cache, even if we don't need it yet
|
|
||||||
if not this.eddn.load_journal_replay():
|
|
||||||
# Shouldn't happen - don't bother localizing
|
|
||||||
this.parent.children['status']['text'] = 'Error: Is another copy of this app already running?'
|
|
||||||
|
|
||||||
if config.eddn_tracking_ui:
|
if config.eddn_tracking_ui:
|
||||||
this.ui = tk.Frame(parent)
|
this.ui = tk.Frame(parent)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user