Beispiel #1
0
 def source(self, path):
     """Set the source directory"""
     if Path(path).is_dir():
         self._source = Path(path)
         self.source_files = FileList(self._source, exclude=self._exclude)
     else:
         logging.error(f'{path} is not a valid directory')
Beispiel #2
0
    def __init__(self,
                 source,
                 dest,
                 mode='copy',
                 structure=None,
                 filename=None,
                 prefix=None,
                 dryrun=False,
                 log_level='info'):
        super(Offloader, self).__init__()
        self.settings = Settings()
        self._logger = utils.setup_logger(log_level)
        self._today = datetime.now()
        self._source = Path(source)
        self._destination = Path(dest)
        # Default to settings if not given
        if structure:
            self._structure = structure
        else:
            self._structure = self.settings.structure

        if filename:
            self._filename = filename
        else:
            self._filename = self.settings.filename

        if prefix:
            self._prefix = prefix
        else:
            self._prefix = self.settings.prefix

        self._mode = mode
        self._dryrun = dryrun
        self._exclude = EXCLUDE_FILES
        self._signal = {
            'percentage': 0,
            'action': '',
            'time': '',
            'is_finished': False
        }
        self._running = True

        # Properties
        logging.info("Getting list of files")
        self.source_files = FileList(self._source, exclude=self._exclude)
        self.source_files.sort()

        # Offload attributes
        self.ol_time_started = 0
        self.ol_bytes_transferred = 0

        # Set some variables
        self.destination_folders = []
        self.skipped_files = []
        self.processed_files = []
        self.errored_files = []

        # Report
        self.report = Report()
Beispiel #3
0
 def setUp(self) -> None:
     self.test_source = Path("test_data/memoryCard").resolve()
     self.test_source.mkdir(exist_ok=True, parents=True)
     for i in range(100):
         f = Path(self.test_source / f"{i:04}.jpg")
         f.write_text(utils.random_string(randint(1, 4096)))
     self.test_destination = Path("test_data/test_destination").resolve()
     self.reporter = Report()
     self.source_files = FileList(self.test_source)
Beispiel #4
0
 def test_sort(self):
     test_list = FileList(self.test_directory)
     list_sorted = sorted(test_list.files, key=lambda f: f.mtime)
     test_list.sort()
     self.assertEqual(list_sorted, test_list.files)
Beispiel #5
0
 def test_update_total_size(self):
     test_list = FileList(self.test_directory)
     self.assertIsInstance(test_list.size, int)
Beispiel #6
0
 def test_get_file_list(self):
     test_list = FileList(self.test_directory)
     self.assertEqual(len(test_list.files), 100)
Beispiel #7
0
class Offloader(QThread):
    _progress_signal = pyqtSignal(dict)

    def __init__(self,
                 source,
                 dest,
                 mode='copy',
                 structure=None,
                 filename=None,
                 prefix=None,
                 dryrun=False,
                 log_level='info'):
        super(Offloader, self).__init__()
        self.settings = Settings()
        self._logger = utils.setup_logger(log_level)
        self._today = datetime.now()
        self._source = Path(source)
        self._destination = Path(dest)
        # Default to settings if not given
        if structure:
            self._structure = structure
        else:
            self._structure = self.settings.structure

        if filename:
            self._filename = filename
        else:
            self._filename = self.settings.filename

        if prefix:
            self._prefix = prefix
        else:
            self._prefix = self.settings.prefix

        self._mode = mode
        self._dryrun = dryrun
        self._exclude = EXCLUDE_FILES
        self._signal = {
            'percentage': 0,
            'action': '',
            'time': '',
            'is_finished': False
        }
        self._running = True

        # Properties
        logging.info("Getting list of files")
        self.source_files = FileList(self._source, exclude=self._exclude)
        self.source_files.sort()

        # Offload attributes
        self.ol_time_started = 0
        self.ol_bytes_transferred = 0

        # Set some variables
        self.destination_folders = []
        self.skipped_files = []
        self.processed_files = []
        self.errored_files = []

        # Report
        self.report = Report()

    def update_from_settings(self):
        """Update structure, filename and prefix from settings"""
        self._structure = self.settings.structure
        logging.debug(f'Folder structure preset is {self._structure}')

        self._filename = self.settings.filename
        logging.debug(f'Filename preset is {self._filename}')

        self._prefix = self.settings.prefix
        logging.debug(f'Filename prefix preset is {self._prefix}')

    @property
    def source(self):
        """Get the source directory"""
        return self._source

    @source.setter
    def source(self, path):
        """Set the source directory"""
        if Path(path).is_dir():
            self._source = Path(path)
            self.source_files = FileList(self._source, exclude=self._exclude)
        else:
            logging.error(f'{path} is not a valid directory')

    @property
    def destination(self):
        """Get the source directory"""
        return self._destination

    @destination.setter
    def destination(self, path):
        """Set the source directory"""
        self._destination = Path(path)

    @property
    def structure(self):
        """Get the folder structure preset"""
        return self._structure

    @structure.setter
    def structure(self, preset):
        """Set the folder structure preset"""
        self._structure = preset

    @property
    def ol_percentage(self):
        return round(
            (self.ol_bytes_transferred / self.source_files.size) * 100, 2)

    @property
    def ol_time_elapsed(self):
        return time.time() - self.ol_time_started

    @property
    def ol_bytes_remaining(self):
        return self.source_files.size - self.ol_bytes_transferred

    @property
    def ol_time_remaining(self):
        return self.ol_bytes_remaining / self.ol_speed if self.ol_speed else 0

    @property
    def ol_speed(self):
        return self.ol_bytes_transferred / self.ol_time_elapsed

    def offload(self):
        """Offload files"""
        # Offload start time
        self.ol_time_started = time.time()

        # Get list of files in source folder
        logging.info(f"Total file size: {self.source_files.hsize}")
        logging.info(
            f"Average file size: {utils.convert_size(self.source_files.avg_file_size)}"
        )
        logging.info("---\n")

        # Iterate over all the files
        for file_id, source_file in enumerate(self.source_files.files):
            skip = False

            # Display how far along the transfer we are
            logging.info(
                f"Processing file {file_id + 1}/{len(self.source_files.files)} "
                f"(~{self.ol_percentage}%) | {source_file.filename}")

            # Send signal to GUI
            self._signal['percentage'] = int(self.ol_percentage)
            self._signal[
                'action'] = f'Processing file {file_id + 1}/{len(self.source_files.files)}'
            self._signal['time'] = self.ol_time_remaining
            self._progress_signal.emit(self._signal)

            # Create File object for destination file
            dest_folder = self._destination / utils.destination_folder(
                source_file.mdate, preset=self._structure)
            dest_file = File(dest_folder / source_file.filename,
                             prefix=self._prefix)

            # Change filename
            if self._filename:
                logging.debug(f'New user given filename is {self._filename}')
                new_name = source_file.exifdata.get(
                    utils.Preset.filename(self._filename), "unknown").lower()
                logging.debug(new_name)
                dest_file.name = new_name

            # Add prefix to filename
            dest_file.set_prefix(self._prefix, custom_date=source_file.mdate)

            # Add destination folder to list of destination folders
            if dest_folder not in self.destination_folders:
                self.destination_folders.append(dest_folder)

            # Write to report
            if not self._running:
                self.report.write(source_file,
                                  dest_file,
                                  'Not started',
                                  checksum=False)
                continue

            # Print meta
            logging.info(f"File modification date: {source_file.mdate}")
            logging.info(f"Source path: {source_file.path}")
            logging.info(f"Destination path: {dest_file.path}")

            # Check for existing files and update filename
            while True:
                # Check if destination file exists
                if dest_file.is_file:
                    # Send signal to GUI
                    self._signal[
                        'action'] = f'Processing file {file_id + 1}/{len(self.source_files.files)} [verifying]'
                    self._progress_signal.emit(self._signal)

                    # Add increment
                    if dest_file.inc < 1:
                        logging.info(
                            "File with the same name exists in destination, comparing checksums"
                        )
                    else:
                        logging.debug(
                            f"File with incremented name {dest_file.filename} exists, comparing checksums"
                        )

                    # If checksums are matching
                    if utils.compare_checksums(source_file.checksum,
                                               dest_file.checksum):
                        logging.warning(
                            f"File ({dest_file.filename}) with matching checksums "
                            f"already exists in destination, skipping")
                        # Write to report
                        self.report.write(source_file, dest_file, 'Skipped')

                        self.skipped_files.append(source_file.path)

                        skip = True
                        break
                    else:
                        logging.warning(
                            f"File ({dest_file.filename}) with the same name already exists in destination,"
                            f" adding incremental")
                        dest_file.increment_filename()
                        logging.debug(
                            f'Incremented filename is {dest_file.filename}')
                        continue
                else:
                    break

            # Perform file actions
            if not skip:
                if source_file.path.is_file():
                    if self._dryrun:
                        logging.info(
                            "DRYRUN ENABLED, NOT PERFORMING FILE ACTIONS")
                    else:
                        # Create destination folder
                        dest_file.path.parent.mkdir(exist_ok=True,
                                                    parents=True)

                        # Send signal to GUI
                        self._signal[
                            'action'] = f'Processing file {file_id + 1}/{len(self.source_files.files)} [copying]'
                        self._progress_signal.emit(self._signal)

                        # Copy file
                        utils.pathlib_copy(source_file.path, dest_file.path)

                        # Send signal to GUI
                        self._signal[
                            'action'] = f'Processing file {file_id + 1}/{len(self.source_files.files)} [verifying]'
                        self._progress_signal.emit(self._signal)

                        # Verify file transfer
                        logging.info("Verifying transferred file")

                        # File transfer successful
                        if utils.compare_checksums(source_file.checksum,
                                                   dest_file.checksum):
                            logging.info("File transferred successfully")

                            # Write to report
                            self.report.write(source_file, dest_file,
                                              'Successful')

                            # Delete source file
                            if self._mode == "move":
                                source_file.delete()

                        # File transfer unsuccessful
                        else:
                            logging.error(
                                "File NOT transferred successfully, mismatching checksums"
                            )

                            # Write to report
                            self.report.write(source_file, dest_file, 'Failed')

                            self.errored_files.append({
                                source_file.path:
                                "Mismatching checksum after transfer"
                            })

            # Add file size to total
            self.ol_bytes_transferred += source_file.size

            # Add file to processed files
            self.processed_files.append(source_file.filename)

            # Calculate remaining time
            logging.info(
                f"Elapsed time: {utils.time_to_string(self.ol_time_elapsed)}")

            # Log transfer speed
            logging.info(
                f"Avg. transfer speed: {utils.convert_size(self.ol_speed)}/s")

            logging.info(
                f"Size remaining: {utils.convert_size(self.ol_bytes_remaining)}"
            )
            logging.info(f"Approx. time remaining: {self.ol_time_remaining}")
            logging.info("---\n")

        # Print created destination folders
        if self.destination_folders:
            # Sort folder for better output
            self.destination_folders.sort()

            logging.info(
                f"Created the following folders {', '.join([str(x.name) for x in self.destination_folders])}"
            )
            logging.debug([str(x.resolve()) for x in self.destination_folders])

        logging.info(f"{len(self.processed_files)} files processed")
        logging.debug(f"Processed files: {self.processed_files}")

        logging.info(f"{len(self.destination_folders)} destination folders")
        logging.debug(f"Destination folders: {self.destination_folders}")

        logging.info(f"{len(self.skipped_files)} files skipped")
        logging.debug(f"Skipped files: {self.skipped_files}")

        # Save report to desktop
        print(self._running)
        self.report.save()
        self.report.write_html()
        self._signal['time'] = 0
        self._signal['is_finished'] = True
        self._progress_signal.emit(self._signal)
        return True

    def run(self):
        logging.info('Hello')
        self.offload()