mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-04 23:02:02 +01:00
d55dd7e206
been counting on this for several days.
420 lines
14 KiB
Python
Executable file
420 lines
14 KiB
Python
Executable file
#!/usr/bin/python
|
|
# Script meant to be installed on eehouse.org.
|
|
|
|
import logging, shelve, hashlib, sys, json, subprocess, glob, os
|
|
import mk_for_download, mygit
|
|
import xwconfig
|
|
|
|
from stat import ST_CTIME
|
|
try:
|
|
from mod_python import apache
|
|
apacheAvailable = True
|
|
except ImportError:
|
|
apacheAvailable = False
|
|
|
|
# constants that are also used in UpdateCheckReceiver.java
|
|
VERBOSE = False
|
|
k_NAME = 'name'
|
|
k_AVERS = 'avers'
|
|
k_GVERS = 'gvers'
|
|
k_INSTALLER = 'installer'
|
|
k_DEVOK = 'devOK'
|
|
k_APP = 'app'
|
|
k_DICTS = 'dicts'
|
|
k_XLATEINFO = 'xlatinfo'
|
|
k_CALLBACK = 'callback'
|
|
k_LOCALE = 'locale'
|
|
k_XLATPROTO = 'proto'
|
|
k_XLATEVERS = 'xlatevers'
|
|
k_STRINGSHASH = 'strings'
|
|
|
|
k_OLD = 'old'
|
|
k_NEW = 'new'
|
|
k_PAIRS = 'pairs'
|
|
|
|
k_LANG = 'lang'
|
|
k_MD5SUM = 'md5sum'
|
|
k_INDEX = 'index'
|
|
k_ISUM = 'isum'
|
|
k_SUCCESS = 'success'
|
|
k_URL = 'url'
|
|
|
|
k_SUMS = 'sums'
|
|
k_COUNT = 'count'
|
|
|
|
# Version for those sticking with RELEASES
|
|
k_REL_REV = 'android_beta_81'
|
|
|
|
# Version for those getting intermediate builds
|
|
k_DBG_REV = 'android_beta_81-29-gb776b7c'
|
|
k_DBG_REV = 'android_beta_81-34-g73a1083'
|
|
|
|
k_suffix = '.xwd'
|
|
k_filebase = "/var/www/"
|
|
k_apkDir = "xw4/android/"
|
|
k_shelfFile = k_filebase + 'xw4/info_shelf_2'
|
|
k_urlbase = "http://eehouse.org"
|
|
k_versions = { 'org.eehouse.android.xw4': {
|
|
'version' : 74,
|
|
k_AVERS : 74,
|
|
k_URL : k_apkDir + 'XWords4-release_' + k_REL_REV + '.apk',
|
|
},
|
|
}
|
|
|
|
# k_versions_dbg = { 'org.eehouse.android.xw4': {
|
|
# 'version' : 74,
|
|
# k_AVERS : 74,
|
|
# k_GVERS : k_DBG_REV,
|
|
# k_URL : k_apkDir + 'XWords4-release_' + k_DBG_REV + '.apk',
|
|
# },
|
|
# }
|
|
s_shelf = None
|
|
|
|
|
|
logging.basicConfig(level=logging.DEBUG
|
|
,format='%(asctime)s [[%(levelname)s]] %(message)s'
|
|
,datefmt='%d %b %y %H:%M'
|
|
,filename='/tmp/info_py.log')
|
|
# ,filemode='w')
|
|
|
|
# This seems to be required to prime the pump somehow.
|
|
# logging.debug( "loaded...." )
|
|
|
|
def getInternalSum( filePath ):
|
|
filePath = k_filebase + "and_wordlists/" + filePath
|
|
proc = subprocess.Popen(['/usr/bin/perl',
|
|
'--',
|
|
'/var/www/xw4/dawg2dict.pl',
|
|
'-get-sum',
|
|
'-dict', filePath ],
|
|
stdout = subprocess.PIPE,
|
|
stderr = subprocess.PIPE)
|
|
return proc.communicate()[0].strip()
|
|
|
|
def md5Checksums( sums, filePath ):
|
|
if not filePath.endswith(k_suffix): filePath += k_suffix
|
|
if filePath in sums:
|
|
result = sums[filePath]
|
|
else:
|
|
logging.debug( "opening %s" % (k_filebase + "and_wordlists/" + filePath))
|
|
try:
|
|
file = open( k_filebase + "and_wordlists/" + filePath, 'rb' )
|
|
md5 = hashlib.md5()
|
|
while True:
|
|
buffer = file.read(128)
|
|
if not buffer: break
|
|
md5.update( buffer )
|
|
|
|
sums[filePath] = [ md5.hexdigest(),
|
|
getInternalSum( filePath ) ]
|
|
logging.debug( "figured sum for %s: %s" % (filePath,
|
|
sums[filePath] ) )
|
|
result = sums[filePath]
|
|
except:
|
|
# logging.debug( "Unexpected error: " + sys.exc_info()[0] )
|
|
result = None
|
|
return result
|
|
|
|
def getDictSums():
|
|
global s_shelf
|
|
# shelve will fail if permissions are wrong. That's ok for some
|
|
# testing: just make a fake shelf and test before saving it later.
|
|
try:
|
|
s_shelf = shelve.open(k_shelfFile)
|
|
except:
|
|
s_shelf = {}
|
|
|
|
if not k_SUMS in s_shelf: s_shelf[k_SUMS] = {}
|
|
if not k_COUNT in s_shelf: s_shelf[k_COUNT] = 0
|
|
s_shelf[k_COUNT] += 1
|
|
logging.debug( "Count now %d" % s_shelf[k_COUNT] )
|
|
return s_shelf[k_SUMS]
|
|
|
|
def getOrderedApks( path ):
|
|
apks = []
|
|
|
|
pattern = path + "/XWords4-release_android_beta_*.apk"
|
|
|
|
files = ((os.stat(apk).st_mtime, apk) for apk in glob.glob(pattern))
|
|
for mtime, file in sorted(files, reverse=True):
|
|
# logging.debug( file + ": " + str(mtime) )
|
|
apks.append( file )
|
|
|
|
return apks
|
|
|
|
# public, but deprecated
|
|
def curVersion( req, name, avers = 41, gvers = None, installer = None ):
|
|
global k_versions
|
|
result = { k_SUCCESS : True }
|
|
if apacheAvailable:
|
|
logging.debug( 'IP address of requester is %s'
|
|
% req.get_remote_host(apache.REMOTE_NAME) )
|
|
|
|
logging.debug( "name: %s; avers: %s; installer: %s; gvers: %s"
|
|
% (name, avers, installer, gvers) )
|
|
if name in k_versions:
|
|
versions = k_versions[name]
|
|
if versions[k_AVERS] > int(avers):
|
|
logging.debug( avers + " is old" )
|
|
result[k_URL] = k_urlbase + '/' + versions[k_URL]
|
|
else:
|
|
logging.debug(name + " is up-to-date")
|
|
else:
|
|
logging.debug( 'Error: bad name ' + name )
|
|
return json.dumps( result )
|
|
|
|
# public, but deprecated
|
|
def dictVersion( req, name, lang, md5sum ):
|
|
result = { k_SUCCESS : True }
|
|
if not name.endswith(k_suffix): name += k_suffix
|
|
dictSums = getDictSums()
|
|
path = lang + "/" + name
|
|
if not path in dictSums:
|
|
sums = md5Checksums( dictSums, path )
|
|
if sums:
|
|
dictSums[path] = sums
|
|
s_shelf[k_SUMS] = dictSums
|
|
if path in dictSums:
|
|
if not md5sum in dictSums[path]:
|
|
result[k_URL] = k_urlbase + "/and_wordlists/" + path
|
|
else:
|
|
logging.debug( path + " not known" )
|
|
if 'close' in s_shelf: s_shelf.close()
|
|
return json.dumps( result )
|
|
|
|
def getApp( params, name ):
|
|
result = None
|
|
if k_NAME in params:
|
|
name = params[k_NAME]
|
|
if name:
|
|
# If we're a dev device, always push the latest
|
|
if k_DEVOK in params and params[k_DEVOK]:
|
|
apks = getOrderedApks( k_filebase + k_apkDir )
|
|
if 0 < len(apks):
|
|
apk = apks[0]
|
|
# Does path NOT contain name of installed file
|
|
curApk = params[k_GVERS] + '.apk'
|
|
if curApk in apk:
|
|
logging.debug( "already have " + curApk )
|
|
else:
|
|
url = k_urlbase + '/' + apk[len(k_filebase):]
|
|
result = {k_URL: url}
|
|
logging.debug( result )
|
|
|
|
elif k_AVERS in params and k_GVERS in params:
|
|
avers = params[k_AVERS]
|
|
gvers = params[k_GVERS]
|
|
if k_INSTALLER in params: installer = params[k_INSTALLER]
|
|
else: installer = ''
|
|
|
|
logging.debug( "name: %s; avers: %s; installer: %s; gvers: %s"
|
|
% (name, avers, installer, gvers) )
|
|
if name in k_versions:
|
|
versForName = k_versions[name]
|
|
if versForName[k_AVERS] > int(avers):
|
|
result = {k_URL: k_urlbase + '/' + versForName[k_URL]}
|
|
elif k_GVERS in versForName and not gvers == versForName[k_GVERS]:
|
|
result = {k_URL: k_urlbase + '/' + versForName[k_URL]}
|
|
else:
|
|
logging.debug(name + " is up-to-date")
|
|
else:
|
|
logging.debug( 'Error: bad name ' + name )
|
|
else:
|
|
logging.debug( 'missing param' )
|
|
return result
|
|
|
|
# create obj containing array of objects each with 'lang' and 'xwds',
|
|
# the latter an array of objects giving info about a dict.
|
|
def listDicts():
|
|
ldict = {}
|
|
root = k_filebase + "and_wordlists/"
|
|
dictSums = getDictSums()
|
|
for path in glob.iglob( root + "*/*.xwd" ):
|
|
path = path.replace( root, '' )
|
|
lang, xwd = path.split( '/' )
|
|
if not lang in ldict: ldict[lang] = []
|
|
ldict[lang].append({ 'md5sums' : md5Checksums( dictSums, path ),
|
|
'xwd' : xwd,
|
|
})
|
|
|
|
# now format as we want 'em
|
|
langs = []
|
|
for lang, entry in ldict.iteritems():
|
|
obj = { 'lang' : lang,
|
|
'dicts' : entry,
|
|
}
|
|
langs.append( obj )
|
|
|
|
return { 'langs' : langs }
|
|
|
|
def getDicts( params ):
|
|
result = []
|
|
dictSums = getDictSums()
|
|
for param in params:
|
|
name = param[k_NAME]
|
|
lang = param[k_LANG]
|
|
md5sum = param[k_MD5SUM]
|
|
index = param[k_INDEX]
|
|
if not name.endswith(k_suffix): name += k_suffix
|
|
path = lang + "/" + name
|
|
if not path in dictSums:
|
|
sums = md5Checksums( dictSums, path )
|
|
if sums:
|
|
dictSums[path] = sums
|
|
s_shelf[k_SUMS] = dictSums
|
|
if path in dictSums:
|
|
if not md5sum in dictSums[path]:
|
|
cur = { k_URL : k_urlbase + "/and_wordlists/" + path,
|
|
k_INDEX : index, k_ISUM: dictSums[path][1] }
|
|
result.append( cur )
|
|
else:
|
|
logging.debug( path + " not known" )
|
|
|
|
if 'close' in s_shelf: s_shelf.close()
|
|
if 0 == len(result): result = None
|
|
return result
|
|
|
|
def variantFor( name ):
|
|
if name == 'xw4': result = 'XWords4'
|
|
logging.debug( 'variantFor(%s)=>%s' % (name, result))
|
|
return result
|
|
|
|
def getXlate( params, name, stringsHash ):
|
|
result = []
|
|
path = xwconfig.k_REPOPATH
|
|
logging.debug('creating repo with path ' + path)
|
|
repo = mygit.GitRepo( path )
|
|
logging.debug( "getXlate: %s, hash=%s" % (json.dumps(params), stringsHash) )
|
|
# logging.debug( 'status: ' + repo.status() )
|
|
|
|
# reduce org.eehouse.anroid.xxx to xxx, then turn it into a
|
|
# variant and get the contents of the R.java file
|
|
splits = name.split('.')
|
|
name = splits[-1]
|
|
variant = variantFor( name );
|
|
rPath = '%s/archive/R.java' % variant
|
|
rDotJava = repo.cat( rPath, stringsHash )
|
|
|
|
# Figure out the newest hash possible for translated strings.xml
|
|
# files. If our R.java's the newest, that's HEAD. Otherwise it's
|
|
# the revision BEFORE the revision that changed R.java
|
|
|
|
head = repo.getHeadRev()
|
|
logging.debug('head = %s' % head)
|
|
rjavarevs = repo.getRevsBetween(head, stringsHash, rPath)
|
|
assert( 1 >= len(rjavarevs) )
|
|
assert( stringsHash == rjavarevs[-1] )
|
|
if 1 == len(rjavarevs):
|
|
firstPossible = head
|
|
else:
|
|
firstPossible = rjavarevs[-2] + '^'
|
|
# get actual number for rev^
|
|
firstPossible = repo.getRevsBetween( firstPossible, firstPossible )[0]
|
|
logging.debug('firstPossible: %s' % firstPossible)
|
|
|
|
for entry in params:
|
|
curVers = entry[k_XLATEVERS]
|
|
if not curVers == firstPossible:
|
|
locale = entry[k_LOCALE]
|
|
|
|
data = mk_for_download.getXlationFor( repo, rDotJava, locale, \
|
|
firstPossible )
|
|
if data: result.append( { k_LOCALE: locale,
|
|
k_OLD: curVers,
|
|
k_NEW: firstPossible,
|
|
k_PAIRS: data,
|
|
} )
|
|
|
|
if 0 == len(result): result = None
|
|
logging.debug( "getXlate=>%s" % (json.dumps(result)) )
|
|
return result
|
|
|
|
# public
|
|
def getUpdates( req, params ):
|
|
result = { k_SUCCESS : True }
|
|
appResult = None
|
|
logging.debug( "getUpdates: got params: %s" % params )
|
|
asJson = json.loads( params )
|
|
if k_APP in asJson:
|
|
name = None
|
|
if k_NAME in asJson: name = asJson[k_NAME]
|
|
appResult = getApp( asJson[k_APP], name )
|
|
if appResult:
|
|
result[k_APP] = appResult
|
|
if k_DICTS in asJson:
|
|
dictsResult = getDicts( asJson[k_DICTS] )
|
|
if dictsResult:
|
|
result[k_DICTS] = dictsResult
|
|
|
|
# Let's not upgrade strings at the same time as we're upgrading the app
|
|
if appResult:
|
|
logging.debug( 'skipping xlation upgrade because app being updated' )
|
|
elif k_XLATEINFO in asJson and k_NAME in asJson and k_STRINGSHASH in asJson:
|
|
xlateResult = getXlate( asJson[k_XLATEINFO], asJson[k_NAME], asJson[k_STRINGSHASH] )
|
|
if xlateResult:
|
|
logging.debug( xlateResult )
|
|
result[k_XLATEINFO] = xlateResult;
|
|
else:
|
|
logging.debug( "NOT FOUND xlate info" )
|
|
|
|
logging.debug( 'getUpdates done' )
|
|
return json.dumps( result )
|
|
|
|
def clearShelf():
|
|
shelf = shelve.open(k_shelfFile)
|
|
shelf[k_SUMS] = {}
|
|
shelf.close()
|
|
|
|
def usage():
|
|
print "usage:", sys.argv[0], '--get-sums [lang/dict]*'
|
|
print ' | --test-get-app app <org.eehouse.app.name> avers gvers'
|
|
print ' | --test-get-dicts name lang curSum'
|
|
print ' | --list-apks [path/to/apks]'
|
|
print ' | --list-dicts'
|
|
print ' | --clear-shelf'
|
|
sys.exit(-1)
|
|
|
|
def main():
|
|
if 1 >= len(sys.argv): usage();
|
|
arg = sys.argv[1]
|
|
if arg == '--clear-shelf':
|
|
clearShelf()
|
|
elif arg == '--list-dicts':
|
|
dictsJson = listDicts()
|
|
print json.dumps( dictsJson )
|
|
elif arg == '--get-sums':
|
|
dictSums = getDictSums()
|
|
for arg in sys.argv[2:]:
|
|
print arg, md5Checksums(dictSums, arg)
|
|
s_shelf[k_SUMS] = dictSums
|
|
if 'close' in s_shelf: s_shelf.close()
|
|
elif arg == '--test-get-app':
|
|
if not 5 == len(sys.argv): usage()
|
|
params = { k_NAME: sys.argv[2],
|
|
k_AVERS: int(sys.argv[3]),
|
|
k_GVERS: sys.argv[4],
|
|
}
|
|
print getApp( params )
|
|
elif arg == '--test-get-dicts':
|
|
if not 5 == len(sys.argv): usage()
|
|
params = { k_NAME: sys.argv[2],
|
|
k_LANG : sys.argv[3],
|
|
k_MD5SUM : sys.argv[4],
|
|
k_INDEX : 0,
|
|
}
|
|
print getDicts( [params] )
|
|
elif arg == '--list-apks':
|
|
argc = len(sys.argv)
|
|
if argc >= 4: usage()
|
|
path = ""
|
|
if argc >= 3: path = sys.argv[2]
|
|
apks = getOrderedApks( path )
|
|
if 0 == len(apks): print "No apks in", path
|
|
for apk in apks:
|
|
print apk
|
|
else:
|
|
usage()
|
|
|
|
##############################################################################
|
|
if __name__ == '__main__':
|
|
main()
|