예제 #1
0
    def testConvertWildcardExprStrToRegexpStr(self):
        if os.name == 'posix':
            configFilePathName = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/transfiles.ini'
        else:
            configFilePathName = 'D:\\Development\\Python\\trans_file_cloud\\test\\transfiles.ini'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        wildchardLst = [
            '*Solemne*.mp3', 'test*.py', '/excldir/subdir/*.py',
            'd:\\excldir\\subdir\\*.py', '/excldir/subdir/*.*',
            'd:\\excldir\\subdir\\*.*'
        ]
        expectedRegexpLst = [
            '.*Solemne.*\.mp3\Z', 'test.*\.py\Z', '/excldir/subdir/.*\.py\Z',
            'd:\\\\excldir\\\\subdir\\\\.*\.py\Z', '/excldir/subdir/.*\..*\Z',
            'd:\\\\excldir\\\\subdir\\\\.*\..*\Z'
        ]

        for wildchardExpr, expectedRegexp in zip(wildchardLst,
                                                 expectedRegexpLst):
            self.assertEqual(
                expectedRegexp,
                fl.convertWildcardExprStrToRegexpStr(wildchardExpr))
예제 #2
0
    def testCreateRegexpPatternLstFromWildchardExprLst(self):
        if os.name == 'posix':
            configFilePathName = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/transfiles.ini'
        else:
            configFilePathName = 'D:\\Development\\Python\\trans_file_cloud\\test\\transfiles.ini'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        wildchardLst = [
            '*Solemne*.mp3', 'test*.py', '/excldir/subdir/*.py',
            'd:\\excldir\\subdir\\*.py', '/excldir/subdir/*.*',
            'd:\\excldir\\subdir\\*.*'
        ]
        expectedPatternLst = [
            re.compile('.*Solemne.*\.mp3\Z'),
            re.compile('test.*\.py\Z'),
            re.compile('/excldir/subdir/.*\.py\Z'),
            re.compile('d:\\\\excldir\\\\subdir\\\\.*\.py\Z'),
            re.compile('/excldir/subdir/.*\..*\Z'),
            re.compile('d:\\\\excldir\\\\subdir\\\\.*\..*\Z')
        ]

        self.assertEqual(
            expectedPatternLst,
            fl.createRegexpPatternLstFromWildchardExprLst(wildchardLst))
예제 #3
0
    def testGetFilesByOrderedTypes(self):
        if os.name == 'posix':
            configFilePathName = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/transfiles.ini'
        else:
            configFilePathName = 'D:\\Development\\Python\\trans_file_cloud\\test\\transfiles.ini'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)
        cloudFileLst = [
            'aa_current.jpg', 'constants_2.py', 'current_state_21.jpg',
            'current_state_22.jpg', 'doc_21.docx', 'doc_22.docx',
            'filelister_2.py', 'filemover_2.py', 'README_2.md',
            'testfilelister_2.py', 'testfilemover_2.py'
        ]

        orderedFileTypeWildchardExprLst, fileTypeDic = fl.getFilesByOrderedTypes(
            'transFileCloudTestProject', cloudFileLst=cloudFileLst)

        self.assertEqual([
            'aa*.jpg', '*Solemne*.mp3', 'test*.py', '*.mp3', '*.jpg', '*.docx',
            '*.py', '*.md'
        ], orderedFileTypeWildchardExprLst)

        if os.name == 'posix':
            self.assertEqual(
                {
                    '*.jpg':
                    ('/images',
                     ['current_state_21.jpg', 'current_state_22.jpg']),
                    '*.docx': ('/doc', ['doc_21.docx', 'doc_22.docx']),
                    '*.md': ('', ['README_2.md']),
                    '*.mp3': ('/mp3', []),
                    'test*.py':
                    ('/test', ['testfilelister_2.py', 'testfilemover_2.py']),
                    '*.py':
                    ('',
                     ['constants_2.py', 'filelister_2.py', 'filemover_2.py']),
                    '*Solemne*.mp3': ('/mp3/solemne', []),
                    'aa*.jpg': ('/images/aa', ['aa_current.jpg'])
                }, fileTypeDic)
        else:
            self.assertEqual(
                {
                    '*.jpg':
                    ('\\images',
                     ['current_state_21.jpg', 'current_state_22.jpg']),
                    '*.docx': ('\\doc', ['doc_21.docx', 'doc_22.docx']),
                    '*.md': ('', ['README_2.md']),
                    '*.mp3': ('\\mp3', []),
                    'test*.py':
                    ('\\test', ['testfilelister_2.py', 'testfilemover_2.py']),
                    '*.py':
                    ('',
                     ['constants_2.py', 'filelister_2.py', 'filemover_2.py']),
                    '*Solemne*.mp3': ('\\mp3\\solemne', []),
                    'aa*.jpg': ('\\images\\aa', ['aa_current.jpg'])
                }, fileTypeDic)
	def __init__(self, configManager, projectName):
		"""
		FileMover constructor.
		
		@param configManager: ConfigManager giving access to the local configuration
							  file data
		@param projectName: project name as defined in the local configuration
							file
		"""
		self.projectName = projectName
		self.downloadDir = configManager.downloadPath
		self.projectDir = configManager.getProjectLocalDir(projectName)
		self.fileNameLister = FileLister(configManager)
예제 #5
0
    def testGetModifiedAndNotExcludedFileLst(self):
        if os.name == 'posix':
            configFilePathName = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/transfiles.ini'
            projectDir = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir'
        else:
            configFilePathName = 'D:\\Development\\Python\\trans_file_cloud\\test\\transfiles.ini'
            projectDir = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        expectedAllFileNameLst = [
            'constants_2.py', 'filelister_2.py', 'filemover_2.py',
            'README_2.md', 'testfilelister_2.py', 'testfilemover_2.py'
        ]

        if os.name == 'posix':
            expectedAllFilePathNameLst = [
                '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/constants_2.py',
                '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/filelister_2.py',
                '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/filemover_2.py',
                '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/README_2.md',
                '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/test/testfilelister_2.py',
                '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/test/testfilemover_2.py'
            ]
        else:
            expectedAllFilePathNameLst = [
                'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\constants_2.py',
                'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\filelister_2.py',
                'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\filemover_2.py',
                'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\README_2.md',
                'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\test\\testfilelister_2.py',
                'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\test\\testfilemover_2.py'
            ]

        excludedDirLst = []
        excludedFileTypeWildchardLst = ['*.ini', '*.tmp', '*.jpg', '*.docx']

        excludedFileTypePatternLst = fl.createRegexpPatternLstFromWildchardExprLst(
            excludedFileTypeWildchardLst)
        lastSyncTime = datetime.datetime.strptime(
            '15/06/2020 08:45:23', DATE_TIME_FORMAT_CONFIG_FILE)

        actualAllFileNameLst, actualAllFilePathNameLst = fl.getModifiedAndNotExcludedFileLst(
            projectDir, lastSyncTime, excludedDirLst,
            excludedFileTypePatternLst)
        self.assertEqual(sorted(expectedAllFileNameLst),
                         sorted(actualAllFileNameLst))
        self.assertEqual(sorted(expectedAllFilePathNameLst),
                         sorted(actualAllFilePathNameLst))
예제 #6
0
    def testSortDirTupleListUsingComputeMoveOrder_empty_root_dir(self):
        if os.name == 'posix':
            configFilePathName = '/sdcard/transfiles.ini'
            filePatternDirDic = {
                'test*.py': '/test',
                '*.py': '',
                '*.rd': '',
                '*.docx': '/doc',
                '*.jpg': '/images',
                'aacc*.mp3': '/aaccmp3',
                'cc*.mp3': '/ccmp3',
                '*.mp3': '/mp3',
                'aa*.mp3': '/aamp3',
                'bb*.mp3': '/bbmp3',
                ' *Rimsky-Korsakov*.mp3': '/mp3/Rimsky Korsakov'
            }
        else:
            configFilePathName = 'c:\\temp\\transfiles.ini'
            filePatternDirDic = {
                'test*.py': '\\test',
                '*.py': '',
                '*.rd': '',
                '*.docx': '\\doc',
                '*.jpg': '\\images',
                'aacc*.mp3': '\\aaccmp3',
                'cc*.mp3': '\\ccmp3',
                '*.mp3': '\\mp3',
                'aa*.mp3': '\\aamp3',
                'bb*.mp3': '\\bbmp3',
                ' *Rimsky-Korsakov*.mp3': '\\mp3\\Rimsky Korsakov'
            }

        filePatternDirTupleLst = [item for item in filePatternDirDic.items()]

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        if os.name == 'posix':
            self.assertEqual(
                [(' *Rimsky-Korsakov*.mp3', '/mp3/Rimsky Korsakov'),
                 ('test*.py', '/test'), ('cc*.mp3', '/ccmp3'),
                 ('bb*.mp3', '/bbmp3'), ('aacc*.mp3', '/aaccmp3'),
                 ('aa*.mp3', '/aamp3'), ('*.mp3', '/mp3'),
                 ('*.jpg', '/images'), ('*.docx', '/doc'), ('*.rd', ''),
                 ('*.py', '')],
                sorted(filePatternDirTupleLst,
                       key=functools.cmp_to_key(fl.computeMoveOrder)))
        else:
            self.assertEqual(
                [(' *Rimsky-Korsakov*.mp3', '\\mp3\\Rimsky Korsakov'),
                 ('test*.py', '\\test'), ('cc*.mp3', '\\ccmp3'),
                 ('bb*.mp3', '\\bbmp3'), ('aacc*.mp3', '\\aaccmp3'),
                 ('aa*.mp3', '\\aamp3'), ('*.mp3', '\\mp3'),
                 ('*.jpg', '\\images'), ('*.docx', '\\doc'), ('*.rd', ''),
                 ('*.py', '')],
                sorted(filePatternDirTupleLst,
                       key=functools.cmp_to_key(fl.computeMoveOrder)))
    def initTransferFileOnProject(self, configFilePath=None, projectName=None):
        """
		This method enables the transfer files utility to be executed in a loop.
		It initializes the TransferFile class with the data specific to the
		project selected by the user.
		
	    @param configFilePath: used for unit testing only
	    @param projectName used for unit testing only
	    
		@return: True if the user selects another project, False otherwise
		"""
        if projectName == None:
            # we are not unit testing ...
            projectName = self.requester.getProjectName(commandLineArgs=None)

            if projectName == None:
                # user did choose Quit
                self.projectName = None
                return False

        self.projectName = projectName
        self.localProjectDir = None

        try:
            self.localProjectDir = self.configManager.getProjectLocalDir(
                self.projectName)
        except KeyError as e:
            # this happens only when TransferFiles is launched from the command line
            # with an invalid project name passed as -p command line parm
            print(
                '\nProject {} not defined in configuration file {}. Program closed.\n'
                .format(str(e), configFilePath))
            self.projectName = None
            return False

        # currently, only Dropbox as cloud space is implemented
        self.cloudAccess = DropboxAccess(self.configManager, self.projectName)
        self.fileLister = FileLister(self.configManager)

        return True
예제 #8
0
    def testSortFilePatternDirTupleLst_2_items_one_start_With_star(self):
        if os.name == 'posix':
            configFilePathName = '/sdcard/transfiles.ini'
        else:
            configFilePathName = 'c:\\temp\\transfiles.ini'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        if os.name == 'posix':
            filePatternDirTupleLst = [('*.mp3', ''),
                                      ('*Rimsky-Korsakov*.mp3', '/rim')]
            self.assertEqual(
                [('*Rimsky-Korsakov*.mp3', '/rim'), ('*.mp3', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))

            filePatternDirTupleLst = [('*Rimsky-Korsakov*.mp3', '/rim'),
                                      ('*.mp3', '')]
            self.assertEqual(
                [('*Rimsky-Korsakov*.mp3', '/rim'), ('*.mp3', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))
        else:
            filePatternDirTupleLst = [('*.mp3', ''),
                                      ('*Rimsky-Korsakov*.mp3', '\\rim')]
            self.assertEqual(
                [('*Rimsky-Korsakov*.mp3', '\\rim'), ('*.mp3', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))

            filePatternDirTupleLst = [('*Rimsky-Korsakov*.mp3', '\\rim'),
                                      ('*.mp3', '')]
            self.assertEqual(
                [('*Rimsky-Korsakov*.mp3', '\\rim'), ('*.mp3', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))
예제 #9
0
    def testSortFilePatternDirTupleLst_2_items(self):
        if os.name == 'posix':
            configFilePathName = '/sdcard/transfiles.ini'
        else:
            configFilePathName = 'c:\\temp\\transfiles.ini'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        if os.name == 'posix':
            filePatternDirTupleLst = [('*.py', ''), ('test*.py', '/test')]
            self.assertEqual(
                [('test*.py', '/test'), ('*.py', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))

            filePatternDirTupleLst = [('test*.py', '/test'), ('*.py', '')]
            self.assertEqual(
                [('test*.py', '/test'), ('*.py', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))
        else:
            filePatternDirTupleLst = [('*.py', ''), ('test*.py', '\\test')]
            self.assertEqual(
                [('test*.py', '\\test'), ('*.py', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))

            filePatternDirTupleLst = [('test*.py', '\\test'), ('*.py', '')]
            self.assertEqual(
                [('test*.py', '\\test'), ('*.py', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))
예제 #10
0
    def testSortFilePatternDirTupleLst_n_items_one_start_With_star(self):
        if os.name == 'posix':
            configFilePathName = '/sdcard/transfiles.ini'
        else:
            configFilePathName = 'c:\\temp\\transfiles.ini'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        if os.name == 'posix':
            filePatternDirTupleLst = [('test*.py', '/test'), ('*.py', ''),
                                      ('*.md', ''), ('*.docx', '/doc'),
                                      ('*.jpg', '/images'),
                                      ('sub*.jpg', '/images/sub'),
                                      ('*.mp3', '/mp3'),
                                      ('*Rimsky-Korsakov*.mp3',
                                       '/mp3/Rimsky-Korsakov')]
            self.assertEqual(
                [('sub*.jpg', '/images/sub'),
                 ('*Rimsky-Korsakov*.mp3', '/mp3/Rimsky-Korsakov'),
                 ('test*.py', '/test'), ('*.mp3', '/mp3'),
                 ('*.jpg', '/images'), ('*.docx', '/doc'), ('*.py', ''),
                 ('*.md', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))
        else:
            filePatternDirTupleLst = [('test*.py', '\\test'), ('*.py', ''),
                                      ('*.md', ''), ('*.docx', '\\doc'),
                                      ('*.jpg', '\\images'),
                                      ('sub*.jpg', '\\images\\sub'),
                                      ('*.mp3', '\\mp3'),
                                      ('*Rimsky-Korsakov*.mp3',
                                       '\\mp3\\Rimsky-Korsakov')]
            self.assertEqual(
                [('sub*.jpg', '\\images\\sub'),
                 ('*Rimsky-Korsakov*.mp3', '\\mp3\\Rimsky-Korsakov'),
                 ('test*.py', '\\test'), ('*.mp3', '\\mp3'),
                 ('*.jpg', '\\images'), ('*.docx', '\\doc'), ('*.py', ''),
                 ('*.md', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))
예제 #11
0
    def testExcludeFile(self):
        if os.name == 'posix':
            configFilePathName = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/transfiles.ini'
        else:
            configFilePathName = 'D:\\Development\\Python\\trans_file_cloud\\test\\transfiles.ini'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        excludedFileSpecLst = [
            '*.ini', '*.temp', 'help*.*', 'modified*', '*.pyc'
        ]
        excludedPatternLst = fl.createRegexpPatternLstFromWildchardExprLst(
            excludedFileSpecLst)

        self.assertTrue(fl.excludeFile('transfiles.ini', excludedPatternLst))
        self.assertFalse(fl.excludeFile('transfiles.py', excludedPatternLst))
        self.assertTrue(fl.excludeFile('transfiles.temp', excludedPatternLst))
        self.assertFalse(fl.excludeFile('transfiles.tmp', excludedPatternLst))
        self.assertTrue(fl.excludeFile('helpMe.txt', excludedPatternLst))
        self.assertTrue(fl.excludeFile('modified_no_type', excludedPatternLst))
        self.assertTrue(fl.excludeFile('transfiles.pyc', excludedPatternLst))
예제 #12
0
    def testGetModifiedFileLst(self):
        if os.name == 'posix':
            configFilePathName = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/transfiles.ini'
        else:
            configFilePathName = 'D:\\Development\\Python\\trans_file_cloud\\test\\transfiles.ini'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)
        allFileNameLst, allFilePathNameLst, lastSyncTimeStr = fl.getModifiedFileLst(
            'transFileCloudTestProject')

        self.assertEqual(
            sorted([
                'constants_2.py', 'filelister_2.py', 'filemover_2.py',
                'testfilelister_2.py', 'testfilemover_2.py', 'README_2.md'
            ]), sorted(allFileNameLst))
        if os.name == 'posix':
            self.assertEqual(
                sorted([
                    '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/constants_2.py',
                    '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/filelister_2.py',
                    '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/filemover_2.py',
                    '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/test/testfilelister_2.py',
                    '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/test/testfilemover_2.py',
                    '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_3/projectdir/README_2.md'
                ]), sorted(allFilePathNameLst))
        else:
            self.assertEqual(
                sorted([
                    'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\constants_2.py',
                    'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\filelister_2.py',
                    'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\filemover_2.py',
                    'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\test\\testfilelister_2.py',
                    'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\test\\testfilemover_2.py',
                    'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_3\\projectdir\\README_2.md'
                ]), sorted(allFilePathNameLst))

        self.assertEqual('15/06/2020 08:45:23', lastSyncTimeStr)
예제 #13
0
class FileMover:
    def __init__(self):
        self.fileLister = FileLister()

    def moveFiles(self):
        for fileName in self.fileLister.allTestPythonFileNameLst:
            file = DIR_SEP + fileName
            shutil.move(SRC_DIR + file, TEST_FILE_DST + file)
            print('moving {} to {}'.format(SRC_DIR + file,
                                           TEST_FILE_DST + file))

        self.fileLister.removeTestFilesFromPythonFiles()

        for fileName in self.fileLister.allPythonFileNameLst:
            file = DIR_SEP + fileName
            shutil.move(SRC_DIR + file, PYTHON_FILE_DST + file)
            print('moving {} to {}'.format(SRC_DIR + file,
                                           PYTHON_FILE_DST + file))

        for fileName in self.fileLister.allImageFileNameLst:
            file = DIR_SEP + fileName
            shutil.move(SRC_DIR + file, IMG_FILE_DST + file)
            print('moving {} to {}'.format(SRC_DIR + file,
                                           IMG_FILE_DST + file))

        for fileName in self.fileLister.allDocFileNameLst:
            file = DIR_SEP + fileName
            shutil.move(SRC_DIR + file, DOC_FILE_DST + file)
            print('moving {} to {}'.format(SRC_DIR + file,
                                           DOC_FILE_DST + file))

        for fileName in self.fileLister.allReadmeFileNameLst:
            file = DIR_SEP + fileName
            shutil.move(SRC_DIR + file, PYTHON_FILE_DST + file)
            print('moving {} to {}'.format(SRC_DIR + file,
                                           PYTHON_FILE_DST + file))
예제 #14
0
    def testGetModifiedFileLst_invalid_local_dir(self):
        '''
		Tests that the getModifiedFileLst() method raises a NotADirectoryError
		if the project path specified in the transfiles.ini does not exist.
		'''
        if os.name == 'posix':
            configFilePathName = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/transfiles.ini'
        else:
            configFilePathName = 'D:\\Development\\Python\\trans_file_cloud\\test\\transfiles.ini'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        # project name which has an invalid (not existing) project path in the
        # transfiles.ini file
        invalidProjectName = 'transFileCloudInvalidProject'
        self.assertRaises(NotADirectoryError, fl.getModifiedFileLst,
                          invalidProjectName)
예제 #15
0
    def testSortFilePatternDirTupleLst_n_items(self):
        if os.name == 'posix':
            configFilePathName = '/sdcard/transfiles.ini'
        else:
            configFilePathName = 'c:\\temp\\transfiles.ini'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        if os.name == 'posix':
            filePatternDirTupleLst = [('*.py', ''), ('test*.py', '/test'),
                                      ('*.jpg', '/images'),
                                      ('sub*.jpg', '/images/sub'),
                                      ('*.docx', '/doc')]
            self.assertEqual(
                [('sub*.jpg', '/images/sub'), ('test*.py', '/test'),
                 ('*.jpg', '/images'), ('*.docx', '/doc'), ('*.py', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))

            filePatternDirTupleLst = [('sub*.jpg', '/images/sub'),
                                      ('*.jpg', '/images'), ('*.docx', '/doc'),
                                      ('aa*.docx', '/doc/aa_sub_dir'),
                                      ('test*.py', '/test'), ('*.py', '')]
            self.assertEqual(
                [('sub*.jpg', '/images/sub'), ('aa*.docx', '/doc/aa_sub_dir'),
                 ('test*.py', '/test'), ('*.jpg', '/images'),
                 ('*.docx', '/doc'), ('*.py', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))
        else:
            filePatternDirTupleLst = [('*.py', ''), ('test*.py', '\\test'),
                                      ('*.jpg', '\\images'),
                                      ('sub*.jpg', '\\images\\sub'),
                                      ('*.docx', '\\doc')]
            self.assertEqual(
                [('sub*.jpg', '\\images\\sub'), ('test*.py', '\\test'),
                 ('*.jpg', '\\images'), ('*.docx', '\\doc'), ('*.py', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))

            filePatternDirTupleLst = [('sub*.jpg', '\\images\\sub'),
                                      ('*.jpg', '\\images'),
                                      ('*.docx', '\\doc'),
                                      ('aa*.docx', '\\doc\\aa_sub_dir'),
                                      ('test*.py', '\\test'), ('*.py', '')]
            self.assertEqual(
                [('sub*.jpg', '\\images\\sub'),
                 ('aa*.docx', '\\doc\\aa_sub_dir'), ('test*.py', '\\test'),
                 ('*.jpg', '\\images'), ('*.docx', '\\doc'), ('*.py', '')],
                fl.sortFilePatternDirTupleLst(filePatternDirTupleLst))
예제 #16
0
    def testIsRootAsDirOrSubDirInExcludedDirLst(self):
        if os.name == 'posix':
            configFilePathName = '/sdcard/transfiles.ini'
            excludedDirLst = [
                '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_1/fromdir',
                '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_1/fromdir_saved'
            ]

            subDir1 = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_1/fromdir/fromSubDir'
            subDir2 = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_1/fromdir_saved/fromSubDir'
            subDir3 = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_2/fromdir_saved/fromSubDir'
            dir1 = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_1/fromdir'
            dir2 = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_2'
        else:
            configFilePathName = 'c:\\temp\\transfiles.ini'
            excludedDirLst = [
                'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_1\\fromdir',
                'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_1\\fromdir_saved'
            ]

            subDir1 = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_1\\fromdir\\fromSubDir'
            subDir2 = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_1\\fromdir_saved\\fromSubDir'
            subDir3 = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_2\\fromdir_saved\\fromSubDir'
            dir1 = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_1\\fromdir'
            dir2 = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_2'

        cm = ConfigManager(configFilePathName)
        fl = FileLister(cm)

        self.assertTrue(
            fl.isRootAsDirOrSubDirInExcludedDirLst(subDir1, excludedDirLst))
        self.assertTrue(
            fl.isRootAsDirOrSubDirInExcludedDirLst(subDir2, excludedDirLst))
        self.assertTrue(
            fl.isRootAsDirOrSubDirInExcludedDirLst(dir1, excludedDirLst))
        self.assertFalse(
            fl.isRootAsDirOrSubDirInExcludedDirLst(subDir3, excludedDirLst))
        self.assertFalse(
            fl.isRootAsDirOrSubDirInExcludedDirLst(dir2, excludedDirLst))
class TransferFiles:
    """
	This is the main class of the TransferFiles utility.
	
	The TransferFiles utility is used to transfer files from one device to 
	another device using the cloud as intermediary location. What makes 
	TransferFiles unique is that the directory structures on both source and
	target devices can be different. A local configuration file (transfiles.ini)
	stores the information required for the transfer to be done correctly.
	"""
    def __init__(self, configFilePath=None, projectName=None):
        """
			TransferFiles constructor.

		    @param configFilePath: used for unit testing only
		    @param projectName used for unit testing only
		"""
        if configFilePath == None:
            # we are not unit testing ...
            configFilePath = CONFIG_FILE_PATH_NAME

        self.configManager = ConfigManager(configFilePath)
        self.requester = Requester(self.configManager)

        self.initTransferFileOnProject(configFilePath, projectName)

    def initTransferFileOnProject(self, configFilePath=None, projectName=None):
        """
		This method enables the transfer files utility to be executed in a loop.
		It initializes the TransferFile class with the data specific to the
		project selected by the user.
		
	    @param configFilePath: used for unit testing only
	    @param projectName used for unit testing only
	    
		@return: True if the user selects another project, False otherwise
		"""
        if projectName == None:
            # we are not unit testing ...
            projectName = self.requester.getProjectName(commandLineArgs=None)

            if projectName == None:
                # user did choose Quit
                self.projectName = None
                return False

        self.projectName = projectName
        self.localProjectDir = None

        try:
            self.localProjectDir = self.configManager.getProjectLocalDir(
                self.projectName)
        except KeyError as e:
            # this happens only when TransferFiles is launched from the command line
            # with an invalid project name passed as -p command line parm
            print(
                '\nProject {} not defined in configuration file {}. Program closed.\n'
                .format(str(e), configFilePath))
            self.projectName = None
            return False

        # currently, only Dropbox as cloud space is implemented
        self.cloudAccess = DropboxAccess(self.configManager, self.projectName)
        self.fileLister = FileLister(self.configManager)

        return True

    def transferFiles(self):
        """
		This is the main TransferFiles method. Depending on the content of the
		cloud space for the current project, the user is prompted for uploading
		files modified locally or for downloading and transferring to the right
		local dirs of the files contained on the cloud.
		"""
        if self.projectName == None:
            # user did choose Quit
            return

        cloudFileLst = []

        try:
            if self.configManager.isProjectSubDirSynchronized(
                    self.projectName):
                cloudFileLst = self.cloudAccess.getCloudFilePathNameList()
            else:
                cloudFileLst = self.cloudAccess.getCloudFileNameList()
        except NotADirectoryError as e:
            # means that the cloud project directory does not yet exist
            if self.requester.getCreateCloudFolderConfirmation(
                    self.projectName):
                self.cloudAccess.createProjectFolder()
            else:
                return

        if cloudFileLst == []:
            # if the cloud directory is empty, this means that we are in the state of uploading
            # to the cloud the files modified on the current device so that they will be available
            # to be transferred from the cloud to the local directories on the other device.
            self.uploadModifiedFilesToCloud()
        else:
            # if the cloud directory contains files, this means that we are in the state of transferring
            # those files to the current device. The transferred files will be downloaded to the
            # local download dir and deleted from the cloud. They will then be moved from the download
            # dir to the correct project dir and sub dirs.
            self.transferFilesFromCloudToLocalDirs(cloudFileLst)

        return self.initTransferFileOnProject()

    def transferFilesFromCloudToLocalDirs(self, cloudFileLst):
        """
		This method first ask the user to confirm the download and transfer of
		the files available on the cloud. If the user refuses the download, he
		will be asked if he wants instead to upload files modified locally
		provided that local files exist whose modification date is after
		the last update date stored in the local configuration file.
		
		If the user confirms the download, the cloud files are downloaded to
		the local download dir and then deleted from the cloud. Then, the
		files are moved from the download dir to the correct project dir and
		sub-dirs, using the information specified in the download section of
		the project in the local configuration file.
		
		Finally, the last synch date is updated to now in the local configuration
		file.
		
		But the user may be nn the situation where he uploaded some modified files
		and then modified some uploaded files again or changed new files. In this
		case, he does not accept the download and will then be prompted for
		uploading the new modified files.
		
		@param cloudFileLst: contains the list of file names for the files
							 available on the cloud
		"""
        localProjectDirShort = sep.join(self.localProjectDir.split(sep)[-3:])

        questionStr = 'vvv {} files will be transferred from the cloud and then moved to the correct dir and sub-dir of {}.\nIf you want to upload new modified files instead, type N, or Enter to select another project'.format(
            len(cloudFileLst), localProjectDirShort)
        doDownload, doKeepFileOnCloud = self.requester.getUserConfirmation(
            questionStr, cloudFileLst)

        if doDownload is None:
            # user typed Enter, which means he does not want to download the files
            # and wants to select another project or quit.
            return

        if doDownload:
            if self.configManager.isProjectSubDirSynchronized(
                    self.projectName):
                # downloading the files from the cloud keeping their path component
                # directly to their final destination dir, the local project dir

                print('')  # empty line
                self.downloadAndDeleteFilesFromCloud(
                    downloadPath=self.localProjectDir,
                    cloudFileLst=cloudFileLst,
                    targetName='directly to the project',
                    doKeepFileOnCloud=doKeepFileOnCloud)
                print('')  # empty line
            else:
                # downloading the files which have no path component from the cloud
                # to the download path and then moving them from the download path
                # according to the sub dir parameters defined in the configuration
                # file for the project

                print('')  # empty line
                self.downloadAndDeleteFilesFromCloud(
                    downloadPath=self.configManager.downloadPath,
                    cloudFileLst=cloudFileLst,
                    targetName='to download',
                    doKeepFileOnCloud=doKeepFileOnCloud)
                print('')  # empty line

                # moving file from download dir to project dest dir and sub-dirs
                # according to the sub dir parameters defined in the configuration
                # file for the project

                fileMover = FileMover(self.configManager, self.projectName)
                fileMover.moveFilesToLocalDirs(cloudFileLst)

            # updating last synch time for the project in the local config file
            self.updateLastSynchTime()
        else:
            # lists modified local files and asks if they should be uploaded. Handles
            # the case where you did an upload and then modified files again on the
            # same device and want to add those files to the cloud
            self.uploadModifiedFilesToCloud()

    def uploadModifiedFilesToCloud(self):
        """
		This method first obtain the list of local files whose modification date
		is after the last update date stored in the local configuration file.
		
		If this list is not empty, an upload confirmation is asked to the user.
		
		At this stage, the user has several choices: he can confirm the upload,
		he can ask to display a more detailed list showing the path of the
		modified files or he can decide to update the the last update date stored
		in the local configuration file so that the next time the program is
		executed, the list of modified files will be different.
		
		If the user confirms the upload, the modified files are uploaded to the
		cloud in the cloud dir specific to the project (having the same name
		than the project name) and the last update date stored in the local
		configuration file is set to now.
		
		In case no files were modified locally, we give the user the possibility
		to modify the last synch time. This can be useful if the user wants to
		move backward the last synch time in order to upload local files he
		modified during the last hours ...
		"""
        updatedFileNameLst, updatedFilePathNameLst, lastSyncTimeStr = self.fileLister.getModifiedFileLst(
            self.projectName)

        if updatedFileNameLst != []:
            if self.configManager.isProjectSubDirSynchronized(
                    self.projectName):
                questionStr = '^^^ {} files were modified locally after {}\nand will be uploaded to the cloud, keeping the file path information.\nChoose P to display the path or U to update the last sync time'.format(
                    len(updatedFileNameLst), lastSyncTimeStr)
            else:
                questionStr = '^^^ {} files were modified locally after {}\nand will be uploaded to the cloud.\nChoose P to display the path or U to update the last sync time, or Enter to select another project'.format(
                    len(updatedFileNameLst), lastSyncTimeStr)

            doUpload, lastSynchTimeChoice = self.requester.getUserConfirmation(
                questionStr, updatedFileNameLst, updatedFilePathNameLst)

            if doUpload:
                print('')  # empty line
                if self.configManager.isProjectSubDirSynchronized(
                        self.projectName):
                    self.pathUploadToCloud(updatedFilePathNameLst)
                else:
                    self.uploadToCloud(updatedFilePathNameLst)
            else:
                self.handleLastSynchTimeChoice(lastSynchTimeChoice)
        else:
            # here, neither modified files upload nor cloud files download is adequate. Instead
            # of simply closing the utility, we give the user the possibility to update the last
            # synch time
            questionStr = 'No files modified locally since last sync time {}.\nChoose U to update the last sync time, Enter to loop or quit'.format(
                lastSyncTimeStr)
            _, lastSynchTimeChoice = self.requester.getUserConfirmation(
                questionStr, updatedFileNameLst, updatedFilePathNameLst)
            self.handleLastSynchTimeChoice(lastSynchTimeChoice)

    def handleLastSynchTimeChoice(self, lastSynchTimeChoice):
        if lastSynchTimeChoice == '':
            # the user did choose not to upload anything and to leave the last sync
            # time unchanged
            return
        elif lastSynchTimeChoice == 'N':
            # the user did choose to update the last sync time to current time (now)
            self.updateLastSynchTime()
        else:
            # the user did enter a last sync time manually
            self.updateLastSynchTime(lastSynchTimeChoice)

    def uploadToCloud(self, updatedFilePathNameLst):
        """
		Physically uploads the files contained in updatedFilePathNameLst to
		the cloud and sets the last update date stored in the configuration file
		to now. All the files are uploaded in the project cloud root dir.
		
		@param updatedFilePathNameLst: list of file path names to upload
		"""
        for localFilePathName in updatedFilePathNameLst:
            printFileName = localFilePathName.split(sep)[-1]
            print('Uploading {} to the cloud ...'.format(printFileName))

            try:
                self.cloudAccess.uploadFileName(localFilePathName)
            except NameError as e:
                print(
                    '\tUploading {} failed. Possible cause: invalid file name ...'
                    .format(printFileName))

        # updating last synch time for the project in config file
        self.updateLastSynchTime()

    def pathUploadToCloud(self, updatedFilePathNameLst):
        """
		Physically uploads the files contained in updatedFilePathNameLst, creating
		project sub dirs on the cloud project dir and uploading the files in their
		correct sub dir. Then, the method sets the last update date stored in the
		configuration file to now.
		
		@param updatedFilePathNameLst: list of file path names to upload
		"""
        for localFilePathName in updatedFilePathNameLst:
            filePathNameElementLst = localFilePathName.split(sep)[-4:]
            printFilePathName = sep.join(filePathNameElementLst)
            print('Uploading {} to the cloud ...'.format(printFilePathName))

            try:
                self.cloudAccess.uploadFilePathName(localFilePathName)
            except NameError as e:
                print(
                    '\tUploading {} failed. Possible cause: invalid file name ...'
                    .format(printFilePathName))

        # updating last synch time for the project in config file
        self.updateLastSynchTime()

    def updateLastSynchTime(self, userInpuLlastSynchTimeStr=''):
        """
		If the passed lastSynchTimeStr is empty, sets the last update date
		stored in the configuration file to now. Else, if the user specified
		a synch date, validates it before setting it in the config file.
		
		@param userInpuLlastSynchTimeStr last synch time string as defined
										 manually by the user
		"""
        if userInpuLlastSynchTimeStr == '':
            # the user choosed to update synch time to Now !
            validSynchTimeStr = datetime.now().strftime(
                DATE_TIME_FORMAT_CONFIG_FILE)
        else:
            isValid, validSynchTimeStr = self.validateLastSynchTimeStr(
                userInpuLlastSynchTimeStr)

            if not isValid:
                print(
                    '\nSynch time format invalid {}. Nothing changed.'.format(
                        userInpuLlastSynchTimeStr))

                return

        self.configManager.updateLastSynchTime(self.projectName,
                                               validSynchTimeStr)
        print('\nUpdated last synch time to ' + validSynchTimeStr)

    def validateLastSynchTimeStr(self, userInputLastSynchTimeStr):
        try:
            dateTimeMod = datetime.strptime(userInputLastSynchTimeStr,
                                            DATE_TIME_FORMAT_USER_INPUT)
            return True, dateTimeMod.strftime(DATE_TIME_FORMAT_CONFIG_FILE)
        except ValueError:
            try:
                dateTimeMod = datetime.strptime(
                    userInputLastSynchTimeStr,
                    DATE_TIME_FORMAT_USER_INPUT_SHORT)
                return True, dateTimeMod.strftime(DATE_TIME_FORMAT_CONFIG_FILE)
            except ValueError:
                return False, ''

    def downloadAndDeleteFilesFromCloud(self,
                                        downloadPath,
                                        cloudFileLst,
                                        targetName,
                                        doKeepFileOnCloud=False):
        """
		Physically downloads the files from the cloud and deletes them from
		the cloud.

		@param downloadPath: path to which the cloud files will be downloaded,
							 either the local download dir or directly to the
							 project dir and sub dirs
		@param cloudFileLst: list of files to transfer from the cloud. The list
							 contains either file names or file path names,
							 according to the value of the project
							 synchProjectSubDirStructure parm value as defined
							 in the configuration file
		@param targetName: 	 either 'download' or 'project', according to the value
							 of the project synchProjectSubDirStructure parm
							 value as defined
		@param doKeepFileOnCloud if True, do not delete files on cloud after
								 downloading them
		"""
        for cloudFilePathName in cloudFileLst:
            destFilePathName = downloadPath + sep + cloudFilePathName
            print('Transferring {} from the cloud {} dir ...'.format(
                cloudFilePathName, targetName))
            self.cloudAccess.downloadFile(cloudFilePathName, destFilePathName)

            if not doKeepFileOnCloud:
                self.cloudAccess.deleteFile(cloudFilePathName)
    def testMoveFilesToLocalDirs(self):
        if os.name == 'posix':
            downloadDir = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_1/fromdir'
            downloadDirSaved = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_1/fromdir_saved'
            projectDir = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_1/projectdir'
            projectDirEmpty = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_1/projectdir_empty'

            TEST_SUB_DIR = '/test'
            IMG_SUB_DIR = '/images'
            DOC_SUB_DIR = '/doc'
        else:
            # Windows
            downloadDir = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_1\\fromdir'
            downloadDirSaved = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_1\\fromdir_saved'
            projectDir = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_1\\projectdir'
            projectDirEmpty = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_1\\projectdir_empty'

            TEST_SUB_DIR = '/test'
            IMG_SUB_DIR = '/images'
            DOC_SUB_DIR = '/doc'

        configManager = ConfigManager(CONFIG_FILE_PATH_NAME)

        # deleting downloadDir (dir and content)
        if os.path.exists(downloadDir):
            shutil.rmtree(downloadDir)

        # restoring downloadDir from its saved version
        shutil.copytree(downloadDirSaved, downloadDir)

        # deleting projectDir
        if os.path.exists(projectDir):
            shutil.rmtree(projectDir)

        # restoring a directory structure only project dir (no files) from its saved empty version
        shutil.copytree(projectDirEmpty, projectDir)

        # ensuring downloadDir contains the required files

        fl = FileLister(configManager)
        projectName = 'transFileCloudTestProject'
        cloudFileLst = [
            'constants_1.py', 'current_state_11.jpg', 'current_state_12.jpg',
            'doc_11.docx', 'doc_12.docx', 'filelister_1.py', 'filemover_1.py',
            'README_1.md', 'testfilelister_1.py', 'testfilemover_1.py'
        ]
        _, fileTypeDic = fl.getFilesByOrderedTypes(projectName,
                                                   cloudFileLst=cloudFileLst)

        self.assertEqual(
            sorted(['filelister_1.py', 'filemover_1.py', 'constants_1.py']),
            fileTypeDic['*.py'][1])
        self.assertEqual(sorted(['testfilelister_1.py', 'testfilemover_1.py']),
                         fileTypeDic['test*.py'][1])
        self.assertEqual(
            sorted(['current_state_12.jpg', 'current_state_11.jpg']),
            fileTypeDic['*.jpg'][1])
        self.assertEqual(sorted(['doc_12.docx', 'doc_11.docx']),
                         fileTypeDic['*.docx'][1])
        self.assertEqual(sorted(['README_1.md']), fileTypeDic['*.md'][1])

        fm = FileMover(configManager, projectName)
        fm.projectDir = projectDir

        # capturing stdout into StringIO to avoid outputing in terminal
        # window while unit testing

        stdout = sys.stdout
        outputCapturingString = StringIO()
        sys.stdout = outputCapturingString

        fm.moveFilesToLocalDirs(cloudFileLst)

        sys.stdout = stdout

        if os.name == 'posix':
            self.assertEqual([
                'moving test/testproject_1/fromdir/testfilelister_1.py to '
                'testproject_1/projectdir/test/testfilelister_1.py',
                'moving test/testproject_1/fromdir/testfilemover_1.py to '
                'testproject_1/projectdir/test/testfilemover_1.py',
                'moving test/testproject_1/fromdir/current_state_11.jpg to '
                'testproject_1/projectdir/images/current_state_11.jpg',
                'moving test/testproject_1/fromdir/current_state_12.jpg to '
                'testproject_1/projectdir/images/current_state_12.jpg',
                'moving test/testproject_1/fromdir/doc_11.docx to '
                'testproject_1/projectdir/doc/doc_11.docx',
                'moving test/testproject_1/fromdir/doc_12.docx to '
                'testproject_1/projectdir/doc/doc_12.docx',
                'moving test/testproject_1/fromdir/constants_1.py to '
                'test/testproject_1/projectdir/constants_1.py',
                'moving test/testproject_1/fromdir/filelister_1.py to '
                'test/testproject_1/projectdir/filelister_1.py',
                'moving test/testproject_1/fromdir/filemover_1.py to '
                'test/testproject_1/projectdir/filemover_1.py',
                'moving test/testproject_1/fromdir/README_1.md to '
                'test/testproject_1/projectdir/README_1.md', ''
            ],
                             outputCapturingString.getvalue().split('\n'))
        else:
            self.assertEqual([
                'moving test\\testproject_1\\fromdir\\testfilelister_1.py to '
                'testproject_1\\projectdir\\test\\testfilelister_1.py',
                'moving test\\testproject_1\\fromdir\\testfilemover_1.py to '
                'testproject_1\\projectdir\\test\\testfilemover_1.py',
                'moving test\\testproject_1\\fromdir\\current_state_11.jpg to '
                'testproject_1\\projectdir\\images\\current_state_11.jpg',
                'moving test\\testproject_1\\fromdir\\current_state_12.jpg to '
                'testproject_1\\projectdir\\images\\current_state_12.jpg',
                'moving test\\testproject_1\\fromdir\\doc_11.docx to '
                'testproject_1\\projectdir\\doc\\doc_11.docx',
                'moving test\\testproject_1\\fromdir\\doc_12.docx to '
                'testproject_1\\projectdir\\doc\\doc_12.docx',
                'moving test\\testproject_1\\fromdir\\constants_1.py to '
                'test\\testproject_1\\projectdir\\constants_1.py',
                'moving test\\testproject_1\\fromdir\\filelister_1.py to '
                'test\\testproject_1\\projectdir\\filelister_1.py',
                'moving test\\testproject_1\\fromdir\\filemover_1.py to '
                'test\\testproject_1\\projectdir\\filemover_1.py',
                'moving test\\testproject_1\\fromdir\\README_1.md to '
                'test\\testproject_1\\projectdir\\README_1.md', ''
            ],
                             outputCapturingString.getvalue().split('\n'))

        # verifying project dir
        fileNameLst = [
            x.split(sep)[-1] for x in glob.glob(projectDir + sep + '*.*')
        ]
        self.assertEqual(
            sorted([
                'filelister_1.py', 'filemover_1.py', 'constants_1.py',
                'README_1.md'
            ]), sorted(fileNameLst))

        # verifying project test sub dir
        fileNameLst = [
            x.split(sep)[-1]
            for x in glob.glob(projectDir + TEST_SUB_DIR + sep + '*.*')
        ]
        self.assertEqual(sorted(['testfilelister_1.py', 'testfilemover_1.py']),
                         sorted(fileNameLst))

        # verifying project images sub dir
        fileNameLst = [
            x.split(sep)[-1]
            for x in glob.glob(projectDir + IMG_SUB_DIR + sep + '*.*')
        ]
        self.assertEqual(
            sorted(['current_state_12.jpg', 'current_state_11.jpg']),
            sorted(fileNameLst))

        # verifying project doc sub dir
        fileNameLst = [
            x.split(sep)[-1]
            for x in glob.glob(projectDir + DOC_SUB_DIR + sep + '*.*')
        ]
        self.assertEqual(sorted(['doc_12.docx', 'doc_11.docx']),
                         sorted(fileNameLst))

        # testing that download no longer contains the files defined in cloudFileLst
        fileNameLst = [
            x.split(sep)[-1] for x in glob.glob(downloadDir + sep + '*.*')
        ]
        self.assertEqual(
            sorted([
                'constants_1.mp3',
                'Nikolay Rimsky-Korsakov - Отче наш   Notre Père   Our Father - Cep.mp3'
            ]), sorted(fileNameLst))
예제 #19
0
 def __init__(self):
     self.fileLister = FileLister()
예제 #20
0
    def testTransferFilesFromCloudToLocalDirs_filePath(self):
        # avoid warning resourcewarning unclosed ssl.sslsocket due to Dropbox
        warnings.filterwarnings(action="ignore",
                                message="unclosed",
                                category=ResourceWarning)

        if os.name == 'posix':
            localProjectDir = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_2/projectdir'
            localProjectDirSaved = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/testproject_2/projectdir_saved'
            configFilePathName = '/storage/emulated/0/Android/data/ru.iiec.pydroid3/files/trans_file_cloud/test/test_TransferFiles.ini'
        else:
            localProjectDir = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_2\\projectdir'
            localProjectDirSaved = 'D:\\Development\\Python\\trans_file_cloud\\test\\testproject_2\\projectdir_saved'
            configFilePathName = 'D:\\Development\\Python\\trans_file_cloud\\test\\test_TransferFiles.ini'

        cm = ConfigManager(configFilePathName)
        projectName = 'TransferPathFilesTestProject'

        # storing the last synch update time to compare it to the new update
        # time once download and move has been performed
        storedLastSynchTimeStr = cm.getLastSynchTime(projectName)
        storedLastSyncTime = datetime.datetime.strptime(
            storedLastSynchTimeStr, DATE_TIME_FORMAT_CONFIG_FILE)

        # cleaning up the cloud folder before uploading the test files

        drpa = DropboxAccess(cm, projectName)

        cloudFileLst = drpa.getCloudFilePathNameList()

        for file in cloudFileLst:
            drpa.deleteFile(file)

        self.assertEqual([], drpa.getCloudFileNameList())

        # listing the test files which wiLl be uploaded to
        # the cloud in order to be available for download
        # and move to local dirs

        tstFileToUploadLst = ['testfilemover_2.py']
        pythonFileToUploadLst = ['filemover_2.py', 'filelister_2.py']
        docFileToUploadLst = ['doc_21.docx', 'doc_22.docx']
        imgFileToUploadLst = ['current_state_21.jpg']

        fileNameToUploadLst = tstFileToUploadLst + pythonFileToUploadLst + docFileToUploadLst + imgFileToUploadLst

        tstFilePathNameToUploadLst = [
            localProjectDir + sep + 'test' + sep + x
            for x in tstFileToUploadLst
        ]
        pythonFilePathNameToUploadLst = [
            localProjectDir + sep + x for x in pythonFileToUploadLst
        ]
        docFilePathNameToUploadLst = [
            localProjectDir + sep + 'doc' + sep + x for x in docFileToUploadLst
        ]
        imgFilePathNameToUploadLst = [
            localProjectDir + sep + 'images' + sep + x
            for x in imgFileToUploadLst
        ]

        filePathNameToUploadLst = tstFilePathNameToUploadLst + pythonFilePathNameToUploadLst + docFilePathNameToUploadLst + imgFilePathNameToUploadLst

        # uploading the test files which will then be downloaded and moved to
        # local dirs

        drpa = DropboxAccess(cm, projectName)

        for filePathName in filePathNameToUploadLst:
            drpa.uploadFilePathName(filePathName)

        # simulating user input

        stdin = sys.stdin

        # selecting project 2 (the test project 'TransferPathFilesTestProject' is
        # the second project defined in test_TransferFiles.ini !)
        sys.stdin = StringIO('2')  # TransferPathFilesTestProject

        print('\nstdout temporarily captured. Test is running ...')

        stdout = sys.stdout
        outputCapturingString = StringIO()
        sys.stdout = outputCapturingString

        # now asking TransferFiles to download the cloud files and move them
        # to the local dirs

        tf = TransferFiles(configFilePath=configFilePathName)

        # confirming cloud files download
        sys.stdin = StringIO('Y')

        tf.transferFilesFromCloudToLocalDirs(drpa.getCloudFilePathNameList())

        sys.stdin = stdin
        sys.stdout = stdout

        # testing that the last synch time is after the stored synch time

        cm_reloaded = ConfigManager(configFilePathName)
        newLastSynchTimeStr = cm_reloaded.getLastSynchTime(projectName)
        newLastSyncTime = datetime.datetime.strptime(
            newLastSynchTimeStr, DATE_TIME_FORMAT_CONFIG_FILE)
        self.assertTrue(newLastSyncTime > storedLastSyncTime)

        # now testing that the files downloaded from the cloud and moved to
        # the local dirs are the expected ones

        # first, reset the last synch time to the stored one so that FileLister
        # will list files whose modification date is oreater than this time
        cm.updateLastSynchTime(projectName, storedLastSynchTimeStr)

        fl = FileLister(cm)
        allFileNameLst, allFilePathNameLst, lastSyncTimeStr = fl.getModifiedFileLst(
            projectName)

        self.assertEqual(sorted(fileNameToUploadLst), sorted(allFileNameLst))
        self.assertEqual(sorted(filePathNameToUploadLst),
                         sorted(allFilePathNameLst))

        # now restoring the modified files dir to its saved version
        shutil.rmtree(localProjectDir)
        dir_util.copy_tree(localProjectDirSaved, localProjectDir)
class FileMover:
	"""
	This class manages the physical moving of the files from the local
	download dir to the correct target dirs which depend on the file type or
	name pattern as defined in the local configuration file.
	"""
	def __init__(self, configManager, projectName):
		"""
		FileMover constructor.
		
		@param configManager: ConfigManager giving access to the local configuration
							  file data
		@param projectName: project name as defined in the local configuration
							file
		"""
		self.projectName = projectName
		self.downloadDir = configManager.downloadPath
		self.projectDir = configManager.getProjectLocalDir(projectName)
		self.fileNameLister = FileLister(configManager)

	def moveFilesToLocalDirs(self, cloudFileLst):
		"""
		This method performs the physical moving of the files from the local
		download dir to the correct target dirs which depend on the file type or
		name pattern as defined in the local configuration file.
		
		It uses a FileLister instance to obtain the required information for 
		transferring the files at their right destination.
		
		Here are an example of the two data structures returned by FileLister 
		and used by FileMover to transfer files in the adequate order at their 
		local destination:
			
		orderedFileTypeWildchardExprLst and fileTypeDic examples:
				
		['test*.py', 'aa*.jpg', '*.jpg', '*.docx', '*.py', '*.rd'], orderedFileTypeWildchardExprLst)

		{'*.jpg': ('/images', ['current_state_21.jpg', 'current_state_22.jpg']), 
		'*.docx': ('/doc', ['doc_21.docx', 'doc_22.docx']),
		'*.rd': ('/', ['README_2.rd']),
		'aa*.jpg': ('/images/aa', ['aa_current.jpg']),
		'test*.py': ('/test', ['testfilelister_2.py', 'testfilemover_2.py']),
		'*.py': ('/', ['constants_2.py', 'filelister_2.py', 'filemover_2.py'])}, fileTypeDic)

		@param cloudFileLst: list of files downloaded from the cloud to the download dir which
							 must be moved to their destination dir
		"""
		orderedFileTypeWildchardExprLst, fileTypeDic = self.fileNameLister.getFilesByOrderedTypes(self.projectName, cloudFileLst=cloudFileLst)

		try:
			for fileTypeWildchardExpr in orderedFileTypeWildchardExprLst:
				fileTypeEntryTuple = fileTypeDic[fileTypeWildchardExpr]
				destinationDir = self.projectDir + fileTypeEntryTuple[0]
				fileToMoveNameLst = fileTypeEntryTuple[1]

				for fileToMoveName in fileToMoveNameLst:
					fromFilePath = self.downloadDir + sep + fileToMoveName
					toFilePath = destinationDir + sep + fileToMoveName
					shutil.move(fromFilePath, toFilePath)
					fromFilePathShortened = self.shortenFileNamePath(fromFilePath)
					toFilePathShortened = self.shortenFileNamePath(toFilePath)
					print('moving {} to {}'.format(fromFilePathShortened, toFilePathShortened))
		except FileNotFoundError as e:
			pathElemLst = e.filename.split(sep)
			pathOnly = sep.join(pathElemLst[:-1])
			print('Destination dir {} does not exist. Program stopped.'.format(pathOnly))

	def shortenFileNamePath(self, completeFilePathName):
		"""
		This method shortens the full path file name string to make it more
		readable by the user. Here, the completeFilePathName is a fuLl file path
		name. In order to display a more readable file list, only the last 4 file
		pathName elements are kept.
		
		@param completeFilePathName: fuLl file path name
		"""
		filePathNameElementLst = completeFilePathName.split(sep)
		
		return sep.join(filePathNameElementLst[-4:])