tools.win_pg_dump_controller

Yohn Y. 2022-07-11 Parent:a38a008ce3e8

7:92c88f8fb6c9 Go to Latest

tools.win_pg_dump_controller/win_pg_dump_controller/store_controller.py

.. import D6

History
awgur@0 1 # coding: utf-8
awgur@0 2
awgur@0 3 import json
awgur@0 4 from datetime import datetime
awgur@7 5 from os.path import exists, join as p_join
awgur@0 6 from os import remove as file_remove
awgur@0 7 from typing import Dict, List
awgur@7 8 from dataclasses import dataclass, asdict
awgur@7 9 from os import stat as f_stat
awgur@0 10
awgur@0 11 from .error import Error
awgur@0 12 from .config import DBTask
awgur@0 13
awgur@0 14
awgur@0 15 class StoreError(Error):
awgur@0 16 pass
awgur@0 17
awgur@0 18
awgur@0 19 class StoreControllerBase(object):
awgur@0 20 def get_filename(self, filename: str) -> str:
awgur@0 21 raise NotImplemented()
awgur@0 22
awgur@0 23
awgur@0 24 class IndexItem(object):
awgur@4 25 def __init__(self, controller: StoreControllerBase, time: int, filename: str, epoch: int = 0):
awgur@0 26 self._controller = controller
awgur@0 27 self.filename = filename
awgur@0 28 self.time = time
awgur@4 29 self.epoch = epoch
awgur@0 30 self._my_path = None
awgur@0 31
awgur@0 32 def get_path(self):
awgur@0 33 if self._my_path is None:
awgur@0 34 self._my_path = self._controller.get_filename(self.filename)
awgur@0 35
awgur@0 36 return self._my_path
awgur@0 37
awgur@4 38 def set_epoch(self, num_days: int):
awgur@4 39 """\
awgur@4 40 Устанавливает значение эпохи для элемиента в соответствии с количеством дней в эпохе
awgur@4 41 """
awgur@4 42 self.epoch = divmod(self.time, num_days * 86400)[0] # 86400 - секунд в сутках
awgur@4 43
awgur@1 44 def __str__(self) -> str:
awgur@1 45 return self.filename
awgur@1 46
awgur@4 47 def full_desc(self) -> str:
awgur@4 48 return f'{self.filename} [epoch="{self.epoch}" created={self.get_datetime()}]'
awgur@4 49
awgur@0 50 def get_datetime(self):
awgur@0 51 return datetime.fromtimestamp(self.time)
awgur@0 52
awgur@0 53 def is_exists(self) -> bool:
awgur@0 54 return exists(self.get_path())
awgur@0 55
awgur@0 56 def to_dict(self) -> dict:
awgur@4 57 return dict((i, getattr(self, i)) for i in ('filename', 'time', 'epoch'))
awgur@0 58
awgur@0 59 @classmethod
awgur@0 60 def from_dict(cls, controller: StoreControllerBase, d: Dict):
awgur@0 61 return cls(
awgur@0 62 controller=controller,
awgur@0 63 **d
awgur@0 64 )
awgur@0 65
awgur@0 66 @classmethod
awgur@0 67 def new(cls, controller: StoreControllerBase, short_filename: str):
awgur@0 68 time = datetime.now()
awgur@0 69 time_prefix = time.strftime('%Y-%m-%d_%H-%M-%S')
awgur@0 70 filename = f'{time_prefix} - {short_filename}'
awgur@0 71
awgur@0 72 return cls(
awgur@0 73 controller=controller,
awgur@0 74 time=int(time.timestamp()),
awgur@0 75 filename=filename
awgur@0 76 )
awgur@0 77
awgur@0 78
awgur@7 79 @dataclass()
awgur@7 80 class TaskStatus(object):
awgur@7 81 status: bool = False
awgur@7 82 size: int = 0
awgur@7 83 time: int = 0
awgur@7 84
awgur@7 85
awgur@0 86 class StoreController(StoreControllerBase):
awgur@0 87 def __init__(self, task: DBTask):
awgur@0 88 self.task = task
awgur@0 89 self.index_name = self.get_filename(f'{task.name}.index')
awgur@7 90 self.status_name = self.get_filename(f'{task.name}.status')
awgur@0 91 self.idx: Dict[int, IndexItem] = {}
awgur@1 92 self.op_adv_status = []
awgur@0 93
awgur@0 94 if exists(self.index_name):
awgur@0 95 self.load_index()
awgur@0 96
awgur@0 97 def get_filename(self, filename: str) -> str:
awgur@0 98 return str(p_join(self.task.dst_dir, filename))
awgur@0 99
awgur@0 100 def load_index(self) -> None:
awgur@0 101 with open(self.index_name, 'r') as IN:
awgur@0 102 result = {}
awgur@0 103 for itm in json.load(IN):
awgur@0 104 itm = IndexItem.from_dict(self, itm)
awgur@0 105 if itm.is_exists():
awgur@0 106 result[itm.time] = itm
awgur@0 107
awgur@0 108 self.idx = result
awgur@0 109
awgur@4 110 def set_epoch(self, num_days: int):
awgur@4 111 for i in self.idx.values():
awgur@4 112 i.set_epoch(num_days)
awgur@4 113
awgur@0 114 def save_index(self) -> None:
awgur@0 115 with open(self.index_name, 'w') as OUT:
awgur@0 116 json.dump(list(map(lambda x: x.to_dict(), self.idx.values())), OUT)
awgur@0 117
awgur@1 118 def add_op_status(self, msg: str) -> None:
awgur@1 119 self.op_adv_status.append(msg)
awgur@1 120
awgur@0 121 def new_item(self) -> IndexItem:
awgur@0 122 return IndexItem.new(self, f'{self.task.name}.backup')
awgur@0 123
awgur@0 124 def add_item(self, item: IndexItem) -> None:
awgur@0 125 if not item.is_exists():
awgur@0 126 raise StoreError(f'Storing to index file not found: {item.get_path()}')
awgur@0 127
awgur@0 128 if item.time in self.idx and self.idx[item.time].filename != item.filename:
awgur@0 129 if self.idx[item.time].is_exists():
awgur@0 130 file_remove(self.idx[item.time].get_path())
awgur@0 131
awgur@0 132 self.idx[item.time] = item
awgur@0 133
awgur@0 134 def remove(self, item: IndexItem) -> None:
awgur@0 135 if item.time in self.idx:
awgur@0 136 del self.idx[item.time]
awgur@0 137
awgur@0 138 if item.is_exists():
awgur@0 139 file_remove(item.get_path())
awgur@0 140
awgur@7 141 def status(self, last_status: bool):
awgur@7 142 res = TaskStatus(status=last_status)
awgur@7 143
awgur@7 144 for i in self.idx.values():
awgur@7 145 if res.time < i.time:
awgur@7 146 res.time = i.time
awgur@7 147
awgur@7 148 res.size += f_stat(i.get_path()).st_size
awgur@7 149
awgur@7 150 with open(self.status_name, 'w') as OUT:
awgur@7 151 json.dump(asdict(res), OUT)
awgur@7 152
awgur@0 153 def __iter__(self):
awgur@1 154 for i in sorted(self.idx, reverse=True):
awgur@0 155 yield self.idx[i]
awgur@0 156
awgur@4 157 def clean(self, tier1_days: int, tier2_store_days: int, epoch_days: int) -> List[str]:
awgur@0 158 to_remove = []
awgur@0 159 tier2_idx = {}
awgur@0 160 now = datetime.now()
awgur@0 161
awgur@4 162 self.set_epoch(epoch_days)
awgur@4 163
awgur@0 164 for item in self:
awgur@0 165 if not item.is_exists():
awgur@0 166 to_remove.append(item)
awgur@0 167
awgur@0 168 else:
awgur@0 169 storing_days = (now - item.get_datetime()).days
awgur@0 170
awgur@0 171 if not storing_days <= tier1_days:
awgur@0 172 if storing_days > tier2_store_days:
awgur@0 173 to_remove.append(item)
awgur@0 174
awgur@0 175 else:
awgur@4 176 tier2_storing_days = storing_days - tier1_days
awgur@0 177
awgur@4 178 self.add_op_status(f'"{item.full_desc()}": storing_days={storing_days}'
awgur@4 179 f' tier2_storing_days={tier2_storing_days}')
awgur@0 180
awgur@4 181 if item.epoch in tier2_idx:
awgur@4 182 to_remove.append(tier2_idx[item.epoch])
awgur@1 183
awgur@4 184 tier2_idx[item.epoch] = item
awgur@0 185
awgur@0 186 result = []
awgur@0 187 for item in to_remove:
awgur@0 188 file_remove(item.get_path())
awgur@0 189 result.append(item.filename)
awgur@0 190
awgur@0 191 return result