Source code for onix.contexts

"""Object, factories and utilities for bundling and accessing resources"""
import datetime
import json

from future.utils import iteritems

from onix import scrapers
import onix.utilities as ut


[docs]class Context(object): """ Grouping of resources and static lookups, bundled together for ease of access. Notes: Be careful when using this in a multiprocessor context, as transmitting all the lookups to each worker process is a metric ton of overhead when it's extremely unlikely that each worker needs *every single* resource. Args: **resources : the resources to be bundled. Note that there's no need to limit one's self to the attributes listed below. Attributes: aliases (dict) : the data encoded in `aliases.js` on PS. The keys are the alternate names, the values are the correct names. formats (dict) : the data encoded in `formats.js` on PS, post-processed for increased utility. The keys are the sanitized format names. The values are the various configurations and metadata for the metagame. formats_data (dict) : the data encoded in `formats-data.js` on PS. The keys are the species / forme names, the values contain information like current tier and random battle move pool items (dict) : the data encoded in `items.js` on PS. The keys are sanitized item names, the values associated metadata including, for mega stones, which Pokemon it mega-evolves. moves (dict) : the data encoded in `moves.js` on PS. The keys are sanitized move names, the values the associated metadata including move type and base power pokedex (dict) : the data encoded in `pokedex.js` on PS. The keys are the species / forme names. The values contain information like base stats and valid abilities. accessible_formes (dict) : a mapping of species and formes to the routes they have to access other formes. natures (dict) : The keys are the sanitized nature names, the values the associated metadata, such as which stat gets boosted and which gets lowered. species_lookup (dict) : mapping of sanitized species names or forme-concatenations (e.g. "sceptile,sceptilemega") to their display names. This is what handles things like determing whether megas are tiered together or separately or what counts as an "appearance-only" forme. sanitizer (onix.utilities.Sanitizer) : the sanitizer to use to normalize data and model objedts. """ def __init__(self, **resources): self.aliases = None self.formats = None self.formats_data = None self.items = None self.moves = None self.pokedex = None self.accessible_formes = None self.natures = None self.species_lookup = None self.sanitizer = None for name, resource in iteritems(resources): setattr(self, name, resource)
[docs]class ResourceMissingError(Exception): """Raised if an expected resource is not present in a given context Args: resource (str) : the name of the missing resource """ def __init__(self, resource): msg = 'Context does not include the "{0}" resource'.format(resource) super(ResourceMissingError, self).__init__(msg)
[docs]def require(context, *resources): """ Validate that the specified ``Context`` has all the specified resources Args: context (Context) : the context to validate *resources : the names of the attributes to require Raises: ResourceMissingError: if a required resource is missing from the context """ for resource in resources: if hasattr(context, resource): if getattr(context, resource) is not None: continue raise ResourceMissingError(resource)
def _get_context(commit, force_refresh): psdata = dict(aliases=None, formats=None, formats_data=None, items=None, moves=None, pokedex=None) if not force_refresh: folder = '.psdata/' if commit: folder += '{}'.format(commit) for resource in psdata.keys(): try: psdata[resource] = json.load(open('{0}/{1}.json' .format(folder, resource))) except IOError: pass if psdata['aliases'] is None: psdata['aliases'] = scrapers.scrape_battle_aliases(commit=commit) if psdata['formats'] is None: psdata['formats'] = scrapers.scrape_formats(commit=commit) if psdata['formats_data'] is None: psdata['formats_data'] = scrapers.scrape_battle_formats_data(commit) if psdata['items'] is None: psdata['items'] = scrapers.scrape_battle_items(commit=commit) if psdata['moves'] is None: psdata['moves'] = scrapers.scrape_battle_movedex(commit=commit) if psdata['pokedex'] is None: psdata['pokedex'] = scrapers.scrape_battle_pokedex(commit=commit) return Context(accessible_formes=ut.load_accessible_formes(), natures=ut.load_natures(), species_lookup=ut.load_species_lookup(), sanitizer=ut.Sanitizer(psdata['pokedex'], psdata['aliases']), **psdata)
[docs]def get_standard_context(force_refresh=False): """ Create a ``Context`` with all the standard (current generation, non-mod) resources. Args: force_refresh (:obj:`bool`, optional) : By default, this method will try to load the resources from the local file cache. Set to False to force it to freshly download scrape the Pokemon Showdown data. Returns: Context : A context with all the standard resources """ return _get_context(None, force_refresh)
[docs]def get_historical_context(timestamp): """ Create a ``Context`` with all the resources as they were at a given date and time. Useful for performing historical analyses or for working around mid-month changes Args: timestamp (datetime.datetime) : The date and time (in UTC) desired Returns: Context : A context with all the resources as they were at the specified ``timestamp`` """ return _get_context(scrapers.get_commit_from_timestamp(timestamp), False)