From ebf18ba0209c8b8f6a3dab1b0858787a9e800b22 Mon Sep 17 00:00:00 2001 From: Michael Shepanski Date: Thu, 9 Feb 2017 16:29:23 -0500 Subject: [PATCH] Update fetchItem to search all subitems as well --- plexapi/base.py | 74 ++++++++++++++++++++++++++++------------------ tools/plexattrs.py | 43 +++++++++++++++++---------- 2 files changed, 73 insertions(+), 44 deletions(-) diff --git a/plexapi/base.py b/plexapi/base.py index 52c6cad0..eb307854 100644 --- a/plexapi/base.py +++ b/plexapi/base.py @@ -169,40 +169,58 @@ class PlexObject(object): return self def _checkAttrs(self, elem, **kwargs): - for kwarg, query in kwargs.items(): - # strip underscore from special cased attrs - if kwarg in ('_key', '_cls', '_tag'): - kwarg = kwarg[1:] - # extract the kwarg operator if present - op, operator = 'exact', OPERATORS['exact'] - if '__' in kwarg: - kwarg, op = kwarg.rsplit('__', 1) - if op not in OPERATORS: - raise BadRequest('Invalid filter operator: __%s' % op) - operator = OPERATORS[op] - # get value from elem and check ismissing operator - value = elem.attrib.get(kwarg) - log.debug("Checking %s.%s__%s=%s (value=%s)", elem.tag, kwarg, op, - str(query)[:20], str(value)[:20]) + logme = elem.attrib.get('ratingKey') == '746' + attrsFound = {} + for attr, query in kwargs.items(): + attr, op, operator = self._getAttrOperator(attr) + values = self._getAttrValue(elem, attr) + if logme: log.debug('Check %s.%s__%s=%s (value=%s)', elem.tag, attr, op, str(query)[:20], str(values)[:20]) + # special case ismissing operator if op == 'ismissing': if query not in (True, False): raise BadRequest('Value when using __ismissing must be in (True, False).') - if (query is True and value) or (query is False and not value): + if (query is True and values) or (query is False and not values): return False - # special case query=None,0,'' to include missing attr - if op == 'exact' and query in (None, 0, '') and value is None: + # special case query in (None,0,'') to include missing attr + if op == 'exact' and query in (None, 0, '') and not values: return True # return if attr were looking for is missing - if not value: - return False - # cast value to the same type as query - if isinstance(query, int): value = int(value) - if isinstance(query, float): value = float(value) - if isinstance(query, bool): value = bool(int(value)) - # perform the comparison - if not operator(value, query): - return False - return True + attrsFound[attr] = False + for value in values: + if isinstance(query, int): value = int(value) + if isinstance(query, float): value = float(value) + if isinstance(query, bool): value = bool(int(value)) + if logme: log.info('%s %s %s', value, op, query) + if operator(value, query): + attrsFound[attr] = True + break + if logme: log.info(attrsFound) + return all(attrsFound.values()) + + def _getAttrOperator(self, attr): + attr = attr.lstrip('_') + for op, operator in OPERATORS.items(): + if attr.endswith('__%s' % op): + attr = attr.rsplit('__', 1)[0] + return attr, op, operator + # default to exact match + return attr, 'exact', OPERATORS['exact'] + + def _getAttrValue(self, elem, attrstr, results=None): + #log.debug('Fetching %s in %s', attrstr, elem.tag) + parts = attrstr.split('__', 1) + attr = parts[0] + attrstr = parts[1] if len(parts) == 2 else None + if attrstr: + results = [] if results is None else results + for child in [c for c in elem if c.tag.lower() == attr.lower()]: + results += self._getAttrValue(child, attrstr, results) + return [r for r in results if r is not None] + # loop through attrs so we can perform case-insensative match + for _attr, value in elem.attrib.items(): + if attr.lower() == _attr.lower(): + return [value] + return [] def _loadData(self, data): raise NotImplementedError('Abstract method not implemented.') diff --git a/tools/plexattrs.py b/tools/plexattrs.py index 2d5edf46..7fbc472b 100755 --- a/tools/plexattrs.py +++ b/tools/plexattrs.py @@ -173,7 +173,7 @@ class PlexAttributes(): self._load_attrs(playqueue, 'pq') def _parse_sync(self): - # TODO: Get this working.. + # TODO: Get plexattrs._parse_sync() working. pass def _load_attrs(self, obj, cat=None): @@ -229,29 +229,39 @@ class PlexAttributes(): def print_report(self): total_attrs = 0 for clsname in sorted(self.attrs.keys()): - meta = self.attrs[clsname] - count = meta['total'] - print(_('\n%s (%s)\n%s' % (clsname, count, '-'*30), 'yellow')) - attrs = sorted(set(list(meta['xml'].keys()) + list(meta['obj'].keys()))) - for attr in attrs: - state = self._attr_state(clsname, attr, meta) - count = meta['xml'].get(attr, 0) - categories = ','.join(meta['categories'].get(attr, ['--'])) - examples = '; '.join(list(meta['examples'].get(attr, ['--']))[:3])[:80] - print('%7s %3s %-30s %-20s %s' % (count, state, attr, categories, examples)) - total_attrs += count + if self._clsname_match(clsname): + meta = self.attrs[clsname] + count = meta['total'] + print(_('\n%s (%s)\n%s' % (clsname, count, '-'*30), 'yellow')) + attrs = sorted(set(list(meta['xml'].keys()) + list(meta['obj'].keys()))) + for attr in attrs: + state = self._attr_state(clsname, attr, meta) + count = meta['xml'].get(attr, 0) + categories = ','.join(meta['categories'].get(attr, ['--'])) + examples = '; '.join(list(meta['examples'].get(attr, ['--']))[:3])[:80] + print('%7s %3s %-30s %-20s %s' % (count, state, attr, categories, examples)) + total_attrs += count print(_('\nSUMMARY\n%s' % ('-'*30), 'yellow')) print('%7s %3s %3s %-20s %s' % ('total', 'new', 'old', 'categories', 'clsname')) for clsname in sorted(self.attrs.keys()): - print('%7s %12s %12s %s' % (self.attrs[clsname]['total'], - _(self.attrs[clsname]['new'] or '', 'cyan'), - _(self.attrs[clsname]['old'] or '', 'red'), - clsname)) + if self._clsname_match(clsname): + print('%7s %12s %12s %s' % (self.attrs[clsname]['total'], + _(self.attrs[clsname]['new'] or '', 'cyan'), + _(self.attrs[clsname]['old'] or '', 'red'), + clsname)) print('\nPlex Version %s' % self.plex.version) print('PlexAPI Version %s' % plexapi.VERSION) print('Total Objects %s' % sum([x['total'] for x in self.attrs.values()])) print('Runtime %s min\n' % self.runtime) + def _clsname_match(self, clsname): + if not self.clsnames: + return True + for cname in self.clsnames: + if cname.lower() in clsname.lower(): + return True + return False + def _attr_state(self, clsname, attr, meta): if attr in meta['xml'].keys() and attr not in meta['obj'].keys(): self.attrs[clsname]['new'] += 1 @@ -280,6 +290,7 @@ if __name__ == '__main__': if not opts.force and os.path.exists(CACHEPATH): with open(CACHEPATH, 'rb') as handle: plexattrs = pickle.load(handle) + plexattrs.clsnames = [c for c in opts.clsnames.split(',') if c] if not plexattrs: plexattrs = PlexAttributes(opts).run() with open(CACHEPATH, 'wb') as handle: