-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcontroller.py
More file actions
199 lines (170 loc) · 6.87 KB
/
controller.py
File metadata and controls
199 lines (170 loc) · 6.87 KB
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
from typing import Dict, List
from monitor import (
monitor,
) # imports from the EDMarketConnector. Is used to obtain the logs folder.
from Data import MissionInterface, Massacres
from Display import MassacreFrame, TargetFrame
import logging
import time
import json
import os
from datetime import timedelta, datetime
import re
import tkinter as tk
import tkinter.ttk as ttk
from theme import theme
from prefs import AutoInc
# Logging set-up as per EDMC directive
from common import logger_name
logger = logging.getLogger(logger_name)
class Controller:
"""
This class is meant to control the database access, hold all the trackers,
and handle event assignment to the different event trackers
"""
_event_watchers: Dict[str, List[MissionInterface]]
@staticmethod
def get_journal_folder():
"""Returns the journal folder"""
return monitor.currentdir
@staticmethod
def filter_logs_by_date(
logs: List[str], start_date: datetime, end_date: datetime = datetime.now()
) -> List[str]:
"""
Parses log filenames and returns those whose parsed date are
within the specified dates (inclusive).
:param logs: List of log filenames to search through.
:param start_date: Oldest file date to search for.
:param end_date: Newest file date to search for.
:return: list of logs
"""
assert start_date < end_date, "start_date must come before end_date"
wanted_logs = []
matcher = re.compile(
r"^Journal\."
r"(?P<YY>\d\d)"
r"(?P<MM>\d\d)"
r"(?P<DD>\d\d)"
r"(?P<hh>\d\d)"
r"(?P<mm>\d\d)"
r"(?P<ss>\d\d)"
)
for log in logs:
match = matcher.search(log)
if match:
# Note - these are all 2 digits, including the year
log_date = datetime(
year=int(match.group("YY")) + 2000,
month=int(match.group("MM")),
day=int(match.group("DD")),
)
if start_date <= log_date <= end_date:
wanted_logs.append(log)
return wanted_logs
def rebuild_from_logs(self):
"""
This will attempt to read the past seven days from the log files.
"""
journal_folder = self.get_journal_folder()
logger.info(f"Starting to read previous journal files: {journal_folder}")
logs = [f for f in os.listdir(journal_folder) if ".log" in f] # get logs only
a_week_ago = datetime.now() - timedelta(days=7)
wanted_logs = self.filter_logs_by_date(logs, start_date=a_week_ago)
files_with_issues = []
for log in wanted_logs:
log_file = os.path.join(journal_folder, log)
logger.info(f"Reading {log_file}")
with open(log_file, "r", encoding="UTF-8") as file:
line_no = 1
line = file.readline()
while line:
try:
event = json.loads(line)
self.add_new_event(event, update_gui=False)
line = file.readline()
line_no = line_no + 1
except:
if log_file not in files_with_issues:
logger.exception(
f"Error parsing {log_file}, {line_no}: {line}"
)
files_with_issues.append(log_file)
break
logger.info("Finished reading journals.")
self.update_massacre_display()
self._logs_scanned = True
def __init__(self):
"""
Initialise the Controller class.
"""
# launch a thread that will wait for `monitor` to be ready,
# then launch use the reader to get the latest relevant lines of mission data.
self.master_frame = None
self.target_frame = None
self.massacre_frame = None # initialise with register_frame
self._event_watchers = {} # key: event name, value: List(DataInterface)
self._massacre_tracker = Massacres()
self._logs_scanned = False # flag to indicate that the logs have been scanned
logger.debug("Combat controller initialised")
@property
def logs_scanned(self):
"""Flag indicating whether the initial startup scan has been done yet or not."""
return self._logs_scanned
def register_frame(self, version, parent=tk.Frame):
"""Registers the parent frame and initialises the tracker panels"""
row = AutoInc()
self.master_frame = tk.Frame(parent)
self.target_frame = TargetFrame(self.master_frame)
self.target_frame.grid(row=row.get(), sticky=tk.W)
tk.Frame(self.master_frame, highlightthickness=1).grid(
row=row.get(), column=0, columnspan=2, sticky=tk.EW
) # separator
self.massacre_frame = MassacreFrame(self.master_frame)
self.massacre_frame.grid(row=row.get(), sticky=tk.W)
tk.Label(
self.master_frame,
text=f"Version: {version}",
font=("Helvetica", "8", "italic"),
).grid(
row=0,
column=1,
sticky=tk.NE,
)
return self.master_frame
def add_new_event(self, new_event: dict, update_gui: bool = True) -> None:
"""
Records the occurrence of a new event.
:param new_event: The event to record.
:param update_gui: If true, the GUI frame is also updated
"""
event_name = new_event["event"]
if "Mission" in event_name and "Name" in new_event:
if "Massacre" in new_event["Name"]:
self._massacre_tracker.add_event(event=new_event)
if update_gui:
self.update_massacre_display()
elif event_name == "ShipTargeted" and update_gui:
self.target_frame.update_data(new_event)
def update_massacre_display(self):
"""Updates the massacre display frame"""
massacre_data = self._massacre_tracker.grouped_by_faction()
if massacre_data:
while not self.massacre_frame:
pass
self.massacre_frame.update_data(massacre_data)
else:
self.massacre_frame.clear_content()
def stop(self):
"""Shutdown the controller"""
pass
# Here, we can set this up to use an environment variable
# to replace the journal folder location. We'll update the Controller
# to use all the files from that directory.
test_folder = os.getenv("PLUGIN_TEST_FOLDER")
if test_folder:
target_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), test_folder)
def return_files(obj, files, *args, **kwargs):
return files
Controller.filter_logs_by_date = return_files
Controller.get_journal_folder = lambda *args: target_path