Skip to content

V3.0 - #1

Open
halicki wants to merge 29 commits into
MorganMLGman:masterfrom
halicki:v3.0
Open

V3.0#1
halicki wants to merge 29 commits into
MorganMLGman:masterfrom
halicki:v3.0

Conversation

@halicki

@halicki halicki commented Mar 6, 2025

Copy link
Copy Markdown

Hej! Tworzę tego pull requesta, żeby mieć na czym pisaś komentarze.

@halicki halicki left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Największy problem. Nie ma testów. A nie ma testów, bo pewnie ciężko było by je dodać. A ciężko je dodać, bo wszystko zależy od wszystkiego, bo architektura jest trudna. Możemy się ktoregoś dnia zdzwonić, żeby przegadać to.

Comment thread src/backup.py
Comment on lines +77 to +97
@property
def logger(self) -> logging.Logger:
"""Returns logger for the class.

Returns:
logging.Logger: Logger for the class.
"""
return self._logger

@logger.setter
def logger(self, logger:logging.Logger) -> None:
"""Sets logger for the class.

Args:
logger (logging.Logger): Logger for the class.
"""
if logger is None:
logging.config.fileConfig("log_dev.conf")
self._logger = logging.getLogger('pybackupper_logger')
else:
self._logger = logger

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To jest nadmiarowe. Nie ma konieczności przypisywania loggera do klasy. Możesz na górze po prostu zainicjalizować. Albo kolejnym fajnym podjeściem jest korzystanie tylko z jednego loggera. Który umieściłbyś w logging'u.

Comment thread src/backup.py
Comment on lines +99 to +129
@property
def name(self) -> str:
"""Returns name of the backup.

Returns:
str: Name of the backup.
"""
return self._name

@name.setter
def name(self, name:str) -> None:
"""Sets name of the backup.

Args:
name (str): Name of the backup.

Raises:
ValueError: Name of the backup is not valid.
PermissionError: Change of `name` property is not allowed for Backup.
"""
if name is None or name == "":
self.logger.error(f"Backup {name} is not valid.")
raise ValueError(f"Backup {name} is not valid.")

try:
if self._name != "":
self.logger.error("Cannot change name of the backup.")
raise PermissionError("Cannot change name of the backup.")
except AttributeError:
self.logger.debug(f"Setting name of the backup to {name}.")
self._name = name

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nadmiar, rzadko kiedy settery i gettery są przydatne w realnym kodzie.

Comment thread src/backup.py
return self._dest_path

@dest_path.setter
def dest_path(self, dest_path:str) -> None:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Korzystaj z pathlib.Path

Comment thread src/backup.py
Comment on lines +186 to +189
try:
return self._completed
except AttributeError:
return False

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nie wiem po co jest ten blok try except.

Comment thread src/backup.py
Comment on lines +204 to +205
self.logger.debug(f"Setting completed property of the backup to {completed}.")
self._completed = completed

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

W tym miejscu masz błąd logiczny. W tym miejscu jeszcze nie wiemy że back up się zakończył.

Comment thread src/backup_manager.py
Comment on lines +74 to +104
def __str__(self) -> str:
"""Get the string representation of the object.

Returns:
str: String representation of the object.
"""
local_size = 0
s3_size = 0

for backup in self.backups["local"]:
local_size += backup.get_size()

s3_size = self.s3_handler.get_bucket_size() if not self.s3_handler is None else 0

return f"BackupManager:\n" \
f" src_path: {self.src_path}\n" \
f" dest_path: {self.dest_path}\n" \
f" ignored: {self.ignored}\n" \
f" raw_to_keep: {self.raw_to_keep}\n" \
f" compressed_to_keep: {self.compressed_to_keep}\n" \
f" s3_handler: {True if self.s3_handler else False}\n" \
f" telegram_handler: {True if self.telegram_handler else False}\n" \
f" backups:\n" \
f" local:\n" \
f" count: {len(self.backups['local'])}\n" \
f" size: {size_to_human_readable(local_size)}\n" \
f" s3:\n" \
f" count: {len(self.backups['s3'])}\n" \
f" size: {size_to_human_readable(s3_size)}\n" \
f" last:\n" \
f" {self.backups['local'][-1].name if len(self.backups['local']) > 0 else None}"

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zamiast budować to na piechotę pomyśl jak wyciągnąć klasę która by enkapsulowała informacje potrzebne w tym str. I użyj np. dataclasses do tego.

Comment thread src/backup_manager.py
self.pending_backup = False
return True

def run_backup(self, callback=None) -> str:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Za duża ta funkcja

Comment thread src/backup_manager.py
Comment on lines +1142 to +1162
except OSError:
self.logger.error(f"Backup info cannot be saved.")
self.logger.error(f"Printing backup info:\n{pformat(self.__dict__(), sort_dicts=False, compact=True, indent=2)}")

if self.telegram_handler:
self.telegram_handler.send_message(
f"*Backup info cannot be saved\\.*\n" \
f"Printing backup info:\n```json\n{pformat(self.__dict__(), sort_dicts=False, compact=True, indent=2)}```\n",
markdown=True)
except botocoreClientError:
self.logger.error(f"Backup info cannot be uploaded to S3.")
self.logger.error(f"Printing backup info:\n{pformat(self.__dict__(), sort_dicts=False, compact=True, indent=2)}")

if self.telegram_handler:
self.telegram_handler.send_message(
f"*Backup info cannot be uploaded to S3\\.*\n" \
f"Printing backup info:\n```json\n{pformat(self.__dict__(), sort_dicts=False, compact=True, indent=2)}```\n",
markdown=True)

self.pending_backup = False
return self.backups["local"][-1].name

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To powinno być triggerowane po zakonczeniu processowania, nie jako element wykonania backapu.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://www.cosmicpython.com/book/chapter_11_external_events.html rzuć sobie okiem tutaj. Tu chodzi oto, że są jakieś listenery na eventach i tak to powinno być zrobione. Może nie trzeba w twoim przypadku iść aż do redisa itp. można by z jakieś klasy ogarniającej eventy skorzystać.

Comment thread src/scheduler.py
Comment on lines +57 to +90
def __dict__(self) -> dict:
"""Dictionary representation.

Returns:
dict: Dictionary representation.
"""
return {
"id": self.sched_job.id,
"cron": ", ".join(f"{x.name}: {str(x)}" for x in self.trigger.fields),
"timezone": self.trigger.timezone,
"next_run": timestamp_to_human_readable(self.trigger.get_next_fire_time(datetime.now(), datetime.now()).timestamp())
}

@property
def logger(self) -> logging.Logger:
"""The logger property.

Returns:
logging.Logger: The logger instance
"""
return self._logger

@logger.setter
def logger(self, logger:logging.Logger) -> None:
"""Set the logger.

Args:
logger (logging.Logger): Logger.
"""
if logger is None:
logging.config.fileConfig("log_dev.conf")
self._logger = logging.getLogger('pybackupper_logger')
else:
self._logger = logger

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nie potrzebne.

Comment thread src/scheduler.py
Comment on lines +194 to +214
if day_of_week == "*":
return CronTrigger(
minute=minute,
hour=hour,
day=day_of_month,
month=month,
timezone=timezone)

day_of_week = day_of_week.lower().split(",")

for i, day in enumerate(day_of_week):
if day not in ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]:
try:
i_day = int(day)
except ValueError as e:
raise ValueError("Invalid days of week") from e

if i_day < 0 or i_day > 6:
raise ValueError("Invalid days of week")

day_of_week[i] = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"][i_day]

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewriting too common logic.

@halicki
halicki marked this pull request as ready for review March 6, 2025 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants