-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from aptly-io/feature_re_selection
feat: select items that match a regular expression Matching is based on the "{folder}/{title}" value. Notice the "/" inserted between folder and title value as was it a directory and a filename.
- Loading branch information
Showing
9 changed files
with
68 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,6 +58,12 @@ pwsync --from demo/from.kdbx --to bitwarden | |
# see below for an example of typical output. | ||
pwsync --from demo/from.kdbx --to demo/to.kdbx | ||
|
||
# Do a dry-run (-d) for syncing all (-a) items that match the selection (-s) | ||
# Item's are matched based on the concatenation of their <folder-value> '/' <title-value> | ||
python -mpwsync.main -d -f demo/from.kdbx -t demo/to.kdbx -a \ | ||
-s ".*/folder 1.1/.*" -s "folder 2/.*" \ | ||
--from-master-password=pw --to-master-password=pw | ||
|
||
# a description of all options | ||
pwsync -h | ||
``` | ||
|
@@ -198,4 +204,4 @@ An curated dump of the console output is shown below: | |
|
||
`pwsync` is necessarily GPL3 since it (currently) depends upon the GPL3 python module `pykeepass`. | ||
|
||
Copyright 2022 Francis Meyvis ([email protected]) | ||
Copyright 2022, 2023 Francis Meyvis ([email protected]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
# Copyright 2022 Francis Meyvis ([email protected]) | ||
# Copyright 2022, 2023 Francis Meyvis ([email protected]) | ||
|
||
"""common constants and helper functions""" | ||
|
||
from dataclasses import dataclass | ||
from typing import List | ||
from typing import List, Optional | ||
|
||
LOGGER_NAME = "PWSYNC" | ||
|
||
|
@@ -57,6 +57,7 @@ class PwsQueryInfo: | |
ids: List[str] | ||
id_sep: str = ":" | ||
sync: bool = True | ||
filters: Optional[List[str]] = None | ||
|
||
|
||
@dataclass | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
# Copyright 2022 Francis Meyvis ([email protected]) | ||
# Copyright 2022, 2023 Francis Meyvis ([email protected]) | ||
|
||
"""interactive synchronize using the console""" | ||
|
||
import sys | ||
from difflib import SequenceMatcher | ||
from os.path import join | ||
from typing import List, Optional | ||
|
||
from prompt_toolkit import HTML | ||
|
@@ -184,9 +185,23 @@ def _sync_element(kind: str, element: PwsDiffElement, to_db: PwsDatabaseClient): | |
raise PwsUnsupported(f"_sync_element({kind})") | ||
|
||
|
||
def _sync_section( | ||
kind: str, query_info: PwsQueryInfo, syncer: PwsSyncer, run_options: RunOptions, to_dataset: PasswordDataset | ||
): | ||
def _match_selector(selector, item: Optional[PwsItem]) -> bool: | ||
if item is None: | ||
return False | ||
path = join(item.folder if item.folder is not None else "", item.title) | ||
return selector.fullmatch(path) is not None | ||
|
||
|
||
def _match_selectors(selectors, element): | ||
for selector in selectors: | ||
if _match_selector(selector, element.from_item): | ||
return True | ||
if _match_selector(selector, element.to_item): | ||
return True | ||
return False # non-matching element skipped | ||
|
||
|
||
def _switch_kind(kind: str, query_info: PwsQueryInfo, syncer): | ||
def _get_key_using_from_item(diff_element: PwsDiffElement): | ||
return diff_element.from_item.make_id(query_info) if diff_element.from_item else "" | ||
|
||
|
@@ -211,14 +226,28 @@ def _get_key_using_to_item(diff_element: PwsDiffElement): | |
key_getter = _get_key_using_from_item | ||
props_name = "add_props" | ||
elif kind == "skipped": | ||
return # TODO handle skipped | ||
raise NotImplementedError("kind 'skipped' not handled") | ||
elif kind == "unchanged": | ||
return # TODO handle skipped | ||
raise NotImplementedError("kind 'unchanged' not handled") | ||
|
||
if query_info.filters: | ||
filtered_data = list(filter(lambda e: _match_selectors(query_info.filters, e), data)) | ||
else: | ||
filtered_data = data | ||
|
||
return filtered_data, key_getter, props_name | ||
|
||
|
||
def _sync_section( | ||
kind: str, query_info: PwsQueryInfo, syncer: PwsSyncer, run_options: RunOptions, to_dataset: PasswordDataset | ||
): | ||
data, key_getter, props_name = _switch_kind(kind, query_info, syncer) | ||
|
||
print_ft(HTML(_markup(f"To {kind}: {len(data)}", "info")), style=STYLE) | ||
|
||
section_folder = "" | ||
count = 0 | ||
|
||
for element in sorted(data, key=key_getter): | ||
count += 1 | ||
section_folder = _print_ft_element( | ||
|
@@ -249,5 +278,5 @@ def console_sync(query_info: PwsQueryInfo, syncer: PwsSyncer, run_options: RunOp | |
_sync_section("update", query_info, syncer, run_options, to_dataset) | ||
_sync_section("create", query_info, syncer, run_options, to_dataset) | ||
_sync_section("delete", query_info, syncer, run_options, to_dataset) | ||
_sync_section("skipped", query_info, syncer, run_options, to_dataset) | ||
_sync_section("unchanged", query_info, syncer, run_options, to_dataset) | ||
# _sync_section("skipped", query_info, syncer, run_options, to_dataset) # TODO handle skipped | ||
# _sync_section("unchanged", query_info, syncer, run_options, to_dataset) # TODO handle unchanged |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,10 @@ | ||
#!/usr/bin/env python3 | ||
|
||
# Copyright 2022 Francis Meyvis ([email protected]) | ||
# Copyright 2022, 2023 Francis Meyvis ([email protected]) | ||
|
||
""" password sync's entry point""" | ||
|
||
import re | ||
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser | ||
from dataclasses import dataclass | ||
from getpass import getpass | ||
|
@@ -125,6 +126,15 @@ def _parse_command_line(): | |
+ "or stored in the shell history buffer, etc. If left empty, it is prompted for on the command line", | ||
) | ||
|
||
parser.add_argument( | ||
"-s", | ||
"--select", | ||
action="append", | ||
nargs="*", | ||
default=[], | ||
help="Select the items to sync by matching their folder/title value with the regular expression.", | ||
) | ||
|
||
parser.add_argument("-U", "--auto-update", action="store_true", help="automatically update all entries") | ||
parser.add_argument("-C", "--auto-create", action="store_true", help="automatically create all entries") | ||
|
||
|
@@ -211,7 +221,8 @@ def main(): | |
logger.addHandler(file_handler) | ||
logger.propagate = False # do not further propagate to the root handler (stdout) | ||
|
||
query_info = PwsQueryInfo(args.id.split(","), args.id_sep, args.sync) | ||
filters = list(map(re.compile, map(lambda i: i[0], args.select))) | ||
query_info = PwsQueryInfo(args.id.split(","), args.id_sep, args.sync, filters) | ||
|
||
access = _AccessInfo(args.from_username, args.from_secret, args.from_master_password) | ||
from_dataset = _open_password_db(query_info, "from", getattr(args, "from"), access) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters