Skip to content

Commit 366f4e1

Browse files
feat(series): offload series log data to external store
1 parent fc1770f commit 366f4e1

File tree

6 files changed

+146
-15
lines changed

6 files changed

+146
-15
lines changed

PyReconstruct/modules/constants/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,7 @@
4343
)
4444

4545
from .getdatetime import(
46-
getDateTime
46+
getDateTime,
47+
get_now,
48+
remove_days_from_today
4749
)
Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
1-
from datetime import datetime
1+
from datetime import datetime, timedelta
22

33
from PySide6.QtCore import QSettings
44

5-
def getDateTime(date_str="%y-%m-%d", time_str="%H:%M"):
5+
6+
def utc_p() -> bool:
7+
"""Determine if user using UTC."""
8+
69
settings = QSettings("KHLab", "PyReconstruct")
710
utc = settings.value("utc", False)
8-
dt = datetime.utcnow() if utc else datetime.now()
11+
12+
return False if utc == "false" else True
13+
14+
15+
def get_now() -> datetime:
16+
"""Return now's datetime object."""
17+
18+
return datetime.utcnow() if utc_p() else datetime.now()
19+
20+
21+
def remove_days_from_today(delta_days: int):
22+
"""Remove days from now."""
23+
24+
return get_now().date() - timedelta(days=delta_days)
25+
26+
27+
def getDateTime(date_str="%y-%m-%d", time_str="%H:%M"):
28+
29+
dt = get_now()
30+
931
d = dt.strftime(date_str)
1032
t = dt.strftime(time_str)
11-
return d, t
33+
34+
return d, t

PyReconstruct/modules/datatypes/log.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import re
2+
from pathlib import Path
23
from datetime import datetime
34

4-
from PyReconstruct.modules.constants import getDateTime
5+
from PyReconstruct.modules.constants import getDateTime, get_now, remove_days_from_today
6+
57

68
class Log():
79

@@ -234,14 +236,6 @@ def addLog(self, user : str, obj_name : str, snum : int, event : str):
234236
else:
235237
self.all_logs.append(log)
236238

237-
# # for debugging
238-
# # Clear console
239-
# if os.name == 'nt': # Windows
240-
# _ = os.system('cls')
241-
# else: # Mac and Linux
242-
# _ = os.system('clear')
243-
# print(str(self).replace(", ", "\t"))
244-
245239
def addExistingLog(self, log : Log, track_dyn=False):
246240
"""Add an existing log object to the set.
247241
@@ -334,6 +328,42 @@ def getLastIndex(self, snum : int, cname : str):
334328
i -= 1
335329
return i
336330

331+
@staticmethod
332+
def exportLogHistory(hidden_dir: str, output_fp: str, older_than: int) -> None:
333+
"""Export log history as CSV for external storage."""
334+
335+
existing_log = Path(hidden_dir) / "existing_log.csv"
336+
storage_log = Path(output_fp)
337+
new_log = existing_log.with_name("new_log.csv")
338+
339+
if storage_log.exists(): storage_log.unlink() # remove if already exists
340+
341+
older_than = remove_days_from_today(older_than)
342+
343+
with storage_log.open("a") as external_store, new_log.open("a") as new:
344+
345+
with existing_log.open("r") as log:
346+
347+
for line in log.readlines():
348+
349+
if "Date" in line:
350+
351+
external_store.write(line)
352+
new.write(line)
353+
354+
else:
355+
356+
log_date = line.split(",")[0].strip()
357+
log_date = datetime.strptime(log_date, "%y-%m-%d").date()
358+
359+
if log_date <= older_than:
360+
external_store.write(line)
361+
else:
362+
new.write(line)
363+
364+
new_log.replace(existing_log) # overwrite old log
365+
366+
337367
class LogSetPair():
338368

339369
def __init__(self, logset0 : LogSet, logset1 : LogSet):

PyReconstruct/modules/gui/main/main_imports.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@
127127
Series,
128128
Trace,
129129
Transform,
130-
Flag
130+
Flag,
131+
LogSet
131132
)
132133

133134
from PyReconstruct.modules.constants import (

PyReconstruct/modules/gui/main/main_window.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,6 +2278,73 @@ def hideSeriesTraces(self, hidden=True):
22782278
self.series.hideAllTraces(hidden)
22792279
self.field.reload()
22802280

2281+
def exportLog(self):
2282+
"""Export complete log history"""
2283+
2284+
pass
2285+
2286+
def offloadLog(self):
2287+
"""Offload log history to external file for storage."""
2288+
2289+
## Notify user
2290+
note_offloading = (
2291+
f"Log creation in PyReconstruct is still a work in progress. Series with extensive "
2292+
f"log history may become very large. You can reduce series size in some cases by "
2293+
f"offloading and saving log history externally as a CSV file. \n\nClick OK to select "
2294+
f"where to save the log CSV."
2295+
)
2296+
2297+
confirm = notifyConfirm(note_offloading)
2298+
2299+
if not confirm:
2300+
return
2301+
2302+
## Get filepath
2303+
2304+
path_check_ok = False
2305+
2306+
while not path_check_ok:
2307+
2308+
output_fp = FileDialog.get("save", self, "Save data as CSV file", "*.csv", "log_export.csv")
2309+
if not output_fp:
2310+
2311+
return
2312+
2313+
elif Path(output_fp).exists():
2314+
2315+
notify(
2316+
f"To prevent accidnetally overwriting previously offloaded logs, "
2317+
f"please provide a filename that does not yet exist."
2318+
)
2319+
2320+
else:
2321+
2322+
path_check_ok = True
2323+
2324+
## Query for age of exported existing data
2325+
2326+
structure = [
2327+
["Export history older than x days: ", ("int", 10)],
2328+
]
2329+
response, confirmed = QuickDialog.get(self, structure, "Export log history")
2330+
2331+
if not confirmed:
2332+
return
2333+
2334+
days = response[0]
2335+
2336+
## Export for external storage
2337+
2338+
LogSet.exportLogHistory(self.series.hidden_dir, output_fp, days)
2339+
self.series.addLog(None, None, f"Offloaded log to {output_fp}")
2340+
2341+
notify(
2342+
f"Log offloaded to external CSV file.\n\n"
2343+
f"Be sure to save offloaded log data in a safe place."
2344+
)
2345+
2346+
return None
2347+
22812348
def setFindZoom(self):
22822349
"""Set the magnification for find contour."""
22832350
z, confirmed = QInputDialog.getInt(

PyReconstruct/modules/gui/main/menubar.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,14 @@ def return_series_menu(self):
145145
("unhidealltraces_act", "Unhide all traces", "", lambda : self.hideSeriesTraces(hidden=False))
146146
]
147147
},
148+
{
149+
"attr_name": "serieslogmenu",
150+
"text": "Log",
151+
"opts":
152+
[
153+
("offloadlog_act", "Offload log history...", "", self.offloadLog),
154+
]
155+
},
148156
{
149157
"attr_name": "threedeemenu",
150158
"text": "3D",

0 commit comments

Comments
 (0)