def waitForWindowToDisappear(self, winName, timeout=<NUM_LIT:10>):

callback = AXCallbacks.elemDisappearedCallback<EOL>retelem = None<EOL>args = (retelem, self)<EOL>win = self.findFirst(AXRole='<STR_LIT>', AXTitle=winName)<EOL>return self.waitFor(timeout, '<STR_LIT>',<EOL>callback=callback, args=args,<EOL>AXRole='<STR_LIT>', AXTitle=winName)<EOL>

Convenience method to wait for a window with the given name to
        disappear.

        Returns: Boolean


return self.waitForCreation(timeout, '<STR_LIT>')<EOL>

Convenience method to wait for a sheet to appear.

        Returns: the sheet that appeared (element) or None


<EOL>callback = AXCallbacks.returnElemCallback<EOL>retelem = None<EOL>return self.waitFor(timeout, '<STR_LIT>', callback=callback,<EOL>args=(retelem,))<EOL>

Convenience method to wait for value attribute of given element to
        change.

        Some types of elements (e.g. menu items) have their titles change,
        so this will not work for those.  This seems to work best if you set
        the notification at the application level.

        Returns: Element or None


def waitForFocusToChange(self, newFocusedElem, timeout=<NUM_LIT:10>):

return self.waitFor(timeout, '<STR_LIT>',<EOL>AXRole=newFocusedElem.AXRole,<EOL>AXPosition=newFocusedElem.AXPosition)<EOL>

Convenience method to wait for focused element to change (to new
        element given).

        Returns: Boolean


def waitForFocusedWindowToChange(self, nextWinName, timeout=<NUM_LIT:10>):

callback = AXCallbacks.returnElemCallback<EOL>retelem = None<EOL>return self.waitFor(timeout, '<STR_LIT>',<EOL>AXTitle=nextWinName)<EOL>

Convenience method to wait for focused window to change

        Returns: Boolean


def _convenienceMatch(self, role, attr, match):

kwargs = {}<EOL>if match:<EOL><INDENT>kwargs[attr] = match<EOL><DEDENT>return self.findAll(AXRole=role, **kwargs)<EOL>

Method used by role based convenience functions to find a match


def _convenienceMatchR(self, role, attr, match):

kwargs = {}<EOL>if match:<EOL><INDENT>kwargs[attr] = match<EOL><DEDENT>return self.findAllR(AXRole=role, **kwargs)<EOL>


return self._convenienceMatch('<STR_LIT>', '<STR_LIT>', match)<EOL>

Return a list of text areas with an optional match parameter.


return self._convenienceMatchR('<STR_LIT>', '<STR_LIT>', match)<EOL>


Return a list of textfields with an optional match parameter.


return self._convenienceMatchR('<STR_LIT>', '<STR_LIT>',<EOL>match)<EOL>


Return a list of buttons with an optional match parameter.


Return a list of windows with an optional match parameter.


Return a list of sheets with an optional match parameter.


Return a list of statictexts with an optional match parameter.


Return a list of statictexts with an optional match parameter


Return a list of genericelements with an optional match parameter.


Return a list of groups with an optional match parameter.


Return a list of radio buttons with an optional match parameter.


Return a list of popup menus with an optional match parameter.


Return a list of rows with an optional match parameter.


Return a list of sliders with an optional match parameter.


logger.addHandler(handler)<EOL>return <NUM_LIT:1><EOL>

Add custom log handler
@param handler: Handler instance
@type handler: object

@return: 1 on success and 0 on error
@rtype: integer


logger.removeHandler(handler)<EOL>return <NUM_LIT:1><EOL>

Remove custom log handler
@param handler: Handler instance
@type handler: object

@return: 1 on success and 0 on error
@rtype: integer


if _ldtp_debug:<EOL><INDENT>print(message)<EOL><DEDENT>logger.log(level, str(message))<EOL>return <NUM_LIT:1><EOL>

Logs the message in the root logger with the log level
@param message: Message to be logged
@type message: string
@param level: Log level, defaul DEBUG
@type level: integer

@return: 1 on success and 0 on error
@rtype: integer


if not filename:<EOL><INDENT>return <NUM_LIT:0><EOL><DEDENT>if overwrite:<EOL><INDENT>_mode = '<STR_LIT:w>'<EOL><DEDENT>else:<EOL><INDENT>_mode = '<STR_LIT:a>'<EOL><DEDENT>global _file_logger<EOL>_file_logger = logging.FileHandler(os.path.expanduser(filename), _mode)<EOL>_formatter = logging.Formatter('<STR_LIT>')<EOL>_file_logger.setFormatter(_formatter)<EOL>logger.addHandler(_file_logger)<EOL>if _ldtp_debug:<EOL><INDENT>_file_logger.setLevel(logging.DEBUG)<EOL><DEDENT>else:<EOL><INDENT>_file_logger.setLevel(logging.ERROR)<EOL><DEDENT>return <NUM_LIT:1><EOL>

@param filename: Start logging on the specified file
@type filename: string
@param overwrite: Overwrite or append
    False - Append log to an existing file
    True - Write log to a new file. If file already exist,
    then erase existing file content and start log
@type overwrite: boolean

@return: 1 on success and 0 on error
@rtype: integer


global _file_logger<EOL>if _file_logger:<EOL><INDENT>logger.removeHandler(_file_logger)<EOL>_file_logger = None<EOL><DEDENT>return <NUM_LIT:1><EOL>

Stop logging.

    @return: 1 on success and 0 on error
    @rtype: integer


def imagecapture(window_name=None, out_file=None, x=<NUM_LIT:0>, y=<NUM_LIT:0>,<EOL>width=None, height=None):

if not out_file:<EOL><INDENT>out_file = tempfile.mktemp('<STR_LIT>', '<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>out_file = os.path.expanduser(out_file)<EOL><DEDENT>if _ldtp_windows_env:<EOL><INDENT>if width == None:<EOL><INDENT>width = -<NUM_LIT:1><EOL><DEDENT>if height == None:<EOL><INDENT>height = -<NUM_LIT:1><EOL><DEDENT>if window_name == None:<EOL><INDENT>window_name = '<STR_LIT>'<EOL><DEDENT><DEDENT>data = _remote_imagecapture(window_name, x, y, width, height)<EOL>f = open(out_file, '<STR_LIT:wb>')<EOL>f.write(b64decode(data))<EOL>f.close()<EOL>return out_file<EOL>

Captures screenshot of the whole desktop or given window

@param window_name: Window name to look for, either full name,
LDTP's name convention, or a Unix glob.
@type window_name: string
@param x: x co-ordinate value
@type x: integer
@param y: y co-ordinate value
@type y: integer
@param width: width co-ordinate value
@type width: integer
@param height: height co-ordinate value
@type height: integer

@return: screenshot filename
@rtype: string


def onwindowcreate(window_name, fn_name, *args):

_pollEvents._callback[window_name] = ["<STR_LIT>", fn_name, args]<EOL>return _remote_onwindowcreate(window_name)<EOL>

On window create, call the function with given arguments

@param window_name: Window name to look for, either full name,
LDTP's name convention, or a Unix glob.
@type window_name: string
@param fn_name: Callback function
@type fn_name: function
@param *args: arguments to be passed to the callback function
@type *args: var args

@return: 1 if registration was successful, 0 if not.
@rtype: integer


if window_name in _pollEvents._callback:<EOL><INDENT>del _pollEvents._callback[window_name]<EOL><DEDENT>return _remote_removecallback(window_name)<EOL>

Remove registered callback on window create

@param window_name: Window name to look for, either full name,
LDTP's name convention, or a Unix glob.
@type window_name: string

@return: 1 if registration was successful, 0 if not.
@rtype: integer


def registerevent(event_name, fn_name, *args):

if not isinstance(event_name, str):<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>_pollEvents._callback[event_name] = [event_name, fn_name, args]<EOL>return _remote_registerevent(event_name)<EOL>

Register at-spi event

@param event_name: Event name in at-spi format.
@type event_name: string
@param fn_name: Callback function
@type fn_name: function
@param *args: arguments to be passed to the callback function
@type *args: var args

@return: 1 if registration was successful, 0 if not.
@rtype: integer


if event_name in _pollEvents._callback:<EOL><INDENT>del _pollEvents._callback[event_name]<EOL><DEDENT>return _remote_deregisterevent(event_name)<EOL>

Remove callback of registered event

@param event_name: Event name in at-spi format.
@type event_name: string

@return: 1 if registration was successful, 0 if not.
@rtype: integer


def registerkbevent(keys, modifiers, fn_name, *args):

event_name = "<STR_LIT>" % (keys, modifiers)<EOL>_pollEvents._callback[event_name] = [event_name, fn_name, args]<EOL>return _remote_registerkbevent(keys, modifiers)<EOL>

Register keystroke events

@param keys: key to listen
@type keys: string
@param modifiers: control / alt combination using gtk MODIFIERS
@type modifiers: int
@param fn_name: Callback function
@type fn_name: function
@param *args: arguments to be passed to the callback function
@type *args: var args

@return: 1 if registration was successful, 0 if not.
@rtype: integer


event_name = "<STR_LIT>" % (keys, modifiers)<EOL>if event_name in _pollEvents._callback:<EOL><INDENT>del _pollEvents._callback[event_name]<EOL><DEDENT>return _remote_deregisterkbevent(keys, modifiers)<EOL>

Remove callback of registered event

@param keys: key to listen
@type keys: string
@param modifiers: control / alt combination using gtk MODIFIERS
@type modifiers: int

@return: 1 if registration was successful, 0 if not.
@rtype: integer


tmp_time = _remote_windowuptime(window_name)<EOL>if tmp_time:<EOL><INDENT>tmp_time = tmp_time.split('<STR_LIT:->')<EOL>start_time = tmp_time[<NUM_LIT:0>].split('<STR_LIT:U+0020>')<EOL>end_time = tmp_time[<NUM_LIT:1>].split('<STR_LIT:U+0020>')<EOL>_start_time = datetime.datetime(int(start_time[<NUM_LIT:0>]), int(start_time[<NUM_LIT:1>]),<EOL>int(start_time[<NUM_LIT:2>]), int(start_time[<NUM_LIT:3>]),<EOL>int(start_time[<NUM_LIT:4>]), int(start_time[<NUM_LIT:5>]))<EOL>_end_time = datetime.datetime(int(end_time[<NUM_LIT:0>]), int(end_time[<NUM_LIT:1>]),<EOL>int(end_time[<NUM_LIT:2>]), int(end_time[<NUM_LIT:3>]),<EOL>int(end_time[<NUM_LIT:4>]), int(end_time[<NUM_LIT:5>]))<EOL>return _start_time, _end_time<EOL><DEDENT>return None<EOL>

Get window uptime

@param window_name: Window name to look for, either full name,
LDTP's name convention, or a Unix glob.
@type window_name: string

@return: "starttime, endtime" as datetime python object


try:<EOL><INDENT>self.alive = False<EOL><DEDENT>except:<EOL><INDENT>pass<EOL><DEDENT>


try:<EOL><INDENT>self.alive = False<EOL>self.join(self.sleep_time)<EOL><DEDENT>except:<EOL><INDENT>pass<EOL><DEDENT>


Stop callback when destroying this class


def elemDisappearedCallback(retelem, obj, **kwargs):

return not obj.findFirstR(**kwargs)<EOL>

Callback for checking if a UI element is no longer onscreen.

    kwargs should contains some unique set of identifier (e.g. title/value, role)
    Returns:  Boolean


return retelem<EOL>

Callback for when a sheet appears.

    Returns: element returned by observer callback


return open(os.path.join(os.path.dirname(__file__), fname)).read()<EOL>

Returns the contents of the specified file located in the same dir as
the script


@classmethod<EOL><INDENT>def Async(cls, token, session=None, **options):<DEDENT>

return cls(token, session=session, is_async=True, **options)<EOL>


return self._get_model(self.api.VERSION)<EOL>

Gets the version of RoyaleAPI. Returns a string


return self._get_model(self.api.ENDPOINTS)<EOL>

Gets a list of endpoints available in RoyaleAPI


@typecasted<EOL><INDENT>def get_constants(self, **params: keys):<DEDENT>

url = self.api.CONSTANTS<EOL>return self._get_model(url, **params)<EOL>

Get the CR Constants

        Parameters
        ----------
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_player(self, *tags: crtag, **params: keys):<DEDENT>

url = self.api.PLAYER + '<STR_LIT:/>' + '<STR_LIT:U+002C>'.join(tags)<EOL>return self._get_model(url, FullPlayer, **params)<EOL>

Get a player information

        Parameters
        ----------
        \*tags: str
            Valid player tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_player_verify(self, tag: crtag, apikey: str, **params: keys):<DEDENT>

url = self.api.PLAYER + '<STR_LIT:/>' + tag + '<STR_LIT>'<EOL>params.update({'<STR_LIT>': apikey})<EOL>return self._get_model(url, FullPlayer, **params)<EOL>

Check the API Key of a player.
        This endpoint has been **restricted** to
        certain members of the community

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        apikey: str
            The API Key in the player's settings
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_player_battles(self, *tags: crtag, **params: keys):<DEDENT>

url = self.api.PLAYER + '<STR_LIT:/>' + '<STR_LIT:U+002C>'.join(tags) + '<STR_LIT>'<EOL>return self._get_model(url, **params)<EOL>

Get a player's battle log

        Parameters
        ----------
        \*tags: str
            Valid player tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_player_chests(self, *tags: crtag, **params: keys):<DEDENT>

Get information about a player's chest cycle

        Parameters
        ----------
        \*tags: str
            Valid player tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_clan(self, *tags: crtag, **params: keys):<DEDENT>

url = self.api.CLAN + '<STR_LIT:/>' + '<STR_LIT:U+002C>'.join(tags)<EOL>return self._get_model(url, FullClan, **params)<EOL>

Get a clan information

        Parameters
        ----------
        \*tags: str
            Valid clan tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted  <EOL><INDENT>def search_clans(self, **params: clansearch):<DEDENT>

url = self.api.CLAN + '<STR_LIT>'<EOL>return self._get_model(url, PartialClan, **params)<EOL>

Search for a clan. At least one
        of the filters must be present

        Parameters
        ----------
        name: Optional[str]
            The name of a clan
        minMembers: Optional[int]
            The minimum member count
            of a clan
        maxMembers: Optional[int]
            The maximum member count
            of a clan
        score: Optional[int]
            The minimum trophy score of
            a clan
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


def get_tracking_clans(self, **params: keys):

url = self.api.CLAN + '<STR_LIT>'<EOL>return self._get_model(url, **params)<EOL>

Get a list of clans that are being
        tracked by having either cr-api.com or
        royaleapi.com in the description

        Parameters
        ----------
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_clan_tracking(self, *tags: crtag, **params: keys):<DEDENT>

url = self.api.CLAN + '<STR_LIT:/>' + '<STR_LIT:U+002C>'.join(tags) + '<STR_LIT>'<EOL>return self._get_model(url, **params)<EOL>

Returns if the clan is currently being tracked
        by the API by having either cr-api.com or royaleapi.com
        in the clan description

        Parameters
        ----------
        \*tags: str
            Valid clan tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_clan_battles(self, *tags: crtag, **params: keys):<DEDENT>

Get the battle log from everyone in the clan

        Parameters
        ----------
        \*tags: str
            Valid player tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*type: str
            Filters what kind of battles. Pick from:
            :all:, :war:, :clanMate:
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_clan_history(self, *tags: crtag, **params: keys):<DEDENT>

Get the clan history. Only works if the clan is being tracked
        by having either cr-api.com or royaleapi.com in the clan's
        description

        Parameters
        ----------
        \*tags: str
            Valid clan tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_clan_war(self, tag: crtag, **params: keys):<DEDENT>

url = self.api.CLAN + '<STR_LIT:/>' + tag + '<STR_LIT>'<EOL>return self._get_model(url, **params)<EOL>

Get inforamtion about a clan's current clan war

        Parameters
        ----------
        *tag: str
            A valid clan tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_clan_war_log(self, tag: crtag, **params: keys):<DEDENT>

Get a clan's war log

        Parameters
        ----------
        \*tags: str
            Valid clan tags. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_tournament(self, tag: crtag, **params: keys):<DEDENT>

url = self.api.TOURNAMENT + '<STR_LIT:/>' + tag<EOL>return self._get_model(url, **params)<EOL>

Get a tournament information

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def search_tournaments(self, **params: keys):<DEDENT>

url = self.api.TOURNAMENT + '<STR_LIT>'<EOL>return self._get_model(url, PartialClan, **params)<EOL>

Search for a tournament

        Parameters
        ----------
        name: str
            The name of the tournament
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_top_clans(self, country_key='<STR_LIT>', **params: keys):<DEDENT>

url = self.api.TOP + '<STR_LIT>' + str(country_key)<EOL>return self._get_model(url, PartialClan, **params)<EOL>

Get a list of top clans by trophy

        location_id: Optional[str] = ''
            A location ID or '' (global)
            See https://github.com/RoyaleAPI/cr-api-data/blob/master/json/regions.json
            for a list of acceptable location IDs
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_top_war_clans(self, country_key='<STR_LIT>', **params: keys):<DEDENT>

Get a list of top clans by war

        location_id: Optional[str] = ''
            A location ID or '' (global)
            See https://github.com/RoyaleAPI/cr-api-data/blob/master/json/regions.json
            for a list of acceptable location IDs
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_top_players(self, country_key='<STR_LIT>', **params: keys):<DEDENT>

url = self.api.TOP + '<STR_LIT>' + str(country_key)<EOL>return self._get_model(url, PartialPlayerClan, **params)<EOL>

Get a list of top players

        location_id: Optional[str] = ''
            A location ID or '' (global)
            See https://github.com/RoyaleAPI/cr-api-data/blob/master/json/regions.json
            for a list of acceptable location IDs
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_popular_clans(self, **params: keys):<DEDENT>

url = self.api.POPULAR + '<STR_LIT>'<EOL>return self._get_model(url, PartialClan, **params)<EOL>

Get a list of most queried clans

        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_popular_players(self, **params: keys):<DEDENT>

url = self.api.POPULAR + '<STR_LIT>'<EOL>return self._get_model(url, PartialPlayerClan, **params)<EOL>

Get a list of most queried players

        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_popular_tournaments(self, **params: keys):<DEDENT>

url = self.api.POPULAR + '<STR_LIT>'<EOL>return self._get_model(url, PartialTournament, **params)<EOL>

Get a list of most queried tournaments

        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_popular_decks(self, **params: keys):<DEDENT>

url = self.api.POPULAR + '<STR_LIT>'<EOL>return self._get_model(url, **params)<EOL>

Get a list of most queried decks

        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_known_tournaments(self, **params: tournamentfilter):<DEDENT>

url = self.api.TOURNAMENT + '<STR_LIT>'<EOL>return self._get_model(url, PartialTournament, **params)<EOL>

Get a list of queried tournaments

        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_open_tournaments(self, **params: tournamentfilter):<DEDENT>

Get a list of open tournaments

        \*\*1k: Optional[int] = 0
            Set to 1 to filter tournaments that have
            at least 1000 max players
        \*\*full: Optional[int] = 0
            Set to 1 to filter tournaments that are
            full
        \*\*inprep: Optional[int] = 0
            Set to 1 to filter tournaments that are
            in preperation
        \*\*joinable: Optional[int] = 0
            Set to 1 to filter tournaments that are
            joinable
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_1k_tournaments(self, **params: tournamentfilter):<DEDENT>

Get a list of tournaments that have at least 1000
        max players

        \*\*open: Optional[int] = 0
            Set to 1 to filter tournaments that are
            open
        \*\*full: Optional[int] = 0
            Set to 1 to filter tournaments that are
            full
        \*\*inprep: Optional[int] = 0
            Set to 1 to filter tournaments that are
            in preperation
        \*\*joinable: Optional[int] = 0
            Set to 1 to filter tournaments that are
            joinable
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_prep_tournaments(self, **params: tournamentfilter):<DEDENT>

Get a list of tournaments that are in preperation

        \*\*1k: Optional[int] = 0
            Set to 1 to filter tournaments that have
            at least 1000 max players
        \*\*open: Optional[int] = 0
            Set to 1 to filter tournaments that are
            open
        \*\*full: Optional[int] = 0
            Set to 1 to filter tournaments that are
            full
        \*\*joinable: Optional[int] = 0
            Set to 1 to filter tournaments that are
            joinable
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_joinable_tournaments(self, **params: tournamentfilter):<DEDENT>

Get a list of tournaments that are joinable

        \*\*1k: Optional[int] = 0
            Set to 1 to filter tournaments that have
            at least 1000 max players
        \*\*open: Optional[int] = 0
            Set to 1 to filter tournaments that are
            open
        \*\*full: Optional[int] = 0
            Set to 1 to filter tournaments that are
            full
        \*\*inprep: Optional[int] = 0
            Set to 1 to filter tournaments that are
            in preperation
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_full_tournaments(self, **params: tournamentfilter):<DEDENT>

Get a list of tournaments that are full

        \*\*1k: Optional[int] = 0
            Set to 1 to filter tournaments that have
            at least 1000 max players
        \*\*open: Optional[int] = 0
            Set to 1 to filter tournaments that are
            open
        \*\*inprep: Optional[int] = 0
            Set to 1 to filter tournaments that are
            in preperation
        \*\*joinable: Optional[int] = 0
            Set to 1 to filter tournaments that are
            joinable
        \*\*keys: Optional[list] = None
            Filter which keys should be included in the
            response
        \*\*exclude: Optional[list] = None
            Filter which keys should be excluded from the
            response
        \*\*max: Optional[int] = None
            Limit the number of items returned in the response
        \*\*page: Optional[int] = None
            Works with max, the zero-based page of the
            items
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


signature = inspect.signature(func).parameters.items()<EOL>@wraps(func)<EOL>def wrapper(*args, **kwargs):<EOL><INDENT>args = list(args)<EOL>new_args = []<EOL>new_kwargs = {}<EOL>for _, param in signature:<EOL><INDENT>converter = param.annotation<EOL>if converter is inspect._empty:<EOL><INDENT>converter = lambda a: a  <EOL><DEDENT>if param.kind is param.POSITIONAL_OR_KEYWORD:<EOL><INDENT>if args:<EOL><INDENT>to_conv = args.pop(<NUM_LIT:0>)<EOL>new_args.append(converter(to_conv))<EOL><DEDENT><DEDENT>elif param.kind is param.VAR_POSITIONAL:<EOL><INDENT>for a in args:<EOL><INDENT>new_args.append(converter(a))<EOL><DEDENT><DEDENT>else:<EOL><INDENT>for k, v in kwargs.items():<EOL><INDENT>nk, nv = converter(k, v)<EOL>new_kwargs[nk] = nv<EOL><DEDENT><DEDENT><DEDENT>return func(*new_args, **new_kwargs)<EOL><DEDENT>return wrapper<EOL>

Decorator that converts arguments via annotations.


if self.client.is_async:<EOL><INDENT>return self._arefresh()<EOL><DEDENT>data, cached, ts, response = self.client.request(self.url, timeout=None, refresh=True)<EOL>return self.from_data(data, cached, ts, response)<EOL>


try:<EOL><INDENT>return self.client.get_clan(self.clan.tag)<EOL><DEDENT>except AttributeError:<EOL><INDENT>try:<EOL><INDENT>return self.client.get_clan(self.tag)<EOL><DEDENT>except AttributeError:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT><DEDENT>


return self.client.get_player(self.tag)<EOL>


@classmethod<EOL><INDENT>def Async(cls, token, session=None, **options):<DEDENT>

return cls(token, session=session, is_async=True, **options)<EOL>


@typecasted<EOL><INDENT>def get_player(self, tag: crtag, timeout=None):<DEDENT>

url = self.api.PLAYER + '<STR_LIT:/>' + tag<EOL>return self._get_model(url, FullPlayer, timeout=timeout)<EOL>

Get information about a player

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_player_verify(self, tag: crtag, apikey: str, timeout=None):<DEDENT>

url = self.api.PLAYER + '<STR_LIT:/>' + tag + '<STR_LIT>'<EOL>return self._get_model(url, FullPlayer, timeout=timeout, method='<STR_LIT:POST>', json={'<STR_LIT>': apikey})<EOL>

Check the API Key of a player.
        This endpoint has been **restricted** to
        certain members of the community

        Raises BadRequest if the apikey is invalid

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        apikey: str
            The API Key in the player's settings
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_player_battles(self, tag: crtag, **params: keys):<DEDENT>

url = self.api.PLAYER + '<STR_LIT:/>' + tag + '<STR_LIT>'<EOL>return self._get_model(url, **params)<EOL>

Get a player's battle log

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_player_chests(self, tag: crtag, timeout: int=None):<DEDENT>

url = self.api.PLAYER + '<STR_LIT:/>' + tag + '<STR_LIT>'<EOL>return self._get_model(url, timeout=timeout)<EOL>

Get information about a player's chest cycle

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_clan(self, tag: crtag, timeout: int=None):<DEDENT>

url = self.api.CLAN + '<STR_LIT:/>' + tag<EOL>return self._get_model(url, FullClan, timeout=timeout)<EOL>

Get inforamtion about a clan

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def search_clans(self, **params: clansearch):<DEDENT>

url = self.api.CLAN<EOL>return self._get_model(url, PartialClan, **params)<EOL>

Search for a clan. At least one
        of the filters must be present

        Parameters
        ----------
        name: Optional[str]
            The name of a clan
            (has to be at least 3 characters long)
        locationId: Optional[int]
            A location ID
        minMembers: Optional[int]
            The minimum member count
            of a clan
        maxMembers: Optional[int]
            The maximum member count
            of a clan
        minScore: Optional[int]
            The minimum trophy score of
            a clan
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_clan_war(self, tag: crtag, timeout: int=None):<DEDENT>

url = self.api.CLAN + '<STR_LIT:/>' + tag + '<STR_LIT>'<EOL>return self._get_model(url, timeout=timeout)<EOL>

Get inforamtion about a clan's current clan war

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_clan_members(self, tag: crtag, **params: keys):<DEDENT>

url = self.api.CLAN + '<STR_LIT:/>' + tag + '<STR_LIT>'<EOL>return self._get_model(url, **params)<EOL>

Get the clan's members

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_clan_war_log(self, tag: crtag, **params: keys):<DEDENT>

Get a clan's war log

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_tournament(self, tag: crtag, timeout=<NUM_LIT:0>):<DEDENT>

url = self.api.TOURNAMENT + '<STR_LIT:/>' + tag<EOL>return self._get_model(url, PartialTournament, timeout=timeout)<EOL>

Get a tournament information

        Parameters
        ----------
        tag: str
            A valid tournament tag. Minimum length: 3
            Valid characters: 0289PYLQGRJCUV
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def search_tournaments(self, name: str, **params: keys):<DEDENT>

url = self.api.TOURNAMENT<EOL>params['<STR_LIT:name>'] = name<EOL>return self._get_model(url, PartialTournament, **params)<EOL>

Search for a tournament by its name

        Parameters
        ----------
        name: str
            The name of a tournament
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_all_cards(self, timeout: int=None):<DEDENT>

url = self.api.CARDS<EOL>return self._get_model(url, timeout=timeout)<EOL>

Get a list of all the cards in the game

        Parameters
        ----------
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_all_locations(self, timeout: int=None):<DEDENT>

url = self.api.LOCATIONS<EOL>return self._get_model(url, timeout=timeout)<EOL>

Get a list of all locations

        Parameters
        ----------
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_location(self, location_id: int, timeout: int=None):<DEDENT>

url = self.api.LOCATIONS + '<STR_LIT:/>' + str(location_id)<EOL>return self._get_model(url, timeout=timeout)<EOL>

Get a location information

        Parameters
        ----------
        location_id: int
            A location ID
            See https://github.com/RoyaleAPI/cr-api-data/blob/master/json/regions.json
            for a list of acceptable location IDs
        timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_top_clans(self, location_id='<STR_LIT>', **params: keys):<DEDENT>

url = self.api.LOCATIONS + '<STR_LIT:/>' + str(location_id) + '<STR_LIT>'<EOL>return self._get_model(url, PartialClan, **params)<EOL>

Get a list of top clans by trophy

        Parameters
        ----------
        location_id: Optional[str] = 'global'
            A location ID or global
            See https://github.com/RoyaleAPI/cr-api-data/blob/master/json/regions.json
            for a list of acceptable location IDs
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_top_clanwar_clans(self, location_id='<STR_LIT>', **params: keys):<DEDENT>

Get a list of top clan war clans

        Parameters
        ----------
        location_id: Optional[str] = 'global'
            A location ID or global
            See https://github.com/RoyaleAPI/cr-api-data/blob/master/json/regions.json
            for a list of acceptable location IDs
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


@typecasted<EOL><INDENT>def get_top_players(self, location_id='<STR_LIT>', **params: keys):<DEDENT>

url = self.api.LOCATIONS + '<STR_LIT:/>' + str(location_id) + '<STR_LIT>'<EOL>return self._get_model(url, PartialPlayerClan, **params)<EOL>

Get a list of top players

        Parameters
        ----------
        location_id: Optional[str] = 'global'
            A location ID or global
            See https://github.com/RoyaleAPI/cr-api-data/blob/master/json/regions.json
            for a list of acceptable location IDs
        \*\*limit: Optional[int] = None
            Limit the number of items returned in the response
        \*\*timeout: Optional[int] = None
            Custom timeout that overwrites Client.timeout


def get_clan_image(self, obj: BaseAttrDict):

try:<EOL><INDENT>badge_id = obj.clan.badge_id<EOL><DEDENT>except AttributeError:<EOL><INDENT>try:<EOL><INDENT>badge_id = obj.badge_id<EOL><DEDENT>except AttributeError:<EOL><INDENT>return '<STR_LIT>'<EOL><DEDENT><DEDENT>if badge_id is None:<EOL><INDENT>return '<STR_LIT>'<EOL><DEDENT>for i in self.constants.alliance_badges:<EOL><INDENT>if i.id == badge_id:<EOL><INDENT>return '<STR_LIT>' + i.name + '<STR_LIT>'<EOL><DEDENT><DEDENT>

Get the clan badge image URL

        Parameters
        ---------
        obj: official_api.models.BaseAttrDict
            An object that has the clan badge ID either in ``.clan.badge_id`` or ``.badge_id``
            Can be a clan or a profile for example.

        Returns str


def get_arena_image(self, obj: BaseAttrDict):

badge_id = obj.arena.id<EOL>for i in self.constants.arenas:<EOL><INDENT>if i.id == badge_id:<EOL><INDENT>return '<STR_LIT>'.format(i.arena_id)<EOL><DEDENT><DEDENT>

Get the arena image URL

        Parameters
        ---------
        obj: official_api.models.BaseAttrDict
            An object that has the arena ID in ``.arena.id``
            Can be ``Profile`` for example.

        Returns None or str


def get_card_info(self, card_name: str):

for c in self.constants.cards:<EOL><INDENT>if c.name == card_name:<EOL><INDENT>return c<EOL><DEDENT><DEDENT>

Returns card info from constants

        Parameters
        ---------
        card_name: str
            A card name

        Returns None or Constants


for c in self.constants.rarities:<EOL><INDENT>if c.name == rarity:<EOL><INDENT>return c<EOL><DEDENT><DEDENT>

Returns card info from constants

        Parameters
        ---------
        rarity: str
            A rarity name

        Returns None or Constants


def get_deck_link(self, deck: BaseAttrDict):

deck_link = '<STR_LIT>'<EOL>for i in deck:<EOL><INDENT>card = self.get_card_info(i.name)<EOL>deck_link += '<STR_LIT>'.format(card)<EOL><DEDENT>return deck_link<EOL>

Form a deck link

        Parameters
        ---------
        deck: official_api.models.BaseAttrDict
            An object is a deck. Can be retrieved from ``Player.current_deck``

        Returns str


def get_datetime(self, timestamp: str, unix=True):

time = datetime.strptime(timestamp, '<STR_LIT>')<EOL>if unix:<EOL><INDENT>return int(time.timestamp())<EOL><DEDENT>else:<EOL><INDENT>return time<EOL><DEDENT>

Converts a %Y%m%dT%H%M%S.%fZ to a UNIX timestamp
        or a datetime.datetime object

        Parameters
        ---------
        timestamp: str
            A timstamp in the %Y%m%dT%H%M%S.%fZ format, usually returned by the API
            in the ``created_time`` field for example (eg. 20180718T145906.000Z)
        unix: Optional[bool] = True
            Whether to return a POSIX timestamp (seconds since epoch) or not

        Returns int or datetime.datetime


signature = inspect.signature(func).parameters.items()<EOL>@wraps(func)<EOL>def wrapper(*args, **kwargs):<EOL><INDENT>args = list(args)<EOL>new_args = []<EOL>new_kwargs = {}<EOL>for _, param in signature:<EOL><INDENT>converter = param.annotation<EOL>if converter is inspect._empty:<EOL><INDENT>converter = lambda a: a  <EOL><DEDENT>if param.kind is param.POSITIONAL_OR_KEYWORD:<EOL><INDENT>if args:<EOL><INDENT>to_conv = args.pop(<NUM_LIT:0>)<EOL>new_args.append(converter(to_conv))<EOL><DEDENT><DEDENT>elif param.kind is param.VAR_POSITIONAL:<EOL><INDENT>for a in args:<EOL><INDENT>new_args.append(converter(a))<EOL><DEDENT><DEDENT>else:<EOL><INDENT>for k, v in kwargs.items():<EOL><INDENT>nk, nv = converter(k, v)<EOL>new_kwargs[nk] = nv<EOL><DEDENT><DEDENT><DEDENT>return func(*new_args, **new_kwargs)<EOL><DEDENT>return wrapper<EOL>

Decorator that converts arguments via annotations.


if self.client.is_async:<EOL><INDENT>return self._aupdate_data()<EOL><DEDENT>if self.cursor['<STR_LIT>']:<EOL><INDENT>data, cached, ts, response = self.client._request(self.response.url, timeout=None, after=self.cursor['<STR_LIT>'])<EOL>self.cursor = {'<STR_LIT>': data['<STR_LIT>']['<STR_LIT>'].get('<STR_LIT>'), '<STR_LIT>': data['<STR_LIT>']['<STR_LIT>'].get('<STR_LIT>')}<EOL>self.raw_data += [self.model(self.client, d, response, cached=cached, ts=ts) for d in data['<STR_LIT>']]<EOL>return True<EOL><DEDENT>return False<EOL>

Adds the NEXT data in the raw_data dictionary.
        Returns True if data is added.
        Returns False if data is not added


if self.client.is_async:<EOL><INDENT>return self._aall_data()<EOL><DEDENT>while self.update_data():<EOL><INDENT>pass<EOL><DEDENT>

Loops through and adds all data to the raw_data

        This has a chance to get 429 RatelimitError


if self.client.is_async:<EOL><INDENT>return self._arefresh()<EOL><DEDENT>data, cached, ts, response = self.client._request(self.response.url, timeout=None, refresh=True)<EOL>return self.from_data(data, cached, ts, response)<EOL>


try:<EOL><INDENT>return self.client.get_clan(self.clan.tag)<EOL><DEDENT>except AttributeError:<EOL><INDENT>try:<EOL><INDENT>return self.client.get_clan(self.tag)<EOL><DEDENT>except AttributeError:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT><DEDENT>


return self.client.get_player(self.tag)<EOL>


def wrapper(resource, parent):<EOL><INDENT>if isinstance(resource, DeclarativeMeta):<EOL><INDENT>resource = ListResource(resource)<EOL><DEDENT>if not getattr(resource, '<STR_LIT>', None):<EOL><INDENT>resource.__parent__ = parent<EOL><DEDENT>return resource<EOL><DEDENT>resources_preparing_factory(app, wrapper)<EOL>


a_tag = Page.default_link_tag(item)<EOL>if item['<STR_LIT:type>'] == '<STR_LIT>':<EOL><INDENT>return make_html_tag('<STR_LIT>', a_tag, **{'<STR_LIT:class>': '<STR_LIT>'})<EOL><DEDENT>return make_html_tag('<STR_LIT>', a_tag)<EOL>

Create an A-HREF tag that points to another page usable in paginate.


from pkg_resources import iter_entry_points<EOL>def entrypoints(group, name=None):<EOL><INDENT>if group == mocked_group:<EOL><INDENT>for entrypoint in data:<EOL><INDENT>yield entrypoint<EOL><DEDENT><DEDENT>else:<EOL><INDENT>for x in iter_entry_points(group=group, name=name):<EOL><INDENT>yield x<EOL><DEDENT><DEDENT><DEDENT>return entrypoints<EOL>

Create a mock iter_entry_points function.


if start_date >= end_date:<EOL><INDENT>for n in range((start_date - end_date).days + <NUM_LIT:1>):<EOL><INDENT>yield end_date + datetime.timedelta(n)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>for n in range((end_date - start_date).days + <NUM_LIT:1>):<EOL><INDENT>yield start_date + datetime.timedelta(n)<EOL><DEDENT><DEDENT>


def handler(sender, **kwargs):<EOL><INDENT>g.user = user<EOL><DEDENT>with appcontext_pushed.connected_to(handler, app):<EOL><INDENT>yield<EOL><DEDENT>


def generate_events(app, file_number=<NUM_LIT:5>, event_number=<NUM_LIT:100>, robot_event_number=<NUM_LIT:0>,<EOL>start_date=datetime.date(<NUM_LIT>, <NUM_LIT:1>, <NUM_LIT:1>),<EOL>end_date=datetime.date(<NUM_LIT>, <NUM_LIT:1>, <NUM_LIT:7>)):

current_queues.declare()<EOL>for t in current_search.put_templates(ignore=[<NUM_LIT>]):<EOL><INDENT>pass<EOL><DEDENT>def _unique_ts_gen():<EOL><INDENT>ts = <NUM_LIT:0><EOL>while True:<EOL><INDENT>ts += <NUM_LIT:1><EOL>yield ts<EOL><DEDENT><DEDENT>def generator_list():<EOL><INDENT>unique_ts = _unique_ts_gen()<EOL>for file_idx in range(file_number):<EOL><INDENT>for entry_date in date_range(start_date, end_date):<EOL><INDENT>file_id = '<STR_LIT>'.format(file_idx + <NUM_LIT:1>)<EOL>bucket_id = '<STR_LIT>'.format(file_idx + <NUM_LIT:1>)<EOL>def build_event(is_robot=False):<EOL><INDENT>ts = next(unique_ts)<EOL>return dict(<EOL>timestamp=datetime.datetime.combine(<EOL>entry_date,<EOL>datetime.time(minute=ts % <NUM_LIT>,<EOL>second=ts % <NUM_LIT>)).<EOL>isoformat(),<EOL>bucket_id=bucket_id,<EOL>file_id=file_id,<EOL>file_key='<STR_LIT>',<EOL>size=<NUM_LIT>,<EOL>visitor_id=<NUM_LIT:100>,<EOL>is_robot=is_robot<EOL>)<EOL><DEDENT>for event_idx in range(event_number):<EOL><INDENT>yield build_event()<EOL><DEDENT>for event_idx in range(robot_event_number):<EOL><INDENT>yield build_event(True)<EOL><DEDENT><DEDENT><DEDENT><DEDENT>mock_queue = Mock()<EOL>mock_queue.consume.return_value = generator_list()<EOL>mock_queue.routing_key = '<STR_LIT>'<EOL>EventsIndexer(<EOL>mock_queue,<EOL>preprocessors=[<EOL>build_file_unique_id<EOL>],<EOL>double_click_window=<NUM_LIT:0><EOL>).run()<EOL>current_search_client.indices.refresh(index='<STR_LIT:*>')<EOL>


return current_search_client.indices.stats()[<EOL>'<STR_LIT>'][index]['<STR_LIT>']['<STR_LIT>'][<EOL>'<STR_LIT>']<EOL>


pass<EOL>

Mock constructor to accept the query_config parameters.


return dict(bucket_id='<STR_LIT>',<EOL>value=<NUM_LIT:100>)<EOL>


queue = current_queues.queues[queue_name]<EOL>_, size, _ = queue.queue.queue_declare(passive=True)<EOL>return size<EOL>

Get the current number of messages in a queue.


pass<EOL>


return [dict(name='<STR_LIT>'.format(event['<STR_LIT>']),<EOL>exchange=current_stats.exchange)<EOL>for event in current_stats._events_config.values()]<EOL>


def file_download_event_builder(event, sender_app, obj=None, **kwargs):


doc['<STR_LIT>'] = '<STR_LIT>'.format(doc['<STR_LIT>'], doc['<STR_LIT>'])<EOL>return doc<EOL>


def record_view_event_builder(event, sender_app, pid=None, record=None,<EOL>**kwargs):


event_templates = [current_stats._events_config[e]<EOL>['<STR_LIT>']<EOL>for e in<EOL>current_stats._events_config]<EOL>aggregation_templates = [current_stats._aggregations_config[a]<EOL>['<STR_LIT>']<EOL>for a in<EOL>current_stats._aggregations_config]<EOL>return event_templates + aggregation_templates<EOL>

Register elasticsearch templates for events.


def __init__(self, description, **kwargs):

super(RESTException, self).__init__(**kwargs)<EOL>self.description = description<EOL>


super(RESTException, self).__init__()<EOL>self.query_name = query_name<EOL>self.description = '<STR_LIT>'.format(query_name)<EOL>

Constructor.

        :param query_name: name of the unknown query.


return query.filter('<STR_LIT>', is_robot=False)<EOL>

Modify an elasticsearch query so that robot events are filtered out.


self.name = name<EOL>self.client = client or current_search_client<EOL>self.event = event<EOL>self.aggregation_alias = '<STR_LIT>'.format(self.event)<EOL>self.aggregation_field = aggregation_field<EOL>self.metric_aggregation_fields = metric_aggregation_fields or {}<EOL>self.allowed_metrics = {<EOL>'<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>',<EOL>'<STR_LIT>', '<STR_LIT>', '<STR_LIT>'}<EOL>if any(v not in self.allowed_metrics<EOL>for k, (v, _, _) in (metric_aggregation_fields or {}).items()):<EOL><INDENT>raise(ValueError('<STR_LIT>'<EOL>.format('<STR_LIT:U+002CU+0020>'.join(self.allowed_metrics))))<EOL><DEDENT>self.copy_fields = copy_fields or {}<EOL>self.aggregation_interval = aggregation_interval<EOL>self.index_interval = index_interval<EOL>self.query_modifiers = (query_modifiers if query_modifiers is not None<EOL>else [filter_robots])<EOL>self.supported_intervals = OrderedDict([('<STR_LIT>', '<STR_LIT>'),<EOL>('<STR_LIT>', '<STR_LIT>'),<EOL>('<STR_LIT>', '<STR_LIT>'),<EOL>('<STR_LIT>', '<STR_LIT>')])<EOL>self.dt_rounding_map = {<EOL>'<STR_LIT>': '<STR_LIT:h>', '<STR_LIT>': '<STR_LIT:d>', '<STR_LIT>': '<STR_LIT:M>', '<STR_LIT>': '<STR_LIT:y>'}<EOL>if list(self.supported_intervals.keys()).index(aggregation_interval)>list(self.supported_intervals.keys()).index(index_interval):<EOL><INDENT>raise(ValueError('<STR_LIT>'<EOL>'<STR_LIT>'))<EOL><DEDENT>self.index_name_suffix = self.supported_intervals[index_interval]<EOL>self.doc_id_suffix = self.supported_intervals[aggregation_interval]<EOL>self.batch_size = batch_size<EOL>self.event_index = '<STR_LIT>'.format(self.event)<EOL>

Construct aggregator instance.

        :param event: aggregated event.
        :param client: elasticsearch client.
        :param aggregation_field: field on which the aggregation will be done.
        :param metric_aggregation_fields: dictionary of fields on which a
            metric aggregation will be computed. The format of the dictionary
            is "destination field" ->
            tuple("metric type", "source field", "metric_options").
        :param copy_fields: list of fields which are copied from the raw events
            into the aggregation.
        :param query_modifiers: list of functions modifying the raw events
            query. By default the query_modifiers are [filter_robots].
        :param aggregation_interval: aggregation time window. default: month.
        :param index_interval: time window of the elasticsearch indices which
            will contain the resulting aggregations.
        :param batch_size: max number of days for which raw events are being
            fetched in one query. This number has to be coherent with the
            aggregation_interval.


return '<STR_LIT>'.format(self.name)<EOL>

Get document type for the aggregation's bookmark.


return '<STR_LIT>'.format(<EOL>self.event, self.aggregation_interval)<EOL>


<EOL>query_events = Search(<EOL>using=self.client,<EOL>index=self.event_index<EOL>)[<NUM_LIT:0>:<NUM_LIT:1>].sort(<EOL>{'<STR_LIT>': {'<STR_LIT>': '<STR_LIT>'}}<EOL>)<EOL>result = query_events.execute()<EOL>if len(result) == <NUM_LIT:0>:<EOL><INDENT>return None<EOL><DEDENT>return parser.parse(result[<NUM_LIT:0>]['<STR_LIT>'])<EOL>


if not Index(self.aggregation_alias,<EOL>using=self.client).exists():<EOL><INDENT>if not Index(self.event_index,<EOL>using=self.client).exists():<EOL><INDENT>return datetime.date.today()<EOL><DEDENT>return self._get_oldest_event_timestamp()<EOL><DEDENT>query_bookmark = Search(<EOL>using=self.client,<EOL>index=self.aggregation_alias,<EOL>doc_type=self.bookmark_doc_type<EOL>)[<NUM_LIT:0>:<NUM_LIT:1>].sort(<EOL>{'<STR_LIT:date>': {'<STR_LIT>': '<STR_LIT>'}}<EOL>)<EOL>bookmarks = query_bookmark.execute()<EOL>if len(bookmarks) == <NUM_LIT:0>:<EOL><INDENT>return self._get_oldest_event_timestamp()<EOL><DEDENT>bookmark = datetime.datetime.strptime(bookmarks[<NUM_LIT:0>].date,<EOL>self.doc_id_suffix)<EOL>return bookmark<EOL>


def _success_date():<EOL><INDENT>bookmark = {<EOL>'<STR_LIT:date>': self.new_bookmark or datetime.datetime.utcnow().<EOL>strftime(self.doc_id_suffix)<EOL>}<EOL>yield dict(_index=self.last_index_written,<EOL>_type=self.bookmark_doc_type,<EOL>_source=bookmark)<EOL><DEDENT>if self.last_index_written:<EOL><INDENT>bulk(self.client,<EOL>_success_date(),<EOL>stats_only=True)<EOL><DEDENT>

Set bookmark for starting next aggregation.


if not isinstance(d, six.string_types):<EOL><INDENT>d = d.isoformat()<EOL><DEDENT>return '<STR_LIT>'.format(<EOL>d, self.dt_rounding_map[self.aggregation_interval])<EOL>

Format range filter datetime to the closest aggregation interval.


def agg_iter(self, lower_limit=None, upper_limit=None):

lower_limit = lower_limit or self.get_bookmark().isoformat()<EOL>upper_limit = upper_limit or (<EOL>datetime.datetime.utcnow().replace(microsecond=<NUM_LIT:0>).isoformat())<EOL>aggregation_data = {}<EOL>self.agg_query = Search(using=self.client,<EOL>index=self.event_index).filter('<STR_LIT>', timestamp={<EOL>'<STR_LIT>': self._format_range_dt(lower_limit),<EOL>'<STR_LIT>': self._format_range_dt(upper_limit)})<EOL>for modifier in self.query_modifiers:<EOL><INDENT>self.agg_query = modifier(self.agg_query)<EOL><DEDENT>hist = self.agg_query.aggs.bucket(<EOL>'<STR_LIT>',<EOL>'<STR_LIT>',<EOL>field='<STR_LIT>',<EOL>interval=self.aggregation_interval<EOL>)<EOL>terms = hist.bucket(<EOL>'<STR_LIT>', '<STR_LIT>', field=self.aggregation_field, size=<NUM_LIT:0><EOL>)<EOL>top = terms.metric(<EOL>'<STR_LIT>', '<STR_LIT>', size=<NUM_LIT:1>, sort={'<STR_LIT>': '<STR_LIT>'}<EOL>)<EOL>for dst, (metric, src, opts) in self.metric_aggregation_fields.items():<EOL><INDENT>terms.metric(dst, metric, field=src, **opts)<EOL><DEDENT>results = self.agg_query.execute()<EOL>index_name = None<EOL>for interval in results.aggregations['<STR_LIT>'].buckets:<EOL><INDENT>interval_date = datetime.datetime.strptime(<EOL>interval['<STR_LIT>'], '<STR_LIT>')<EOL>for aggregation in interval['<STR_LIT>'].buckets:<EOL><INDENT>aggregation_data['<STR_LIT>'] = interval_date.isoformat()<EOL>aggregation_data[self.aggregation_field] = aggregation['<STR_LIT:key>']<EOL>aggregation_data['<STR_LIT:count>'] = aggregation['<STR_LIT>']<EOL>if self.metric_aggregation_fields:<EOL><INDENT>for f in self.metric_aggregation_fields:<EOL><INDENT>aggregation_data[f] = aggregation[f]['<STR_LIT:value>']<EOL><DEDENT><DEDENT>doc = aggregation.top_hit.hits.hits[<NUM_LIT:0>]['<STR_LIT>']<EOL>for destination, source in self.copy_fields.items():<EOL><INDENT>if isinstance(source, six.string_types):<EOL><INDENT>aggregation_data[destination] = doc[source]<EOL><DEDENT>else:<EOL><INDENT>aggregation_data[destination] = source(<EOL>doc,<EOL>aggregation_data<EOL>)<EOL><DEDENT><DEDENT>index_name = '<STR_LIT>'.format(self.event,<EOL>interval_date.strftime(<EOL>self.index_name_suffix))<EOL>self.indices.add(index_name)<EOL>yield dict(_id='<STR_LIT>'.<EOL>format(aggregation['<STR_LIT:key>'],<EOL>interval_date.strftime(<EOL>self.doc_id_suffix)),<EOL>_index=index_name,<EOL>_type=self.aggregation_doc_type,<EOL>_source=aggregation_data)<EOL><DEDENT><DEDENT>self.last_index_written = index_name<EOL>

Aggregate and return dictionary to be indexed in ES.


def run(self, start_date=None, end_date=None, update_bookmark=True):

<EOL>if not Index(self.event_index, using=self.client).exists():<EOL><INDENT>return<EOL><DEDENT>lower_limit = start_date or self.get_bookmark()<EOL>if lower_limit is None:<EOL><INDENT>return<EOL><DEDENT>upper_limit = min(<EOL>end_date or datetime.datetime.max,  <EOL>datetime.datetime.utcnow().replace(microsecond=<NUM_LIT:0>),<EOL>datetime.datetime.combine(<EOL>lower_limit + datetime.timedelta(self.batch_size),<EOL>datetime.datetime.min.time())<EOL>)<EOL>while upper_limit <= datetime.datetime.utcnow():<EOL><INDENT>self.indices = set()<EOL>self.new_bookmark = upper_limit.strftime(self.doc_id_suffix)<EOL>bulk(self.client,<EOL>self.agg_iter(lower_limit, upper_limit),<EOL>stats_only=True,<EOL>chunk_size=<NUM_LIT:50>)<EOL>current_search_client.indices.flush(<EOL>index='<STR_LIT:U+002C>'.join(self.indices),<EOL>wait_if_ongoing=True<EOL>)<EOL>if update_bookmark:<EOL><INDENT>self.set_bookmark()<EOL><DEDENT>self.indices = set()<EOL>lower_limit = lower_limit + datetime.timedelta(self.batch_size)<EOL>upper_limit = min(<EOL>end_date or datetime.datetime.max,  <EOL>datetime.datetime.utcnow().replace(microsecond=<NUM_LIT:0>),<EOL>lower_limit + datetime.timedelta(self.batch_size)<EOL>)<EOL>if lower_limit > upper_limit:<EOL><INDENT>break<EOL><DEDENT><DEDENT>


def list_bookmarks(self, start_date=None, end_date=None, limit=None):

query = Search(<EOL>using=self.client,<EOL>index=self.aggregation_alias,<EOL>doc_type=self.bookmark_doc_type<EOL>).sort({'<STR_LIT:date>': {'<STR_LIT>': '<STR_LIT>'}})<EOL>range_args = {}<EOL>if start_date:<EOL><INDENT>range_args['<STR_LIT>'] = self._format_range_dt(<EOL>start_date.replace(microsecond=<NUM_LIT:0>))<EOL><DEDENT>if end_date:<EOL><INDENT>range_args['<STR_LIT>'] = self._format_range_dt(<EOL>end_date.replace(microsecond=<NUM_LIT:0>))<EOL><DEDENT>if range_args:<EOL><INDENT>query = query.filter('<STR_LIT>', date=range_args)<EOL><DEDENT>return query[<NUM_LIT:0>:limit].execute() if limit else query.scan()<EOL>


def delete(self, start_date=None, end_date=None):

aggs_query = Search(<EOL>using=self.client,<EOL>index=self.aggregation_alias,<EOL>doc_type=self.aggregation_doc_type<EOL>).extra(_source=False)<EOL>range_args = {}<EOL>if start_date:<EOL><INDENT>range_args['<STR_LIT>'] = self._format_range_dt(<EOL>start_date.replace(microsecond=<NUM_LIT:0>))<EOL><DEDENT>if end_date:<EOL><INDENT>range_args['<STR_LIT>'] = self._format_range_dt(<EOL>end_date.replace(microsecond=<NUM_LIT:0>))<EOL><DEDENT>if range_args:<EOL><INDENT>aggs_query = aggs_query.filter('<STR_LIT>', timestamp=range_args)<EOL><DEDENT>bookmarks_query = Search(<EOL>using=self.client,<EOL>index=self.aggregation_alias,<EOL>doc_type=self.bookmark_doc_type<EOL>).sort({'<STR_LIT:date>': {'<STR_LIT>': '<STR_LIT>'}})<EOL>if range_args:<EOL><INDENT>bookmarks_query = bookmarks_query.filter('<STR_LIT>', date=range_args)<EOL><DEDENT>def _delete_actions():<EOL><INDENT>for query in (aggs_query, bookmarks_query):<EOL><INDENT>affected_indices = set()<EOL>for doc in query.scan():<EOL><INDENT>affected_indices.add(doc.meta.index)<EOL>yield dict(_index=doc.meta.index,<EOL>_op_type='<STR_LIT>',<EOL>_id=doc.meta.id,<EOL>_type=doc.meta.doc_type)<EOL><DEDENT>current_search_client.indices.flush(<EOL>index='<STR_LIT:U+002C>'.join(affected_indices), wait_if_ongoing=True)<EOL><DEDENT><DEDENT>bulk(self.client, _delete_actions(), refresh=True)<EOL>


super(StatsQueryResource, self).__init__(<EOL>serializers={<EOL>'<STR_LIT:application/json>':<EOL>lambda data, *args, **kwargs: jsonify(data),<EOL>},<EOL>default_method_media_type={<EOL>'<STR_LIT:GET>': '<STR_LIT:application/json>',<EOL>},<EOL>default_media_type='<STR_LIT:application/json>',<EOL>**kwargs)<EOL>


data = request.get_json(force=False)<EOL>if data is None:<EOL><INDENT>data = {}<EOL><DEDENT>result = {}<EOL>for query_name, config in data.items():<EOL><INDENT>if config is None or not isinstance(config, dict)or (set(config.keys()) != {'<STR_LIT>', '<STR_LIT>'} and<EOL>set(config.keys()) != {'<STR_LIT>'}):<EOL><INDENT>raise InvalidRequestInputError(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'<EOL>)<EOL><DEDENT>stat = config['<STR_LIT>']<EOL>params = config.get('<STR_LIT>', {})<EOL>try:<EOL><INDENT>query_cfg = current_stats.queries[stat]<EOL><DEDENT>except KeyError:<EOL><INDENT>raise UnknownQueryError(stat)<EOL><DEDENT>permission = current_stats.permission_factory(stat, params)<EOL>if permission is not None and not permission.can():<EOL><INDENT>message = ('<STR_LIT>'<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'.format(stat))<EOL>if current_user.is_authenticated:<EOL><INDENT>abort(<NUM_LIT>, message)<EOL><DEDENT>abort(<NUM_LIT>, message)<EOL><DEDENT>try:<EOL><INDENT>query = query_cfg.query_class(**query_cfg.query_config)<EOL>result[query_name] = query.run(**params)<EOL><DEDENT>except ValueError as e:<EOL><INDENT>raise InvalidRequestInputError(e.args[<NUM_LIT:0>])<EOL><DEDENT>except NotFoundError as e:<EOL><INDENT>return None<EOL><DEDENT><DEDENT>return self.make_response(result)<EOL>


@wraps(f)<EOL>def decorated(ctx, param, value):<EOL><INDENT>return LocalProxy(lambda: f(ctx, param, value))<EOL><DEDENT>return decorated<EOL>


event_types = event_types or list(current_stats.enabled_events)<EOL>if eager:<EOL><INDENT>process_events.apply((event_types,), throw=True)<EOL>click.secho('<STR_LIT>', fg='<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>process_events.delay(event_types)<EOL>click.secho('<STR_LIT>', fg='<STR_LIT>')<EOL><DEDENT>


aggregation_types = (aggregation_types or<EOL>list(current_stats.enabled_aggregations))<EOL>if eager:<EOL><INDENT>aggregate_events.apply(<EOL>(aggregation_types,),<EOL>dict(start_date=start_date, end_date=end_date,<EOL>update_bookmark=update_bookmark),<EOL>throw=True)<EOL>click.secho('<STR_LIT>', fg='<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>aggregate_events.delay(<EOL>aggregation_types, start_date=start_date, end_date=end_date)<EOL>click.secho('<STR_LIT>', fg='<STR_LIT>')<EOL><DEDENT>


aggregation_types = (aggregation_types or<EOL>list(current_stats.enabled_aggregations))<EOL>for a in aggregation_types:<EOL><INDENT>aggr_cfg = current_stats.aggregations[a]<EOL>aggregator = aggr_cfg.aggregator_class(<EOL>name=aggr_cfg.name, **aggr_cfg.aggregator_config)<EOL>aggregator.delete(start_date, end_date)<EOL><DEDENT>


aggregation_types = (aggregation_types or<EOL>list(current_stats.enabled_aggregations))<EOL>for a in aggregation_types:<EOL><INDENT>aggr_cfg = current_stats.aggregations[a]<EOL>aggregator = aggr_cfg.aggregator_class(<EOL>name=aggr_cfg.name, **aggr_cfg.aggregator_config)<EOL>bookmarks = aggregator.list_bookmarks(start_date, end_date, limit)<EOL>click.echo('<STR_LIT>'.format(a))<EOL>for b in bookmarks:<EOL><INDENT>click.echo('<STR_LIT>'.format(b.date))<EOL><DEDENT><DEDENT>


<EOL>result = {}<EOL>for ep in iter_entry_points(<EOL>group=self.entry_point_group_events):<EOL><INDENT>for cfg in ep.load()():<EOL><INDENT>if cfg['<STR_LIT>'] not in self.enabled_events:<EOL><INDENT>continue<EOL><DEDENT>elif cfg['<STR_LIT>'] in result:<EOL><INDENT>raise DuplicateEventError(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'.format(cfg['<STR_LIT>'], ep.name))<EOL><DEDENT>cfg.update(<EOL>self.enabled_events[cfg['<STR_LIT>']] or {}<EOL>)<EOL>result[cfg['<STR_LIT>']] = cfg<EOL><DEDENT><DEDENT>return result<EOL>


result = {}<EOL>for ep in iter_entry_points(<EOL>group=self.entry_point_group_aggs):<EOL><INDENT>for cfg in ep.load()():<EOL><INDENT>if cfg['<STR_LIT>'] not in self.enabled_aggregations:<EOL><INDENT>continue<EOL><DEDENT>elif cfg['<STR_LIT>'] in result:<EOL><INDENT>raise DuplicateAggregationError(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'.format(cfg['<STR_LIT>'], ep.name))<EOL><DEDENT>cfg.update(<EOL>self.enabled_aggregations[cfg['<STR_LIT>']] or {}<EOL>)<EOL>result[cfg['<STR_LIT>']] = cfg<EOL><DEDENT><DEDENT>return result<EOL>


result = {}<EOL>for ep in iter_entry_points(group=self.entry_point_group_queries):<EOL><INDENT>for cfg in ep.load()():<EOL><INDENT>if cfg['<STR_LIT>'] not in self.enabled_queries:<EOL><INDENT>continue<EOL><DEDENT>elif cfg['<STR_LIT>'] in result:<EOL><INDENT>raise DuplicateQueryError(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'.format(cfg['<STR_LIT>'], ep.name))<EOL><DEDENT>cfg.update(<EOL>self.enabled_queries[cfg['<STR_LIT>']] or {}<EOL>)<EOL>result[cfg['<STR_LIT>']] = cfg<EOL><DEDENT><DEDENT>return result<EOL>


return load_or_import_from_config(<EOL>'<STR_LIT>', app=self.app<EOL>)<EOL>

Load default permission factory for Buckets collections.


assert event_type in self.events<EOL>current_queues.queues['<STR_LIT>'.format(event_type)].publish(events)<EOL>


def consume(self, event_type, no_ack=True, payload=True):

assert event_type in self.events<EOL>return current_queues.queues['<STR_LIT>'.format(event_type)].consume(<EOL>payload=payload)<EOL>


if app:<EOL><INDENT>self.init_app(app, **kwargs)<EOL><DEDENT>


self.init_config(app)<EOL>state = _InvenioStatsState(<EOL>app,<EOL>entry_point_group_events=entry_point_group_events,<EOL>entry_point_group_aggs=entry_point_group_aggs,<EOL>entry_point_group_queries=entry_point_group_queries<EOL>)<EOL>self._state = app.extensions['<STR_LIT>'] = state<EOL>if app.config['<STR_LIT>']:<EOL><INDENT>signal_receivers = {key: value for key, value in<EOL>app.config.get('<STR_LIT>', {}).items()<EOL>if '<STR_LIT>' in value}<EOL>register_receivers(app, signal_receivers)<EOL><DEDENT>return state<EOL>


for k in dir(config):<EOL><INDENT>if k.startswith('<STR_LIT>'):<EOL><INDENT>app.config.setdefault(k, getattr(config, k))<EOL><DEDENT><DEDENT>


return getattr(self._state, name, None)<EOL>


def __init__(self, query_name, doc_type, index, client=None,<EOL>*args, **kwargs):

super(ESQuery, self).__init__()<EOL>self.index = index<EOL>self.client = client or current_search_client<EOL>self.query_name = query_name<EOL>self.doc_type = doc_type<EOL>

Constructor.

        :param doc_type: queried document type.
        :param index: queried index.
        :param client: elasticsearch client used to query.


if isinstance(date, six.string_types):<EOL><INDENT>try:<EOL><INDENT>date = dateutil.parser.parse(date)<EOL><DEDENT>except ValueError:<EOL><INDENT>raise ValueError(<EOL>'<STR_LIT>'<EOL>).format(self.query_name)<EOL><DEDENT><DEDENT>if not isinstance(date, datetime):<EOL><INDENT>raise TypeError(<EOL>'<STR_LIT>'<EOL>).format(self.query_name)<EOL><DEDENT>return date<EOL>

Extract date from string if necessary.

        :returns: the extracted date.


raise NotImplementedError()<EOL>


def __init__(self, time_field='<STR_LIT>', copy_fields=None,<EOL>query_modifiers=None, required_filters=None,<EOL>metric_fields=None, *args, **kwargs):

super(ESDateHistogramQuery, self).__init__(*args, **kwargs)<EOL>self.time_field = time_field<EOL>self.copy_fields = copy_fields or {}<EOL>self.query_modifiers = query_modifiers or []<EOL>self.required_filters = required_filters or {}<EOL>self.metric_fields = metric_fields or {'<STR_LIT:value>': ('<STR_LIT>', '<STR_LIT:count>', {})}<EOL>self.allowed_metrics = {<EOL>'<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>', '<STR_LIT>',<EOL>'<STR_LIT>', '<STR_LIT>', '<STR_LIT>'}<EOL>if any(v not in self.allowed_metrics<EOL>for k, (v, _, _) in (self.metric_fields or {}).items()):<EOL><INDENT>raise(ValueError('<STR_LIT>'<EOL>.format('<STR_LIT:U+002CU+0020>'.join(self.allowed_metrics))))<EOL><DEDENT>

Constructor.

        :param time_field: name of the timestamp field.
        :param copy_fields: list of fields to copy from the top hit document
            into the resulting aggregation.
        :param query_modifiers: List of functions accepting a ``query`` and
            ``**kwargs`` (same as provided to the ``run`` method), that will
            be applied to the aggregation query.
        :param required_filters: Dict of "mandatory query parameter" ->
            "filtered field".
        :param metric_fields: Dict of "destination field" ->
            tuple("metric type", "source field", "metric_options").


def validate_arguments(self, interval, start_date, end_date, **kwargs):

if interval not in self.allowed_intervals:<EOL><INDENT>raise InvalidRequestInputError(<EOL>'<STR_LIT>'<EOL>).format(self.query_name)<EOL><DEDENT>if set(kwargs) < set(self.required_filters):<EOL><INDENT>raise InvalidRequestInputError(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'.format(set(self.required_filters.keys()),<EOL>self.query_name)<EOL>)<EOL><DEDENT>


def build_query(self, interval, start_date, end_date, **kwargs):

agg_query = Search(using=self.client,<EOL>index=self.index,<EOL>doc_type=self.doc_type)[<NUM_LIT:0>:<NUM_LIT:0>]<EOL>if start_date is not None or end_date is not None:<EOL><INDENT>time_range = {}<EOL>if start_date is not None:<EOL><INDENT>time_range['<STR_LIT>'] = start_date.isoformat()<EOL><DEDENT>if end_date is not None:<EOL><INDENT>time_range['<STR_LIT>'] = end_date.isoformat()<EOL><DEDENT>agg_query = agg_query.filter(<EOL>'<STR_LIT>',<EOL>**{self.time_field: time_range})<EOL><DEDENT>for modifier in self.query_modifiers:<EOL><INDENT>agg_query = modifier(agg_query, **kwargs)<EOL><DEDENT>base_agg = agg_query.aggs.bucket(<EOL>'<STR_LIT>',<EOL>'<STR_LIT>',<EOL>field=self.time_field,<EOL>interval=interval<EOL>)<EOL>for destination, (metric, field, opts) in self.metric_fields.items():<EOL><INDENT>base_agg.metric(destination, metric, field=field, **opts)<EOL><DEDENT>if self.copy_fields:<EOL><INDENT>base_agg.metric(<EOL>'<STR_LIT>', '<STR_LIT>', size=<NUM_LIT:1>, sort={'<STR_LIT>': '<STR_LIT>'}<EOL>)<EOL><DEDENT>for query_param, filtered_field in self.required_filters.items():<EOL><INDENT>if query_param in kwargs:<EOL><INDENT>agg_query = agg_query.filter(<EOL>'<STR_LIT>', **{filtered_field: kwargs[query_param]}<EOL>)<EOL><DEDENT><DEDENT>return agg_query<EOL>


def process_query_result(self, query_result, interval,<EOL>start_date, end_date):

def build_buckets(agg):<EOL><INDENT>"""<STR_LIT>"""<EOL>bucket_result = dict(<EOL>key=agg['<STR_LIT:key>'],<EOL>date=agg['<STR_LIT>'],<EOL>)<EOL>for metric in self.metric_fields:<EOL><INDENT>bucket_result[metric] = agg[metric]['<STR_LIT:value>']<EOL><DEDENT>if self.copy_fields and agg['<STR_LIT>']['<STR_LIT>']['<STR_LIT>']:<EOL><INDENT>doc = agg['<STR_LIT>']['<STR_LIT>']['<STR_LIT>'][<NUM_LIT:0>]['<STR_LIT>']<EOL>for destination, source in self.copy_fields.items():<EOL><INDENT>if isinstance(source, six.string_types):<EOL><INDENT>bucket_result[destination] = doc[source]<EOL><DEDENT>else:<EOL><INDENT>bucket_result[destination] = source(bucket_result, doc)<EOL><DEDENT><DEDENT><DEDENT>return bucket_result<EOL><DEDENT>buckets = query_result['<STR_LIT>']['<STR_LIT>']['<STR_LIT>']<EOL>return dict(<EOL>interval=interval,<EOL>key_type='<STR_LIT:date>',<EOL>start_date=start_date.isoformat() if start_date else None,<EOL>end_date=end_date.isoformat() if end_date else None,<EOL>buckets=[build_buckets(b) for b in buckets]<EOL>)<EOL>

Build the result using the query result.


def run(self, interval='<STR_LIT>', start_date=None,<EOL>end_date=None, **kwargs):

start_date = self.extract_date(start_date) if start_date else None<EOL>end_date = self.extract_date(end_date) if end_date else None<EOL>self.validate_arguments(interval, start_date, end_date, **kwargs)<EOL>agg_query = self.build_query(interval, start_date,<EOL>end_date, **kwargs)<EOL>query_result = agg_query.execute().to_dict()<EOL>res = self.process_query_result(query_result, interval,<EOL>start_date, end_date)<EOL>return res<EOL>


def __init__(self, time_field='<STR_LIT>', copy_fields=None,<EOL>query_modifiers=None, required_filters=None,<EOL>aggregated_fields=None, metric_fields=None, *args, **kwargs):

super(ESTermsQuery, self).__init__(*args, **kwargs)<EOL>self.time_field = time_field<EOL>self.copy_fields = copy_fields or {}<EOL>self.query_modifiers = query_modifiers or []<EOL>self.required_filters = required_filters or {}<EOL>self.aggregated_fields = aggregated_fields or []<EOL>self.metric_fields = metric_fields or {'<STR_LIT:value>': ('<STR_LIT>', '<STR_LIT:count>', {})}<EOL>

Constructor.

        :param time_field: name of the timestamp field.
        :param copy_fields: list of fields to copy from the top hit document
            into the resulting aggregation.
        :param query_modifiers: List of functions accepting a ``query`` and
            ``**kwargs`` (same as provided to the ``run`` method), that will
            be applied to the aggregation query.
        :param required_filters: Dict of "mandatory query parameter" ->
            "filtered field".
        :param aggregated_fields: List of fields which will be used in the
            terms aggregations.
        :param metric_fields: Dict of "destination field" ->
            tuple("metric type", "source field").


def validate_arguments(self, start_date, end_date, **kwargs):

if set(kwargs) < set(self.required_filters):<EOL><INDENT>raise InvalidRequestInputError(<EOL>'<STR_LIT>'<EOL>'<STR_LIT>'.format(set(self.required_filters.keys()),<EOL>self.query_name)<EOL>)<EOL><DEDENT>


def build_query(self, start_date, end_date, **kwargs):

agg_query = Search(using=self.client,<EOL>index=self.index,<EOL>doc_type=self.doc_type)[<NUM_LIT:0>:<NUM_LIT:0>]<EOL>if start_date is not None or end_date is not None:<EOL><INDENT>time_range = {}<EOL>if start_date is not None:<EOL><INDENT>time_range['<STR_LIT>'] = start_date.isoformat()<EOL><DEDENT>if end_date is not None:<EOL><INDENT>time_range['<STR_LIT>'] = end_date.isoformat()<EOL><DEDENT>agg_query = agg_query.filter(<EOL>'<STR_LIT>',<EOL>**{self.time_field: time_range})<EOL><DEDENT>for modifier in self.query_modifiers:<EOL><INDENT>agg_query = modifier(agg_query, **kwargs)<EOL><DEDENT>base_agg = agg_query.aggs<EOL>def _apply_metric_aggs(agg):<EOL><INDENT>for dst, (metric, field, opts) in self.metric_fields.items():<EOL><INDENT>agg.metric(dst, metric, field=field, **opts)<EOL><DEDENT><DEDENT>_apply_metric_aggs(base_agg)<EOL>if self.aggregated_fields:<EOL><INDENT>cur_agg = base_agg<EOL>for term in self.aggregated_fields:<EOL><INDENT>cur_agg = cur_agg.bucket(term, '<STR_LIT>', field=term, size=<NUM_LIT:0>)<EOL>_apply_metric_aggs(cur_agg)<EOL><DEDENT><DEDENT>if self.copy_fields:<EOL><INDENT>base_agg.metric(<EOL>'<STR_LIT>', '<STR_LIT>', size=<NUM_LIT:1>, sort={'<STR_LIT>': '<STR_LIT>'}<EOL>)<EOL><DEDENT>for query_param, filtered_field in self.required_filters.items():<EOL><INDENT>if query_param in kwargs:<EOL><INDENT>agg_query = agg_query.filter(<EOL>'<STR_LIT>', **{filtered_field: kwargs[query_param]}<EOL>)<EOL><DEDENT><DEDENT>return agg_query<EOL>


def process_query_result(self, query_result, start_date, end_date):

def build_buckets(agg, fields, bucket_result):<EOL><INDENT>"""<STR_LIT>"""<EOL>for metric in self.metric_fields:<EOL><INDENT>bucket_result[metric] = agg[metric]['<STR_LIT:value>']<EOL><DEDENT>if fields:<EOL><INDENT>current_level = fields[<NUM_LIT:0>]<EOL>bucket_result.update(dict(<EOL>type='<STR_LIT>',<EOL>field=current_level,<EOL>key_type='<STR_LIT>',<EOL>buckets=[build_buckets(b, fields[<NUM_LIT:1>:], dict(key=b['<STR_LIT:key>']))<EOL>for b in agg[current_level]['<STR_LIT>']]<EOL>))<EOL><DEDENT>return bucket_result<EOL><DEDENT>aggs = query_result['<STR_LIT>']<EOL>result = dict(<EOL>start_date=start_date.isoformat() if start_date else None,<EOL>end_date=end_date.isoformat() if end_date else None,<EOL>)<EOL>if self.copy_fields and aggs['<STR_LIT>']['<STR_LIT>']['<STR_LIT>']:<EOL><INDENT>doc = aggs['<STR_LIT>']['<STR_LIT>']['<STR_LIT>'][<NUM_LIT:0>]['<STR_LIT>']<EOL>for destination, source in self.copy_fields.items():<EOL><INDENT>if isinstance(source, six.string_types):<EOL><INDENT>result[destination] = doc[source]<EOL><DEDENT>else:<EOL><INDENT>result[destination] = source(result, doc)<EOL><DEDENT><DEDENT><DEDENT>return build_buckets(aggs, self.aggregated_fields, result)<EOL>


def run(self, start_date=None, end_date=None, **kwargs):

start_date = self.extract_date(start_date) if start_date else None<EOL>end_date = self.extract_date(end_date) if end_date else None<EOL>self.validate_arguments(start_date, end_date, **kwargs)<EOL>agg_query = self.build_query(start_date, end_date, **kwargs)<EOL>query_result = agg_query.execute().to_dict()<EOL>res = self.process_query_result(query_result, start_date, end_date)<EOL>return res<EOL>


ip = doc.pop('<STR_LIT>', None)<EOL>if ip:<EOL><INDENT>doc.update({'<STR_LIT>': get_geoip(ip)})<EOL><DEDENT>user_id = doc.pop('<STR_LIT>', '<STR_LIT>')<EOL>session_id = doc.pop('<STR_LIT>', '<STR_LIT>')<EOL>user_agent = doc.pop('<STR_LIT>', '<STR_LIT>')<EOL>timestamp = arrow.get(doc.get('<STR_LIT>'))<EOL>timeslice = timestamp.strftime('<STR_LIT>')<EOL>salt = get_anonymization_salt(timestamp)<EOL>visitor_id = hashlib.sha224(salt.encode('<STR_LIT:utf-8>'))<EOL>if user_id:<EOL><INDENT>visitor_id.update(user_id.encode('<STR_LIT:utf-8>'))<EOL><DEDENT>elif session_id:<EOL><INDENT>visitor_id.update(session_id.encode('<STR_LIT:utf-8>'))<EOL><DEDENT>elif ip and user_agent:<EOL><INDENT>vid = '<STR_LIT>'.format(ip, user_agent, timeslice)<EOL>visitor_id.update(vid.encode('<STR_LIT:utf-8>'))<EOL><DEDENT>else:<EOL><INDENT>pass<EOL><DEDENT>unique_session_id = hashlib.sha224(salt.encode('<STR_LIT:utf-8>'))<EOL>if user_id:<EOL><INDENT>sid = '<STR_LIT>'.format(user_id, timeslice)<EOL>unique_session_id.update(sid.encode('<STR_LIT:utf-8>'))<EOL><DEDENT>elif session_id:<EOL><INDENT>sid = '<STR_LIT>'.format(session_id, timeslice)<EOL>unique_session_id.update(sid.encode('<STR_LIT:utf-8>'))<EOL><DEDENT>elif ip and user_agent:<EOL><INDENT>sid = '<STR_LIT>'.format(ip, user_agent, timeslice)<EOL>unique_session_id.update(sid.encode('<STR_LIT:utf-8>'))<EOL><DEDENT>doc.update(dict(<EOL>visitor_id=visitor_id.hexdigest(),<EOL>unique_session_id=unique_session_id.hexdigest()<EOL>))<EOL>return doc<EOL>

Preprocess an event by anonymizing user information.

    The anonymization is done by removing fields that can uniquely identify a
    user, such as the user's ID, session ID, IP address and User Agent, and
    hashing them to produce a ``visitor_id`` and ``unique_session_id``. To
    further secure the method, a randomly generated 32-byte salt is used, that
    expires after 24 hours and is discarded. The salt values are stored in
    Redis (or whichever backend Invenio-Cache uses). The ``unique_session_id``
    is calculated in the same way as the ``visitor_id``, with the only
    difference that it also takes into account the hour of the event . All of
    these rules effectively mean that a user can have a unique ``visitor_id``
    for each day and unique ``unique_session_id`` for each hour of a day.

    This session ID generation process was designed according to the `Project
    COUNTER Code of Practice <https://www.projectcounter.org/code-of-
    practice-sections/general-information/>`_.

    In addition to that the country of the user is extracted from the IP
    address as a ISO 3166-1 alpha-2 two-letter country code (e.g. "CH" for
    Switzerland).


doc['<STR_LIT>'] = '<STR_LIT>' in doc and is_robot(doc['<STR_LIT>'])<EOL>return doc<EOL>

Flag events which are created by robots.

    The list of robots is defined by the `COUNTER-robots Python package
    <https://github.com/inveniosoftware/counter-robots>`_ , which follows the
    `list defined by Project COUNTER
    <https://www.projectcounter.org/appendices/850-2/>`_ that was later split
    into robots and machines by `the Make Data Count project
    <https://github.com/CDLUC3/Make-Data-Count/tree/master/user-agents>`_.


doc['<STR_LIT>'] = '<STR_LIT>' in doc and is_machine(doc['<STR_LIT>'])<EOL>return doc<EOL>

Flag events which are created by machines.

    The list of machines is defined by the `COUNTER-robots Python package
    <https://github.com/inveniosoftware/counter-robots>`_ , which follows the
    `list defined by Project COUNTER
    <https://www.projectcounter.org/appendices/850-2/>`_ that was later split
    into robots and machines by `the Make Data Count project
    <https://github.com/CDLUC3/Make-Data-Count/tree/master/user-agents>`_.


return '<STR_LIT>'.format(iso_timestamp,<EOL>hashlib.sha1(<EOL>msg.get('<STR_LIT>').encode('<STR_LIT:utf-8>') +<EOL>str(msg.get('<STR_LIT>')).<EOL>encode('<STR_LIT:utf-8>')).<EOL>hexdigest())<EOL>


def __init__(self, queue, prefix='<STR_LIT>', suffix='<STR_LIT>', client=None,<EOL>preprocessors=None, double_click_window=<NUM_LIT:10>):

self.queue = queue<EOL>self.client = client or current_search_client<EOL>self.doctype = queue.routing_key<EOL>self.index = '<STR_LIT>'.format(prefix, self.queue.routing_key)<EOL>self.suffix = suffix<EOL>self.preprocessors = [<EOL>obj_or_import_string(preproc) for preproc in preprocessors<EOL>] if preprocessors is not None else self.default_preprocessors<EOL>self.double_click_window = double_click_window<EOL>

Initialize indexer.

        :param prefix: prefix appended to elasticsearch indices' name.
        :param suffix: suffix appended to elasticsearch indices' name.
        :param double_click_window: time window during which similar events are
            deduplicated (counted as one occurence).
        :param client: elasticsearch client.
        :param preprocessors: a list of functions which are called on every
            event before it is indexed. Each function should return the
            processed event. If it returns None, the event is filtered and
            won't be indexed.


for msg in self.queue.consume():<EOL><INDENT>try:<EOL><INDENT>for preproc in self.preprocessors:<EOL><INDENT>msg = preproc(msg)<EOL>if msg is None:<EOL><INDENT>break<EOL><DEDENT><DEDENT>if msg is None:<EOL><INDENT>continue<EOL><DEDENT>suffix = arrow.get(msg.get('<STR_LIT>')).strftime(self.suffix)<EOL>ts = parser.parse(msg.get('<STR_LIT>'))<EOL>ts = ts.replace(microsecond=<NUM_LIT:0>)<EOL>msg['<STR_LIT>'] = ts.isoformat()<EOL>if self.double_click_window > <NUM_LIT:0>:<EOL><INDENT>timestamp = mktime(utc.localize(ts).utctimetuple())<EOL>ts = ts.fromtimestamp(<EOL>timestamp // self.double_click_window *<EOL>self.double_click_window<EOL>)<EOL><DEDENT>yield dict(<EOL>_id=hash_id(ts.isoformat(), msg),<EOL>_op_type='<STR_LIT:index>',<EOL>_index='<STR_LIT>'.format(self.index, suffix),<EOL>_type=self.doctype,<EOL>_source=msg,<EOL>)<EOL><DEDENT>except Exception:<EOL><INDENT>current_app.logger.exception(u'<STR_LIT>')<EOL><DEDENT><DEDENT>


return elasticsearch.helpers.bulk(<EOL>self.client,<EOL>self.actionsiter(),<EOL>stats_only=True,<EOL>chunk_size=<NUM_LIT:50><EOL>)<EOL>


for event_name, event_config in config.items():<EOL><INDENT>event_builders = [<EOL>obj_or_import_string(func)<EOL>for func in event_config.get('<STR_LIT>', [])<EOL>]<EOL>signal = obj_or_import_string(event_config['<STR_LIT>'])<EOL>signal.connect(<EOL>EventEmmiter(event_name, event_builders), sender=app, weak=False<EOL>)<EOL><DEDENT>

Register signal receivers which send events.


self.name = name<EOL>self.builders = builders<EOL>


<EOL>try:<EOL><INDENT>if self.name in current_stats.events:<EOL><INDENT>event = {}<EOL>for builder in self.builders:<EOL><INDENT>event = builder(event, *args, **kwargs)<EOL>if event is None:<EOL><INDENT>return<EOL><DEDENT><DEDENT>current_stats.publish(self.name, [event])<EOL><DEDENT><DEDENT>except Exception:<EOL><INDENT>current_app.logger.exception(u'<STR_LIT>')<EOL><DEDENT>


results = []<EOL>for e in event_types:<EOL><INDENT>processor = current_stats.events[e].processor_class(<EOL>**current_stats.events[e].processor_config)<EOL>results.append((e, processor.run()))<EOL><DEDENT>return results<EOL>


@shared_task<EOL>def aggregate_events(aggregations, start_date=None, end_date=None,<EOL>update_bookmark=True):

start_date = dateutil_parse(start_date) if start_date else None<EOL>end_date = dateutil_parse(end_date) if end_date else None<EOL>results = []<EOL>for a in aggregations:<EOL><INDENT>aggr_cfg = current_stats.aggregations[a]<EOL>aggregator = aggr_cfg.aggregator_class(<EOL>name=aggr_cfg.name, **aggr_cfg.aggregator_config)<EOL>results.append(aggregator.run(start_date, end_date, update_bookmark))<EOL><DEDENT>return results<EOL>


salt_key = '<STR_LIT>'.format(ts.date().isoformat())<EOL>salt = current_cache.get(salt_key)<EOL>if not salt:<EOL><INDENT>salt_bytes = os.urandom(<NUM_LIT:32>)<EOL>salt = b64encode(salt_bytes).decode('<STR_LIT:utf-8>')<EOL>current_cache.set(salt_key, salt, timeout=<NUM_LIT> * <NUM_LIT> * <NUM_LIT>)<EOL><DEDENT>return salt<EOL>

Get the anonymization salt based on the event timestamp's day.


reader = geolite2.reader()<EOL>ip_data = reader.get(ip) or {}<EOL>return ip_data.get('<STR_LIT>', {}).get('<STR_LIT>')<EOL>


return dict(<EOL>ip_address=request.remote_addr,<EOL>user_agent=request.user_agent.string,<EOL>user_id=(<EOL>current_user.get_id() if current_user.is_authenticated else None<EOL>),<EOL>session_id=session.get('<STR_LIT>')<EOL>)<EOL>

User information.

    .. note::

       **Privacy note** A users IP address, user agent string, and user id
       (if logged in) is sent to a message queue, where it is stored for about
       5 minutes. The information is used to:

       - Detect robot visits from the user agent string.
       - Generate an anonymized visitor id (using a random salt per day).
       - Detect the users host contry based on the IP address.

       The information is then discarded.


if isinstance(value, six.string_types):<EOL><INDENT>return import_string(value)<EOL><DEDENT>elif value:<EOL><INDENT>return value<EOL><DEDENT>return default<EOL>

Import string or return object.

    :params value: Import path or class object to instantiate.
    :params default: Default object to return if the import fails.
    :returns: The imported object.


def load_or_import_from_config(key, app=None, default=None):

app = app or current_app<EOL>imp = app.config.get(key)<EOL>return obj_or_import_string(imp, default=default)<EOL>

Load or import value from config.

    :returns: The loaded value.


from invenio_stats import current_stats<EOL>if current_stats.queries[query_name].permission_factory is None:<EOL><INDENT>return AllowAllPermission<EOL><DEDENT>else:<EOL><INDENT>return current_stats.queries[query_name].permission_factory(<EOL>query_name, params<EOL>)<EOL><DEDENT>

Default permission factory.

    It enables by default the statistics if they don't have a dedicated
    permission factory.


r = []<EOL>s = environ.get(cls.header_range, '<STR_LIT>').replace('<STR_LIT:U+0020>','<STR_LIT>').lower()<EOL>if s:<EOL><INDENT>l = s.split('<STR_LIT:=>')<EOL>if len(l) == <NUM_LIT:2>:<EOL><INDENT>unit, vals = tuple(l)<EOL>if unit == '<STR_LIT>' and vals:<EOL><INDENT>gen_rng = ( tuple(rng.split('<STR_LIT:->')) for rng in vals.split('<STR_LIT:U+002C>') if '<STR_LIT:->' in rng )<EOL>for start, end in gen_rng:<EOL><INDENT>if start or end:<EOL><INDENT>r.append( (int(start) if start else None, int(end) if end else None) )<EOL><DEDENT><DEDENT><DEDENT><DEDENT><DEDENT>return r<EOL>

Outputs a list of tuples with ranges or the empty list
According to the rfc, start or end values can be omitted


@classmethod<EOL><INDENT>def check_ranges(cls, ranges, length):<DEDENT>

result = []<EOL>for start, end in ranges:<EOL><INDENT>if isinstance(start, int) or isinstance(end, int):<EOL><INDENT>if isinstance(start, int) and not (<NUM_LIT:0> <= start < length):<EOL><INDENT>continue<EOL><DEDENT>elif isinstance(start, int) and isinstance(end, int) and not (start <= end):<EOL><INDENT>continue<EOL><DEDENT>elif start is None and end == <NUM_LIT:0>:<EOL><INDENT>continue<EOL><DEDENT>result.append( (start,end) )<EOL><DEDENT><DEDENT>return result<EOL>


@classmethod<EOL><INDENT>def convert_ranges(cls, ranges, length):<DEDENT>

result = []<EOL>for start, end in ranges:<EOL><INDENT>if end is None:<EOL><INDENT>result.append( (start, length-<NUM_LIT:1>) )<EOL><DEDENT>elif start is None:<EOL><INDENT>s = length - end<EOL>result.append( (<NUM_LIT:0> if s < <NUM_LIT:0> else s, length-<NUM_LIT:1>) )<EOL><DEDENT>else:<EOL><INDENT>result.append( (start, end if end < length else length-<NUM_LIT:1>) )<EOL><DEDENT><DEDENT>return result<EOL>


result = []<EOL>if ranges:<EOL><INDENT>ranges.sort(key=lambda tup: tup[<NUM_LIT:0>])<EOL>result.append(ranges[<NUM_LIT:0>])<EOL>for i in range(<NUM_LIT:1>, len(ranges)):<EOL><INDENT>if result[-<NUM_LIT:1>][<NUM_LIT:1>] + <NUM_LIT:1> >= ranges[i][<NUM_LIT:0>]:<EOL><INDENT>result[-<NUM_LIT:1>] = (result[-<NUM_LIT:1>][<NUM_LIT:0>], max(result[-<NUM_LIT:1>][<NUM_LIT:1>], ranges[i][<NUM_LIT:1>]))<EOL><DEDENT>else:<EOL><INDENT>result.append(ranges[i])<EOL><DEDENT><DEDENT><DEDENT>return result<EOL>