<commit_before>"""Support for balance data via the Starling Bank API."""
from __future__ import annotations

import logging

import requests
from starlingbank import StarlingAccount
import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_NAME
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

_LOGGER = logging.getLogger(__name__)

BALANCE_TYPES = ["cleared_balance", "effective_balance"]

CONF_ACCOUNTS = "accounts"
CONF_BALANCE_TYPES = "balance_types"
CONF_SANDBOX = "sandbox"

DEFAULT_SANDBOX = False
DEFAULT_ACCOUNT_NAME = "Starling"

ICON = "mdi:currency-gbp"

ACCOUNT_SCHEMA = vol.Schema(
    {
        vol.Required(CONF_ACCESS_TOKEN): cv.string,
        vol.Optional(CONF_BALANCE_TYPES, default=BALANCE_TYPES): vol.All(
            cv.ensure_list, [vol.In(BALANCE_TYPES)]
        ),
        vol.Optional(CONF_NAME, default=DEFAULT_ACCOUNT_NAME): cv.string,
        vol.Optional(CONF_SANDBOX, default=DEFAULT_SANDBOX): cv.boolean,
    }
)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {vol.Required(CONF_ACCOUNTS): vol.Schema([ACCOUNT_SCHEMA])}
)

def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_devices: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Sterling Bank sensor platform."""

    sensors = []
    for account in config[CONF_ACCOUNTS]:
        try:
            starling_account = StarlingAccount(
                account[CONF_ACCESS_TOKEN], sandbox=account[CONF_SANDBOX]
            )
            for balance_type in account[CONF_BALANCE_TYPES]:
                sensors.append(
                    StarlingBalanceSensor(
                        starling_account, account[CONF_NAME], balance_type
                    )
                )
        except requests.exceptions.HTTPError as error:
            _LOGGER.error(
                "Unable to set up Starling account '%s': %s", account[CONF_NAME], error
            )

    add_devices(sensors, True)

class StarlingBalanceSensor(SensorEntity):
    """Representation of a Starling balance sensor."""

    def __init__(self, starling_account, account_name, balance_data_type):
        """Initialize the sensor."""
        self._starling_account = starling_account
        self._balance_data_type = balance_data_type
        self._state = None
        self._account_name = account_name

    @property
    def name(self):
        """Return the name of the sensor."""
        return "{} {}".format(
            self._account_name, self._balance_data_type.replace("_", " ").capitalize()
        )

    @property
    def native_value(self):
        """Return the state of the sensor."""
        return self._state

    @property
    def native_unit_of_measurement(self):
        """Return the unit of measurement."""
        return self._starling_account.currency

    @property
    def icon(self):
        """Return the entity icon."""
        return ICON

    def update(self):
        """Fetch new state data for the sensor."""
        self._starling_account.update_balance_data()
        if self._balance_data_type == "cleared_balance":
            self._state = self._starling_account.cleared_balance / 100
        elif self._balance_data_type == "effective_balance":
            self._state = self._starling_account.effective_balance / 100
<commit_msg>Fix polling frequency for Starling integration (#73282)<commit_after>"""Support for balance data via the Starling Bank API."""
from __future__ import annotations

from datetime import timedelta
import logging

import requests
from starlingbank import StarlingAccount
import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_NAME
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType

_LOGGER = logging.getLogger(__name__)

BALANCE_TYPES = ["cleared_balance", "effective_balance"]

CONF_ACCOUNTS = "accounts"
CONF_BALANCE_TYPES = "balance_types"
CONF_SANDBOX = "sandbox"

DEFAULT_SANDBOX = False
DEFAULT_ACCOUNT_NAME = "Starling"

ICON = "mdi:currency-gbp"
SCAN_INTERVAL = timedelta(seconds=180)

ACCOUNT_SCHEMA = vol.Schema(
    {
        vol.Required(CONF_ACCESS_TOKEN): cv.string,
        vol.Optional(CONF_BALANCE_TYPES, default=BALANCE_TYPES): vol.All(
            cv.ensure_list, [vol.In(BALANCE_TYPES)]
        ),
        vol.Optional(CONF_NAME, default=DEFAULT_ACCOUNT_NAME): cv.string,
        vol.Optional(CONF_SANDBOX, default=DEFAULT_SANDBOX): cv.boolean,
    }
)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {vol.Required(CONF_ACCOUNTS): vol.Schema([ACCOUNT_SCHEMA])}
)

def setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    add_devices: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Sterling Bank sensor platform."""

    sensors = []
    for account in config[CONF_ACCOUNTS]:
        try:
            starling_account = StarlingAccount(
                account[CONF_ACCESS_TOKEN], sandbox=account[CONF_SANDBOX]
            )
            for balance_type in account[CONF_BALANCE_TYPES]:
                sensors.append(
                    StarlingBalanceSensor(
                        starling_account, account[CONF_NAME], balance_type
                    )
                )
        except requests.exceptions.HTTPError as error:
            _LOGGER.error(
                "Unable to set up Starling account '%s': %s", account[CONF_NAME], error
            )

    add_devices(sensors, True)

class StarlingBalanceSensor(SensorEntity):
    """Representation of a Starling balance sensor."""

    def __init__(self, starling_account, account_name, balance_data_type):
        """Initialize the sensor."""
        self._starling_account = starling_account
        self._balance_data_type = balance_data_type
        self._state = None
        self._account_name = account_name

    @property
    def name(self):
        """Return the name of the sensor."""
        return "{} {}".format(
            self._account_name, self._balance_data_type.replace("_", " ").capitalize()
        )

    @property
    def native_value(self):
        """Return the state of the sensor."""
        return self._state

    @property
    def native_unit_of_measurement(self):
        """Return the unit of measurement."""
        return self._starling_account.currency

    @property
    def icon(self):
        """Return the entity icon."""
        return ICON

    def update(self):
        """Fetch new state data for the sensor."""
        self._starling_account.update_balance_data()
        if self._balance_data_type == "cleared_balance":
            self._state = self._starling_account.cleared_balance / 100
        elif self._balance_data_type == "effective_balance":
            self._state = self._starling_account.effective_balance / 100
<|endoftext|>


<commit_before>"""
Support for system log.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/system_log/
"""
import asyncio
from collections import deque
from io import StringIO
import logging
import re
import traceback

import voluptuous as vol

from homeassistant import __path__ as HOMEASSISTANT_PATH
from homeassistant.components.http import HomeAssistantView
import homeassistant.helpers.config_validation as cv
from homeassistant.const import EVENT_HOMEASSISTANT_STOP

CONF_MAX_ENTRIES = 'max_entries'
CONF_MESSAGE = 'message'
CONF_LEVEL = 'level'
CONF_LOGGER = 'logger'

DATA_SYSTEM_LOG = 'system_log'
DEFAULT_MAX_ENTRIES = 50
DEPENDENCIES = ['http']
DOMAIN = 'system_log'

EVENT_SYSTEM_LOG = 'system_log_event'

SERVICE_CLEAR = 'clear'
SERVICE_WRITE = 'write'

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({
        vol.Optional(CONF_MAX_ENTRIES, default=DEFAULT_MAX_ENTRIES):
            cv.positive_int,
    }),
}, extra=vol.ALLOW_EXTRA)

SERVICE_CLEAR_SCHEMA = vol.Schema({})
SERVICE_WRITE_SCHEMA = vol.Schema({
    vol.Required(CONF_MESSAGE): cv.string,
    vol.Optional(CONF_LEVEL, default='error'):
        vol.In(['debug', 'info', 'warning', 'error', 'critical']),
    vol.Optional(CONF_LOGGER): cv.string,
})

def _figure_out_source(record, call_stack, hass):
    paths = [HOMEASSISTANT_PATH[0], hass.config.config_dir]
    try:
        # If netdisco is installed check its path too.
        from netdisco import __path__ as netdisco_path
        paths.append(netdisco_path[0])
    except ImportError:
        pass
    # If a stack trace exists, extract file names from the entire call stack.
    # The other case is when a regular "log" is made (without an attached
    # exception). In that case, just use the file where the log was made from.
    if record.exc_info:
        stack = [x[0] for x in traceback.extract_tb(record.exc_info[2])]
    else:
        index = -1
        for i, frame in enumerate(call_stack):
            if frame == record.pathname:
                index = i
                break
        if index == -1:
            # For some reason we couldn't find pathname in the stack.
            stack = [record.pathname]
        else:
            stack = call_stack[0:index+1]

    # Iterate through the stack call (in reverse) and find the last call from
    # a file in Home Assistant. Try to figure out where error happened.
    for pathname in reversed(stack):

        # Try to match with a file within Home Assistant
        match = re.match(r'(?:{})/(.*)'.format('|'.join(paths)), pathname)
        if match:
            return match.group(1)
    # Ok, we don't know what this is
    return record.pathname

def _exception_as_string(exc_info):
    buf = StringIO()
    if exc_info:
        traceback.print_exception(*exc_info, file=buf)
    return buf.getvalue()

class LogErrorHandler(logging.Handler):
    """Log handler for error messages."""

    def __init__(self, hass, maxlen):
        """Initialize a new LogErrorHandler."""
        super().__init__()
        self.hass = hass
        self.records = deque(maxlen=maxlen)

    def _create_entry(self, record, call_stack):
        return {
            'timestamp': record.created,
            'level': record.levelname,
            'message': record.getMessage(),
            'exception': _exception_as_string(record.exc_info),
            'source': _figure_out_source(record, call_stack, self.hass),
            }

    def emit(self, record):
        """Save error and warning logs.

        Everything logged with error or warning is saved in local buffer. A
        default upper limit is set to 50 (older entries are discarded) but can
        be changed if needed.
        """
        if record.levelno >= logging.WARN:
            stack = []
            if not record.exc_info:
                try:
                    stack = [f for f, _, _, _ in traceback.extract_stack()]
                except ValueError:
                    # On Python 3.4 under py.test getting the stack might fail.
                    pass

            entry = self._create_entry(record, stack)
            self.records.appendleft(entry)
            self.hass.bus.fire(EVENT_SYSTEM_LOG, entry)

@asyncio.coroutine
def async_setup(hass, config):
    """Set up the logger component."""
    conf = config.get(DOMAIN)
    if conf is None:
        conf = CONFIG_SCHEMA({DOMAIN: {}})[DOMAIN]

    handler = LogErrorHandler(hass, conf.get(CONF_MAX_ENTRIES))
    logging.getLogger().addHandler(handler)

    hass.http.register_view(AllErrorsView(handler))

    @asyncio.coroutine
    def async_service_handler(service):
        """Handle logger services."""
        if service.service == 'clear':
            handler.records.clear()
            return
        if service.service == 'write':
            logger = logging.getLogger(
                service.data.get(CONF_LOGGER, '{}.external'.format(__name__)))
            level = service.data[CONF_LEVEL]
            getattr(logger, level)(service.data[CONF_MESSAGE])

    @asyncio.coroutine
    def async_shutdown_handler(event):
        """Remove logging handler when Home Assistant is shutdown."""
        # This is needed as older logger instances will remain
        logging.getLogger().removeHandler(handler)

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                               async_shutdown_handler)

    hass.services.async_register(
        DOMAIN, SERVICE_CLEAR, async_service_handler,
        schema=SERVICE_CLEAR_SCHEMA)
    hass.services.async_register(
        DOMAIN, SERVICE_WRITE, async_service_handler,
        schema=SERVICE_WRITE_SCHEMA)

    return True

class AllErrorsView(HomeAssistantView):
    """Get all logged errors and warnings."""

    url = "/api/error/all"
    name = "api:error:all"

    def __init__(self, handler):
        """Initialize a new AllErrorsView."""
        self.handler = handler

    @asyncio.coroutine
    def get(self, request):
        """Get all errors and warnings."""
        # deque is not serializable (it's just "list-like") so it must be
        # converted to a list before it can be serialized to json
        return self.json(list(self.handler.records))
<commit_msg>Fix logger bug on Windows: path contains '\'. (#12197)<commit_after>"""
Support for system log.

For more details about this component, please refer to the documentation at
https://home-assistant.io/components/system_log/
"""
import asyncio
from collections import deque
from io import StringIO
import logging
import re
import traceback

import voluptuous as vol

from homeassistant import __path__ as HOMEASSISTANT_PATH
from homeassistant.components.http import HomeAssistantView
import homeassistant.helpers.config_validation as cv
from homeassistant.const import EVENT_HOMEASSISTANT_STOP

CONF_MAX_ENTRIES = 'max_entries'
CONF_MESSAGE = 'message'
CONF_LEVEL = 'level'
CONF_LOGGER = 'logger'

DATA_SYSTEM_LOG = 'system_log'
DEFAULT_MAX_ENTRIES = 50
DEPENDENCIES = ['http']
DOMAIN = 'system_log'

EVENT_SYSTEM_LOG = 'system_log_event'

SERVICE_CLEAR = 'clear'
SERVICE_WRITE = 'write'

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({
        vol.Optional(CONF_MAX_ENTRIES, default=DEFAULT_MAX_ENTRIES):
            cv.positive_int,
    }),
}, extra=vol.ALLOW_EXTRA)

SERVICE_CLEAR_SCHEMA = vol.Schema({})
SERVICE_WRITE_SCHEMA = vol.Schema({
    vol.Required(CONF_MESSAGE): cv.string,
    vol.Optional(CONF_LEVEL, default='error'):
        vol.In(['debug', 'info', 'warning', 'error', 'critical']),
    vol.Optional(CONF_LOGGER): cv.string,
})

def _figure_out_source(record, call_stack, hass):
    paths = [HOMEASSISTANT_PATH[0], hass.config.config_dir]
    try:
        # If netdisco is installed check its path too.
        from netdisco import __path__ as netdisco_path
        paths.append(netdisco_path[0])
    except ImportError:
        pass
    # If a stack trace exists, extract file names from the entire call stack.
    # The other case is when a regular "log" is made (without an attached
    # exception). In that case, just use the file where the log was made from.
    if record.exc_info:
        stack = [x[0] for x in traceback.extract_tb(record.exc_info[2])]
    else:
        index = -1
        for i, frame in enumerate(call_stack):
            if frame == record.pathname:
                index = i
                break
        if index == -1:
            # For some reason we couldn't find pathname in the stack.
            stack = [record.pathname]
        else:
            stack = call_stack[0:index+1]

    # Iterate through the stack call (in reverse) and find the last call from
    # a file in Home Assistant. Try to figure out where error happened.
    paths_re = r'(?:{})/(.*)'.format('|'.join([re.escape(x) for x in paths]))
    for pathname in reversed(stack):

        # Try to match with a file within Home Assistant
        match = re.match(paths_re, pathname)
        if match:
            return match.group(1)
    # Ok, we don't know what this is
    return record.pathname

def _exception_as_string(exc_info):
    buf = StringIO()
    if exc_info:
        traceback.print_exception(*exc_info, file=buf)
    return buf.getvalue()

class LogErrorHandler(logging.Handler):
    """Log handler for error messages."""

    def __init__(self, hass, maxlen):
        """Initialize a new LogErrorHandler."""
        super().__init__()
        self.hass = hass
        self.records = deque(maxlen=maxlen)

    def _create_entry(self, record, call_stack):
        return {
            'timestamp': record.created,
            'level': record.levelname,
            'message': record.getMessage(),
            'exception': _exception_as_string(record.exc_info),
            'source': _figure_out_source(record, call_stack, self.hass),
            }

    def emit(self, record):
        """Save error and warning logs.

        Everything logged with error or warning is saved in local buffer. A
        default upper limit is set to 50 (older entries are discarded) but can
        be changed if needed.
        """
        if record.levelno >= logging.WARN:
            stack = []
            if not record.exc_info:
                try:
                    stack = [f for f, _, _, _ in traceback.extract_stack()]
                except ValueError:
                    # On Python 3.4 under py.test getting the stack might fail.
                    pass

            entry = self._create_entry(record, stack)
            self.records.appendleft(entry)
            self.hass.bus.fire(EVENT_SYSTEM_LOG, entry)

@asyncio.coroutine
def async_setup(hass, config):
    """Set up the logger component."""
    conf = config.get(DOMAIN)
    if conf is None:
        conf = CONFIG_SCHEMA({DOMAIN: {}})[DOMAIN]

    handler = LogErrorHandler(hass, conf.get(CONF_MAX_ENTRIES))
    logging.getLogger().addHandler(handler)

    hass.http.register_view(AllErrorsView(handler))

    @asyncio.coroutine
    def async_service_handler(service):
        """Handle logger services."""
        if service.service == 'clear':
            handler.records.clear()
            return
        if service.service == 'write':
            logger = logging.getLogger(
                service.data.get(CONF_LOGGER, '{}.external'.format(__name__)))
            level = service.data[CONF_LEVEL]
            getattr(logger, level)(service.data[CONF_MESSAGE])

    @asyncio.coroutine
    def async_shutdown_handler(event):
        """Remove logging handler when Home Assistant is shutdown."""
        # This is needed as older logger instances will remain
        logging.getLogger().removeHandler(handler)

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP,
                               async_shutdown_handler)

    hass.services.async_register(
        DOMAIN, SERVICE_CLEAR, async_service_handler,
        schema=SERVICE_CLEAR_SCHEMA)
    hass.services.async_register(
        DOMAIN, SERVICE_WRITE, async_service_handler,
        schema=SERVICE_WRITE_SCHEMA)

    return True

class AllErrorsView(HomeAssistantView):
    """Get all logged errors and warnings."""

    url = "/api/error/all"
    name = "api:error:all"

    def __init__(self, handler):
        """Initialize a new AllErrorsView."""
        self.handler = handler

    @asyncio.coroutine
    def get(self, request):
        """Get all errors and warnings."""
        # deque is not serializable (it's just "list-like") so it must be
        # converted to a list before it can be serialized to json
        return self.json(list(self.handler.records))
<|endoftext|>


<commit_before>import sublime, sublime_plugin, os, subprocess

class AuraSave(sublime_plugin.EventListener):

  def on_post_save(self, view):
    print 'BuildonSave: on_post_save'

    #let's see if project wants to be autobuilt.
    should_build = view.settings().get('build_on_save')
    filename = view.file_name()
    if os.path.exists(os.path.dirname(filename)+'/.manifest'):
      print os.path.dirname(filename)

      command = '-f=' + filename
      view.window().run_command('exec',{ 'cmd':["force", "pushAura", command] })
      print 'Command should have executed'
    else:
      print 'BuildonSave: Project not configured for build_on_save.  Try setting build_on_save in project settings'<commit_msg>update<commit_after>import sublime, sublime_plugin, os, subprocess

class AuraSave(sublime_plugin.EventListener):

  def on_post_save(self, view):
    print 'BuildonSave: on_post_save'

    #let's see if project wants to be autobuilt.
    should_build = view.settings().get('build_on_save')
    filename = view.file_name()
    if os.path.basename(os.path.dirname(os.path.dirname(filename))) == "aurabundles":
      print os.path.dirname(filename)

      command = '-f=' + filename
      view.window().run_command('exec',{ 'cmd':["force", "pushAura", command] })
      print 'Command should have executed'
    else:
      print 'BuildonSave: Project not configured for build_on_save.  Try setting build_on_save in project settings'<|endoftext|>


<commit_before># originated from https://djangosnippets.org/snippets/2455/
import os

from PIL import Image

from django.conf import settings
from django.contrib.admin.widgets import AdminFileWidget
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _

try:
    from easy_thumbnails.files import get_thumbnailer

    def thumbnail(image_path):
        thumbnailer = get_thumbnailer(image_path)
        thumbnail_options = {'crop': False, 'size': (160, 160), 'detail': True, 'upscale': False}
        t = thumbnailer.get_thumbnail(thumbnail_options)
        media_url = settings.MEDIA_URL
        return u'' % (media_url, t, image_path)
except ImportError:
    def thumbnail(image_path):
        absolute_url = os.path.join(settings.MEDIA_URL, image_path)
        return u'' % (absolute_url, image_path)

def list_display(image):
    file_name = str(image)
    if file_name:
        file_path = '%s%s' % (settings.MEDIA_URL, file_name)
        try:            # is image
            image = Image.open(os.path.join(settings.MEDIA_ROOT, file_name))
            image_tag = '%s' % (file_path, thumbnail(file_name),)
            image.close()
            return image_tag
        except IOError:  # not image
            return None

class AdminImageWidget(AdminFileWidget):
    """
    A FileField Widget that displays an image instead of a file path
    if the current file is an image.
    """
    def render(self, name, value, attrs=None):
        output = []
        file_name = str(value)
        if file_name:
            file_path = '%s%s' % (settings.MEDIA_URL, file_name)
            try:            # is image
                image = Image.open(os.path.join(settings.MEDIA_ROOT, file_name))
                output.append(
                    '%s' %
                    (file_path, thumbnail(file_name),),
                )
                image.close()
            except IOError:  # not image
                output.append(
                    '%s %s
%s ' %
                    (_('Currently:'), file_path, file_name, _('Change:')),
                )

        output.append(super(AdminFileWidget, self).render(name, value, attrs))
        return mark_safe(u''.join(output))
<commit_msg>fix renderer bug after update<commit_after># originated from https://djangosnippets.org/snippets/2455/
import os

from PIL import Image

from django.conf import settings
from django.contrib.admin.widgets import AdminFileWidget
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _

try:
    from easy_thumbnails.files import get_thumbnailer

    def thumbnail(image_path):
        thumbnailer = get_thumbnailer(image_path)
        thumbnail_options = {'crop': False, 'size': (160, 160), 'detail': True, 'upscale': False}
        t = thumbnailer.get_thumbnail(thumbnail_options)
        media_url = settings.MEDIA_URL
        return u'' % (media_url, t, image_path)
except ImportError:
    def thumbnail(image_path):
        absolute_url = os.path.join(settings.MEDIA_URL, image_path)
        return u'' % (absolute_url, image_path)

def list_display(image):
    file_name = str(image)
    if file_name:
        file_path = '%s%s' % (settings.MEDIA_URL, file_name)
        try:            # is image
            image = Image.open(os.path.join(settings.MEDIA_ROOT, file_name))
            image_tag = '%s' % (file_path, thumbnail(file_name),)
            image.close()
            return image_tag
        except IOError:  # not image
            return None

class AdminImageWidget(AdminFileWidget):
    """
    A FileField Widget that displays an image instead of a file path
    if the current file is an image.
    """
    def render(self, name, value, attrs=None, renderer=None):
        output = []
        file_name = str(value)
        if file_name:
            file_path = '%s%s' % (settings.MEDIA_URL, file_name)
            try:            # is image
                image = Image.open(os.path.join(settings.MEDIA_ROOT, file_name))
                output.append(
                    '%s' %
                    (file_path, thumbnail(file_name),),
                )
                image.close()
            except IOError:  # not image
                output.append(
                    '%s %s
%s ' %
                    (_('Currently:'), file_path, file_name, _('Change:')),
                )

        output.append(super(AdminFileWidget, self).render(name, value, attrs))
        return mark_safe(u''.join(output))
<|endoftext|>


<commit_before>from logging import LogTrace, LogError, LogMessage
import symFileManager

import re
import json
import urllib2
from bisect import bisect

# Precompiled regex for validating lib names
gLibNameRE = re.compile("[0-9a-zA-Z_+\-\.]*$") # Empty lib name means client couldn't associate frame with any lib
gPdbSigRE = re.compile("{([0-9a-fA-F]{8})-([0-9a-fA-F]{4})-([0-9a-fA-F]{4})-([0-9a-fA-F]{4})-([0-9a-fA-F]{12})}$")
gPdbSigRE2 = re.compile("[0-9a-fA-F]{32}$")

# Maximum number of times a request can be forwarded to a different server
# for symbolication. Also prevents loops.
MAX_FORWARDED_REQUESTS = 3

class ModuleV3:
  def __init__(self, libName, breakpadId):
    self.libName = libName
    self.breakpadId = breakpadId

def getModuleV2(libName, pdbAge, pdbSig, pdbName):
  if isinstance(pdbSig, basestring):
    matches = gPdbSigRE.match(pdbSig)
    if matches:
      pdbSig = "".join(matches.groups()).upper()
    elif gPdbSigRE2.match(pdbSig):
      pdbSig = pdbSig.upper()
    else:
      LogTrace("Bad PDB signature: " + pdbSig)
      return None
  else:
    LogTrace("Bad PDB signature: " + str(pdbSig))
    return None

  if isinstance(pdbAge, basestring):
    pdbAge = int(pdbAge)
  if not isinstance(pdbAge, (int, long)) or int(pdbAge) < 0:
    LogTrace("Bad PDB age: " + str(pdbAge))
    return None
  pdbAge = (hex(pdbAge)[2:]).lower()

  if not isinstance(pdbName, basestring) or not gLibNameRE.match(pdbName):
    LogTrace("Bad PDB name: " + str(pdbName))
    return None
  return ModuleV3(pdbName, pdbSig + pdbAge)

def getModuleV3(libName, breakpadId):
  if not isinstance(libName, basestring) or not gLibNameRE.match(libName):
    LogTrace("Bad library name: " + str(libName))
    return None

  if not isinstance(breakpadId, basestring):
    LogTrace("Bad breakpad id: " + str(breakpadId))
    return None

  return ModuleV3(libName, breakpadId)

class SymbolicationRequest:
  def __init__(self, symFileManager, rawRequests):
    self.Reset()
    self.symFileManager = symFileManager
    self.stacks = []
    self.memoryMaps = []
    self.symbolSources = []
    self.ParseRequests(rawRequests)

  def Reset(self):
    self.symFileManager = None
    self.isValidRequest = False
    self.memoryMaps = []
    self.stacks = []
    self.appName = ""
    self.osName = ""
    self.forwardCount = 0

  def ParseRequests(self, rawRequests):
    self.isValidRequest = False

    try:
      if not isinstance(rawRequests, dict):
        LogTrace("Request is not a dictionary")
        return

      if "version" not in rawRequests:
        LogTrace("Request is missing 'version' field")
        return
      version = rawRequests["version"]
      if version != 2 and version != 3:
        LogTrace("Invalid version: %s" % version)
        return

      if "forwarded" in rawRequests:
        if not isinstance(rawRequests["forwarded"], (int, long)):
          LogTrace("Invalid 'forwards' field: " + str(rawRequests["forwarded"]))
          return
        self.forwardCount = rawRequests["forwarded"]

      # Only used for compatibility with older clients.
      # TODO: Remove after June 2013.
      if "appName" in rawRequests:
        requestingApp = rawRequests["appName"].upper()
        if requestingApp in self.symFileManager.sOptions["symbolPaths"]:
          self.appName = requestingApp

      # Ditto
      if "osName" in rawRequests:
        requestingOs = rawRequests["osName"].upper()
        if requestingOs in self.symFileManager.sOptions["symbolPaths"]:
          self.osName = requestingOs

      # Client specifies which sets of symbols should be used
      if "symbolSources" in rawRequests:
        try:
          sourceList = [x.upper() for x in rawRequests["symbolSources"]]
          for source in sourceList:
            if source in self.symFileManager.sOptions["symbolPaths"]:
              self.symbolSources.append(source)
            else:
              LogTrace("Unrecognized symbol source: " + source)
              continue
        except:
          self.symbolSources = []
          pass

      if not self.symbolSources:
        self.symbolSources.append(self.symFileManager.sOptions["defaultApp"])
        self.symbolSources.append(self.symFileManager.sOptions["defaultOs"])

      if "memoryMap" not in rawRequests:
        LogTrace("Request is missing 'memoryMap' field")
        return
      memoryMap = rawRequests["memoryMap"]
      if not isinstance(memoryMap, list):
        LogTrace("'memoryMap' field in request is not a list")

      if "stacks" not in rawRequests:
        LogTrace("Request is missing 'stacks' field")
        return
      stacks = rawRequests["stacks"]
      if not isinstance(stacks, list):
        LogTrace("'stacks' field in request is not a list")
        return

      # Check memory map is well-formatted
      cleanMemoryMap = []
      for module in memoryMap:
        if not isinstance(module, list):
          LogTrace("Entry in memory map is not a list: " + str(module))
          return

        if version == 2:
          if len(module) != 4:
            LogTrace("Entry in memory map is not a 4 item list: " + str(module))
            return
          module = getModuleV2(*module)
        else:
          assert version == 3
          if len(module) != 2:
            LogTrace("Entry in memory map is not a 2 item list: " + str(module))
            return
          module = getModuleV3(*module)

        if module is None:
          return

        cleanMemoryMap.append(module)

      self.combinedMemoryMap = cleanMemoryMap

      # Check stack is well-formatted
      for stack in stacks:
        if not isinstance(stack, list):
          LogTrace("stack is not a list")
          return
        for entry in stack:
          if not isinstance(entry, list):
            LogTrace("stack entry is not a list")
            return
          if len(entry) != 2:
            LogTrace("stack entry doesn't have exactly 2 elements")
            return

        self.stacks.append(stack)

    except Exception as e:
      LogTrace("Exception while parsing request: " + str(e))
      return

    self.isValidRequest = True

  def ForwardRequest(self, indexes, stack, modules, symbolicatedStack):
    LogTrace("Forwarding " + str(len(stack)) + " PCs for symbolication")

    try:
      url = self.symFileManager.sOptions["remoteSymbolServer"]
      rawModules =  []
      moduleToIndex = {}
      moduleCount = 0
      for m in modules:
        l = [m.libName, m.breakpadId]
        rawModules.append(l)
        moduleToIndex[m] = moduleCount
        moduleCount += 1

      rawStack = []
      for entry in stack:
        moduleIndex = entry[0]
        offset = entry[1]
        module = self.combinedMemoryMap[moduleIndex]
        newIndex = moduleToIndex[module]
        rawStack.append([newIndex, offset])

      requestObj = { "symbolSources": self.symbolSources,
                     "stacks": [rawStack], "memoryMap": rawModules,
                     "forwarded": self.forwardCount + 1, "version": 3 }
      requestJson = json.dumps(requestObj)
      headers = { "Content-Type": "application/json" }
      requestHandle = urllib2.Request(url, requestJson, headers)
      response = urllib2.urlopen(requestHandle)
    except Exception as e:
      LogError("Exception while forwarding request: " + str(e))
      return

    try:
      responseJson = response.read()
    except Exception as e:
      LogError("Exception while reading server response to forwarded request: " + str(e))
      return

    try:
      responseSymbols = json.loads(responseJson)[0]
      if len(responseSymbols) != len(stack):
        LogError(str(len(responseSymbols)) + " symbols in response, " + str(len(stack)) + " PCs in request!")
        return

      for index in range(0, len(stack)):
        symbol = responseSymbols[index]
        originalIndex = indexes[index]
        symbolicatedStack[originalIndex] = symbol
    except Exception as e:
      LogError("Exception while parsing server response to forwarded request: " + str(e))
      return

  def Symbolicate(self, stackNum):
    # Check if we should forward requests when required sym files don't exist
    shouldForwardRequests = False
    if self.symFileManager.sOptions["remoteSymbolServer"] and self.forwardCount < MAX_FORWARDED_REQUESTS:
      shouldForwardRequests = True

    # Symbolicate each PC
    pcIndex = -1
    symbolicatedStack = []
    missingSymFiles = []
    unresolvedIndexes = []
    unresolvedStack = []
    unresolvedModules = []
    stack = self.stacks[stackNum]

    for entry in stack:
      pcIndex += 1
      moduleIndex = entry[0]
      offset = entry[1]
      if moduleIndex == -1:
        symbolicatedStack.append(hex(offset))
        continue
      module = self.combinedMemoryMap[moduleIndex]

      # Don't look for a missing lib multiple times in one request
      if (module.libName, module.breakpadId) in missingSymFiles:
        if shouldForwardRequests:
          unresolvedIndexes.append(pcIndex)
          unresolvedStack.append(entry)
        symbolicatedStack.append(hex(offset) + " (in " + module.libName + ")")
        continue

      functionName = None
      libSymbolMap = self.symFileManager.GetLibSymbolMap(module.libName,
                                                         module.breakpadId,
                                                         self.symbolSources)
      if libSymbolMap:
        functionName = libSymbolMap.Lookup(offset)
      else:
        if shouldForwardRequests:
          unresolvedIndexes.append(pcIndex)
          unresolvedStack.append(entry)
          unresolvedModules.append(module)
        missingSymFiles.append((module.libName, module.breakpadId))

      if functionName == None:
        functionName = hex(offset)
      symbolicatedStack.append(functionName + " (in " + module.libName + ")")

    # Ask another server for help symbolicating unresolved addresses
    if len(unresolvedStack) > 0:
      self.ForwardRequest(unresolvedIndexes, unresolvedStack, unresolvedModules, symbolicatedStack)

    return symbolicatedStack
<commit_msg>Fixed backwards-compatibility<commit_after>from logging import LogTrace, LogError, LogMessage
import symFileManager

import re
import json
import urllib2
from bisect import bisect

# Precompiled regex for validating lib names
gLibNameRE = re.compile("[0-9a-zA-Z_+\-\.]*$") # Empty lib name means client couldn't associate frame with any lib
gPdbSigRE = re.compile("{([0-9a-fA-F]{8})-([0-9a-fA-F]{4})-([0-9a-fA-F]{4})-([0-9a-fA-F]{4})-([0-9a-fA-F]{12})}$")
gPdbSigRE2 = re.compile("[0-9a-fA-F]{32}$")

# Maximum number of times a request can be forwarded to a different server
# for symbolication. Also prevents loops.
MAX_FORWARDED_REQUESTS = 3

class ModuleV3:
  def __init__(self, libName, breakpadId):
    self.libName = libName
    self.breakpadId = breakpadId

def getModuleV2(libName, pdbAge, pdbSig, pdbName):
  if isinstance(pdbSig, basestring):
    matches = gPdbSigRE.match(pdbSig)
    if matches:
      pdbSig = "".join(matches.groups()).upper()
    elif gPdbSigRE2.match(pdbSig):
      pdbSig = pdbSig.upper()
    else:
      LogTrace("Bad PDB signature: " + pdbSig)
      return None
  else:
    LogTrace("Bad PDB signature: " + str(pdbSig))
    return None

  if isinstance(pdbAge, basestring):
    pdbAge = int(pdbAge)
  if not isinstance(pdbAge, (int, long)) or int(pdbAge) < 0:
    LogTrace("Bad PDB age: " + str(pdbAge))
    return None
  pdbAge = (hex(pdbAge)[2:]).lower()

  if not isinstance(pdbName, basestring) or not gLibNameRE.match(pdbName):
    LogTrace("Bad PDB name: " + str(pdbName))
    return None
  return ModuleV3(pdbName, pdbSig + pdbAge)

def getModuleV3(libName, breakpadId):
  if not isinstance(libName, basestring) or not gLibNameRE.match(libName):
    LogTrace("Bad library name: " + str(libName))
    return None

  if not isinstance(breakpadId, basestring):
    LogTrace("Bad breakpad id: " + str(breakpadId))
    return None

  return ModuleV3(libName, breakpadId)

class SymbolicationRequest:
  def __init__(self, symFileManager, rawRequests):
    self.Reset()
    self.symFileManager = symFileManager
    self.stacks = []
    self.memoryMaps = []
    self.symbolSources = []
    self.ParseRequests(rawRequests)

  def Reset(self):
    self.symFileManager = None
    self.isValidRequest = False
    self.memoryMaps = []
    self.stacks = []
    self.appName = ""
    self.osName = ""
    self.forwardCount = 0

  def ParseRequests(self, rawRequests):
    self.isValidRequest = False

    try:
      if not isinstance(rawRequests, dict):
        LogTrace("Request is not a dictionary")
        return

      if "version" not in rawRequests:
        LogTrace("Request is missing 'version' field")
        return
      version = rawRequests["version"]
      if version != 2 and version != 3:
        LogTrace("Invalid version: %s" % version)
        return

      if "forwarded" in rawRequests:
        if not isinstance(rawRequests["forwarded"], (int, long)):
          LogTrace("Invalid 'forwards' field: " + str(rawRequests["forwarded"]))
          return
        self.forwardCount = rawRequests["forwarded"]

      # Only used for compatibility with older clients.
      # TODO: Remove after June 2013.
      if "appName" in rawRequests:
        requestingApp = rawRequests["appName"].upper()
        if requestingApp in self.symFileManager.sOptions["symbolPaths"]:
          self.symbolSources.append(requestingApp)

      # Ditto
      if "osName" in rawRequests:
        requestingOs = rawRequests["osName"].upper()
        if requestingOs in self.symFileManager.sOptions["symbolPaths"]:
          self.symbolSources.append(requestingOs)

      # Client specifies which sets of symbols should be used
      if "symbolSources" in rawRequests:
        try:
          sourceList = [x.upper() for x in rawRequests["symbolSources"]]
          for source in sourceList:
            if source in self.symFileManager.sOptions["symbolPaths"]:
              self.symbolSources.append(source)
            else:
              LogTrace("Unrecognized symbol source: " + source)
              continue
        except:
          self.symbolSources = []
          pass

      if not self.symbolSources:
        self.symbolSources.append(self.symFileManager.sOptions["defaultApp"])
        self.symbolSources.append(self.symFileManager.sOptions["defaultOs"])

      if "memoryMap" not in rawRequests:
        LogTrace("Request is missing 'memoryMap' field")
        return
      memoryMap = rawRequests["memoryMap"]
      if not isinstance(memoryMap, list):
        LogTrace("'memoryMap' field in request is not a list")

      if "stacks" not in rawRequests:
        LogTrace("Request is missing 'stacks' field")
        return
      stacks = rawRequests["stacks"]
      if not isinstance(stacks, list):
        LogTrace("'stacks' field in request is not a list")
        return

      # Check memory map is well-formatted
      cleanMemoryMap = []
      for module in memoryMap:
        if not isinstance(module, list):
          LogTrace("Entry in memory map is not a list: " + str(module))
          return

        if version == 2:
          if len(module) != 4:
            LogTrace("Entry in memory map is not a 4 item list: " + str(module))
            return
          module = getModuleV2(*module)
        else:
          assert version == 3
          if len(module) != 2:
            LogTrace("Entry in memory map is not a 2 item list: " + str(module))
            return
          module = getModuleV3(*module)

        if module is None:
          return

        cleanMemoryMap.append(module)

      self.combinedMemoryMap = cleanMemoryMap

      # Check stack is well-formatted
      for stack in stacks:
        if not isinstance(stack, list):
          LogTrace("stack is not a list")
          return
        for entry in stack:
          if not isinstance(entry, list):
            LogTrace("stack entry is not a list")
            return
          if len(entry) != 2:
            LogTrace("stack entry doesn't have exactly 2 elements")
            return

        self.stacks.append(stack)

    except Exception as e:
      LogTrace("Exception while parsing request: " + str(e))
      return

    self.isValidRequest = True

  def ForwardRequest(self, indexes, stack, modules, symbolicatedStack):
    LogTrace("Forwarding " + str(len(stack)) + " PCs for symbolication")

    try:
      url = self.symFileManager.sOptions["remoteSymbolServer"]
      rawModules =  []
      moduleToIndex = {}
      moduleCount = 0
      for m in modules:
        l = [m.libName, m.breakpadId]
        rawModules.append(l)
        moduleToIndex[m] = moduleCount
        moduleCount += 1

      rawStack = []
      for entry in stack:
        moduleIndex = entry[0]
        offset = entry[1]
        module = self.combinedMemoryMap[moduleIndex]
        newIndex = moduleToIndex[module]
        rawStack.append([newIndex, offset])

      requestObj = { "symbolSources": self.symbolSources,
                     "stacks": [rawStack], "memoryMap": rawModules,
                     "forwarded": self.forwardCount + 1, "version": 3 }
      requestJson = json.dumps(requestObj)
      headers = { "Content-Type": "application/json" }
      requestHandle = urllib2.Request(url, requestJson, headers)
      response = urllib2.urlopen(requestHandle)
    except Exception as e:
      LogError("Exception while forwarding request: " + str(e))
      return

    try:
      responseJson = response.read()
    except Exception as e:
      LogError("Exception while reading server response to forwarded request: " + str(e))
      return

    try:
      responseSymbols = json.loads(responseJson)[0]
      if len(responseSymbols) != len(stack):
        LogError(str(len(responseSymbols)) + " symbols in response, " + str(len(stack)) + " PCs in request!")
        return

      for index in range(0, len(stack)):
        symbol = responseSymbols[index]
        originalIndex = indexes[index]
        symbolicatedStack[originalIndex] = symbol
    except Exception as e:
      LogError("Exception while parsing server response to forwarded request: " + str(e))
      return

  def Symbolicate(self, stackNum):
    # Check if we should forward requests when required sym files don't exist
    shouldForwardRequests = False
    if self.symFileManager.sOptions["remoteSymbolServer"] and self.forwardCount < MAX_FORWARDED_REQUESTS:
      shouldForwardRequests = True

    # Symbolicate each PC
    pcIndex = -1
    symbolicatedStack = []
    missingSymFiles = []
    unresolvedIndexes = []
    unresolvedStack = []
    unresolvedModules = []
    stack = self.stacks[stackNum]

    for entry in stack:
      pcIndex += 1
      moduleIndex = entry[0]
      offset = entry[1]
      if moduleIndex == -1:
        symbolicatedStack.append(hex(offset))
        continue
      module = self.combinedMemoryMap[moduleIndex]

      # Don't look for a missing lib multiple times in one request
      if (module.libName, module.breakpadId) in missingSymFiles:
        if shouldForwardRequests:
          unresolvedIndexes.append(pcIndex)
          unresolvedStack.append(entry)
        symbolicatedStack.append(hex(offset) + " (in " + module.libName + ")")
        continue

      functionName = None
      libSymbolMap = self.symFileManager.GetLibSymbolMap(module.libName,
                                                         module.breakpadId,
                                                         self.symbolSources)
      if libSymbolMap:
        functionName = libSymbolMap.Lookup(offset)
      else:
        if shouldForwardRequests:
          unresolvedIndexes.append(pcIndex)
          unresolvedStack.append(entry)
          unresolvedModules.append(module)
        missingSymFiles.append((module.libName, module.breakpadId))

      if functionName == None:
        functionName = hex(offset)
      symbolicatedStack.append(functionName + " (in " + module.libName + ")")

    # Ask another server for help symbolicating unresolved addresses
    if len(unresolvedStack) > 0:
      self.ForwardRequest(unresolvedIndexes, unresolvedStack, unresolvedModules, symbolicatedStack)

    return symbolicatedStack
<|endoftext|>


<commit_before>"""Various algorithms for helping identifying numbers and sequences."""
from __future__ import print_function, division

from sympy.utilities import public

from sympy.core.compatibility import range
from sympy.core import Function, Symbol
from sympy.core.numbers import Zero
from sympy import sympify, floor, sqrt, lcm, denom, Integer, Rational

@public
def find_simple_recurrence_vector(l):
    """
    This function is used internally by other functions from the
    sympy.concrete.guess module. While most users may want to rather use the
    function find_simple_recurrence when looking for recurrence relations
    among rational numbers, the current function may still be useful when
    some post-processing has to be done.

    The function returns a vector of length n when a recurrence relation of
    order n is detected in the sequence of rational numbers v.

    If the returned vector has a length 1, then the returned value is always
    the list [0], which means that no relation has been found.

    While the functions is intended to be used with rational numbers, it should
    work for other kinds of real numbers except for some cases involving
    quadratic numbers; for that reason it should be used with some caution when
    the argument is not a list of rational numbers.

    Examples
    ========

    >>> from sympy.concrete.guess import find_simple_recurrence_vector
    >>> from sympy import fibonacci
    >>> find_simple_recurrence_vector([fibonacci(k) for k in range(12)])
    [1, -1, -1]

    See also
    ========

    See the function sympy.concrete.guess.find_simple_recurrence which is more
    user-friendly.

    """
    q1 = [0]
    q2 = [Integer(1)]
    z = len(l) >> 1
    while len(q2) <= z:
        b = 0
        while l[b]==0:
            b += 1
            if b == len(l):
                c = 1
                for x in q2:
                    c = lcm(c, denom(x))
                if q2[0]*c < 0: c = -c
                for k in range(len(q2)):
                    q2[k] = int(q2[k]*c)
                return q2
        m = [Integer(1)/l[b]]
        for k in range(b+1, len(l)):
            s = 0
            for j in range(b, k):
                s -= l[j+1] * m[b-j-1]
            m.append(s/l[b])
        l = m
        a, l[0] = l[0], 0
        q = [0] * max(len(q2), b+len(q1))
        for k in range(len(q2)):
            q[k] = a*q2[k]
        for k in range(b, b+len(q1)):
            q[k] += q1[k-b]
        while q[-1]==0: q.pop() # because trailing zeros can occur
        q1, q2 = q2, q
    return [0]

@public
def find_simple_recurrence(v, A=Function('a'), N=Symbol('n')):
    """
    Detects and returns a recurrence relation from a sequence of several integer
    (or rational) terms. The name of the function in the returned expression is
    'a' by default; the main variable is 'n' by default. The smallest index in
    the returned expression is always n (and never n-1, n-2, etc.).

    Examples
    ========

    >>> from sympy.concrete.guess import find_simple_recurrence
    >>> from sympy import fibonacci
    >>> find_simple_recurrence([fibonacci(k) for k in range(12)])
    -a(n) - a(n + 1) + a(n + 2)

    >>> from sympy import Function, Symbol
    >>> a = [1, 1, 1]
    >>> for k in range(15): a.append(5*a[-1]-3*a[-2]+8*a[-3])
    >>> find_simple_recurrence(a, A=Function('f'), N=Symbol('i'))
    -8*f(i) + 3*f(i + 1) - 5*f(i + 2) + f(i + 3)

    """
    p = find_simple_recurrence_vector(v)
    n = len(p)
    if n <= 1: return Zero()

    rel = Zero()
    for k in range(n):
        rel += A(N+n-1-k)*p[k]

    return rel

@public
def rationalize(x, maxcoeff=10000):
    """
    Helps identifying a rational number from a float (or mpmath.mpf) value by
    using a continued fraction. The algorithm stops as soon as a large partial
    quotient is detected (greater than 10000 by default).

    Examples
    ========

    >>> from sympy.concrete.guess import rationalize
    >>> from mpmath import cos, pi
    >>> rationalize(cos(pi/3))
    1/2

    >>> from mpmath import mpf
    >>> rationalize(mpf("0.333333333333333"))
    1/3

    While the function is rather intended to help 'identifying' rational
    values, it may be used in some cases for approximating real numbers.
    (Though other functions may be more relevant in that case.)

    >>> rationalize(pi, maxcoeff = 250)
    355/113

    See also
    ========
    Several other methods can approximate a real number as a rational, like:

      * fractions.Fraction.from_decimal
      * fractions.Fraction.from_float
      * mpmath.identify
      * mpmath.pslq by using the following syntax: mpmath.pslq([x, 1])
      * mpmath.findpoly by using the following syntax: mpmath.findpoly(x, 1)
      * sympy.simplify.nsimplify (which is a more general function)

    The main difference between the current function and all these variants is
    that control focuses on magnitude of partial quotients here rather than on
    global precision of the approximation. If the real is "known to be" a
    rational number, the current function should be able to detect it correctly
    with the default settings even when denominator is great (unless its
    expansion contains unusually big partial quotients) which may occur
    when studying sequences of increasing numbers. If the user cares more
    on getting simple fractions, other methods may be more convenient.

    """
    p0, p1 = 0, 1
    q0, q1 = 1, 0
    a = floor(x)
    while a < maxcoeff or q1==0:
        p = a*p1 + p0
        q = a*q1 + q0
        p0, p1 = p1, p
        q0, q1 = q1, q
        if x==a: break
        x = 1/(x-a)
        a = floor(x)
    return sympify(p) / q

@public
def guess_generating_function_rational(v, X=Symbol('x')):
    """
    Tries to "guess" a rational generating function for a sequence of rational
    numbers v.

    Examples
    ========

    >>> from sympy.concrete.guess import guess_generating_function_rational
    >>> from sympy import fibonacci
    >>> l = [fibonacci(k) for k in range(5,15)]
    >>> guess_generating_function_rational(l)
    (3*x + 5)/(-x**2 - x + 1)

    See also
    ========
    See function sympy.series.approximants and mpmath.pade

    """
    #   a) compute the denominator as q
    q = find_simple_recurrence_vector(v)
    n = len(q)
    if n <= 1: return None
    #   b) compute the numerator as p
    p = [sum(v[i-k]*q[k] for k in range(min(i+1, n)))
            for i in range(len(v))] # TODO: maybe better with:  len(v)>>1
    return (sum(p[k]*X**k for k in range(len(p)))
            / sum(q[k]*X**k for k in range(n)))

@public
def guess_generating_function(v, X=Symbol('x'), types=['all'], maxsqrtn=2):
    """
    Tries to "guess" a generating function for a sequence of rational numbers v.
    Only a few patterns are implemented yet.

    The function returns a dictionary where keys are the name of a given type of
    generating function (the most basic type being 'ogf'). Currently implemented
    types are: 'ogf' (ordinary g.f.), 'egf' (exponential g.f.), 'lgf'
    (logarithmic g.f.), 'hlgf' (hyperbolic logarithmic g.f.).

    In order to spare time, the user can select only some types of generating
    functions (default being ['all']). While forgetting to use a list in the
    case of a single type may seem to work most of the time as in: types='ogf'
    this (convenient) syntax may leed to unexpected extra results in some cases.

    Examples
    ========

    >>> from sympy.concrete.guess import guess_generating_function as ggf
    >>> ggf([k+1 for k in range(12)])
    {'hlgf': 1/(-x + 1), 'lgf': 1/(x + 1), 'ogf': 1/(x**2 - 2*x + 1)}

    >>> from sympy import sympify
    >>> l = sympify("[3/2, 11/2, 0, -121/2, -363/2, 121]")
    >>> ggf(l)
    {'ogf': (x + 3/2)/(11*x**2 - 3*x + 1)}

    >>> from sympy import fibonacci
    >>> ggf([fibonacci(k) for k in range(5, 15)], types=['ogf'])
    {'ogf': (3*x + 5)/(-x**2 - x + 1)}

    >>> from sympy import simplify, factorial
    >>> ggf([factorial(k) for k in range(12)], types=['ogf', 'egf', 'lgf'])
    {'egf': 1/(-x + 1)}

    N-th root of a rational function can also be detected (below is an example
    coming from the sequence A108626 from http://oeis.org ).
    The greatest n-th root to be tested is specified as maxsqrtn (default 2).

    >>> ggf([1, 2, 5, 14, 41, 124, 383, 1200, 3799, 12122, 38919])
    {'ogf': sqrt(1/(x**4 + 2*x**2 - 4*x + 1))}

    References
    ==========
    "Concrete Mathematics", R.L. Graham, D.E. Knuth, O. Patashnik
    https://oeis.org/wiki/Generating_functions

    """
    # List of all types of all g.f. known by the algorithm
    if 'all' in types:
        types = ['ogf', 'egf', 'lgf', 'hlgf']

    result = {}

    # Ordinary Generating Function (ogf)
    if 'ogf' in types:
        # Perform some convolutions of the sequence with itself
        t = [1 if k==0 else 0 for k in range(len(v))]
        for d in range(max(1, maxsqrtn)):
            t = [sum(t[n-i]*v[i] for i in range(n+1)) for n in range(len(v))]
            g = guess_generating_function_rational(t, X=X)
            if g:
                result['ogf'] = g**Rational(1, d+1)
                break

    # Exponential Generating Function (egf)
    if 'egf' in types:
        # Transform sequence (division by factorial)
        w, f = [], Integer(1)
        for i, k in enumerate(v):
            f *= i if i else 1
            w.append(k/f)
        # Perform some convolutions of the sequence with itself
        t = [1 if k==0 else 0 for k in range(len(w))]
        for d in range(max(1, maxsqrtn)):
            t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
            g = guess_generating_function_rational(t, X=X)
            if g:
                result['egf'] = g**Rational(1, d+1)
                break

    # Logarithmic Generating Function (lgf)
    if 'lgf' in types:
        # Transform sequence (multiplication by (-1)^(n+1) / n)
        w, f = [], Integer(-1)
        for i, k in enumerate(v):
            f = -f
            w.append(f*k/Integer(i+1))
        # Perform some convolutions of the sequence with itself
        t = [1 if k==0 else 0 for k in range(len(w))]
        for d in range(max(1, maxsqrtn)):
            t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
            g = guess_generating_function_rational(t, X=X)
            if g:
                result['lgf'] = g**Rational(1, d+1)
                break

    # Hyperbolic logarithmic Generating Function (hlgf)
    if 'hlgf' in types:
        # Transform sequence (division by n+1)
        w = []
        for i, k in enumerate(v):
            w.append(k/Integer(i+1))
        # Perform some convolutions of the sequence with itself
        t = [1 if k==0 else 0 for k in range(len(w))]
        for d in range(max(1, maxsqrtn)):
            t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
            g = guess_generating_function_rational(t, X=X)
            if g:
                result['hlgf'] = g**Rational(1, d+1)
                break

    # TODO: add more types of generating functions

    return result
<commit_msg>Added the new type 'lgdogf' to guess_generating_function.<commit_after>"""Various algorithms for helping identifying numbers and sequences."""
from __future__ import print_function, division

from sympy.utilities import public

from sympy.core.compatibility import range
from sympy.core import Function, Symbol
from sympy.core.numbers import Zero
from sympy import sympify, floor, sqrt, lcm, denom, Integer, Rational

@public
def find_simple_recurrence_vector(l):
    """
    This function is used internally by other functions from the
    sympy.concrete.guess module. While most users may want to rather use the
    function find_simple_recurrence when looking for recurrence relations
    among rational numbers, the current function may still be useful when
    some post-processing has to be done.

    The function returns a vector of length n when a recurrence relation of
    order n is detected in the sequence of rational numbers v.

    If the returned vector has a length 1, then the returned value is always
    the list [0], which means that no relation has been found.

    While the functions is intended to be used with rational numbers, it should
    work for other kinds of real numbers except for some cases involving
    quadratic numbers; for that reason it should be used with some caution when
    the argument is not a list of rational numbers.

    Examples
    ========

    >>> from sympy.concrete.guess import find_simple_recurrence_vector
    >>> from sympy import fibonacci
    >>> find_simple_recurrence_vector([fibonacci(k) for k in range(12)])
    [1, -1, -1]

    See also
    ========

    See the function sympy.concrete.guess.find_simple_recurrence which is more
    user-friendly.

    """
    q1 = [0]
    q2 = [Integer(1)]
    z = len(l) >> 1
    while len(q2) <= z:
        b = 0
        while l[b]==0:
            b += 1
            if b == len(l):
                c = 1
                for x in q2:
                    c = lcm(c, denom(x))
                if q2[0]*c < 0: c = -c
                for k in range(len(q2)):
                    q2[k] = int(q2[k]*c)
                return q2
        m = [Integer(1)/l[b]]
        for k in range(b+1, len(l)):
            s = 0
            for j in range(b, k):
                s -= l[j+1] * m[b-j-1]
            m.append(s/l[b])
        l = m
        a, l[0] = l[0], 0
        q = [0] * max(len(q2), b+len(q1))
        for k in range(len(q2)):
            q[k] = a*q2[k]
        for k in range(b, b+len(q1)):
            q[k] += q1[k-b]
        while q[-1]==0: q.pop() # because trailing zeros can occur
        q1, q2 = q2, q
    return [0]

@public
def find_simple_recurrence(v, A=Function('a'), N=Symbol('n')):
    """
    Detects and returns a recurrence relation from a sequence of several integer
    (or rational) terms. The name of the function in the returned expression is
    'a' by default; the main variable is 'n' by default. The smallest index in
    the returned expression is always n (and never n-1, n-2, etc.).

    Examples
    ========

    >>> from sympy.concrete.guess import find_simple_recurrence
    >>> from sympy import fibonacci
    >>> find_simple_recurrence([fibonacci(k) for k in range(12)])
    -a(n) - a(n + 1) + a(n + 2)

    >>> from sympy import Function, Symbol
    >>> a = [1, 1, 1]
    >>> for k in range(15): a.append(5*a[-1]-3*a[-2]+8*a[-3])
    >>> find_simple_recurrence(a, A=Function('f'), N=Symbol('i'))
    -8*f(i) + 3*f(i + 1) - 5*f(i + 2) + f(i + 3)

    """
    p = find_simple_recurrence_vector(v)
    n = len(p)
    if n <= 1: return Zero()

    rel = Zero()
    for k in range(n):
        rel += A(N+n-1-k)*p[k]

    return rel

@public
def rationalize(x, maxcoeff=10000):
    """
    Helps identifying a rational number from a float (or mpmath.mpf) value by
    using a continued fraction. The algorithm stops as soon as a large partial
    quotient is detected (greater than 10000 by default).

    Examples
    ========

    >>> from sympy.concrete.guess import rationalize
    >>> from mpmath import cos, pi
    >>> rationalize(cos(pi/3))
    1/2

    >>> from mpmath import mpf
    >>> rationalize(mpf("0.333333333333333"))
    1/3

    While the function is rather intended to help 'identifying' rational
    values, it may be used in some cases for approximating real numbers.
    (Though other functions may be more relevant in that case.)

    >>> rationalize(pi, maxcoeff = 250)
    355/113

    See also
    ========
    Several other methods can approximate a real number as a rational, like:

      * fractions.Fraction.from_decimal
      * fractions.Fraction.from_float
      * mpmath.identify
      * mpmath.pslq by using the following syntax: mpmath.pslq([x, 1])
      * mpmath.findpoly by using the following syntax: mpmath.findpoly(x, 1)
      * sympy.simplify.nsimplify (which is a more general function)

    The main difference between the current function and all these variants is
    that control focuses on magnitude of partial quotients here rather than on
    global precision of the approximation. If the real is "known to be" a
    rational number, the current function should be able to detect it correctly
    with the default settings even when denominator is great (unless its
    expansion contains unusually big partial quotients) which may occur
    when studying sequences of increasing numbers. If the user cares more
    on getting simple fractions, other methods may be more convenient.

    """
    p0, p1 = 0, 1
    q0, q1 = 1, 0
    a = floor(x)
    while a < maxcoeff or q1==0:
        p = a*p1 + p0
        q = a*q1 + q0
        p0, p1 = p1, p
        q0, q1 = q1, q
        if x==a: break
        x = 1/(x-a)
        a = floor(x)
    return sympify(p) / q

@public
def guess_generating_function_rational(v, X=Symbol('x')):
    """
    Tries to "guess" a rational generating function for a sequence of rational
    numbers v.

    Examples
    ========

    >>> from sympy.concrete.guess import guess_generating_function_rational
    >>> from sympy import fibonacci
    >>> l = [fibonacci(k) for k in range(5,15)]
    >>> guess_generating_function_rational(l)
    (3*x + 5)/(-x**2 - x + 1)

    See also
    ========
    See function sympy.series.approximants and mpmath.pade

    """
    #   a) compute the denominator as q
    q = find_simple_recurrence_vector(v)
    n = len(q)
    if n <= 1: return None
    #   b) compute the numerator as p
    p = [sum(v[i-k]*q[k] for k in range(min(i+1, n)))
            for i in range(len(v))] # TODO: maybe better with:  len(v)>>1
    return (sum(p[k]*X**k for k in range(len(p)))
            / sum(q[k]*X**k for k in range(n)))

@public
def guess_generating_function(v, X=Symbol('x'), types=['all'], maxsqrtn=2):
    """
    Tries to "guess" a generating function for a sequence of rational numbers v.
    Only a few patterns are implemented yet.

    The function returns a dictionary where keys are the name of a given type of
    generating function (the most basic type being 'ogf'). Currently implemented
    types are: 'ogf' (ordinary g.f.), 'egf' (exponential g.f.), 'lgf'
    (logarithmic g.f.), 'hlgf' (hyperbolic logarithmic g.f.), 'lgdogf'
    (logarithmic derivative of generating function).

    In order to spare time, the user can select only some types of generating
    functions (default being ['all']). While forgetting to use a list in the
    case of a single type may seem to work most of the time as in: types='ogf'
    this (convenient) syntax may leed to unexpected extra results in some cases.

    Examples
    ========

    >>> from sympy.concrete.guess import guess_generating_function as ggf
    >>> ggf([k+1 for k in range(12)], types=['ogf', 'lgf', 'hlgf'])
    {'hlgf': 1/(-x + 1), 'lgf': 1/(x + 1), 'ogf': 1/(x**2 - 2*x + 1)}

    >>> from sympy import sympify
    >>> l = sympify("[3/2, 11/2, 0, -121/2, -363/2, 121]")
    >>> ggf(l)
    {'ogf': (x + 3/2)/(11*x**2 - 3*x + 1)}

    >>> from sympy import fibonacci
    >>> ggf([fibonacci(k) for k in range(5, 15)], types=['ogf'])
    {'ogf': (3*x + 5)/(-x**2 - x + 1)}

    >>> from sympy import simplify, factorial
    >>> ggf([factorial(k) for k in range(12)], types=['ogf', 'egf', 'lgf'])
    {'egf': 1/(-x + 1)}

    N-th root of a rational function can also be detected (below is an example
    coming from the sequence A108626 from http://oeis.org ).
    The greatest n-th root to be tested is specified as maxsqrtn (default 2).

    >>> ggf([1, 2, 5, 14, 41, 124, 383, 1200, 3799, 12122, 38919])
    {'ogf': sqrt(1/(x**4 + 2*x**2 - 4*x + 1))}

    References
    ==========
    "Concrete Mathematics", R.L. Graham, D.E. Knuth, O. Patashnik
    https://oeis.org/wiki/Generating_functions

    """
    # List of all types of all g.f. known by the algorithm
    if 'all' in types:
        types = ['ogf', 'egf', 'lgf', 'hlgf', 'lgdogf']

    result = {}

    # Ordinary Generating Function (ogf)
    if 'ogf' in types:
        # Perform some convolutions of the sequence with itself
        t = [1 if k==0 else 0 for k in range(len(v))]
        for d in range(max(1, maxsqrtn)):
            t = [sum(t[n-i]*v[i] for i in range(n+1)) for n in range(len(v))]
            g = guess_generating_function_rational(t, X=X)
            if g:
                result['ogf'] = g**Rational(1, d+1)
                break

    # Exponential Generating Function (egf)
    if 'egf' in types:
        # Transform sequence (division by factorial)
        w, f = [], Integer(1)
        for i, k in enumerate(v):
            f *= i if i else 1
            w.append(k/f)
        # Perform some convolutions of the sequence with itself
        t = [1 if k==0 else 0 for k in range(len(w))]
        for d in range(max(1, maxsqrtn)):
            t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
            g = guess_generating_function_rational(t, X=X)
            if g:
                result['egf'] = g**Rational(1, d+1)
                break

    # Logarithmic Generating Function (lgf)
    if 'lgf' in types:
        # Transform sequence (multiplication by (-1)^(n+1) / n)
        w, f = [], Integer(-1)
        for i, k in enumerate(v):
            f = -f
            w.append(f*k/Integer(i+1))
        # Perform some convolutions of the sequence with itself
        t = [1 if k==0 else 0 for k in range(len(w))]
        for d in range(max(1, maxsqrtn)):
            t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
            g = guess_generating_function_rational(t, X=X)
            if g:
                result['lgf'] = g**Rational(1, d+1)
                break

    # Hyperbolic logarithmic Generating Function (hlgf)
    if 'hlgf' in types:
        # Transform sequence (division by n+1)
        w = []
        for i, k in enumerate(v):
            w.append(k/Integer(i+1))
        # Perform some convolutions of the sequence with itself
        t = [1 if k==0 else 0 for k in range(len(w))]
        for d in range(max(1, maxsqrtn)):
            t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
            g = guess_generating_function_rational(t, X=X)
            if g:
                result['hlgf'] = g**Rational(1, d+1)
                break

    # Logarithmic derivatif of Generating Function (ldogf)
    if 'lgdogf' in types:
        # Transform sequence by computing f'(x)/f(x)
        # because log(f(x)) = integrate( f'(x)/f(x) )
        a, w = sympify(v[0]), []
        for n in range(len(v)-1):
            w.append(
               (v[n+1]*(n+1) - sum( w[-i-1]*v[i+1] for i in range(n) ))/a)
        # Perform some convolutions of the sequence with itself
        t = [1 if k==0 else 0 for k in range(len(w))]
        for d in range(max(1, maxsqrtn)):
            t = [sum(t[n-i]*w[i] for i in range(n+1)) for n in range(len(w))]
            g = guess_generating_function_rational(t, X=X)
            if g:
                result['lgdogf'] = g**Rational(1, d+1)
                break

    # TODO: add more types of generating functions

    return result
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import os
import json
import codecs
import errno
import httplib
import functools
import requests
import uuid

import furl
from flask import request
from flask import redirect
from flask import make_response
from modularodm.exceptions import NoResultsFound

from framework.auth import Auth
from framework.sentry import log_exception
from framework.exceptions import HTTPError
from framework.mongo.utils import to_mongo_key
from framework.render.tasks import build_rendered_html, get_file_contents
from framework.auth.decorators import must_be_logged_in, must_be_signed

from mfr.core import get_file_extension

from website import settings
from website.project import decorators
from website.addons.base import exceptions
from website.models import User, Node, NodeLog
from website.util import rubeus, waterbutler_url_for
from website.profile.utils import get_gravatar
from website.project.utils import serialize_node
from website.project.decorators import must_be_valid_project, must_be_contributor_or_public

@decorators.must_have_permission('write')
@decorators.must_not_be_registration
def disable_addon(auth, **kwargs):
    node = kwargs['node'] or kwargs['project']

    addon_name = kwargs.get('addon')
    if addon_name is None:
        raise HTTPError(httplib.BAD_REQUEST)

    deleted = node.delete_addon(addon_name, auth)

    return {'deleted': deleted}

@must_be_logged_in
def get_addon_user_config(**kwargs):

    user = kwargs['auth'].user

    addon_name = kwargs.get('addon')
    if addon_name is None:
        raise HTTPError(httplib.BAD_REQUEST)

    addon = user.get_addon(addon_name)
    if addon is None:
        raise HTTPError(httplib.BAD_REQUEST)

    return addon.to_json(user)

def check_file_guid(guid):

    guid_url = '/{0}/'.format(guid._id)
    if not request.path.startswith(guid_url):
        url_split = request.url.split(guid.file_url)
        try:
            guid_url += url_split[1].lstrip('/')
        except IndexError:
            pass
        return guid_url
    return None

permission_map = {
    'create_folder': 'write',
    'revisions': 'read',
    'metadata': 'read',
    'download': 'read',
    'upload': 'write',
    'delete': 'write',
    'copy': 'write',
    'move': 'write',
}

def check_access(node, user, action, key=None):
    """Verify that user can perform requested action on resource. Raise appropriate
    error code if action cannot proceed.
    """
    permission = permission_map.get(action, None)
    if permission is None:
        raise HTTPError(httplib.BAD_REQUEST)
    if node.has_permission(user, permission):
        return True
    if permission == 'read':
        if node.is_public or key in node.private_link_keys_active:
            return True
    code = httplib.FORBIDDEN if user else httplib.UNAUTHORIZED
    raise HTTPError(code)

def make_auth(user):
    if user is not None:
        return {
            'id': user._id,
            'email': '{}@osf.io'.format(user._id),
            'name': user.fullname,
        }
    return {}

def restrict_addrs(*addrs):
    def wrapper(func):
        @functools.wraps(func)
        def wrapped(*args, **kwargs):
            remote = request.remote_addr
            if remote not in addrs:
                raise HTTPError(httplib.FORBIDDEN)
            return func(*args, **kwargs)
        return wrapped
    return wrapper

restrict_waterbutler = restrict_addrs(*settings.WATERBUTLER_ADDRS)

@restrict_waterbutler
def get_auth(**kwargs):
    try:
        action = request.args['action']
        cookie = request.args['cookie']
        node_id = request.args['nid']
        provider_name = request.args['provider']
    except KeyError:
        raise HTTPError(httplib.BAD_REQUEST)

    view_only = request.args.get('view_only')

    user = User.from_cookie(cookie)

    node = Node.load(node_id)
    if not node:
        raise HTTPError(httplib.NOT_FOUND)

    check_access(node, user, action, key=view_only)

    provider_settings = node.get_addon(provider_name)
    if not provider_settings:
        raise HTTPError(httplib.BAD_REQUEST)

    try:
        credentials = provider_settings.serialize_waterbutler_credentials()
        settings = provider_settings.serialize_waterbutler_settings()
    except exceptions.AddonError:
        log_exception()
        raise HTTPError(httplib.BAD_REQUEST)

    return {
        'auth': make_auth(user),
        'credentials': credentials,
        'settings': settings,
        'callback_url': node.api_url_for(
            'create_waterbutler_log',
            _absolute=True,
        ),
    }

LOG_ACTION_MAP = {
    'create': NodeLog.FILE_ADDED,
    'update': NodeLog.FILE_UPDATED,
    'delete': NodeLog.FILE_REMOVED,
    'create_folder': NodeLog.FOLDER_CREATED,
}

@must_be_signed
@restrict_waterbutler
@must_be_valid_project
def create_waterbutler_log(payload, **kwargs):
    try:
        auth = payload['auth']
        action = payload['action']
        provider = payload['provider']
        metadata = payload['metadata']
    except KeyError:
        raise HTTPError(httplib.BAD_REQUEST)

    metadata['path'] = metadata['path'].lstrip('/')

    user = User.load(auth['id'])
    if user is None:
        raise HTTPError(httplib.BAD_REQUEST)
    node = kwargs['node'] or kwargs['project']
    node_addon = node.get_addon(provider)
    if node_addon is None:
        raise HTTPError(httplib.BAD_REQUEST)
    try:
        osf_action = LOG_ACTION_MAP[action]
    except KeyError:
        raise HTTPError(httplib.BAD_REQUEST)
    auth = Auth(user=user)
    node_addon.create_waterbutler_log(auth, osf_action, metadata)

    return {'status': 'success'}

def get_or_start_render(file_guid, start_render=True):
    try:
        file_guid.enrich()
    except exceptions.AddonEnrichmentError as error:
        return error.as_html()

    try:
        return codecs.open(file_guid.mfr_cache_path, 'r', 'utf-8').read()
    except IOError:
        if start_render:
            # Start rendering job if requested
            build_rendered_html(
                file_guid.mfr_download_url,
                file_guid.mfr_cache_path,
                file_guid.mfr_temp_path,
                file_guid.public_download_url
            )
    return None

def file_content(file_guid):
    content = get_file_contents(
        file_guid.mfr_download_url,
        file_guid.mfr_cache_path,
        file_guid.mfr_temp_path,
        file_guid.public_download_url
    )
    return content

def ensure_path(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

@must_be_valid_project
def addon_view_or_download_file_legacy(**kwargs):
    query_params = request.args.to_dict()
    node = kwargs.get('node') or kwargs['project']

    action = query_params.pop('action', 'view')
    provider = kwargs.get('provider', 'osfstorage')

    if kwargs.get('path'):
        path = kwargs['path']
    elif kwargs.get('fid'):
        path = kwargs['fid']

    if 'download' in request.path or request.path.startswith('/api/v1/'):
        action = 'download'

    if kwargs.get('vid'):
        query_params['version'] = kwargs['vid']

    # If provider is OSFstorage, check existence of requested file in the filetree
    # This prevents invalid GUIDs from being created
    if provider == 'osfstorage':
        node_settings = node.get_addon('osfstorage')

        try:
            path = node_settings.root_node.find_child_by_name(path)._id
        except NoResultsFound:
            raise HTTPError(
                404, data=dict(
                    message_short='File not found',
                    message_long='You requested a file that does not exist.'
                )
            )

    return redirect(
        node.web_url_for(
            'addon_view_or_download_file',
            path=path,
            provider=provider,
            action=action,
            **query_params
        ),
        code=httplib.MOVED_PERMANENTLY
    )

@must_be_valid_project
@must_be_contributor_or_public
def addon_view_or_download_file(auth, path, provider, **kwargs):
    extras = request.args.to_dict()
    action = extras.get('action', 'view')
    node = kwargs.get('node') or kwargs['project']

    node_addon = node.get_addon(provider)

    if not path or not node_addon:
        raise HTTPError(httplib.BAD_REQUEST)

    if not node_addon.has_auth:
        raise HTTPError(httplib.FORBIDDEN)

    if not path.startswith('/'):
        path = '/' + path

    file_guid, created = node_addon.find_or_create_file_guid(path)

    if file_guid.guid_url != request.path:
        guid_url = furl.furl(file_guid.guid_url)
        guid_url.args.update(extras)
        return redirect(guid_url)

    file_guid.maybe_set_version(**extras)

    if request.method == 'HEAD':
        download_url = furl.furl(file_guid.download_url)
        download_url.args.update(extras)
        download_url.args['accept_url'] = 'false'
        return make_response(('', 200, {'Location': download_url.url}))

    if action == 'download':
        download_url = furl.furl(file_guid.download_url)
        download_url.args.update(extras)

        return redirect(download_url.url)

    return addon_view_file(auth, node, node_addon, file_guid, extras)

def addon_view_file(auth, node, node_addon, file_guid, extras):
    # TODO: resolve circular import issue
    from website.addons.wiki.utils import get_sharejs_uuid, generate_private_uuid
    from website.addons.wiki import settings as wiki_settings

    path = file_guid.waterbutler_path
    provider = file_guid.provider

    render_url = node.api_url_for(
        'addon_render_file',
        path=path.lstrip('/'),
        provider=provider,
        render=True,
        **extras
    )

    view_url = node.web_url_for(
        'addon_view_or_download_file',
        path=path.lstrip('/'),
        provider=provider
    )

    can_edit = node.has_permission(auth.user, 'write') and not node.is_registration
    file_guid_str = str(file_guid)
    file_key = to_mongo_key(file_guid_str)

    if can_edit:
        if file_key not in node.wiki_private_uuids:
            generate_private_uuid(node, file_guid_str)
        sharejs_uuid = get_sharejs_uuid(node, file_guid_str)
    else:
        sharejs_uuid = None

    ret = serialize_node(node, auth, primary=True)

    # Disable OSF Storage file deletion in DISK_SAVING_MODE
    if settings.DISK_SAVING_MODE and node_addon.config.short_name == 'osfstorage':
        ret['user']['can_edit'] = False

    ret.update({
        'provider': file_guid.provider,
        'file_path': file_guid.waterbutler_path,
        'sharejs_uuid': sharejs_uuid or '',
        'urls': {
            'sharejs': wiki_settings.SHAREJS_URL,
            'render': render_url,
            'edit': waterbutler_url_for('upload', provider, path, node),
            'view': view_url,
            'gravatar': get_gravatar(auth.user, 25)
        },
        'files_url': node.web_url_for('collect_file_trees'),
        'rendered': get_or_start_render(file_guid),
        'content': file_content(file_guid),
        # Note: must be called after get_or_start_render. This is really only for github
        'extra': json.dumps(getattr(file_guid, 'extra', {})),
        #NOTE: get_or_start_render must be called first to populate name
        'file_name': getattr(file_guid, 'name', os.path.split(file_guid.waterbutler_path)[1]),
        'file_ext': get_file_extension(file_guid.waterbutler_path),
        'panels_used': ['edit', 'view'],

    })

    ret.update(rubeus.collect_addon_assets(node))
    return ret

@must_be_valid_project
@must_be_contributor_or_public
def addon_render_file(auth, path, provider, **kwargs):

    node = kwargs.get('node') or kwargs['project']

    node_addon = node.get_addon(provider)

    if not path:
        raise HTTPError(httplib.BAD_REQUEST)

    if not node_addon:
        raise HTTPError(httplib.BAD_REQUEST, {
            'message_short': 'Bad Request',
            'message_long': 'The add-on containing this file is no longer attached to the {}.'.format(node.project_or_component)
        })

    if not node_addon.has_auth:
        raise HTTPError(httplib.UNAUTHORIZED, {
            'message_short': 'Unauthorized',
            'message_long': 'The add-on containing this file is no longer authorized.'
        })

    if not node_addon.complete:
        raise HTTPError(httplib.BAD_REQUEST, {
            'message_short': 'Bad Request',
            'message_long': 'The add-on containing this file is no longer configured.'
        })

    if not path.startswith('/'):
        path = '/' + path

    file_guid, created = node_addon.find_or_create_file_guid(path)

    file_guid.maybe_set_version(**request.args.to_dict())

    ret = serialize_node(node, auth, primary=True)
    ret.update({
        'rendered': get_or_start_render(file_guid),
        'content': file_content(file_guid)
    })

    return ret
<commit_msg>Remove unused imports<commit_after># -*- coding: utf-8 -*-

import os
import json
import codecs
import errno
import httplib
import functools

import furl
from flask import request
from flask import redirect
from flask import make_response
from modularodm.exceptions import NoResultsFound

from framework.auth import Auth
from framework.sentry import log_exception
from framework.exceptions import HTTPError
from framework.mongo.utils import to_mongo_key
from framework.render.tasks import build_rendered_html, get_file_contents
from framework.auth.decorators import must_be_logged_in, must_be_signed

from mfr.core import get_file_extension

from website import settings
from website.project import decorators
from website.addons.base import exceptions
from website.models import User, Node, NodeLog
from website.util import rubeus, waterbutler_url_for
from website.profile.utils import get_gravatar
from website.project.utils import serialize_node
from website.project.decorators import must_be_valid_project, must_be_contributor_or_public

@decorators.must_have_permission('write')
@decorators.must_not_be_registration
def disable_addon(auth, **kwargs):
    node = kwargs['node'] or kwargs['project']

    addon_name = kwargs.get('addon')
    if addon_name is None:
        raise HTTPError(httplib.BAD_REQUEST)

    deleted = node.delete_addon(addon_name, auth)

    return {'deleted': deleted}

@must_be_logged_in
def get_addon_user_config(**kwargs):

    user = kwargs['auth'].user

    addon_name = kwargs.get('addon')
    if addon_name is None:
        raise HTTPError(httplib.BAD_REQUEST)

    addon = user.get_addon(addon_name)
    if addon is None:
        raise HTTPError(httplib.BAD_REQUEST)

    return addon.to_json(user)

def check_file_guid(guid):

    guid_url = '/{0}/'.format(guid._id)
    if not request.path.startswith(guid_url):
        url_split = request.url.split(guid.file_url)
        try:
            guid_url += url_split[1].lstrip('/')
        except IndexError:
            pass
        return guid_url
    return None

permission_map = {
    'create_folder': 'write',
    'revisions': 'read',
    'metadata': 'read',
    'download': 'read',
    'upload': 'write',
    'delete': 'write',
    'copy': 'write',
    'move': 'write',
}

def check_access(node, user, action, key=None):
    """Verify that user can perform requested action on resource. Raise appropriate
    error code if action cannot proceed.
    """
    permission = permission_map.get(action, None)
    if permission is None:
        raise HTTPError(httplib.BAD_REQUEST)
    if node.has_permission(user, permission):
        return True
    if permission == 'read':
        if node.is_public or key in node.private_link_keys_active:
            return True
    code = httplib.FORBIDDEN if user else httplib.UNAUTHORIZED
    raise HTTPError(code)

def make_auth(user):
    if user is not None:
        return {
            'id': user._id,
            'email': '{}@osf.io'.format(user._id),
            'name': user.fullname,
        }
    return {}

def restrict_addrs(*addrs):
    def wrapper(func):
        @functools.wraps(func)
        def wrapped(*args, **kwargs):
            remote = request.remote_addr
            if remote not in addrs:
                raise HTTPError(httplib.FORBIDDEN)
            return func(*args, **kwargs)
        return wrapped
    return wrapper

restrict_waterbutler = restrict_addrs(*settings.WATERBUTLER_ADDRS)

@restrict_waterbutler
def get_auth(**kwargs):
    try:
        action = request.args['action']
        cookie = request.args['cookie']
        node_id = request.args['nid']
        provider_name = request.args['provider']
    except KeyError:
        raise HTTPError(httplib.BAD_REQUEST)

    view_only = request.args.get('view_only')

    user = User.from_cookie(cookie)

    node = Node.load(node_id)
    if not node:
        raise HTTPError(httplib.NOT_FOUND)

    check_access(node, user, action, key=view_only)

    provider_settings = node.get_addon(provider_name)
    if not provider_settings:
        raise HTTPError(httplib.BAD_REQUEST)

    try:
        credentials = provider_settings.serialize_waterbutler_credentials()
        settings = provider_settings.serialize_waterbutler_settings()
    except exceptions.AddonError:
        log_exception()
        raise HTTPError(httplib.BAD_REQUEST)

    return {
        'auth': make_auth(user),
        'credentials': credentials,
        'settings': settings,
        'callback_url': node.api_url_for(
            'create_waterbutler_log',
            _absolute=True,
        ),
    }

LOG_ACTION_MAP = {
    'create': NodeLog.FILE_ADDED,
    'update': NodeLog.FILE_UPDATED,
    'delete': NodeLog.FILE_REMOVED,
    'create_folder': NodeLog.FOLDER_CREATED,
}

@must_be_signed
@restrict_waterbutler
@must_be_valid_project
def create_waterbutler_log(payload, **kwargs):
    try:
        auth = payload['auth']
        action = payload['action']
        provider = payload['provider']
        metadata = payload['metadata']
    except KeyError:
        raise HTTPError(httplib.BAD_REQUEST)

    metadata['path'] = metadata['path'].lstrip('/')

    user = User.load(auth['id'])
    if user is None:
        raise HTTPError(httplib.BAD_REQUEST)
    node = kwargs['node'] or kwargs['project']
    node_addon = node.get_addon(provider)
    if node_addon is None:
        raise HTTPError(httplib.BAD_REQUEST)
    try:
        osf_action = LOG_ACTION_MAP[action]
    except KeyError:
        raise HTTPError(httplib.BAD_REQUEST)
    auth = Auth(user=user)
    node_addon.create_waterbutler_log(auth, osf_action, metadata)

    return {'status': 'success'}

def get_or_start_render(file_guid, start_render=True):
    try:
        file_guid.enrich()
    except exceptions.AddonEnrichmentError as error:
        return error.as_html()

    try:
        return codecs.open(file_guid.mfr_cache_path, 'r', 'utf-8').read()
    except IOError:
        if start_render:
            # Start rendering job if requested
            build_rendered_html(
                file_guid.mfr_download_url,
                file_guid.mfr_cache_path,
                file_guid.mfr_temp_path,
                file_guid.public_download_url
            )
    return None

def file_content(file_guid):
    content = get_file_contents(
        file_guid.mfr_download_url,
        file_guid.mfr_cache_path,
        file_guid.mfr_temp_path,
        file_guid.public_download_url
    )
    return content

def ensure_path(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

@must_be_valid_project
def addon_view_or_download_file_legacy(**kwargs):
    query_params = request.args.to_dict()
    node = kwargs.get('node') or kwargs['project']

    action = query_params.pop('action', 'view')
    provider = kwargs.get('provider', 'osfstorage')

    if kwargs.get('path'):
        path = kwargs['path']
    elif kwargs.get('fid'):
        path = kwargs['fid']

    if 'download' in request.path or request.path.startswith('/api/v1/'):
        action = 'download'

    if kwargs.get('vid'):
        query_params['version'] = kwargs['vid']

    # If provider is OSFstorage, check existence of requested file in the filetree
    # This prevents invalid GUIDs from being created
    if provider == 'osfstorage':
        node_settings = node.get_addon('osfstorage')

        try:
            path = node_settings.root_node.find_child_by_name(path)._id
        except NoResultsFound:
            raise HTTPError(
                404, data=dict(
                    message_short='File not found',
                    message_long='You requested a file that does not exist.'
                )
            )

    return redirect(
        node.web_url_for(
            'addon_view_or_download_file',
            path=path,
            provider=provider,
            action=action,
            **query_params
        ),
        code=httplib.MOVED_PERMANENTLY
    )

@must_be_valid_project
@must_be_contributor_or_public
def addon_view_or_download_file(auth, path, provider, **kwargs):
    extras = request.args.to_dict()
    action = extras.get('action', 'view')
    node = kwargs.get('node') or kwargs['project']

    node_addon = node.get_addon(provider)

    if not path or not node_addon:
        raise HTTPError(httplib.BAD_REQUEST)

    if not node_addon.has_auth:
        raise HTTPError(httplib.FORBIDDEN)

    if not path.startswith('/'):
        path = '/' + path

    file_guid, created = node_addon.find_or_create_file_guid(path)

    if file_guid.guid_url != request.path:
        guid_url = furl.furl(file_guid.guid_url)
        guid_url.args.update(extras)
        return redirect(guid_url)

    file_guid.maybe_set_version(**extras)

    if request.method == 'HEAD':
        download_url = furl.furl(file_guid.download_url)
        download_url.args.update(extras)
        download_url.args['accept_url'] = 'false'
        return make_response(('', 200, {'Location': download_url.url}))

    if action == 'download':
        download_url = furl.furl(file_guid.download_url)
        download_url.args.update(extras)

        return redirect(download_url.url)

    return addon_view_file(auth, node, node_addon, file_guid, extras)

def addon_view_file(auth, node, node_addon, file_guid, extras):
    # TODO: resolve circular import issue
    from website.addons.wiki.utils import get_sharejs_uuid, generate_private_uuid
    from website.addons.wiki import settings as wiki_settings

    path = file_guid.waterbutler_path
    provider = file_guid.provider

    render_url = node.api_url_for(
        'addon_render_file',
        path=path.lstrip('/'),
        provider=provider,
        render=True,
        **extras
    )

    view_url = node.web_url_for(
        'addon_view_or_download_file',
        path=path.lstrip('/'),
        provider=provider
    )

    can_edit = node.has_permission(auth.user, 'write') and not node.is_registration
    file_guid_str = str(file_guid)
    file_key = to_mongo_key(file_guid_str)

    if can_edit:
        if file_key not in node.wiki_private_uuids:
            generate_private_uuid(node, file_guid_str)
        sharejs_uuid = get_sharejs_uuid(node, file_guid_str)
    else:
        sharejs_uuid = None

    ret = serialize_node(node, auth, primary=True)

    # Disable OSF Storage file deletion in DISK_SAVING_MODE
    if settings.DISK_SAVING_MODE and node_addon.config.short_name == 'osfstorage':
        ret['user']['can_edit'] = False

    ret.update({
        'provider': file_guid.provider,
        'file_path': file_guid.waterbutler_path,
        'sharejs_uuid': sharejs_uuid or '',
        'urls': {
            'sharejs': wiki_settings.SHAREJS_URL,
            'render': render_url,
            'edit': waterbutler_url_for('upload', provider, path, node),
            'view': view_url,
            'gravatar': get_gravatar(auth.user, 25)
        },
        'files_url': node.web_url_for('collect_file_trees'),
        'rendered': get_or_start_render(file_guid),
        'content': file_content(file_guid),
        # Note: must be called after get_or_start_render. This is really only for github
        'extra': json.dumps(getattr(file_guid, 'extra', {})),
        #NOTE: get_or_start_render must be called first to populate name
        'file_name': getattr(file_guid, 'name', os.path.split(file_guid.waterbutler_path)[1]),
        'file_ext': get_file_extension(file_guid.waterbutler_path),
        'panels_used': ['edit', 'view'],

    })

    ret.update(rubeus.collect_addon_assets(node))
    return ret

@must_be_valid_project
@must_be_contributor_or_public
def addon_render_file(auth, path, provider, **kwargs):

    node = kwargs.get('node') or kwargs['project']

    node_addon = node.get_addon(provider)

    if not path:
        raise HTTPError(httplib.BAD_REQUEST)

    if not node_addon:
        raise HTTPError(httplib.BAD_REQUEST, {
            'message_short': 'Bad Request',
            'message_long': 'The add-on containing this file is no longer attached to the {}.'.format(node.project_or_component)
        })

    if not node_addon.has_auth:
        raise HTTPError(httplib.UNAUTHORIZED, {
            'message_short': 'Unauthorized',
            'message_long': 'The add-on containing this file is no longer authorized.'
        })

    if not node_addon.complete:
        raise HTTPError(httplib.BAD_REQUEST, {
            'message_short': 'Bad Request',
            'message_long': 'The add-on containing this file is no longer configured.'
        })

    if not path.startswith('/'):
        path = '/' + path

    file_guid, created = node_addon.find_or_create_file_guid(path)

    file_guid.maybe_set_version(**request.args.to_dict())

    ret = serialize_node(node, auth, primary=True)
    ret.update({
        'rendered': get_or_start_render(file_guid),
        'content': file_content(file_guid)
    })

    return ret
<|endoftext|>