mirror of https://gitlab.com/KKlochko/tui-rsync
parent
c67dc14003
commit
594d9dea3f
@ -1,16 +0,0 @@
|
||||
Feature: Creating the source
|
||||
|
||||
Scenario Outline: Adding an new unique source
|
||||
Given the label "<label>"
|
||||
And the path "<source_path>"
|
||||
And the destinations <destinations>
|
||||
And the arguments "<arguments>"
|
||||
When I add the source
|
||||
Then the source should be added successfully
|
||||
|
||||
Examples:
|
||||
| label | source_path | destinations | arguments |
|
||||
| usb | /mnt/usb | [] | <empty> |
|
||||
| db | /db | ["/backup/db"] | -avuP |
|
||||
| temp | /tmp | ["/backup/tmp1", "/backup/tmp2"] | -avuP --delete |
|
||||
|
@ -1,12 +0,0 @@
|
||||
Feature: Adding an unique path
|
||||
|
||||
Scenario: Adding an new unique path
|
||||
Given a path "/media"
|
||||
When I add the path to the database
|
||||
Then the path should be added successfully
|
||||
|
||||
Scenario: Adding an new path with not uniq path
|
||||
Given a path "/media"
|
||||
When I add the path to the database
|
||||
Then the exception should occur
|
||||
|
@ -1,58 +0,0 @@
|
||||
from behave import given, when, then
|
||||
from peewee import IntegrityError
|
||||
from tui_rsync.models.models import create_tables
|
||||
from tui_rsync.models.models import Source
|
||||
import json
|
||||
|
||||
@given('the label "{label}"')
|
||||
def given_source_label(context, label):
|
||||
create_tables()
|
||||
context.label = label
|
||||
|
||||
@given('the path "{source_path}"')
|
||||
def given_source_path(context, source_path):
|
||||
context.source_path = source_path
|
||||
|
||||
@given('the destinations {destinations_json}')
|
||||
def given_source_destinations(context, destinations_json):
|
||||
context.destinations = json.loads(destinations_json)
|
||||
|
||||
@given('the arguments "{arguments}"')
|
||||
def given_source_arguments(context, arguments):
|
||||
context.args = arguments
|
||||
|
||||
if arguments == "<empty>":
|
||||
context.args = ""
|
||||
|
||||
@when('I add the source')
|
||||
def add_source(context):
|
||||
try:
|
||||
context.source = Source.create_save(
|
||||
context.label,
|
||||
context.source_path,
|
||||
context.destinations,
|
||||
context.args,
|
||||
)
|
||||
|
||||
context.source = Source.get_source(context.label)
|
||||
except IntegrityError:
|
||||
context.exception_raised = True
|
||||
else:
|
||||
context.exception_raised = False
|
||||
|
||||
def compare_destinations(actual:list, expected: list[str]) -> bool:
|
||||
actual_path_set = {destionation.path for destionation in actual}
|
||||
return actual_path_set == set(expected)
|
||||
|
||||
@then('the source should be added successfully')
|
||||
def path_has_added(context):
|
||||
assert context.exception_raised == False
|
||||
|
||||
assert context.source.label == context.label
|
||||
assert context.source.source.path == context.source_path
|
||||
assert compare_destinations(
|
||||
context.source.get_destinations(),
|
||||
context.destinations
|
||||
)
|
||||
assert context.source.args.command == context.args
|
||||
|
@ -1,28 +0,0 @@
|
||||
from behave import given, when, then
|
||||
from peewee import IntegrityError
|
||||
from tui_rsync.models.models import create_tables
|
||||
from tui_rsync.models.models import Path
|
||||
|
||||
@given('a path "{path_str}"')
|
||||
def given_path(context, path_str):
|
||||
create_tables()
|
||||
context.path_str = path_str
|
||||
|
||||
@when('I add the path to the database')
|
||||
def add_path(context):
|
||||
try:
|
||||
context.path = Path.create(path=context.path_str)
|
||||
except IntegrityError:
|
||||
context.exception_raised = True
|
||||
else:
|
||||
context.exception_raised = False
|
||||
|
||||
@then('the path should be added successfully')
|
||||
def path_has_added(context):
|
||||
assert context.path.path == context.path_str
|
||||
assert context.exception_raised == False
|
||||
|
||||
@then('the exception should occur')
|
||||
def path_has_not_added(context):
|
||||
assert context.exception_raised == True
|
||||
|
@ -1,4 +0,0 @@
|
||||
from tui_rsync.cli.cli import cli_app
|
||||
from tui_rsync.cli.sync import sync
|
||||
from tui_rsync.cli.rsync import Rsync
|
||||
from tui_rsync.cli.label_prompt import LabelPrompt
|
@ -1,31 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
import typer
|
||||
from tui_rsync.cli.source.source import source
|
||||
from tui_rsync.cli.sync import sync
|
||||
from tui_rsync.cli.groups.groups import groups
|
||||
|
||||
console = Console()
|
||||
cli_app = typer.Typer(rich_markup_mode="rich")
|
||||
cli_app.add_typer(source, name="source", help="Manage sources")
|
||||
cli_app.add_typer(groups, name="groups", help="Manage groups")
|
||||
cli_app.add_typer(sync, name="sync", help="Sync sources")
|
||||
|
@ -1 +0,0 @@
|
||||
from tui_rsync.cli.groups.groups import groups
|
@ -1,31 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from pyfzf import FzfPrompt
|
||||
from tui_rsync.models.models import all_group_labels
|
||||
from tui_rsync.cli.label_prompt import LabelPrompt
|
||||
|
||||
console = Console()
|
||||
|
||||
class GroupPrompt(LabelPrompt):
|
||||
@staticmethod
|
||||
def get_label_fzf() -> str:
|
||||
fzf = FzfPrompt()
|
||||
return fzf.prompt(all_group_labels().iterator())[0]
|
@ -1,56 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
from typing import List, Optional
|
||||
import typer
|
||||
|
||||
from tui_rsync.models.models import Group
|
||||
from tui_rsync.models.models import all_group_labels
|
||||
from tui_rsync.cli.groups.group_prompt import GroupPrompt
|
||||
|
||||
console = Console()
|
||||
group_remove = typer.Typer()
|
||||
|
||||
@group_remove.command()
|
||||
def one(
|
||||
group_label: str = typer.Option(
|
||||
None, "--group-label", "-g",
|
||||
help="[b]The label[/] is a uniq identification of a [b]group[/].",
|
||||
show_default=False
|
||||
),
|
||||
):
|
||||
"""
|
||||
[red b]Remove[/] an [yellow]existing group[/].
|
||||
"""
|
||||
if group_label is None:
|
||||
group_label = GroupPrompt.get_label_fzf()
|
||||
|
||||
if Group.is_exist(group_label):
|
||||
group = Group.get_group(group_label)
|
||||
group.delete_instance()
|
||||
|
||||
@group_remove.command()
|
||||
def all():
|
||||
"""
|
||||
[red b]Remove[/] [yellow] all existing groups[/].
|
||||
"""
|
||||
for label in all_group_labels().iterator():
|
||||
one(label.label)
|
@ -1,63 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
from typing import List, Optional
|
||||
import typer
|
||||
|
||||
from tui_rsync.models.models import Group, GroupSource
|
||||
from tui_rsync.cli.groups.group_prompt import GroupPrompt
|
||||
from tui_rsync.models.models import all_group_labels
|
||||
|
||||
console = Console()
|
||||
group_show = typer.Typer()
|
||||
|
||||
@group_show.command()
|
||||
def one(
|
||||
group_label: str = typer.Option(
|
||||
None, "--group-label", "-l",
|
||||
help="[b]The label[/] is a uniq identification of a [b]group[/].",
|
||||
show_default=False
|
||||
),
|
||||
):
|
||||
"""
|
||||
[green b]Show[/] an [yellow]existing group[/].
|
||||
"""
|
||||
if group_label is None:
|
||||
console.print("What is the [yellow b]label of the group[/]? ")
|
||||
group_label = GroupPrompt.get_label_fzf()
|
||||
|
||||
if not Group.is_exist(group_label):
|
||||
console.print("[red b][ERROR][/] Source does not exists!!!")
|
||||
return
|
||||
|
||||
group = Group.get_group(group_label)
|
||||
|
||||
console.print(group.show_format())
|
||||
|
||||
@group_show.command()
|
||||
def all():
|
||||
"""
|
||||
[green b]Show[/] [yellow]all existing groups[/].
|
||||
"""
|
||||
for label in all_group_labels().iterator():
|
||||
group = Group.get_group(label.label)
|
||||
console.print(group.show_format())
|
||||
|
@ -1,89 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
from typing import List, Optional
|
||||
import typer
|
||||
|
||||
from tui_rsync.models.models import Group, GroupSource
|
||||
from tui_rsync.cli.label_prompt import LabelPrompt
|
||||
from tui_rsync.cli.groups.group_prompt import GroupPrompt
|
||||
from typer.main import get_group
|
||||
|
||||
console = Console()
|
||||
group_update = typer.Typer()
|
||||
|
||||
@group_update.command()
|
||||
def label(
|
||||
group_label: str = typer.Option(
|
||||
None, "--group-label", "-l",
|
||||
help="[b]The label[/] is a uniq identification of a [b]group[/].",
|
||||
show_default=False
|
||||
),
|
||||
new_group_label: str = typer.Option(
|
||||
None, "--new-group-label", "-nl",
|
||||
help="[b]The new label[/] will replace the [b]old group label[/].",
|
||||
show_default=False
|
||||
),
|
||||
|
||||
):
|
||||
"""
|
||||
[green b]Update[/] an [yellow]existing group label[/].
|
||||
"""
|
||||
if group_label is None:
|
||||
console.print("What is the [yellow b]old label of group[/]? ")
|
||||
group_label = GroupPrompt.get_label_fzf()
|
||||
|
||||
if new_group_label is None:
|
||||
question = "What is the [yellow b]new label of the group[/]? "
|
||||
new_group_label = GroupPrompt.ask_uuid(question)
|
||||
|
||||
if Group.is_exist(group_label):
|
||||
group = Group.get_group(group_label)
|
||||
group.update_label(new_group_label)
|
||||
|
||||
@group_update.command()
|
||||
def labels(
|
||||
group_label: str = typer.Option(
|
||||
None, "--group-label", "-g",
|
||||
help="[b]The label[/] is a uniq identification of a [b]group[/].",
|
||||
show_default=False
|
||||
),
|
||||
new_labels: str = typer.Option(
|
||||
None, "--new-labels", "-nl",
|
||||
help="[b]The new label[/] will replace the [b]old source label[/].",
|
||||
show_default=False
|
||||
),
|
||||
|
||||
):
|
||||
"""
|
||||
[green b]Update[/] [yellow]the group[/] with a [bold]the label[/].
|
||||
[b]The chosen sources[/] will be updated for [b]the group[/].
|
||||
"""
|
||||
if group_label is None:
|
||||
group_label = GroupPrompt.get_label_fzf()
|
||||
|
||||
if new_labels is None:
|
||||
new_labels = LabelPrompt.get_labels()
|
||||
|
||||
group = Group.get_group(group_label)
|
||||
group.remove_sources()
|
||||
GroupSource.create_group_sources(group, new_labels)
|
||||
group.save()
|
@ -1,56 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from rich.prompt import Confirm, Prompt
|
||||
from typing import List, Optional
|
||||
import typer
|
||||
|
||||
from tui_rsync.cli.label_prompt import LabelPrompt
|
||||
from tui_rsync.cli.rsync import Rsync
|
||||
from tui_rsync.models.models import Group, count_all_labels_except
|
||||
from tui_rsync.cli.groups.group_show import group_show
|
||||
from tui_rsync.cli.groups.group_update import group_update
|
||||
from tui_rsync.cli.groups.group_remove import group_remove
|
||||
|
||||
console = Console()
|
||||
groups = typer.Typer()
|
||||
groups.add_typer(group_show, name="show", help="Show groups")
|
||||
groups.add_typer(group_update, name="update", help="Update groups")
|
||||
groups.add_typer(group_remove, name="remove", help="Remove groups")
|
||||
|
||||
@groups.command()
|
||||
def add(
|
||||
group_label: str = typer.Option(
|
||||
None, "--group-label", "-g",
|
||||
help="[b]The label[/] is a uniq identification of a [b]group[/].",
|
||||
show_default=False
|
||||
),
|
||||
):
|
||||
"""
|
||||
[green b]Create[/] a [yellow]new group[/] with a [bold]uniq[/] label.
|
||||
[b]The chosen sources[/] will be united into [b]the group[/].
|
||||
"""
|
||||
if group_label is None:
|
||||
question = "Would you like to change [yellow b]the group label[/]?"
|
||||
group_label = LabelPrompt.ask_uuid(question)
|
||||
|
||||
labels = LabelPrompt.get_labels()
|
||||
Group.create_save(group_label, labels)
|
||||
|
@ -1,70 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from rich.prompt import Confirm, Prompt, IntPrompt
|
||||
from pyfzf import FzfPrompt
|
||||
import uuid
|
||||
from tui_rsync.models.models import all_labels, all_labels_except
|
||||
from tui_rsync.models.models import count_all_labels_except
|
||||
|
||||
console = Console()
|
||||
|
||||
class LabelPrompt:
|
||||
@staticmethod
|
||||
def ask_uuid(
|
||||
question: str = "Would you like to change [yellow b]the label[/]?"
|
||||
) -> str:
|
||||
"""
|
||||
Return the label or the default uuid value.
|
||||
"""
|
||||
uid = uuid.uuid4().hex
|
||||
label = Prompt.ask(question, default=uid)
|
||||
return label
|
||||
|
||||
@staticmethod
|
||||
def get_label_fzf() -> str:
|
||||
fzf = FzfPrompt()
|
||||
return fzf.prompt(all_labels().iterator())[0]
|
||||
|
||||
@staticmethod
|
||||
def get_label_except_fzf(labels = None) -> str:
|
||||
fzf = FzfPrompt()
|
||||
return fzf.prompt(all_labels_except(labels).iterator())[0]
|
||||
|
||||
@staticmethod
|
||||
def get_labels(labels = None) -> list:
|
||||
confirm_question = "Would you like to add a source/sources to the group?"
|
||||
is_fzf = Confirm.ask(confirm_question, default=True)
|
||||
|
||||
if not is_fzf:
|
||||
return []
|
||||
|
||||
count_question = "How much would you like to add sources to the group?"
|
||||
count = IntPrompt.ask(count_question, default=1)
|
||||
|
||||
count_max = count_all_labels_except(labels)
|
||||
count = count_max if count > count_max else count
|
||||
|
||||
labels = []
|
||||
for i in range(count):
|
||||
option = LabelPrompt.get_label_except_fzf(labels)
|
||||
labels.append(option)
|
||||
|
||||
return labels
|
@ -1,40 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but #
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
|
||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from sys import stderr
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
from pyfzf import FzfPrompt
|
||||
import os
|
||||
from tui_rsync.models.models import Destination, Path
|
||||
|
||||
console = Console()
|
||||
err_console = Console(stderr=True)
|
||||
|
||||
class PathPrompt:
|
||||
@staticmethod
|
||||
def get_backup_fzf(label:str) -> str:
|
||||
dests_count = len(Destination.get_all(label))
|
||||
if dests_count == 0:
|
||||
err_console.print("[red b]No backups!!![/]")
|
||||
return ""
|
||||
if dests_count == 1:
|
||||
return Destination.get_all(label).get()
|
||||
fzf = FzfPrompt()
|
||||
return fzf.prompt(Destination.get_all(label).iterator())[0]
|
@ -1,36 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
import shlex
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
class Rsync:
|
||||
def __init__(self, args:str):
|
||||
self.__args = ["rsync"] + shlex.split(args)
|
||||
|
||||
def run_one(self, source, destination):
|
||||
args = self.__args + [source, destination]
|
||||
output = Popen(args, stdout=PIPE)
|
||||
response = output.communicate()
|
||||
|
||||
def dry_one(self, source, destination):
|
||||
args = self.__args + ['--dry-run', source, destination]
|
||||
output = Popen(args, stdout=PIPE)
|
||||
response = output.communicate()
|
||||
return response
|
@ -1 +0,0 @@
|
||||
from tui_rsync.cli.source.source import source
|
@ -1,71 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
from typing import List, Optional
|
||||
import typer
|
||||
|
||||
from tui_rsync.models.models import Source, Destination, SyncCommand, Path
|
||||
from tui_rsync.cli.label_prompt import LabelPrompt
|
||||
from tui_rsync.cli.source.source_show import source_show
|
||||
from tui_rsync.cli.source.source_update import source_update
|
||||
from tui_rsync.cli.source.source_remove import source_remove
|
||||
|
||||
console = Console()
|
||||
source = typer.Typer()
|
||||
source.add_typer(source_show, name="show", help="Show sources")
|
||||
source.add_typer(source_update, name="update", help="Update sources")
|
||||
source.add_typer(source_remove, name="remove", help="Remove sources")
|
||||
|
||||
@source.command()
|
||||
def add(
|
||||
label: str = typer.Option(
|
||||
None, "--label", "-l",
|
||||
help="[b]The label[/] is a uniq identification of a [b]source[/].",
|
||||
show_default=False
|
||||
),
|
||||
source: str = typer.Option(
|
||||
None, "--source", "-s",
|
||||
help="[b]A source[/] of the data.",
|
||||
show_default=False
|
||||
),
|
||||
destinations: Optional[List[str]] = typer.Option(
|
||||
None, "--destination", "-d", help="[b]The backup[/] destinations.",
|
||||
show_default=False
|
||||
),
|
||||
args: str = typer.Option(
|
||||
None, "--args", "-a",
|
||||
help="[b i yellow]Additional[/] rsync [b]arguments[/].",
|
||||
show_default=False
|
||||
)
|
||||
):
|
||||
"""
|
||||
[green b]Create[/] a [yellow]new source[/] with a [bold]uniq[/] label.
|
||||
[b]The source[/] will be connected to [b]backup destinations[/].
|
||||
[yellow i]Optionally, additional[/] arguments for rsync can be added.
|
||||
"""
|
||||
if label is None:
|
||||
label = LabelPrompt.ask_uuid()
|
||||
if source is None:
|
||||
source = console.input("What is the [yellow b]path to the source[/]? ")
|
||||
if args is None:
|
||||
args = console.input("What is the [yellow b]rsync args of source[/]? ")
|
||||
Source.create_save(label, source, destinations, args)
|
||||
|
@ -1,56 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
from typing import List, Optional
|
||||
import typer
|
||||
|
||||
from tui_rsync.models.models import Source, Destination, SyncCommand, Path
|
||||
from tui_rsync.cli.label_prompt import LabelPrompt
|
||||
from tui_rsync.models.models import all_labels
|
||||
|
||||
console = Console()
|
||||
source_remove = typer.Typer()
|
||||
|
||||
@source_remove.command()
|
||||
def one(
|
||||
label: str = typer.Option(
|
||||
None, "--label", "-l",
|
||||
help="[b]The label[/] is a uniq identification of a [b]source[/].",
|
||||
show_default=False
|
||||
)
|
||||
):
|
||||
"""
|
||||
[red b]Remove[/] an [yellow]existing source[/].
|
||||
"""
|
||||
if label is None:
|
||||
label = LabelPrompt.get_label_fzf()
|
||||
|
||||
if Source.is_exist(label):
|
||||
src = Source.get_source(label)
|
||||
src.delete_instance()
|
||||
|
||||
@source_remove.command()
|
||||
def all():
|
||||
"""
|
||||
[red b]Remove[/] [yellow] all existing sources[/].
|
||||
"""
|
||||
for label in all_labels().iterator():
|
||||
one(label.label)
|
@ -1,64 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
from typing import List, Optional
|
||||
import typer
|
||||
|
||||
from tui_rsync.models.models import Source, Destination, SyncCommand, Path
|
||||
from tui_rsync.cli.label_prompt import LabelPrompt
|
||||
from tui_rsync.models.models import all_labels
|
||||
|
||||
console = Console()
|
||||
source_show = typer.Typer()
|
||||
|
||||
@source_show.command()
|
||||
def one(
|
||||
label: str = typer.Option(
|
||||
None, "--label", "-l",
|
||||
help="[b]The label[/] is a uniq identification of a [b]source[/].",
|
||||
show_default=False
|
||||
),
|
||||
):
|
||||
"""
|
||||
[green b]Show[/] an [yellow]existing source[/].
|
||||
"""
|
||||
if label is None:
|
||||
console.print("What is the [yellow b]label of source[/]? ")
|
||||
label = LabelPrompt.get_label_fzf()
|
||||
|
||||
if not Source.is_exist(label):
|
||||
console.print("[red b][ERROR][/] Source does not exists!!!")
|
||||
return
|
||||
|
||||
source = Source.get_source(label)
|
||||
|
||||
console.print(source.show_format())
|
||||
|
||||
@source_show.command()
|
||||
def all():
|
||||
"""
|
||||
[green b]Show[/] [yellow]all existing sources[/].
|
||||
"""
|
||||
|
||||
for label in all_labels().iterator():
|
||||
source = Source.get_source(label.label)
|
||||
console.print(source.show_format())
|
||||
|
@ -1,111 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
from typing import List, Optional
|
||||
import typer
|
||||
|
||||
from tui_rsync.models.models import Source, Destination, SyncCommand, Path
|
||||
from tui_rsync.cli.label_prompt import LabelPrompt
|
||||
|
||||
console = Console()
|
||||
source_update = typer.Typer()
|
||||
|
||||
@source_update.command()
|
||||
def label(
|
||||
label: str = typer.Option(
|
||||
None, "--label", "-l",
|
||||
help="[b]The label[/] is a uniq identification of a [b]source[/].",
|
||||
show_default=False
|
||||
),
|
||||
new_label: str = typer.Option(
|
||||
None, "--new-label", "-nl",
|
||||
help="[b]The new label[/] will replace the [b]old source label[/].",
|
||||
show_default=False
|
||||
),
|
||||
|
||||
):
|
||||
"""
|
||||
[green b]Update[/] an [yellow]existing source label[/].
|
||||
"""
|
||||
if label is None:
|
||||
console.print("What is the [yellow b]old label of source[/]? ")
|
||||
label = LabelPrompt.get_label_fzf()
|
||||
|
||||
if new_label is None:
|
||||
question = "What is the [yellow b]new label of the source[/]? "
|
||||
new_label = LabelPrompt.ask_uuid(question)
|
||||
|
||||
if Source.is_exist(label):
|
||||
src = Source.get_source(label)
|
||||
src.update_label(new_label)
|
||||
|
||||
@source_update.command()
|
||||
def source(
|
||||
label: str = typer.Option(
|
||||
None, "--label", "-l",
|
||||
help="[b]The label[/] is a uniq identification of a [b]source[/].",
|
||||
show_default=False
|
||||
),
|
||||
new_source_path: str = typer.Option(
|
||||
None, "--new-label", "-nl",
|
||||
help="[b]The new source[/] will replace the [b]old source[/].",
|
||||
show_default=False
|
||||
),
|
||||
):
|
||||
"""
|
||||
[green b]Update[/] a source path of an [yellow]existing source[/].
|
||||
"""
|
||||
if label is None:
|
||||
console.print("What is the [yellow b]label of source[/]? ")
|
||||
label = LabelPrompt.get_label_fzf()
|
||||
|
||||
if new_source_path is None:
|
||||
question = "What is the [yellow b]new source path of the source[/]? "
|
||||
new_source_path = console.input(question)
|
||||
|
||||
if Source.is_exist(label):
|
||||
src = Source.get_source(label)
|
||||
src.update_source_path(new_source_path)
|
||||
|
||||
@source_update.command()
|
||||
def args(
|
||||
label: str = typer.Option(
|
||||
None, "--label", "-l",
|
||||
help="[b]The label[/] is a uniq identification of a [b]source[/].",
|
||||
show_default=False
|
||||
),
|
||||
args: str = typer.Option(
|
||||
None, "--args", "-a",
|
||||
help="[b yellow]rsync[/] [b]arguments[/].",
|
||||
show_default=False
|
||||
)
|
||||
|
||||
):
|
||||
"""
|
||||
[green b]Update[/] an [yellow]existing source args[/].
|
||||
"""
|
||||
if args is None:
|
||||
args = console.input("What is the [yellow b]rsync args of source[/]? ")
|
||||
|
||||
if Source.is_exist(label):
|
||||
src = Source.get_source(label)
|
||||
src.update_args(args)
|
||||
|
@ -1,115 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from rich.console import Console
|
||||
from rich.prompt import Prompt
|
||||
from typing import List, Optional
|
||||
import typer
|
||||
|
||||
from tui_rsync.models.models import Source, Group, Destination, SyncCommand
|
||||
from tui_rsync.models.models import Path
|
||||
from tui_rsync.models.models import all_labels
|
||||
from tui_rsync.cli.label_prompt import LabelPrompt
|
||||
from tui_rsync.cli.groups.group_prompt import GroupPrompt
|
||||
from tui_rsync.cli.path_prompt import PathPrompt
|
||||
from tui_rsync.cli.rsync import Rsync
|
||||
|
||||
console = Console()
|
||||
sync = typer.Typer()
|
||||
|
||||
skip_error = "[yellow b]Skippped[/] because the [red b]path was unavailable[/]."
|
||||
|
||||
@sync.command()
|
||||
def one(
|
||||
label: str = typer.Option(
|
||||
None, "--label", "-l",
|
||||
help="[b]The label[/] is a uniq identification of a [b]source[/].",
|
||||
show_default=False
|
||||
),
|
||||
dry: bool = typer.Option(
|
||||
False, "-d", "--dry-run",
|
||||
help="The command will [b]show[/] information about what will be changed.",
|
||||
)
|
||||
):
|
||||
"""
|
||||
[green b]Sync[/] a [yellow]source[/] with the [yellow b]label[/] and its backups.
|
||||
[yellow b]Skips[/] if not available.
|
||||
"""
|
||||
if label is None:
|
||||
label = LabelPrompt.get_label_fzf()
|
||||
src = Source.get_source(label)
|
||||
rsync = Rsync(str(src.args))
|
||||
for dest in src.destinations:
|
||||
if not dest.path.is_exists():
|
||||
console.print(skip_error)
|
||||
continue
|
||||
if dry:
|
||||
response = rsync.dry_one(str(src.source), str(dest))
|
||||
out, err = response
|
||||
console.print(f"{bstr_nonan(out)} {bstr_nonan(err)}")
|
||||
else:
|
||||
rsync.run_one(str(src.source), str(dest))
|
||||
|
||||
@sync.command()
|
||||
def group(
|
||||
group_label: str = typer.Option(
|
||||
None, "--group-label", "-g",
|
||||
help="[b]The label[/] is a uniq identification of a [b]group[/].",
|
||||
show_default=False
|
||||
),
|
||||
):
|
||||
"""
|
||||
[green b]Sync[/] a [yellow]group[/] with the [yellow b] label[/].
|
||||
"""
|
||||
if group_label is None:
|
||||
group_label = GroupPrompt.get_label_fzf()
|
||||
group = Group.get_group(group_label)
|
||||
|
||||
for src in group.get_sources():
|
||||
one(src.label)
|
||||
|
||||
@sync.command()
|
||||
def all():
|
||||
"""
|
||||
[green b]Sync[/] [yellow]all sources[/] with theirs backups.
|
||||
"""
|
||||
for label in all_labels().iterator():
|
||||
one(label.label)
|
||||
|
||||
@sync.command()
|
||||
def recover(
|
||||
label: str = typer.Option(
|
||||
None, "--label", "-l",
|
||||
help="[b]The label[/] is a uniq identification of a [b]source[/].",
|
||||
show_default=False
|
||||
),
|
||||
):
|
||||
"""
|
||||
[green b]Sync[/] the [yellow]chosen backup[/] with its source.
|
||||
"""
|
||||
if label is None:
|
||||
label = LabelPrompt.get_label_fzf()
|
||||
src = Source.get(Source.label == label)
|
||||
dest = PathPrompt.get_backup_fzf(label)
|
||||
|
||||
rsync = Rsync(str(src.args))
|
||||
rsync.run_one(str(dest), str(src.source))
|
||||
|
||||
def bstr_nonan(obj):
|
||||
return "" if obj is None else obj.decode()
|
@ -1,2 +0,0 @@
|
||||
from tui_rsync.config.app import App
|
||||
|
@ -1,52 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
import platformdirs
|
||||
import os
|
||||
|
||||
class App:
|
||||
"""
|
||||
Configuration of the tui-rsync
|
||||
"""
|
||||
__APP_NAME = "tui-rsync"
|
||||
__APP_AUTHOR = "KKlochko"
|
||||
__DB_NAME = "sync.db"
|
||||
|
||||
def get_db_path(self):
|
||||
path = platformdirs.user_data_path(
|
||||
self.__APP_NAME,
|
||||
self.__APP_AUTHOR,
|
||||
self.__DB_NAME
|
||||
)
|
||||
App.safe_create_path(self.get_data_dir())
|
||||
return path
|
||||
|
||||
def get_data_dir(self):
|
||||
return platformdirs.user_data_dir(
|
||||
self.__APP_NAME,
|
||||
self.__APP_AUTHOR,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def safe_create_path(path):
|
||||
"""
|
||||
Create path's folders if they do not exist
|
||||
"""
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
@ -0,0 +1,4 @@
|
||||
from .entities import BackupPlan
|
||||
from .value_objects import BackupPlanId, Path, Source, Destination
|
||||
|
||||
__all__ = ['BackupPlan', 'BackupPlanId', 'Path', 'Source', 'Destination']
|
@ -0,0 +1,3 @@
|
||||
from .backup_plan import BackupPlan
|
||||
|
||||
__all__ = ['BackupPlan']
|
@ -0,0 +1,14 @@
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
|
||||
from ..value_objects import BackupPlanId, Source, Destination
|
||||
from tui_rsync.core.shared_kernel.components.common.domain import Label
|
||||
|
||||
|
||||
@dataclass
|
||||
class BackupPlan:
|
||||
source: Source
|
||||
destinations: List[Destination]
|
||||
|
||||
id: BackupPlanId = field(default_factory=lambda: BackupPlanId.generate())
|
||||
label: Label = field(default='')
|
@ -0,0 +1,6 @@
|
||||
from .backup_plan_id import BackupPlanId
|
||||
from .path import Path
|
||||
from .source import Source
|
||||
from .destionation import Destination
|
||||
|
||||
__all__ = ['BackupPlanId', 'Path', 'Source', 'Destination']
|
@ -0,0 +1,5 @@
|
||||
from tui_rsync.core.shared_kernel.components.common.domain import UUID
|
||||
|
||||
|
||||
class BackupPlanId(UUID):
|
||||
pass
|
@ -0,0 +1,5 @@
|
||||
from .path import Path
|
||||
|
||||
|
||||
class Destination(Path):
|
||||
pass
|
@ -0,0 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Path:
|
||||
path: str
|
@ -0,0 +1,5 @@
|
||||
from .path import Path
|
||||
|
||||
|
||||
class Source(Path):
|
||||
pass
|
@ -0,0 +1,3 @@
|
||||
from .domain import UUID, Label
|
||||
|
||||
__all__ = ['UUID', 'Label']
|
@ -0,0 +1,3 @@
|
||||
from .value_objects import UUID, Label
|
||||
|
||||
__all__ = ['UUID', 'Label']
|
@ -0,0 +1,4 @@
|
||||
from .uuid import UUID
|
||||
from .label import Label
|
||||
|
||||
__all__ = ['UUID', 'Label']
|
@ -0,0 +1,6 @@
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Label:
|
||||
label: str = field(default='')
|
@ -0,0 +1,14 @@
|
||||
from dataclasses import dataclass, field
|
||||
import uuid
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class UUID:
|
||||
id: str = field(default_factory=lambda: UUID.generate().id)
|
||||
|
||||
@classmethod
|
||||
def generate(cls) -> 'UUID':
|
||||
return cls(str(uuid.uuid4()))
|
||||
|
||||
def __str__(self):
|
||||
return self.id
|
@ -1,9 +1,7 @@
|
||||
from .models.models import create_tables
|
||||
from .cli.cli import cli_app
|
||||
|
||||
|
||||
def main():
|
||||
create_tables()
|
||||
cli_app()
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -1,5 +0,0 @@
|
||||
from tui_rsync.models.models import Source
|
||||
from tui_rsync.models.models import Path
|
||||
from tui_rsync.models.models import Destination
|
||||
from tui_rsync.models.models import SyncCommand
|
||||
from tui_rsync.models.models import create_tables
|
@ -1,257 +0,0 @@
|
||||
################################################################################
|
||||
# Copyright (C) 2023 Kostiantyn Klochko <kostya_klochko@ukr.net> #
|
||||
# #
|
||||
# This file is part of tui-rsync. #
|
||||
# #
|
||||
# tui-rsync is free software: you can redistribute it and/or modify it under #
|
||||
# uthe terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation, either version 3 of the License, or (at your option) #
|
||||
# any later version. #
|
||||
# #
|
||||
# tui-rsync is distributed in the hope that it will be useful, but WITHOUT ANY #
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
||||
# details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along with #
|
||||
# tui-rsync. If not, see <https://www.gnu.org/licenses/>. #
|
||||
################################################################################
|
||||
|
||||
from peewee import *
|
||||
from tui_rsync.config.app import App
|
||||
import os
|
||||
|
||||
app = App()
|
||||
db = SqliteDatabase(app.get_db_path())
|
||||
|
||||
class BaseModel(Model):
|
||||
class Meta:
|
||||
database = db
|
||||
|
||||
class Path(BaseModel):
|
||||
path = CharField(unique=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.path}"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Path({self.path})"
|
||||
|
||||
def is_exists(self) -> bool:
|
||||
return os.path.exists(self.path)
|
||||
|
||||
class SyncCommand(BaseModel):
|
||||
command = CharField()
|
||||
|
||||
@staticmethod
|
||||
def get_sync_command(args):
|
||||
sync_cmd, _ = SyncCommand.get_or_create(command=args)
|
||||
return sync_cmd
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.command
|
||||
|
||||
class Source(BaseModel):
|
||||
label = CharField(unique=True)
|
||||
source = ForeignKeyField(Path)
|
||||
args = ForeignKeyField(SyncCommand)
|
||||
|
||||
@staticmethod
|
||||
def is_exist(label) -> bool:
|
||||
return Source.select().where(Source.label == label).exists()
|
||||
|
||||
@staticmethod
|
||||
def get_source(label):
|
||||
if not Source.is_exist(label):
|
||||
return None
|
||||
return Source.select().where(Source.label == label).get()
|
||||
|
||||
@staticmethod
|
||||
def create_save(label:str, source:str, destinations:list[str], args:str):
|
||||
src_path, _ = Path.get_or_create(path=source)
|
||||
src_sync_cmd, _ = SyncCommand.get_or_create(command=args)
|
||||
src = Source.create(
|
||||
label=label,
|
||||
source=src_path,
|
||||
destinations=[],
|
||||
args=src_sync_cmd
|
||||
)
|
||||
|
||||
for destination in destinations:
|
||||
src.add_destionation(destination)
|
||||
|
||||
src.save()
|
||||
return src
|
||||
|
||||
def add_destionation(self, destination):
|
||||
destination_path, _ = Path.get_or_create(path=destination)
|
||||
src_destination, _ = Destination.get_or_create(
|
||||
source=self,
|
||||
path=destination_path
|
||||
)
|
||||
|
||||
def get_destinations(self):
|
||||
destionations = []
|
||||
|
||||
for dest in self.destinations:
|
||||
destionations.append(dest.path)
|
||||
|
||||
return destionations
|
||||
|
||||
def update_label(self, new_label):
|
||||
self.label = new_label
|
||||
self.save()
|
||||
|
||||
def update_source_path(self, new_path):
|
||||
new_source_path, _ = Path.get_or_create(path=new_path)
|
||||
self.source = new_source_path
|
||||
self.save()
|
||||
|
||||
def update_args(self, args):
|
||||
args_obj = SyncCommand.get_sync_command(args)
|
||||
self.args = args_obj
|
||||
self.save()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.label}"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.label}"
|
||||
|
||||
def show_format(self, prefix='') -> str:
|
||||
output = f"[b]label:[/] {self.label}\n" \
|
||||
f"[b]source:[/] {self.source.path}\n" \
|
||||
f"[b]args:[/] {self.args}\n" \
|
||||
f"[b]destionations:[/] \n"
|
||||
|
||||
for destination in self.destinations:
|
||||
output+=f"\t{destination.path}\n"
|
||||
|
||||
if prefix != '':
|
||||
keepends = True
|
||||
output = prefix + f'{prefix}'.join(output.splitlines(keepends))
|
||||
|
||||
return output
|
||||
|
||||
class Destination(BaseModel):
|
||||
source = ForeignKeyField(Source, backref='destinations')
|
||||
path = ForeignKeyField(Path)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.path}"
|
||||
|
||||
@staticmethod
|
||||
def get_all(label:str|None = None):
|
||||
"""
|
||||
Return all destiantions of the source.
|
||||
"""
|
||||
if label is None:
|
||||
return []
|
||||
|
||||
src = Source.get_source(label)
|
||||
if src is None:
|
||||
return []
|
||||
return src.destinations
|
||||
|
||||
class Group(BaseModel):
|
||||
label = CharField(unique=True)
|
||||
|
||||
@staticmethod
|
||||
def is_exist(label) -> bool:
|
||||
return Group.select().where(Group.label == label).exists()
|
||||
|
||||
@staticmethod
|
||||
def get_group(label):
|
||||
if not Group.is_exist(label):
|
||||
return None
|
||||
return Group.select().where(Group.label == label).get()
|
||||
|
||||
@staticmethod
|
||||
def create_save(label:str, source_labels:list[str]):
|
||||
group = Group.create(
|
||||
label=label,
|
||||
sources=[],
|
||||
)
|
||||
|
||||
GroupSource.create_group_sources(group, source_labels)
|
||||
group.save()
|
||||
return group
|
||||
|
||||
def update_label(self, new_label):
|
||||
self.label = new_label
|
||||
self.save()
|
||||
|
||||
def get_sources(self):
|
||||
"""
|
||||
Return iterator of the group sources.
|
||||
"""
|
||||
return (group_src.source for group_src in self.sources)
|
||||
|
||||
def remove_sources(self):
|
||||
GroupSource.delete().where(GroupSource.group == self.label).execute()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.label}"
|
||||
|
||||
def __repl__(self) -> str:
|
||||
return f"{self.label}"
|
||||
|
||||
def show_format(self) -> str:
|
||||
output = f"[b]label:[/] {self.label}\n" \
|
||||
f"[b]sources:[/] \n"
|
||||
|
||||
for source in self.sources:
|
||||
prefix = '\t'
|
||||
output+=f"{source.source.show_format(prefix)}\n"
|
||||
|
||||
return output
|
||||
|
||||
class GroupSource(BaseModel):
|
||||
group = ForeignKeyField(Group, backref='sources')
|
||||
source = ForeignKeyField(Source)
|
||||
|
||||
@staticmethod
|
||||
def create_group_source(group:Group, source_label:str) -> bool:
|
||||
"""
|
||||
Return group-source created status
|
||||
"""
|
||||
src = Source.get(label=source_label)
|
||||
group_src, created = GroupSource.get_or_create(group=group, source=src)
|
||||
group_src.save()
|
||||
return created
|
||||
|
||||
@staticmethod
|
||||
def create_group_sources(group:Group, source_labels:list[str]):
|
||||
for source_label in source_labels:
|
||||
GroupSource.create_group_source(group, source_label)
|
||||
|
||||
def create_tables():
|
||||
with db:
|
||||
tables = [
|
||||
Source,
|
||||
Path,
|
||||
Destination,
|
||||
SyncCommand,
|
||||
Group,
|
||||
GroupSource
|
||||
]
|
||||
db.create_tables(tables, safe=True)
|
||||
|
||||
def all_group_labels():
|
||||
with db:
|
||||
return Group.select(Group.label)
|
||||
|
||||
def all_labels():
|
||||
with db:
|
||||
return Source.select(Source.label)
|
||||
|
||||
def all_labels_except(labels):
|
||||
if labels == None:
|
||||
return all_labels()
|
||||
if len(labels) == 0:
|
||||
return all_labels()
|
||||
with db:
|
||||
return Source.select(Source.label).where(Source.label.not_in(labels))
|
||||
|
||||
def count_all_labels_except(labels):
|
||||
return len(all_labels_except(labels))
|
Loading…
Reference in new issue