Revamp and fix options
This commit is contained in:
		
							parent
							
								
									dde2f4f66f
								
							
						
					
					
						commit
						3baa184a17
					
				
							
								
								
									
										18
									
								
								ordigi.conf
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								ordigi.conf
									
									
									
									
									
								
							@ -1,5 +1,17 @@
 | 
			
		||||
[Exif]
 | 
			
		||||
#album_from_folder=False
 | 
			
		||||
fill_date_original=True
 | 
			
		||||
#cache=True
 | 
			
		||||
#ignore_tags=None
 | 
			
		||||
use_date_filename=True
 | 
			
		||||
#use_file_dates=False
 | 
			
		||||
 | 
			
		||||
[Filters]
 | 
			
		||||
exclude=["**/.directory", "**/.DS_Store"]
 | 
			
		||||
#extensions=None
 | 
			
		||||
#glob=**/*
 | 
			
		||||
#max_deep=None
 | 
			
		||||
remove_duplicates=True
 | 
			
		||||
 | 
			
		||||
[Geolocation]
 | 
			
		||||
geocoder=Nominatim
 | 
			
		||||
@ -15,5 +27,9 @@ day_begins=4
 | 
			
		||||
 | 
			
		||||
# Path format
 | 
			
		||||
dirs_path=<%Y>/<%m-%b>_<location>_<folder>
 | 
			
		||||
name=<%Y%m%d-%H%M%S>_<<original_name>|<name>>.%l<ext>
 | 
			
		||||
name=<%Y%m%d-%H%M%S>_<<name>.%l<ext>|<original_name>>
 | 
			
		||||
# name=<%Y%m%d-%H%M%S>-%u<original_name>.%l<ext>
 | 
			
		||||
 | 
			
		||||
[Terminal]
 | 
			
		||||
dry_run=False
 | 
			
		||||
interactive=False
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ import click
 | 
			
		||||
 | 
			
		||||
from ordigi import log, LOG
 | 
			
		||||
from ordigi.collection import Collection
 | 
			
		||||
from ordigi import constants
 | 
			
		||||
from ordigi.geolocation import GeoLocation
 | 
			
		||||
from ordigi import utils
 | 
			
		||||
 | 
			
		||||
@ -85,7 +86,7 @@ _sort_options = [
 | 
			
		||||
    click.option(
 | 
			
		||||
        '--path-format',
 | 
			
		||||
        '-p',
 | 
			
		||||
        default=None,
 | 
			
		||||
        default=constants.DEFAULT_PATH_FORMAT,
 | 
			
		||||
        help='Custom featured path format',
 | 
			
		||||
    ),
 | 
			
		||||
    click.option(
 | 
			
		||||
@ -147,15 +148,10 @@ def _cli_get_location(collection):
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _cli_sort(collection, src_paths, import_mode, remove_duplicates):
 | 
			
		||||
def _cli_sort(collection, src_paths, import_mode):
 | 
			
		||||
    loc = _cli_get_location(collection)
 | 
			
		||||
 | 
			
		||||
    path_format = collection.opt['Path']['path_format']
 | 
			
		||||
    LOG.debug(f'path_format: {path_format}')
 | 
			
		||||
 | 
			
		||||
    return collection.sort_files(
 | 
			
		||||
        src_paths, path_format, loc, import_mode, remove_duplicates
 | 
			
		||||
    )
 | 
			
		||||
    return collection.sort_files(src_paths, loc, import_mode)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@click.group()
 | 
			
		||||
@ -231,24 +227,21 @@ def _clean(**kwargs):
 | 
			
		||||
    collection = Collection(
 | 
			
		||||
        root,
 | 
			
		||||
        {
 | 
			
		||||
            "dry_run": kwargs['dry_run'],
 | 
			
		||||
            "extensions": kwargs['ext'],
 | 
			
		||||
            "glob": kwargs['glob'],
 | 
			
		||||
            'dry_run': kwargs['dry_run'],
 | 
			
		||||
            'extensions': kwargs['ext'],
 | 
			
		||||
            'glob': kwargs['glob'],
 | 
			
		||||
            'remove_duplicates': kwargs['remove_duplicates'],
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # os.path.join(
 | 
			
		||||
    # TODO make function to remove duplicates
 | 
			
		||||
    # path_format = collection.opt['Path']['path_format']
 | 
			
		||||
    # summary = collection.sort_files(
 | 
			
		||||
    #     paths, path_format, None, remove_duplicates=kwargs['remove_duplicates']
 | 
			
		||||
    # )
 | 
			
		||||
    # summary = collection.sort_files(paths, None)
 | 
			
		||||
 | 
			
		||||
    if kwargs['path_string']:
 | 
			
		||||
        dedup_regex = set(kwargs['dedup_regex'])
 | 
			
		||||
        collection.dedup_path(
 | 
			
		||||
            paths, dedup_regex, kwargs['remove_duplicates']
 | 
			
		||||
        )
 | 
			
		||||
        collection.dedup_path(paths, dedup_regex)
 | 
			
		||||
 | 
			
		||||
    for path in paths:
 | 
			
		||||
        if folders:
 | 
			
		||||
@ -334,9 +327,10 @@ def _compare(**kwargs):
 | 
			
		||||
    collection = Collection(
 | 
			
		||||
        root,
 | 
			
		||||
        {
 | 
			
		||||
            "extensions": kwargs['ext'],
 | 
			
		||||
            "glob": kwargs['glob'],
 | 
			
		||||
            "dry_run": kwargs['dry_run'],
 | 
			
		||||
            'extensions': kwargs['ext'],
 | 
			
		||||
            'glob': kwargs['glob'],
 | 
			
		||||
            'dry_run': kwargs['dry_run'],
 | 
			
		||||
            'remove_duplicates': kwargs['remove_duplicates'],
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
@ -509,7 +503,7 @@ def _import(**kwargs):
 | 
			
		||||
            'dry_run': kwargs['dry_run'],
 | 
			
		||||
            'interactive': kwargs['interactive'],
 | 
			
		||||
            'path_format': kwargs['path_format'],
 | 
			
		||||
 | 
			
		||||
            'remove_duplicates': kwargs['remove_duplicates'],
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
@ -517,7 +511,7 @@ def _import(**kwargs):
 | 
			
		||||
        import_mode = 'copy'
 | 
			
		||||
    else:
 | 
			
		||||
        import_mode = 'move'
 | 
			
		||||
    summary = _cli_sort(collection, src_paths, import_mode, kwargs['remove_duplicates'])
 | 
			
		||||
    summary = _cli_sort(collection, src_paths, import_mode)
 | 
			
		||||
 | 
			
		||||
    if log_level < 30:
 | 
			
		||||
        summary.print()
 | 
			
		||||
@ -566,10 +560,11 @@ def _sort(**kwargs):
 | 
			
		||||
            'glob': kwargs['glob'],
 | 
			
		||||
            'dry_run': kwargs['dry_run'],
 | 
			
		||||
            'interactive': kwargs['interactive'],
 | 
			
		||||
            'remove_duplicates': kwargs['remove_duplicates'],
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    summary = _cli_sort(collection, paths, False, kwargs['remove_duplicates'])
 | 
			
		||||
    summary = _cli_sort(collection, paths, False)
 | 
			
		||||
 | 
			
		||||
    if kwargs['clean']:
 | 
			
		||||
        collection.remove_empty_folders(root)
 | 
			
		||||
 | 
			
		||||
@ -478,6 +478,7 @@ class SortMedias:
 | 
			
		||||
        db=None,
 | 
			
		||||
        dry_run=False,
 | 
			
		||||
        interactive=False,
 | 
			
		||||
        remove_duplicates=False,
 | 
			
		||||
    ):
 | 
			
		||||
 | 
			
		||||
        # Arguments
 | 
			
		||||
@ -490,6 +491,7 @@ class SortMedias:
 | 
			
		||||
        self.dry_run = dry_run
 | 
			
		||||
        self.interactive = interactive
 | 
			
		||||
        self.log = LOG.getChild(self.__class__.__name__)
 | 
			
		||||
        self.remove_duplicates = remove_duplicates
 | 
			
		||||
        self.summary = Summary(self.root)
 | 
			
		||||
 | 
			
		||||
        # Attributes
 | 
			
		||||
@ -601,7 +603,7 @@ class SortMedias:
 | 
			
		||||
                directory_path.mkdir(parents=True, exist_ok=True)
 | 
			
		||||
            self.log.info(f'Create {directory_path}')
 | 
			
		||||
 | 
			
		||||
    def check_conflicts(self, src_path, dest_path, remove_duplicates=False):
 | 
			
		||||
    def check_conflicts(self, src_path, dest_path):
 | 
			
		||||
        """
 | 
			
		||||
        Check if file can be copied or moved file to dest_path.
 | 
			
		||||
        """
 | 
			
		||||
@ -617,7 +619,7 @@ class SortMedias:
 | 
			
		||||
 | 
			
		||||
        if dest_path.is_file():
 | 
			
		||||
            self.log.info(f"File {dest_path} already exist")
 | 
			
		||||
            if remove_duplicates:
 | 
			
		||||
            if self.remove_duplicates:
 | 
			
		||||
                if filecmp.cmp(src_path, dest_path):
 | 
			
		||||
                    self.log.info(
 | 
			
		||||
                        "File in source and destination are identical. Duplicate will be ignored."
 | 
			
		||||
@ -634,13 +636,13 @@ class SortMedias:
 | 
			
		||||
 | 
			
		||||
        return 0
 | 
			
		||||
 | 
			
		||||
    def _solve_conflicts(self, conflicts, remove_duplicates):
 | 
			
		||||
    def _solve_conflicts(self, conflicts):
 | 
			
		||||
        unresolved_conflicts = []
 | 
			
		||||
        while conflicts != []:
 | 
			
		||||
            src_path, dest_path, metadata = conflicts.pop()
 | 
			
		||||
            # Check for conflict status again in case is has changed
 | 
			
		||||
 | 
			
		||||
            conflict = self.check_conflicts(src_path, dest_path, remove_duplicates)
 | 
			
		||||
            conflict = self.check_conflicts(src_path, dest_path)
 | 
			
		||||
 | 
			
		||||
            for i in range(1, 1000):
 | 
			
		||||
                if conflict != 1:
 | 
			
		||||
@ -653,7 +655,7 @@ class SortMedias:
 | 
			
		||||
                else:
 | 
			
		||||
                    stem = dest_path.stem
 | 
			
		||||
                dest_path = dest_path.parent / (stem + '_' + str(i) + suffix)
 | 
			
		||||
                conflict = self.check_conflicts(src_path, dest_path, remove_duplicates)
 | 
			
		||||
                conflict = self.check_conflicts(src_path, dest_path)
 | 
			
		||||
 | 
			
		||||
            if conflict == 1:
 | 
			
		||||
                # i = 100:
 | 
			
		||||
@ -664,7 +666,7 @@ class SortMedias:
 | 
			
		||||
 | 
			
		||||
            yield (src_path, dest_path, metadata), conflict
 | 
			
		||||
 | 
			
		||||
    def sort_medias(self, imp=False, remove_duplicates=False):
 | 
			
		||||
    def sort_medias(self, imp=False):
 | 
			
		||||
        """
 | 
			
		||||
        sort files and solve conflicts
 | 
			
		||||
        """
 | 
			
		||||
@ -675,7 +677,7 @@ class SortMedias:
 | 
			
		||||
        for src_path, metadata in self.medias.datas.items():
 | 
			
		||||
            dest_path = self.root / metadata['file_path']
 | 
			
		||||
 | 
			
		||||
            conflict = self.check_conflicts(src_path, dest_path, remove_duplicates)
 | 
			
		||||
            conflict = self.check_conflicts(src_path, dest_path)
 | 
			
		||||
 | 
			
		||||
            if not conflict:
 | 
			
		||||
                self.sort_file(
 | 
			
		||||
@ -693,9 +695,7 @@ class SortMedias:
 | 
			
		||||
                pass
 | 
			
		||||
 | 
			
		||||
        if conflicts != []:
 | 
			
		||||
            for files_data, conflict in self._solve_conflicts(
 | 
			
		||||
                conflicts, remove_duplicates
 | 
			
		||||
            ):
 | 
			
		||||
            for files_data, conflict in self._solve_conflicts(conflicts):
 | 
			
		||||
 | 
			
		||||
                src_path, dest_path, metadata = files_data
 | 
			
		||||
                if not conflict:
 | 
			
		||||
@ -728,13 +728,13 @@ class Collection(SortMedias):
 | 
			
		||||
        self.log = LOG.getChild(self.__class__.__name__)
 | 
			
		||||
 | 
			
		||||
        # Get config options
 | 
			
		||||
        self.opt = self.get_config_options()
 | 
			
		||||
        self.opt, default_options = self.get_config_options()
 | 
			
		||||
 | 
			
		||||
        # Set client options
 | 
			
		||||
        for option, value in cli_options.items():
 | 
			
		||||
            if value not in (None, set()):
 | 
			
		||||
            for section in self.opt:
 | 
			
		||||
                if option in self.opt[section]:
 | 
			
		||||
                    if value != default_options[section][option]:
 | 
			
		||||
                        if option == 'exclude':
 | 
			
		||||
                            self.opt[section][option].union(set(value))
 | 
			
		||||
                        elif option in ('ignore_tags', 'extensions'):
 | 
			
		||||
@ -773,6 +773,7 @@ class Collection(SortMedias):
 | 
			
		||||
            self.db,
 | 
			
		||||
            self.opt['Terminal']['dry_run'],
 | 
			
		||||
            self.opt['Terminal']['interactive'],
 | 
			
		||||
            self.opt['Filters']['remove_duplicates'],
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # Attributes
 | 
			
		||||
@ -793,7 +794,7 @@ class Collection(SortMedias):
 | 
			
		||||
        """Get collection config"""
 | 
			
		||||
        config = Config(self.root.joinpath('.ordigi', 'ordigi.conf'))
 | 
			
		||||
 | 
			
		||||
        return config.get_config_options()
 | 
			
		||||
        return config.get_config_options(), config.get_default_options()
 | 
			
		||||
 | 
			
		||||
    def _set_option(self, section, option, cli_option):
 | 
			
		||||
        """if client option is set overwrite collection option value"""
 | 
			
		||||
@ -1004,17 +1005,15 @@ class Collection(SortMedias):
 | 
			
		||||
 | 
			
		||||
        return self.summary
 | 
			
		||||
 | 
			
		||||
    def sort_files(
 | 
			
		||||
        self, src_dirs, path_format, loc, imp=False, remove_duplicates=False
 | 
			
		||||
    ):
 | 
			
		||||
    def sort_files(self, src_dirs, loc, imp=False):
 | 
			
		||||
        """
 | 
			
		||||
        Sort files into appropriate folder
 | 
			
		||||
        """
 | 
			
		||||
        # Check db
 | 
			
		||||
        self._init_check_db(loc)
 | 
			
		||||
 | 
			
		||||
        # if path format client option is set overwrite it
 | 
			
		||||
        self._set_option('Path', 'path_format', path_format)
 | 
			
		||||
        path_format = self.opt['Path']['path_format']
 | 
			
		||||
        self.log.debug(f'path_format: {path_format}')
 | 
			
		||||
 | 
			
		||||
        # Get medias data
 | 
			
		||||
        subdirs = set()
 | 
			
		||||
@ -1028,7 +1027,7 @@ class Collection(SortMedias):
 | 
			
		||||
            self.medias.datas[src_path] = copy(metadata)
 | 
			
		||||
 | 
			
		||||
        # Sort files and solve conflicts
 | 
			
		||||
        self.summary = self.sort_medias(imp, remove_duplicates)
 | 
			
		||||
        self.summary = self.sort_medias(imp)
 | 
			
		||||
 | 
			
		||||
        if imp != 'copy':
 | 
			
		||||
            self.remove_empty_subdirs(subdirs, src_dirs)
 | 
			
		||||
@ -1038,7 +1037,7 @@ class Collection(SortMedias):
 | 
			
		||||
 | 
			
		||||
        return self.summary
 | 
			
		||||
 | 
			
		||||
    def dedup_path(self, paths, dedup_regex=None, remove_duplicates=False):
 | 
			
		||||
    def dedup_path(self, paths, dedup_regex=None):
 | 
			
		||||
        """Deduplicate file path parts"""
 | 
			
		||||
 | 
			
		||||
        # Check db
 | 
			
		||||
@ -1079,7 +1078,7 @@ class Collection(SortMedias):
 | 
			
		||||
            self.medias.datas[src_path] = copy(metadata)
 | 
			
		||||
 | 
			
		||||
        # Sort files and solve conflicts
 | 
			
		||||
        self.sort_medias(remove_duplicates=remove_duplicates)
 | 
			
		||||
        self.sort_medias()
 | 
			
		||||
 | 
			
		||||
        if not self.check_db():
 | 
			
		||||
            self.summary.append('check', False)
 | 
			
		||||
@ -1111,7 +1110,7 @@ class Collection(SortMedias):
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def sort_similar_images(self, path, similarity=80, remove_duplicates=False):
 | 
			
		||||
    def sort_similar_images(self, path, similarity=80):
 | 
			
		||||
        """Sort similar images using imagehash library"""
 | 
			
		||||
        # Check db
 | 
			
		||||
        self._init_check_db()
 | 
			
		||||
@ -1130,7 +1129,7 @@ class Collection(SortMedias):
 | 
			
		||||
            )
 | 
			
		||||
            if similar_images:
 | 
			
		||||
                # Move the simlars file into the destination directory
 | 
			
		||||
                self.sort_medias(remove_duplicates=remove_duplicates)
 | 
			
		||||
                self.sort_medias()
 | 
			
		||||
 | 
			
		||||
        nb_row_end = self.db.sqlite.len('metadata')
 | 
			
		||||
        if nb_row_ini and nb_row_ini != nb_row_end:
 | 
			
		||||
 | 
			
		||||
@ -53,9 +53,9 @@ class Config:
 | 
			
		||||
        else:
 | 
			
		||||
            self.conf = conf
 | 
			
		||||
 | 
			
		||||
        self.options = self.set_default_options()
 | 
			
		||||
        self.options = self.get_default_options()
 | 
			
		||||
 | 
			
		||||
    def set_default_options(self) -> dict:
 | 
			
		||||
    def get_default_options(self) -> dict:
 | 
			
		||||
        # Initialize with default options
 | 
			
		||||
        return {
 | 
			
		||||
            'Exif': {
 | 
			
		||||
@ -71,6 +71,7 @@ class Config:
 | 
			
		||||
                'extensions': None,
 | 
			
		||||
                'glob': '**/*',
 | 
			
		||||
                'max_deep': None,
 | 
			
		||||
                'remove_duplicates': False,
 | 
			
		||||
            },
 | 
			
		||||
            'Geolocation': {
 | 
			
		||||
                'geocoder': constants.DEFAULT_GEOCODER,
 | 
			
		||||
@ -137,6 +138,7 @@ class Config:
 | 
			
		||||
            'dry_run',
 | 
			
		||||
            'interactive',
 | 
			
		||||
            'prefer_english_names',
 | 
			
		||||
            'remove_duplicates',
 | 
			
		||||
            'use_date_filename',
 | 
			
		||||
            'use_file_dates',
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -206,7 +206,6 @@ class TestOrdigi:
 | 
			
		||||
            ('--exclude', '.DS_Store'),
 | 
			
		||||
            *self.filter_options,
 | 
			
		||||
            ('--path-format', '{%Y}/{folder}/{stem}.{ext}'),
 | 
			
		||||
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        paths = (str(self.src_path), str(tmp_path))
 | 
			
		||||
 | 
			
		||||
@ -137,11 +137,12 @@ class TestCollection:
 | 
			
		||||
        assert summary.success_table.sum('sort') == nb
 | 
			
		||||
 | 
			
		||||
    def test_sort_files(self, tmp_path):
 | 
			
		||||
        cli_options = {'album_from_folder': True, 'cache': False}
 | 
			
		||||
        cli_options = {
 | 
			
		||||
            'album_from_folder': True, 'cache': False, 'path_format': self.path_format
 | 
			
		||||
        }
 | 
			
		||||
        collection = Collection(tmp_path, cli_options=cli_options)
 | 
			
		||||
        loc = GeoLocation()
 | 
			
		||||
        summary = collection.sort_files([self.src_path],
 | 
			
		||||
                self.path_format, loc, imp='copy')
 | 
			
		||||
        summary = collection.sort_files([self.src_path], loc, imp='copy')
 | 
			
		||||
 | 
			
		||||
        self.assert_import(summary, 29)
 | 
			
		||||
 | 
			
		||||
@ -166,16 +167,16 @@ class TestCollection:
 | 
			
		||||
        collection = Collection(tmp_path, cli_options=cli_options)
 | 
			
		||||
        # Try to change path format and sort files again
 | 
			
		||||
        path_format = 'test_exif/<city>/<%Y>-<name>.%l<ext>'
 | 
			
		||||
        summary = collection.sort_files([tmp_path], path_format, loc)
 | 
			
		||||
        summary = collection.sort_files([tmp_path], loc)
 | 
			
		||||
 | 
			
		||||
        self.assert_sort(summary, 27)
 | 
			
		||||
        self.assert_sort(summary, 23)
 | 
			
		||||
 | 
			
		||||
        shutil.copytree(tmp_path / 'test_exif', tmp_path / 'test_exif_copy')
 | 
			
		||||
        collection.summary = Summary(tmp_path)
 | 
			
		||||
        assert collection.summary.success_table.sum() == 0
 | 
			
		||||
        summary = collection.update(loc)
 | 
			
		||||
        assert summary.success_table.sum('update') == 29
 | 
			
		||||
        assert summary.success_table.sum() == 29
 | 
			
		||||
        assert summary.success_table.sum('update') == 2
 | 
			
		||||
        assert summary.success_table.sum() == 2
 | 
			
		||||
        assert not summary.errors
 | 
			
		||||
        collection.summary = Summary(tmp_path)
 | 
			
		||||
        summary = collection.update(loc)
 | 
			
		||||
@ -195,12 +196,11 @@ class TestCollection:
 | 
			
		||||
        assert not summary.errors
 | 
			
		||||
 | 
			
		||||
    def test_sort_files_invalid_db(self, tmp_path):
 | 
			
		||||
        collection = Collection(tmp_path)
 | 
			
		||||
        collection = Collection(tmp_path, {'path_format': self.path_format})
 | 
			
		||||
        loc = GeoLocation()
 | 
			
		||||
        randomize_db(tmp_path)
 | 
			
		||||
        with pytest.raises(sqlite3.DatabaseError) as e:
 | 
			
		||||
            summary = collection.sort_files([self.src_path],
 | 
			
		||||
                    self.path_format, loc, imp='copy')
 | 
			
		||||
            summary = collection.sort_files([self.src_path], loc, imp='copy')
 | 
			
		||||
 | 
			
		||||
    def test_sort_file(self, tmp_path):
 | 
			
		||||
        for imp in ('copy', 'move', False):
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user