From 467989bbd53e8aaa9dec3a63e452b15901516d42 Mon Sep 17 00:00:00 2001 From: KKlochko Date: Mon, 27 Feb 2023 16:21:10 +0200 Subject: [PATCH] (Added) sync one function. Added Rsync wrappper. Added fzf_prompt, all_labels for choosing a label of source. --- CHANGELOG.org | 4 +++ poetry.lock | 14 +++++++++- pyproject.toml | 1 + tui_rsync/cli/__init__.py | 2 ++ tui_rsync/cli/cli.py | 2 ++ tui_rsync/cli/label_prompt.py | 6 +++++ tui_rsync/cli/rsync.py | 31 ++++++++++++++++++++++ tui_rsync/cli/sync.py | 49 +++++++++++++++++++++++++++++++++++ tui_rsync/models/models.py | 21 ++++++++++++--- 9 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 tui_rsync/cli/rsync.py create mode 100644 tui_rsync/cli/sync.py diff --git a/CHANGELOG.org b/CHANGELOG.org index c93dfab..20a1627 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -2,3 +2,7 @@ ** 0.1.0 <2023-02-26 Sun> First release Added *source add* function. +** 0.3.2 <2023-02-27 Mon> + Added *sync one* function. + Added Rsync wrappper. + Added fzf_prompt, all_labels for choosing a label of source. diff --git a/poetry.lock b/poetry.lock index e551527..561b1a1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -75,6 +75,18 @@ files = [ {file = "peewee-3.15.4.tar.gz", hash = "sha256:2581520c8dfbacd9d580c2719ae259f0637a9e46eda47dfc0ce01864c6366205"}, ] +[[package]] +name = "pyfzf" +version = "0.3.1" +description = "Python wrapper for junegunn's fuzzyfinder (fzf)" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pyfzf-0.3.1-py3-none-any.whl", hash = "sha256:736f71563461b75f6f85b55345bdc638fa0dc14c32c857c59e8b1ca1cfa3cf4a"}, + {file = "pyfzf-0.3.1.tar.gz", hash = "sha256:dd902e34cffeca9c3082f96131593dd20b4b3a9bba5b9dde1b0688e424b46bd2"}, +] + [[package]] name = "pygments" version = "2.14.0" @@ -133,4 +145,4 @@ test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6. [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "35a9606bb210cf139d272de91021f83892b8cfc10674ac383f4ae61dfbd8ec60" +content-hash = "1cd42aa3f295e2a1c11c491125b55a32bdb6878a9e0f262c068397a6ce941a89" diff --git a/pyproject.toml b/pyproject.toml index 4ea6cd1..2b08688 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ python = "^3.10" rich = "^13.3.1" typer = "^0.7.0" peewee = "^3.15.4" +pyfzf = "^0.3.1" [build-system] requires = ["poetry-core"] diff --git a/tui_rsync/cli/__init__.py b/tui_rsync/cli/__init__.py index 8c67bff..8f61cb2 100644 --- a/tui_rsync/cli/__init__.py +++ b/tui_rsync/cli/__init__.py @@ -1,3 +1,5 @@ from cli.cli import cli_app from cli.source import source +from cli.sync import sync +from cli.rsync import Rsync from cli.label_prompt import LabelPrompt diff --git a/tui_rsync/cli/cli.py b/tui_rsync/cli/cli.py index b1cee2f..c972f60 100644 --- a/tui_rsync/cli/cli.py +++ b/tui_rsync/cli/cli.py @@ -20,8 +20,10 @@ from rich.console import Console import typer from cli.source import source +from cli.sync import sync console = Console() cli_app = typer.Typer(rich_markup_mode="rich") cli_app.add_typer(source, name="source", help="Manage sources") +cli_app.add_typer(sync, name="sync", help="Sync sources") diff --git a/tui_rsync/cli/label_prompt.py b/tui_rsync/cli/label_prompt.py index 8814004..cf0514b 100644 --- a/tui_rsync/cli/label_prompt.py +++ b/tui_rsync/cli/label_prompt.py @@ -19,7 +19,9 @@ from rich.console import Console from rich.prompt import Prompt +from pyfzf import FzfPrompt import uuid +from models.models import all_labels console = Console() @@ -34,3 +36,7 @@ class LabelPrompt: uid = uuid.uuid4().hex label = Prompt.ask(question, default=uid) return label + + def get_label_fzf() -> str: + fzf = FzfPrompt() + return fzf.prompt(all_labels().iterator()) diff --git a/tui_rsync/cli/rsync.py b/tui_rsync/cli/rsync.py new file mode 100644 index 0000000..3ba6fe5 --- /dev/null +++ b/tui_rsync/cli/rsync.py @@ -0,0 +1,31 @@ +################################################################################ +# Copyright (C) 2023 Kostiantyn Klochko # +# # +# 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 . # +################################################################################ + +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() + diff --git a/tui_rsync/cli/sync.py b/tui_rsync/cli/sync.py new file mode 100644 index 0000000..73e5c0c --- /dev/null +++ b/tui_rsync/cli/sync.py @@ -0,0 +1,49 @@ +################################################################################ +# Copyright (C) 2023 Kostiantyn Klochko # +# # +# 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 . # +################################################################################ + +from rich.console import Console +from rich.prompt import Prompt +from typing import List, Optional +import typer + +from models.models import Source, Destination, SyncCommand, Path +from cli.label_prompt import LabelPrompt +from cli.rsync import Rsync + +console = Console() +sync = typer.Typer() + +@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 + ), +): + """ + [green b]Sync[/] a [yellow]source[/] with the [yellow b]label[/] and its backups. + """ + if label is None: + label = LabelPrompt.get_label_fzf() + src = Source.get(Source.label == label) + rsync = Rsync(str(src.args)) + for dest in src.destinations: + rsync.run_one(str(src.source), str(dest)) + diff --git a/tui_rsync/models/models.py b/tui_rsync/models/models.py index 0f6f036..efe6646 100644 --- a/tui_rsync/models/models.py +++ b/tui_rsync/models/models.py @@ -29,7 +29,7 @@ class Path(BaseModel): path = CharField(unique=True) def __str__(self) -> str: - return f"Path({self.path})" + return f"{self.path}" def __repr__(self) -> str: return f"Path({self.path})" @@ -37,6 +37,9 @@ class Path(BaseModel): class SyncCommand(BaseModel): command = CharField() + def __str__(self) -> str: + return self.command + class Source(BaseModel): label = CharField(unique=True) source = ForeignKeyField(Path) @@ -58,10 +61,22 @@ class Source(BaseModel): src.save() return src + def __str__(self) -> str: + return f"{self.label}" + + def __repr__(self) -> str: + return f"{self.label}" class Destination(BaseModel): source = ForeignKeyField(Source, backref='destinations') path = ForeignKeyField(Path) + def __str__(self) -> str: + return f"{self.path}" + def create_tables(): - db.connect() - db.create_tables([Source, Path, Destination, SyncCommand], safe=True) + with db: + db.create_tables([Source, Path, Destination, SyncCommand], safe=True) + +def all_labels(): + with db: + return Source.select(Source.label)