Add Clone command and fixes
This commit is contained in:
parent
d55fc63a41
commit
f0a7624b0f
|
@ -8,6 +8,7 @@ import click
|
|||
from ordigi import constants, log, LOG
|
||||
from ordigi.collection import Collection
|
||||
from ordigi.geolocation import GeoLocation
|
||||
from ordigi import utils
|
||||
|
||||
_logger_options = [
|
||||
click.option(
|
||||
|
@ -173,7 +174,7 @@ def _check(**kwargs):
|
|||
if summary.errors:
|
||||
sys.exit(1)
|
||||
else:
|
||||
LOG.logger.error('Db data is not accurate run `ordigi update`')
|
||||
LOG.error('Db data is not accurate run `ordigi update`')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
@ -256,6 +257,39 @@ def _clean(**kwargs):
|
|||
sys.exit(1)
|
||||
|
||||
|
||||
@cli.command('clone')
|
||||
@add_options(_logger_options)
|
||||
@add_options(_dry_run_options)
|
||||
@click.argument('src', required=True, nargs=1, type=click.Path())
|
||||
@click.argument('dest', required=True, nargs=1, type=click.Path())
|
||||
def _clone(**kwargs):
|
||||
"""Clone media collection to another location"""
|
||||
|
||||
log_level = log.get_level(kwargs['verbose'])
|
||||
log.console(LOG, level=log_level)
|
||||
|
||||
src_path = Path(kwargs['src']).expanduser().absolute()
|
||||
dest_path = Path(kwargs['dest']).expanduser().absolute()
|
||||
|
||||
dry_run = kwargs['dry_run']
|
||||
|
||||
src_collection = Collection(
|
||||
src_path, {'cache': True, 'dry_run': dry_run}
|
||||
)
|
||||
|
||||
if dest_path.exists() and not utils.empty_dir(dest_path):
|
||||
LOG.error(f'Destination collection path {dest_path} must be empty directory')
|
||||
sys.exit(1)
|
||||
|
||||
summary = src_collection.clone(dest_path)
|
||||
|
||||
if log_level < 30:
|
||||
summary.print()
|
||||
|
||||
if summary.errors:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@cli.command('compare')
|
||||
@add_options(_logger_options)
|
||||
@add_options(_dry_run_options)
|
||||
|
|
|
@ -3,6 +3,7 @@ Collection methods.
|
|||
"""
|
||||
from copy import copy
|
||||
from datetime import datetime, timedelta
|
||||
from distutils.dir_util import copy_tree
|
||||
import filecmp
|
||||
from fnmatch import fnmatch
|
||||
import os
|
||||
|
@ -299,6 +300,12 @@ class FileIO:
|
|||
|
||||
self.log.info(f'remove: {path}')
|
||||
|
||||
def mkdir(self, directory):
|
||||
if not self.dry_run:
|
||||
directory.mkdir(exist_ok=True)
|
||||
|
||||
self.log.info(f'create dir: {directory}')
|
||||
|
||||
def rmdir(self, directory):
|
||||
if not self.dry_run:
|
||||
directory.rmdir()
|
||||
|
@ -699,14 +706,8 @@ class Collection(SortMedias):
|
|||
if not cli_options:
|
||||
cli_options = {}
|
||||
|
||||
self.log = LOG.getChild(self.__class__.__name__)
|
||||
|
||||
# Check if collection path is valid
|
||||
if not root.exists():
|
||||
self.log.error(f'Collection path {root} does not exist')
|
||||
sys.exit(1)
|
||||
|
||||
self.root = root
|
||||
self.log = LOG.getChild(self.__class__.__name__)
|
||||
|
||||
# Get config options
|
||||
self.opt = self.get_config_options()
|
||||
|
@ -720,8 +721,11 @@ class Collection(SortMedias):
|
|||
if not self.exclude:
|
||||
self.exclude = set()
|
||||
|
||||
self.db = CollectionDb(root)
|
||||
self.fileio = FileIO(self.opt['Terminal']['dry_run'])
|
||||
|
||||
self.root_is_valid()
|
||||
|
||||
self.db = CollectionDb(root)
|
||||
self.paths = Paths(
|
||||
self.opt['Filters'],
|
||||
interactive=self.opt['Terminal']['interactive'],
|
||||
|
@ -749,6 +753,16 @@ class Collection(SortMedias):
|
|||
self.summary = Summary(self.root)
|
||||
self.theme = request.load_theme()
|
||||
|
||||
def root_is_valid(self):
|
||||
"""Check if collection path is valid"""
|
||||
if self.root.exists():
|
||||
if not self.root.is_dir():
|
||||
self.log.error(f'Collection path {self.root} is not a directory')
|
||||
sys.exit(1)
|
||||
else:
|
||||
self.log.error(f'Collection path {self.root} does not exist')
|
||||
sys.exit(1)
|
||||
|
||||
def get_config_options(self):
|
||||
"""Get collection config"""
|
||||
config = Config(self.root.joinpath('.ordigi', 'ordigi.conf'))
|
||||
|
@ -810,6 +824,14 @@ class Collection(SortMedias):
|
|||
|
||||
return True
|
||||
|
||||
def check(self):
|
||||
if self.db.sqlite.is_empty('metadata'):
|
||||
self.log.error('Db data does not exist run `ordigi init`')
|
||||
sys.exit(1)
|
||||
elif not self.check_db():
|
||||
self.log.error('Db data is not accurate run `ordigi update`')
|
||||
sys.exit(1)
|
||||
|
||||
def _init_check_db(self, loc=None):
|
||||
if self.db.sqlite.is_empty('metadata'):
|
||||
self.init(loc)
|
||||
|
@ -817,6 +839,25 @@ class Collection(SortMedias):
|
|||
self.log.error('Db data is not accurate run `ordigi update`')
|
||||
sys.exit(1)
|
||||
|
||||
def clone(self, dest_path):
|
||||
"""Clone collection in another location"""
|
||||
self.check()
|
||||
|
||||
if not self.dry_run:
|
||||
copy_tree(str(self.root), str(dest_path))
|
||||
|
||||
self.log.info(f'copy: {self.root} -> {dest_path}')
|
||||
|
||||
if not self.dry_run:
|
||||
dest_collection = Collection(
|
||||
dest_path, {'cache': True, 'dry_run': self.dry_run}
|
||||
)
|
||||
|
||||
if not dest_collection.check_db():
|
||||
self.summary.append('check', False)
|
||||
|
||||
return self.summary
|
||||
|
||||
def update(self, loc):
|
||||
"""Update collection db"""
|
||||
file_paths = list(self.get_collection_files())
|
||||
|
@ -866,7 +907,7 @@ class Collection(SortMedias):
|
|||
if checksum == self.db.sqlite.get_checksum(relpath):
|
||||
self.summary.append('check', True, file_path)
|
||||
else:
|
||||
self.log.error('{file_path} is corrupted')
|
||||
self.log.error(f'{file_path} is corrupted')
|
||||
self.summary.append('check', False, file_path)
|
||||
|
||||
return self.summary
|
||||
|
|
|
@ -1,19 +1,13 @@
|
|||
from datetime import datetime
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
import sys
|
||||
|
||||
from shutil import copyfile
|
||||
from time import strftime
|
||||
|
||||
from ordigi import constants
|
||||
from ordigi.utils import distance_between_two_points
|
||||
|
||||
|
||||
class Sqlite:
|
||||
|
||||
"""Methods for interacting with Sqlite database"""
|
||||
|
||||
def __init__(self, target_dir):
|
||||
|
@ -30,7 +24,7 @@ class Sqlite:
|
|||
self.db_type = 'SQLite format 3'
|
||||
self.types = {'text': (str, datetime), 'integer': (int,), 'real': (float,)}
|
||||
|
||||
self.filename = Path(db_dir, target_dir.name + '.db')
|
||||
self.filename = Path(db_dir, 'collection.db')
|
||||
self.con = sqlite3.connect(self.filename)
|
||||
# Allow selecting column by name
|
||||
self.con.row_factory = sqlite3.Row
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# import pandas as pd
|
||||
from tabulate import tabulate
|
||||
|
||||
|
||||
class Tables:
|
||||
"""Create table and display result in Pandas DataFrame"""
|
||||
|
||||
|
@ -34,6 +35,7 @@ class Tables:
|
|||
errors_headers = self.columns
|
||||
return tabulate(self.table, headers=errors_headers)
|
||||
|
||||
|
||||
class Summary:
|
||||
"""Result summary of ordigi program call"""
|
||||
|
||||
|
|
|
@ -38,6 +38,10 @@ def distance_between_two_points(lat1, lon1, lat2, lon2):
|
|||
return rad * sqrt(x * x + y * y)
|
||||
|
||||
|
||||
def empty_dir(dir_path):
|
||||
return not next(os.scandir(dir_path), None)
|
||||
|
||||
|
||||
def get_date_regex(user_regex=None):
|
||||
"""Return date regex generator"""
|
||||
if user_regex:
|
||||
|
|
|
@ -2,18 +2,18 @@
|
|||
|
||||
from configparser import RawConfigParser
|
||||
import os
|
||||
import pytest
|
||||
from pathlib import Path, PurePath
|
||||
import random
|
||||
import shutil
|
||||
import string
|
||||
import tempfile
|
||||
|
||||
from ordigi.config import Config
|
||||
import pytest
|
||||
|
||||
from ordigi.exiftool import _ExifToolProc
|
||||
|
||||
ORDIGI_PATH = Path(__file__).parent.parent
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_singletons():
|
||||
""" Need to clean up any ExifTool singletons between tests """
|
||||
|
@ -33,7 +33,6 @@ def sample_files_paths(tmpdir_factory):
|
|||
|
||||
def randomize_files(dest_dir):
|
||||
# Get files randomly
|
||||
paths = Path(dest_dir).glob('*')
|
||||
for path, subdirs, files in os.walk(dest_dir):
|
||||
if '.ordigi' in path:
|
||||
continue
|
||||
|
@ -50,7 +49,7 @@ def randomize_files(dest_dir):
|
|||
|
||||
def randomize_db(dest_dir):
|
||||
# alterate database
|
||||
file_path = Path(str(dest_dir), '.ordigi', str(dest_dir.name) + '.db')
|
||||
file_path = Path(str(dest_dir), '.ordigi', 'collection.db')
|
||||
with open(file_path, 'wb') as fout:
|
||||
fout.write(os.urandom(random.randrange(128, 2048)))
|
||||
|
||||
|
|
|
@ -72,6 +72,8 @@ class TestOrdigi:
|
|||
for command in commands:
|
||||
self.assert_cli(command, ['not_exist'], state=1)
|
||||
|
||||
self.assert_cli(cli._clone, ['not_exist'], state=2)
|
||||
|
||||
def test_sort(self):
|
||||
bool_options = (
|
||||
# '--interactive',
|
||||
|
@ -97,6 +99,20 @@ class TestOrdigi:
|
|||
self.assert_options(cli._sort, bool_options, arg_options, paths)
|
||||
self.assert_all_options(cli._sort, bool_options, arg_options, paths)
|
||||
|
||||
def test_clone(self, tmp_path):
|
||||
|
||||
arg_options = (
|
||||
*self.logger_options,
|
||||
|
||||
)
|
||||
|
||||
paths = (str(self.src_path), str(tmp_path))
|
||||
|
||||
self.assert_cli(cli._init, [str(self.src_path)])
|
||||
self.assert_cli(cli._clone, ['--dry-run', '--verbose', 'DEBUG', *paths])
|
||||
self.assert_cli(cli._clone, paths)
|
||||
|
||||
|
||||
def assert_init(self):
|
||||
for opt, arg in self.logger_options:
|
||||
self.assert_cli(cli._init, [opt, arg, str(self.src_path)])
|
||||
|
|
Loading…
Reference in New Issue