Skip to content

Commit 6859ce5

Browse files
Merge pull request #75 from DiamondLightSource/I04_1-415_backup_change
I04 1 415 backup change
2 parents 22b2ed2 + 672a5d7 commit 6859ce5

File tree

18 files changed

+323
-493
lines changed

18 files changed

+323
-493
lines changed

dls_barcode/config/barcode_config.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ def __init__(self, file, file_manager):
2222

2323
if IS_BUNDLED:
2424
default_store = "./store/"
25+
default_backup = "./backup/"
2526
else:
2627
default_store = "../store/"
28+
default_backup = "../backup/"
29+
2730

2831
self.color_ok = add(ColorConfigItem, "Pin/Puck Read", Color.Green())
2932
self.color_accept = add(ColorConfigItem, "Puck Partially Read", Color.Yellow())
@@ -43,8 +46,9 @@ def __init__(self, file, file_manager):
4346
self.image_crop = add(BoolConfigItem, "Crop to Puck", default=True)
4447

4548
self.store_directory = add(DirectoryConfigItem, "Store Directory", default=default_store)
46-
self.store_capacity = add(IntConfigItem, "Results History Size", default=50)
47-
self.backup_time = add(IntConfigItem, "Backup in Weeks", default=3)
49+
self.backup = add(BoolConfigItem, "Backup before Delete", default=True)
50+
self.backup_directory = add(DirectoryConfigItem, "Backup Directory", default=default_backup)
51+
4852

4953
self.console_frame = add(BoolConfigItem, "Print Frame Summary", default=False)
5054
self.slot_images = add(BoolConfigItem, "Save Debug Images", default=False)
@@ -60,6 +64,12 @@ def __init__(self, file, file_manager):
6064

6165
self.initialize_from_file()
6266

67+
def get_store_directory(self):
68+
return self.store_directory.value()
69+
70+
def get_backup_directory(self):
71+
return self.backup_directory.value()
72+
6373
def col_ok(self):
6474
return self.color_ok.value()
6575

dls_barcode/config/barcode_config_dialog.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ def _init_ui(self):
5555

5656
self.start_group("Store")
5757
self._add_control(StoreDirectoryConfigControl(cfg.store_directory))
58-
add(cfg.backup_time)
59-
add(cfg.store_capacity)
58+
add(cfg.backup)
59+
add(cfg.backup_directory)
6060

6161
self.start_group("Debug")
6262
add(cfg.console_frame)

dls_barcode/data_store/backup.py

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,13 @@
1-
import time
2-
3-
from dls_barcode.data_store.comms_manager import CommsManager
4-
5-
61
class Backup:
72

83
"""
94
Backup class maintains the short time backaup of records which is kept in the same folder as the store files.
105
"""
116

12-
WEEK_IN_SECONDS = 604800
13-
14-
def __init__(self, comms_man, backup_time):
7+
def __init__(self, comms_man):
158
self._comms = comms_man
16-
self._backup_time = backup_time.value()
17-
self._records = self._comms.load_records_from_file()
18-
19-
def _truncate_record_list(self):
20-
self._records = list(filter(lambda x: not self._is_old(x), self._records))
219

2210
def backup_records(self, to_back):
23-
self._records.extend(to_back)
24-
self._truncate_record_list()
25-
self._comms.to_csv_file(self._records)
11+
self._comms.to_csv_file(to_back)
2612

27-
def _is_old(self, record):
28-
tm = time.time()
29-
record_time = record.timestamp
30-
delta = tm - record_time
31-
weeks = self._backup_time * self.WEEK_IN_SECONDS # weeks in seconds
32-
return delta > weeks
3313

dls_barcode/data_store/store.py

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import uuid
2-
import os
2+
import time
33

4+
from dls_barcode.data_store.backup import Backup
5+
from dls_barcode.data_store.store_writer import StoreWriter
46
from .record import Record
57

68

@@ -9,18 +11,12 @@ class Store:
911
""" Maintains a list of records of previous barcodes scans. Any changes (additions
1012
or deletions) are automatically written to the backing file.
1113
"""
12-
MIN_STORE_CAPACITY = 2
1314

14-
def __init__(self, comms_manager, backup, store_capacity):
15+
def __init__(self, store_writer, records):
1516
""" Initializes a new instance of Store.
1617
"""
17-
self._store_capacity = store_capacity
18-
self._backup = backup
19-
self._comms_manager = comms_manager
20-
self._img_dir = self._comms_manager.make_img_dir()
21-
self.records = self._comms_manager.load_records_from_file()
22-
self._truncate_record_list()
23-
self._sort_records()
18+
self._store_writer = store_writer
19+
self.records = records
2420

2521
def size(self):
2622
""" Returns the number of records in the store
@@ -30,17 +26,17 @@ def size(self):
3026
def get_record(self, index):
3127
""" Get record by index where the 0th record is the most recent
3228
"""
29+
self._sort_records()
3330
return self.records[index] if self.records else None
3431

3532
def _add_record(self, holder_barcode, plate, holder_img, pins_img):
3633
""" Add a new record to the store and save to the backing file.
3734
"""
3835
merged_img = self._merge_holder_image_into_pins_image(holder_img, pins_img)
3936
guid = str(uuid.uuid4())
40-
filename = os.path.abspath(os.path.join(self._img_dir, guid + '.png'))
41-
merged_img.save_as(filename)
37+
self._store_writer.to_image(merged_img, guid)
4238

43-
record = Record.from_plate(holder_barcode, plate, filename)
39+
record = Record.from_plate(holder_barcode, plate, self._store_writer.get_img_path())
4440

4541
self.records.append(record)
4642
self._process_change()
@@ -53,35 +49,30 @@ def merge_record(self, holder_barcode, plate, holder_img, pins_img):
5349

5450
self._add_record(holder_barcode, plate, holder_img, pins_img)
5551

52+
def backup_records(self, directory):
53+
ts = time.localtime()
54+
file_name = time.strftime("%Y-%m-%d_%H-%M-%S", ts)
55+
backup_writer = StoreWriter(directory, file_name)
56+
backup = Backup(backup_writer)
57+
self._sort_records()
58+
backup.backup_records(self.records)
59+
5660
def delete_records(self, records_to_delete):
5761
""" Remove all of the records in the supplied list from the store and
5862
save changes to the backing file.
5963
"""
6064
for record in records_to_delete:
6165
self.records.remove(record)
62-
self._comms_manager.remove_img_file(record)
66+
self._store_writer.remove_img_file(record)
6367

6468
self._process_change()
6569

66-
def backup_records(self, records_to_backup):
67-
self._backup.backup_records(records_to_backup)
68-
69-
def _truncate_record_list(self):
70-
71-
actual_store_capacity = max(self._store_capacity.value(), self.MIN_STORE_CAPACITY)
72-
73-
if len(self.records) > actual_store_capacity:
74-
to_delete = self.records[actual_store_capacity:]
75-
self.backup_records(to_delete)
76-
self.delete_records(to_delete)
77-
7870
def _process_change(self):
7971
""" Sort the records and save to file.
8072
"""
8173
self._sort_records()
82-
self._truncate_record_list()
83-
self._comms_manager.to_file(self.records)
84-
self._comms_manager.to_csv_file(self.records)
74+
self._store_writer.to_file(self.records)
75+
self._store_writer.to_csv_file(self.records)
8576

8677
def _sort_records(self):
8778
""" Sort the records in descending date order (most recent first).
@@ -97,5 +88,6 @@ def _merge_holder_image_into_pins_image(self, holder_img, pins_img):
9788
return merged_img
9889

9990
def is_latest_holder_barcode(self, holder_barcode):
91+
self._sort_records()
10092
latest_record = self.get_record(0)
10193
return latest_record is not None and holder_barcode == latest_record.holder_barcode
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import os
2+
3+
from dls_barcode.data_store.record import Record
4+
from dls_util.file import FileManager
5+
6+
7+
class StoreLoader:
8+
9+
def __init__(self, directory, file_name, file_manager=FileManager()):
10+
self._directory = directory
11+
self._file_name = file_name
12+
self._file_manager= file_manager
13+
self._path = None
14+
self._records = []
15+
16+
def load_records_from_file(self):
17+
""" Clear the current record store and load a new set of records from the specified file. """
18+
self._build_file_path()
19+
if not self._check_if_file():
20+
return self._records
21+
self._build_records_from_lines(self._read_lines())
22+
return self._records
23+
24+
def _build_file_path(self):
25+
self._path = os.path.join(self._directory, self._file_name + '.txt')
26+
27+
def _check_if_file(self):
28+
return self._file_manager.is_file(self._path)
29+
30+
def _read_lines(self):
31+
return self._file_manager.read_lines(self._path)
32+
33+
def _build_records_from_lines(self, lines):
34+
for line in lines:
35+
try:
36+
record = Record.from_string(line)
37+
self._records.append(record)
38+
except Exception:
39+
print("Failed to parse store Record: {}".format(line))

dls_barcode/data_store/store_manager.py

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,44 @@
44
from dls_util.file import FileManager
55

66

7-
class CommsManager:
8-
""" Maintains communication between store/backup and the file manager.
7+
class StoreWriter:
8+
""" Maintains writing records to file and saving png images in a sub-folder
99
"""
1010

11-
def __init__(self, directory, file_name):
12-
self._file_manager = FileManager()
13-
self._directory = directory.value()
11+
def __init__(self, directory, file_name, file_manager=FileManager()):
12+
self._file_manager = file_manager
13+
self._directory = directory
1414
self._file_name = file_name
15-
16-
def load_records_from_file(self):
17-
""" Clear the current record store and load a new set of records from the specified file. """
18-
file = os.path.join(self._directory, self._file_name + '.txt')
19-
records = []
20-
21-
if not self._file_manager.is_file(file):
22-
return records
23-
24-
lines = self._file_manager.read_lines(file)
25-
for line in lines:
26-
try:
27-
record = Record.from_string(line)
28-
records.append(record)
29-
except Exception:
30-
print("Failed to parse store Record: {}".format(line))
31-
32-
return records
15+
self._image_path = None
3316

3417
def to_file(self, records):
3518
""" Save the contents of the store to the backing file
3619
"""
20+
self._file_manager.make_dir_when_no_dir(self._directory)
3721
file = os.path.join(self._directory, self._file_name + '.txt')
3822
record_lines = [rec.to_string() + "\n" for rec in records]
3923
self._file_manager.write_lines(file, record_lines)
4024

4125
def to_csv_file(self, records):
4226
""" Save the contents of the store to the backing csv file
4327
"""
28+
self._file_manager.make_dir_when_no_dir(self._directory)
4429
csv_file = os.path.join(self._directory, self._file_name + ".csv")
4530
record_lines = [rec.to_csv_string() + "\n" for rec in records]
4631
self._file_manager.write_lines(csv_file, record_lines)
4732

48-
def make_img_dir(self):
33+
def to_image(self, image, name):
34+
dr = self._make_img_dir()
35+
self._image_path = os.path.abspath(os.path.join(dr, name + '.png'))
36+
image.save_as(self._image_path)
37+
38+
def get_img_path(self):
39+
return self._image_path
40+
41+
def _make_img_dir(self):
42+
self._file_manager.make_dir_when_no_dir(self._directory)
4943
img_dir = os.path.join(self._directory, "img_dir")
50-
if not self._file_manager.is_dir(img_dir):
51-
self._file_manager.make_dir(img_dir)
44+
self._file_manager.make_dir_when_no_dir(img_dir)
5245
return img_dir
5346

5447
def remove_img_file(self, record):

dls_barcode/datamatrix/read/interpret.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,14 @@ def interpret_bytes(data_bytes):
5858
#log.error(NotImplementedError("Datamatrix Extended Channel Interpretation code not implemented"))
5959
raise NotImplementedError("Datamatrix Extended Channel Interpretation code not implemented")
6060

61-
elif 242 <= byte < 256 or byte == 0: # Unused parts of message space.
61+
elif 242 <= byte < 256: # Unused parts of message space.
6262
#log = log = logging.getLogger(".".join([__name__]))
6363
error = ValueError("Code {} is not used in Datamatrix specification".format(byte))
6464
#.error(error)
6565
raise error
66+
elif byte == 0:
67+
error = ValueError("Code {} is not used in Datamatrix specification".format(byte))
68+
print(error)
6669

6770
return ''.join(m for m in message)
6871

dls_barcode/gui/record_table.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from PyQt5.QtWidgets import QGroupBox, QVBoxLayout, QHBoxLayout, QTableWidget, QMessageBox
66

77
from dls_barcode.data_store import Store
8-
from dls_barcode.data_store.store_manager import StoreManager
9-
from dls_util.file import FileManager
8+
from dls_barcode.data_store.store_loader import StoreLoader
9+
from dls_barcode.data_store.store_writer import StoreWriter
1010

1111
# todo: allow delete key to be used for deletion
1212
# todo: allow record selection with arrow keys
@@ -23,7 +23,10 @@ def __init__(self, barcode_table, image_frame, options):
2323
super(ScanRecordTable, self).__init__()
2424

2525
# Read the store from file
26-
self._store = StoreManager(options.store_directory, options.store_capacity, options.backup_time).create_store()
26+
store_writer = StoreWriter(options.get_store_directory(), "store")
27+
store_loader = StoreLoader(options.get_store_directory(), "store")
28+
29+
self._store = Store(store_writer, store_loader.load_records_from_file())
2730
self._options = options
2831

2932
self._barcodeTable = barcode_table
@@ -136,7 +139,8 @@ def _delete_selected_records(self):
136139
record = self._store.get_record(index)
137140
records_to_delete.append(record)
138141

139-
self._store.backup_records(records_to_delete)
142+
if self._options.backup.value():
143+
self._store.backup_records(self._options.get_backup_directory())
140144
self._store.delete_records(records_to_delete)
141145

142146
self._load_store_records()

dls_util/file/file_manager.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,8 @@ def make_dir(self, path):
2929
os.makedirs(path)
3030

3131
def remove(self, path):
32-
os.remove(path)
32+
os.remove(path)
33+
34+
def make_dir_when_no_dir(self, dr):
35+
if not self.is_dir(dr):
36+
self.make_dir(dr)

0 commit comments

Comments
 (0)