Source code for ffflash.inc.rankfile

from operator import itemgetter

from ffflash.info import info
from ffflash.lib.clock import get_iso_timestamp
from ffflash.lib.files import dump_file, load_file
from ffflash.lib.locations import check_file_extension, check_file_location
from ffflash.lib.text import make_pretty


[docs]def _rankfile_load(ff): ''' Load either existing ``rankfile`` from disk, or create empty stub if one does not exist yet. Path and extension (*json*) get validated. :param ff: running :class:`ffflash.main.FFFlash` instance :return: Tuple of either (``False``, ``None``) on error or: * validated path to the ``rankfile`` * ``rankfile`` content ''' if not ff.access_for('rankfile'): return (False, None) ff.log('handling rankfile {}'.format(ff.args.rankfile)) rankfile = check_file_location(ff.args.rankfile, must_exist=False) if not rankfile: return ff.log( 'wrong path for rankfile {}'.format(ff.args.rankfile), level=False ), None if not all(check_file_extension(rankfile, 'json')): return ff.log( 'rankfile {} is no json'.format(rankfile), level=False ), None ranks = load_file(rankfile, fallback={ 'updated_at': 'never', 'nodes': [] }, as_yaml=False) if not ranks or not isinstance(ranks, dict): return ff.log( 'could not load rankfile {}'.format(rankfile), level=False ), None if not all([(a in ranks) for a in ['nodes', 'updated_at']]): return ff.log( 'this is no rankfile {}'.format(rankfile), level=False ), None lranks = len(ranks.get('nodes', 0)) ff.log(( 'creating new rankfile {}'.format(rankfile) if lranks == 0 else 'loaded {} nodes'.format(lranks) )) return rankfile, ranks
[docs]def _rankfile_score(ff, ranks, nodelist): ''' Do some number crunching. :param ff: running :class:`ffflash.main.FFFlash` instance :param ranks: rankfile content from :meth:`_rankfile_load`, should contain a list of dictionaries at the key *nodes* :param nodelist: nodelist from :meth:`ffflash.inc.nodelist._nodelist_fetch`, should contain a list of dictionaries at the key *nodes* :return: ``ranks`` with new scores, sorted by score ''' if not ff.access_for('rankfile'): return False if not all([ ranks, isinstance(ranks, dict), ranks and 'nodes' in ranks, nodelist, isinstance(nodelist, dict), nodelist and 'nodes' in nodelist, ]): return ff.log('wrong input data passed', level=False) res = [] exist = dict( (node.get('id'), node.get('score')) for node in ranks.get('nodes', []) ) for node in nodelist.get('nodes', []): nid = node.get('id') if not nid: ff.log('node without id {}'.format(node)) continue nr = { 'id': nid, 'score': exist.get(nid, ff.args.rankwelcome), 'name': node.get('name') } if node.get('position', False): nr['score'] += ff.args.rankposition if node.get('status', {}).get('online', False): nr['score'] += ff.args.rankonline nr['online'] = True cl = node.get('status', {}).get('clients', 0) nr['score'] += (ff.args.rankclients * cl) nr['clients'] = cl else: nr['score'] -= ff.args.rankoffline nr['online'] = False nr['clients'] = 0 if nr['score'] > 0: res.append(nr) ranks['nodes'] = list(sorted(res, key=itemgetter('score'), reverse=True)) ff.log('scored {} nodes for rankfile'.format(len(ranks.get('nodes')))) return ranks
[docs]def _rankfile_dump(ff, rankfile, ranks): ''' Store ranks in ``rankfile``. Also sets a timestamp and writes the release string into the output. :param ff: running :class:`ffflash.main.FFFlash` instance :param rankfile: validated path to the ``rankfile`` :param ranks: content to store :return: ``True`` on success, or ``False`` on error ''' if not ff.access_for('rankfile'): return False if not all([ rankfile, ranks, isinstance(ranks, dict), all([ (a in ranks) for a in ['nodes', 'updated_at'] if ranks ]) ]): return ff.log('wrong input data passed', level=False) ranks['updated_at'] = get_iso_timestamp() ranks['version'] = info.release if ff.args.dry: ff.log('\n{}'.format(make_pretty(ranks)), level='rankfile preview') return False dump_file(rankfile, ranks, as_yaml=False) ff.log('successfully stored rankfile {}'.format(rankfile)) return True
[docs]def handle_rankfile(ff, nodelist): ''' Entry function gather results from a retrieved ``--nodelist`` to store it into the ``--rankfile``. :param ff: running :class:`ffflash.main.FFFlash` instance :return: ``True`` if rankfile was modified else ``False`` ''' if not ff.access_for('rankfile'): return False if not nodelist or not isinstance(nodelist, dict): return False rankfile, ranks = _rankfile_load(ff) if not all([rankfile, ranks]): return False ranks = _rankfile_score(ff, ranks, nodelist) if not ranks: return False modified = [] modified.append( _rankfile_dump(ff, rankfile, ranks) ) return any(modified)