Created
February 3, 2019 13:21
-
-
Save dzogrim/3124ed8a36cc9063d9df18e508befd6d to your computer and use it in GitHub Desktop.
Sort many versions of downloaded application directories and keep the latest version
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/python3 | |
| # -*- coding: utf8 -*- | |
| #-- Need installed `py37-setuptools` & `py37-pip` | |
| import os, os.path | |
| import re | |
| import shutil | |
| import sys | |
| from collections import OrderedDict as ODict | |
| try : | |
| from pkg_resources import parse_version | |
| except ImportError as e : | |
| print( "Python{} package 'pkg_resources' is required! {}".format( sys.version_info.major, e ) ) | |
| sys.exit( 1 ) | |
| #-- | |
| LISTING = "/Volumes/BIG4TB/_TMP4" | |
| DEST_DIR = "{}_LATEST".format( LISTING ) | |
| rxVersionParsers = ( | |
| re.compile( r'^(?P<appName>.+)v(?P<appVer>[0-9](?:[0-9\.-]+)?(.+)?)(?:\.(?:zip|rar|dmg))' ), | |
| re.compile( r'^(?P<appName>.+)v(?P<appVer>[0-9](?:[0-9\.-]+)?(.+)?)' ), | |
| re.compile( r'^(?P<appName>.+?)[\._\s-]+(?P<appVer>[0-9](?:[0-9\.-]+)?(.+)?)(?:\.(?:zip|rar|dmg))' ), | |
| re.compile( r'^(?P<appName>.+?)[\._\s-]+(?P<appVer>[0-9](?:[0-9\.-]+)?(.+)?)' ), | |
| re.compile( r'^(?P<appName>.+?)(?P<appVer>[0-9](?:[0-9\.-]+)?(.+)?)(?:\.(?:zip|rar|dmg))' ), | |
| re.compile( r'^(?P<appName>.+?)(?P<appVer>[0-9](?:[0-9\.-]+)?(.+)?)' ), | |
| ) | |
| rxCleanAppName = ( | |
| ( re.compile( r'_' ), ' ' ), | |
| ( re.compile( r'[\[\]\s-]*$' ), '' ), | |
| ) | |
| rxCleanAppVer = ( | |
| ( re.compile( r'_' ), '.' ), | |
| ( re.compile( r'[\[\]\(\)-]+' ), '.' ), | |
| ( re.compile( r'[^\d\.]' ), '' ), | |
| ( re.compile( r'(?:^[\s\.]+|[\s\.]+$)' ), '' ), | |
| ( re.compile( r'\s+' ), '.' ), | |
| ( re.compile( r'\.{2,}' ), '.' ), | |
| ) | |
| DRYRUN = True | |
| def Print( text = None ) : | |
| if DRYRUN : | |
| text = '[DRYRUN] {}'.format( text ) | |
| #-- | |
| print( text ) | |
| #-- | |
| def process_listing( data = None ) : | |
| if not data : | |
| return {} | |
| #-- | |
| appList = ODict() | |
| for line in data : | |
| appName = appVer = None | |
| for rx in rxVersionParsers : | |
| res = rx.search( line ) | |
| if res : | |
| appName = res.group( 'appName' ) | |
| appVer = res.group( 'appVer' ) | |
| appVerNorm = appVer | |
| for S, R in rxCleanAppName : | |
| appName = S.sub( R, appName ) | |
| #-- | |
| for S, R in rxCleanAppVer : | |
| appVerNorm = S.sub( R, appVerNorm ) | |
| #-- | |
| appNameNorm = appName.lower().replace( ' ', '' ) | |
| break | |
| #-- | |
| #-- | |
| if not appName : | |
| appName = line | |
| appNameNorm = appName.lower().replace( ' ', '' ) | |
| appVer = appVerNorm = None | |
| #-- | |
| if appNameNorm not in appList and appVer : | |
| appList[ appNameNorm ] = dict( appName = appName, appDir = line, versions = list() ) | |
| #-- | |
| if appVer : | |
| appList[ appNameNorm ][ 'versions' ].append( [ appVer, appVerNorm, line ] ) | |
| #-- | |
| #-- | |
| return appList | |
| #-- | |
| def dispatch_listing( data = None ) : | |
| if not data : | |
| return | |
| #-- | |
| if not DRYRUN : | |
| try : | |
| os.mkdir( DEST_DIR ) | |
| except FileExistsError : | |
| pass | |
| except Exception as e : | |
| print( "Can not create directory '{}'! {}".format( DEST_DIR, e ) ) | |
| return 2 | |
| #-- | |
| #-- | |
| for app, info in data.items() : | |
| versions = info[ 'versions' ] | |
| src = os.path.join( LISTING, info[ 'appDir' ] ) | |
| if not versions : | |
| Print( "\x1B[34;1m{}\x1B[0m have no version".format( info[ 'appName' ] ) ) | |
| elif len( info[ 'versions' ] ) < 2 : | |
| Print( "\x1B[34;1m{}\x1B[0m is single version '\x1B[31;1m{}\x1B[0m'".format( info[ 'appName' ], versions[ 0 ][ 0 ] ) ) | |
| else : | |
| Print( "\x1B[34;1m{}\x1B[0m multi versions '\x1B[31;1m{}\x1B[0m'".format( info[ 'appName' ], "\x1B[0m' '\x1B[31;1m".join( [ i[ 0 ] for i in versions ] ) ) ) | |
| bestVersion, otherVersions = versions[ 0 ], versions[ 1: ] | |
| rc = os.path.join( LISTING, bestVersion[ 2 ] ) | |
| for aVer, nVer, aDir in otherVersions : | |
| if parse_version( nVer ) > parse_version( bestVersion[ 1 ] ) : | |
| bestVersion = [ aVer, nVer, aDir ] | |
| src = os.path.join( LISTING, aDir ) | |
| #-- | |
| #-- | |
| Print( " => Best version : \x1B[32;1m{}\x1B[0m".format( bestVersion[ 0 ] ) ) | |
| #-- | |
| if not DRYRUN : | |
| try : | |
| shutil.move( src, DEST_DIR ) | |
| except Exception as e : | |
| print( "Can not move '{}' to '{}'! {}".format( src, DEST_DIR, e ) ) | |
| return 2 | |
| #-- | |
| #-- | |
| #-- | |
| return 0 | |
| #-- | |
| def main() : | |
| rc = 0 | |
| try : | |
| data = os.listdir( LISTING ) | |
| except IOError as e : | |
| print( "Can not open '{}'!: {}".format( LISTING, e ) ) | |
| rc = 1 | |
| else : | |
| data.sort() | |
| res = process_listing( data ) | |
| rc = dispatch_listing( res ) | |
| #-- | |
| return rc | |
| #-- | |
| ## ======================================================================= | |
| ## :: Main :: | |
| ## ======================================================================= | |
| if __name__ == "__main__" : | |
| if len( sys.argv ) > 1 and sys.argv[ 1 ] == 'APPLY' : | |
| DRYRUN = False | |
| #-- | |
| sys.exit( main() ) | |
| #-- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment