<commit_before>#!/usr/bin/env python2.5
#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module that generates the lists.
"""

__authors__ = [
  '"Lennard de Rijk" <ljvderijk@gmail.com>',
  ]

import logging

from django.utils import simplejson

from soc.views.template import Template

def getListIndex(request):
  """Returns the index of the requested list.
  """
  idx = request.GET.get('idx', '')
  idx = int(idx) if idx.isdigit() else -1

  return idx

class ListConfiguration(object):
  """Resembles the configuration of a list. This object is sent to the client
  on page load.

  See the wiki page on ListProtocols for more information
  (http://code.google.com/p/soc/wiki/ListsProtocol).

  Public fields are:
    description: The description as shown to the end user.
    row_num: The number of rows that should be shown on a page on default.
    row_list: List of integers which is the allowed pagination size a user can
              can choose from.
    autowidth: Whether the width of the columns should be automatically set.
    height: Whether the height of the list should be automatically set.
    multiselect: If true then the list will have a column with checkboxes which
                 allows the user to select a number of rows.
    toolbar: [boolean, string] showing if and where the toolbar with buttons
             should be present.
  """

  def __init__(self):
    """Initializes the configuration.
    """
    self._col_names = []
    self._col_model = []
    self._col_functions = {}
    self.row_num = 50
    self.row_list = [5, 10, 20, 50, 100, 500, 1000]
    self.autowidth = True
    self._sortname = ''
    self._sortorder = 'asc'
    self.height = 'auto'
    self.multiselect = False
    self.toolbar = [True, 'top']

    self._buttons = {}
    self._button_functions = {}
    self._row_operation = {}
    self._row_operation_func = None

    self._addKeyColumn()

  def _addKeyColumn(self):
    """Adds a column for the key.

    Args:
      resizable: Whether the width of the column should be resizable by the
                 end user.
      hidden: Whether the column should be displayed by default.
    """
    func = lambda e, *args: e.key().name()
    self.addColumn('key', 'Key', func, hidden=True)

  def addColumn(self, id, name, func, resizable=True, hidden=False):
    """Adds a column to the end of the list.

    Args:
      id: A unique identifier of this column.
      name: The header of the column that is shown to the user.
      func: The function to be called when rendering this column for
            a single entity. This function should take an entity as first
            argument and args and kwargs if needed. The string rendering of
            the return value will be sent to the end user.
      resizable: Whether the width of the column should be resizable by the
                 end user.
      hidden: Whether the column should be displayed by default.
    """
    if self._col_functions.get(id):
      logging.warning('Column with id %s is already defined' %id)

    if not callable(func):
      raise TypeError('Given function is not callable')

    self._col_model.append({
        'name': id,
        'index': id,
        'resizable': resizable,
        'hidden': hidden,
        })
    self._col_names.append(name)
    self._col_functions[id] = func

  def addSimpleColumn(self, id, name, resizable=True, hidden=False):
    """Adds a column to the end of the list which uses the id of the column as
    attribute name of the entity to get the data from.

    This method is basically a shorthand for addColumn with the function as
    lambda ent, *args: getattr(ent, id).

    Args:
      id: A unique identifier of this column and name of the field to get the
          data from.
      name: The header of the column that is shown to the user.
      resizable: Whether the width of the column should be resizable by the
                 end user.
      hidden: Whether the column should be displayed by default.
    """
    func = lambda ent, *args: getattr(ent, id)
    self.addColumn(id, name, func, resizable=resizable, hidden=hidden)

  def __addButton(self, id, caption, bounds, type, parameters):
    """Internal method for adding buttons so that the uniqueness of the id can
    be checked.
    """
    if self._buttons.get(id):
      logging.warning('Button with id %s is already defined' %id)

    self._buttons[id] = {
        'id': id,
        'caption': caption,
        'bounds': bounds,
        'type': type,
        'parameters': parameters
    }

  def addSimpleRedirectButton(self, id, caption, url, new_window=True):
    """Adds a button to the list that simply opens a URL.

    Args:
      id: The unique id the button.
      caption: The display string shown to the end user.
      url: The url to redirect the user to.
      new_window: Boolean indicating whether the url should open in a new
                  window.
    """
    parameters = {
        'link': url,
        'new_window': new_window
    }
    # add a simple redirect button that is always active.
    self.__addButton(id, caption, [0, 'all'], 'redirect_simple', parameters)

  def addCustomRedirectButton(self, id, caption, func, new_window=True):
    """Adds a button to the list that simply opens a URL.

    Args:
      id: The unique id of the button.
      caption: The display string shown to the end user.
      func: The function to generate a url to redirect the user to.
            This function should take an entity as first argument and args and
            kwargs if needed. The return value of this function should be a
            dictionary with the value for 'link' set to the url to redirect the
            user to. A value for the key 'caption' can also be returned to
            dynamically change the caption off the button.
      new_window: Boolean indicating whether the url should open in a new
                  window.
    """
    if not callable(func):
      raise TypeError('Given function is not callable')

    parameters = {'new_window': new_window}
    # add a custom redirect button that is active on a single row
    self.__addButton(id, caption, [1, 1], 'redirect_custom', parameters)
    self._button_functions[id] = func

  def addPostButton(self, id, caption, url, bounds, keys, refresh='current',
                    redirect=False):
    """This button is used when there is something to send to the backend in a
    POST request.

    Args:
      id: The unique id of the button.
      caption: The display string shown to the end user.
      url: The URL to make the POST request to.
      bounds: An array of size two with integers or of an integer and the
              keyword "all". This indicates how many rows need to be selected
              for the button to be pressable.
      keys: A list of column identifiers of which the content of the selected
            rows will be send to the server when the button is pressed.
      refresh: Indicates which list to refresh, is the current list by default.
               The keyword 'all' can be used to refresh all lists on the page or
               a integer index referring to the idx of the list to refresh can
               be given.
      redirect: Set to True to have the user be redirected to a URL returned by
                the URL where the POST request hits.
    """
    parameters = {
        'url': url,
        'keys': keys,
        'refresh': refresh,
        'redirect': redirect,
    }
    self.__addButton(id, caption, bounds, 'post', parameters)

  def setRowAction(self, func, new_window=True):
    """The redirects the user to a URL when clicking on a row in the list.

    This sets multiselect to False as indicated in the protocol spec.

    Args:
      func: The function that returns the url to redirect the user to.
            This function should take an entity as first argument and args and
            kwargs if needed.
      new_window: Boolean indicating whether the url should open in a new
                  window.
    """
    if not callable(func):
      raise TypeError('Given function is not callable')

    self.multiselect = False

    parameters = {'new_window': new_window}
    self._row_operation = {
        'type': 'redirect_custom',
        'parameters': parameters
        }
    self._row_operation_func = func

  def setDefaultSort(self, id, order='asc'):
    """Sets the default sort order for the list.

    Args:
      id: The id of the column to sort on by default. If this evaluates to
      False then the default sort order will be removed.
      order: The order in which to sort, either 'asc' or 'desc'.
             The default value is 'asc'.
    """
    col_ids = [item.get('name') for item in self._col_model]
    if id and not id in col_ids:
      raise ValueError('Id %s is not a defined column (Known columns %s)'
                       %(id, col_ids))

    if order not in ['asc', 'desc']:
      raise ValueError('%s is not a valid order' %order)

    self._sortname = id if id else ''
    self._sortorder = order

class ListConfigurationResponse(Template):
  """Class that builds the template for configuring a list.
  """

  def __init__(self, config, idx, description=''):
    """Initializes the configuration.

    Args:
      config: A ListConfiguration object.
      idx: A number uniquely identifying this list. ValueError will be raised if
           not an int.
      description: The description of this list, as should be shown to the
                   user.
    """
    self._config = config
    self._idx = int(idx)
    self._description = description

    super(ListConfigurationResponse, self).__init__()

  def context(self):
    """Returns the context for the current template.
    """
    configuration = self._constructConfigDict()

    context = {
        'idx': self._idx,
        'configuration': simplejson.dumps(configuration),
        'description': self._description
        }
    return context

  def _constructConfigDict(self):
    """Builds the core of the list configuration that is sent to the client.

    Among other things this configuration defines the columns and buttons
    present on the list.
    """
    configuration = {
        'autowidth': self._config.autowidth,
        'colNames': self._config._col_names,
        'colModel': self._config._col_model,
        'height': self._config.height,
        'rowList': self._config.row_list,
        'rowNum': max(1, self._config.row_num),
        'sortname': self._config._sortname,
        'sortorder': self._config._sortorder,
        'multiselect': False if self._config._row_operation else \
                       self._config.multiselect,
        'toolbar': self._config.toolbar,
    }

    operations = {
        'buttons': self._config._buttons,
        'row': self._config._row_operation,
    }

    listConfiguration = {
      'configuration': configuration,
      'operations': operations,
    }
    return listConfiguration

  def templatePath(self):
    """Returns the path to the template that should be used in render().
    """
    return 'v2/soc/list/list.html'

class ListContentResponse(object):
  """Class that builds the response for a list content request.
  """

  def __init__(self, request, config):
    """Initializes the list response.

    The request given can define the start parameter in the GET request
    otherwise an empty string will be used indicating a request for the first
    batch.

    Public fields:
      start: The start argument as parsed from the request.
      next: The value that should be used to query for the next set of
            rows. In other words what start will be on the next roundtrip.
      limit: The maximum number of rows to return as indicated by the request,
             defaults to 50.

    Args:
      request: The HTTPRequest containing the request for data.
      config: A ListConfiguration object
    """
    self._request = request
    self._config = config

    self.__rows = []

    get_args = request.GET
    self.next = ''
    self.start =  get_args.get('start', '')
    self.limit = int(get_args.get('limit', 50))

  def addRow(self, entity, *args, **kwargs):
    """Renders a row for a single entity.

    Args:
      entity: The entity to render.
      args: The args passed to the render functions defined in the config.
      kwargs: The kwargs passed to the render functions defined in the config.
    """
    columns = {}
    for id, func in self._config._col_functions.iteritems():
      columns[id] = func(entity, *args, **kwargs)

    row = {}
    buttons= {}

    if self._config._row_operation_func:
      # perform the row operation function to retrieve the link
      row['link'] = self._config._row_operation_func(entity, *args, **kwargs)

    for id, func in self._config._button_functions.iteritems():
      # The function called here should return a dictionary with 'link' and
      # an optional 'caption' as keys.
      buttons[id] = func(entity, *args, **kwargs)

    operations = {
        'row': row,
        'buttons': buttons,
    }

    data = {
      'columns': columns,
      'operations': operations,
    }
    self.__rows.append(data)

  def content(self):
    """Returns the object that should be parsed to JSON.
    """
    # The maximum number of rows to return is determined by the limit
    data = {self.start: self.__rows[0:self.limit]}
    return {'data': data,
            'next': self.next}

class RawQueryContentResponseBuilder(object):
  """Builds a ListContentResponse for lists that are based on a single query.
  """

  def __init__(self, request, config, query, starter, ender=None, prefetch=None):
    """Initializes the fields needed to built a response.

    Args:
      request: The HTTPRequest containing the request for data.
      config: The ListConfiguration object.
      fields: The fields to query on.
      query: The query object to use.
      starter: The function used to retrieve the start entity.
      ender: The function used to retrieve the value for the next start.
      prefetch: The fields that need to be prefetched for increased
                performance.
    """
    if not ender:
      ender = lambda entity: entity.key().id_or_name()

    self._request = request
    self._config = config
    self._query = query
    self._starter = starter
    self._ender = ender
    self._prefetch = prefetch

  def build(self, *args, **kwargs):
    """Returns a ListContentResponse containing the data as indicated by the
    query.

    The start variable will be used as the starting key for our query, the data
    returned does not contain the entity that is referred to by the start key.
    The next variable will be defined as the key of the last entity returned,
    empty if there are no entities to return.

    Args and Kwargs passed into this method will be passed along to
    ListContentResponse.addRow().
    """
    content_response = ListContentResponse(self._request, self._config)

    start = content_response.start
    if start:
      start_entity = self._starter(start)

      if not start_entity:
        logging.warning('Received data query for non-existing start entity')
        # return empty response
        return content_response

      self._query.filter('__key__ >', start_entity.key())

    entities = self._query.fetch(content_response.limit)

    # TODO(SRabbelier): prefetch

    for entity in entities:
      content_response.addRow(entity, *args, **kwargs)

    if entities:
      content_response.next = self._ender(entities[-1])

    return content_response

class QueryContentResponseBuilder(RawQueryContentResponseBuilder):
  """Builds a ListContentResponse for lists that are based on a single query.
  """

  def __init__(self, request, config, logic, fields, ancestors=None,
               prefetch=None):
    """Initializes the fields needed to built a response.

    Args:
      request: The HTTPRequest containing the request for data.
      config: The ListConfiguration object.
      logic: The Logic instance used for querying.
      fields: The fields to query on.
      ancestors: List of ancestor entities to add to the query
      prefetch: The fields that need to be prefetched for increased
                performance.
    """
    starter = lambda start: logic.getFromKeyNameOrID(start)

    query = logic.getQueryForFields(
        filter=fields, ancestors=ancestors)

    super(QueryContentResponseBuilder, self).__init__(
        request, config, query, starter, prefetch=prefetch)
<commit_msg>Fix lists for id-based entities<commit_after>#!/usr/bin/env python2.5
#
# Copyright 2011 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Module that generates the lists.
"""

__authors__ = [
  '"Lennard de Rijk" <ljvderijk@gmail.com>',
  ]

import logging

from django.utils import simplejson

from soc.views.template import Template

def getListIndex(request):
  """Returns the index of the requested list.
  """
  idx = request.GET.get('idx', '')
  idx = int(idx) if idx.isdigit() else -1

  return idx

class ListConfiguration(object):
  """Resembles the configuration of a list. This object is sent to the client
  on page load.

  See the wiki page on ListProtocols for more information
  (http://code.google.com/p/soc/wiki/ListsProtocol).

  Public fields are:
    description: The description as shown to the end user.
    row_num: The number of rows that should be shown on a page on default.
    row_list: List of integers which is the allowed pagination size a user can
              can choose from.
    autowidth: Whether the width of the columns should be automatically set.
    height: Whether the height of the list should be automatically set.
    multiselect: If true then the list will have a column with checkboxes which
                 allows the user to select a number of rows.
    toolbar: [boolean, string] showing if and where the toolbar with buttons
             should be present.
  """

  def __init__(self):
    """Initializes the configuration.
    """
    self._col_names = []
    self._col_model = []
    self._col_functions = {}
    self.row_num = 50
    self.row_list = [5, 10, 20, 50, 100, 500, 1000]
    self.autowidth = True
    self._sortname = ''
    self._sortorder = 'asc'
    self.height = 'auto'
    self.multiselect = False
    self.toolbar = [True, 'top']

    self._buttons = {}
    self._button_functions = {}
    self._row_operation = {}
    self._row_operation_func = None

    self._addKeyColumn()

  def _addKeyColumn(self):
    """Adds a column for the key.

    Args:
      resizable: Whether the width of the column should be resizable by the
                 end user.
      hidden: Whether the column should be displayed by default.
    """
    func = lambda e, *args: e.key().id_or_name()
    self.addColumn('key', 'Key', func, hidden=True)

  def addColumn(self, id, name, func, resizable=True, hidden=False):
    """Adds a column to the end of the list.

    Args:
      id: A unique identifier of this column.
      name: The header of the column that is shown to the user.
      func: The function to be called when rendering this column for
            a single entity. This function should take an entity as first
            argument and args and kwargs if needed. The string rendering of
            the return value will be sent to the end user.
      resizable: Whether the width of the column should be resizable by the
                 end user.
      hidden: Whether the column should be displayed by default.
    """
    if self._col_functions.get(id):
      logging.warning('Column with id %s is already defined' %id)

    if not callable(func):
      raise TypeError('Given function is not callable')

    self._col_model.append({
        'name': id,
        'index': id,
        'resizable': resizable,
        'hidden': hidden,
        })
    self._col_names.append(name)
    self._col_functions[id] = func

  def addSimpleColumn(self, id, name, resizable=True, hidden=False):
    """Adds a column to the end of the list which uses the id of the column as
    attribute name of the entity to get the data from.

    This method is basically a shorthand for addColumn with the function as
    lambda ent, *args: getattr(ent, id).

    Args:
      id: A unique identifier of this column and name of the field to get the
          data from.
      name: The header of the column that is shown to the user.
      resizable: Whether the width of the column should be resizable by the
                 end user.
      hidden: Whether the column should be displayed by default.
    """
    func = lambda ent, *args: getattr(ent, id)
    self.addColumn(id, name, func, resizable=resizable, hidden=hidden)

  def __addButton(self, id, caption, bounds, type, parameters):
    """Internal method for adding buttons so that the uniqueness of the id can
    be checked.
    """
    if self._buttons.get(id):
      logging.warning('Button with id %s is already defined' %id)

    self._buttons[id] = {
        'id': id,
        'caption': caption,
        'bounds': bounds,
        'type': type,
        'parameters': parameters
    }

  def addSimpleRedirectButton(self, id, caption, url, new_window=True):
    """Adds a button to the list that simply opens a URL.

    Args:
      id: The unique id the button.
      caption: The display string shown to the end user.
      url: The url to redirect the user to.
      new_window: Boolean indicating whether the url should open in a new
                  window.
    """
    parameters = {
        'link': url,
        'new_window': new_window
    }
    # add a simple redirect button that is always active.
    self.__addButton(id, caption, [0, 'all'], 'redirect_simple', parameters)

  def addCustomRedirectButton(self, id, caption, func, new_window=True):
    """Adds a button to the list that simply opens a URL.

    Args:
      id: The unique id of the button.
      caption: The display string shown to the end user.
      func: The function to generate a url to redirect the user to.
            This function should take an entity as first argument and args and
            kwargs if needed. The return value of this function should be a
            dictionary with the value for 'link' set to the url to redirect the
            user to. A value for the key 'caption' can also be returned to
            dynamically change the caption off the button.
      new_window: Boolean indicating whether the url should open in a new
                  window.
    """
    if not callable(func):
      raise TypeError('Given function is not callable')

    parameters = {'new_window': new_window}
    # add a custom redirect button that is active on a single row
    self.__addButton(id, caption, [1, 1], 'redirect_custom', parameters)
    self._button_functions[id] = func

  def addPostButton(self, id, caption, url, bounds, keys, refresh='current',
                    redirect=False):
    """This button is used when there is something to send to the backend in a
    POST request.

    Args:
      id: The unique id of the button.
      caption: The display string shown to the end user.
      url: The URL to make the POST request to.
      bounds: An array of size two with integers or of an integer and the
              keyword "all". This indicates how many rows need to be selected
              for the button to be pressable.
      keys: A list of column identifiers of which the content of the selected
            rows will be send to the server when the button is pressed.
      refresh: Indicates which list to refresh, is the current list by default.
               The keyword 'all' can be used to refresh all lists on the page or
               a integer index referring to the idx of the list to refresh can
               be given.
      redirect: Set to True to have the user be redirected to a URL returned by
                the URL where the POST request hits.
    """
    parameters = {
        'url': url,
        'keys': keys,
        'refresh': refresh,
        'redirect': redirect,
    }
    self.__addButton(id, caption, bounds, 'post', parameters)

  def setRowAction(self, func, new_window=True):
    """The redirects the user to a URL when clicking on a row in the list.

    This sets multiselect to False as indicated in the protocol spec.

    Args:
      func: The function that returns the url to redirect the user to.
            This function should take an entity as first argument and args and
            kwargs if needed.
      new_window: Boolean indicating whether the url should open in a new
                  window.
    """
    if not callable(func):
      raise TypeError('Given function is not callable')

    self.multiselect = False

    parameters = {'new_window': new_window}
    self._row_operation = {
        'type': 'redirect_custom',
        'parameters': parameters
        }
    self._row_operation_func = func

  def setDefaultSort(self, id, order='asc'):
    """Sets the default sort order for the list.

    Args:
      id: The id of the column to sort on by default. If this evaluates to
      False then the default sort order will be removed.
      order: The order in which to sort, either 'asc' or 'desc'.
             The default value is 'asc'.
    """
    col_ids = [item.get('name') for item in self._col_model]
    if id and not id in col_ids:
      raise ValueError('Id %s is not a defined column (Known columns %s)'
                       %(id, col_ids))

    if order not in ['asc', 'desc']:
      raise ValueError('%s is not a valid order' %order)

    self._sortname = id if id else ''
    self._sortorder = order

class ListConfigurationResponse(Template):
  """Class that builds the template for configuring a list.
  """

  def __init__(self, config, idx, description=''):
    """Initializes the configuration.

    Args:
      config: A ListConfiguration object.
      idx: A number uniquely identifying this list. ValueError will be raised if
           not an int.
      description: The description of this list, as should be shown to the
                   user.
    """
    self._config = config
    self._idx = int(idx)
    self._description = description

    super(ListConfigurationResponse, self).__init__()

  def context(self):
    """Returns the context for the current template.
    """
    configuration = self._constructConfigDict()

    context = {
        'idx': self._idx,
        'configuration': simplejson.dumps(configuration),
        'description': self._description
        }
    return context

  def _constructConfigDict(self):
    """Builds the core of the list configuration that is sent to the client.

    Among other things this configuration defines the columns and buttons
    present on the list.
    """
    configuration = {
        'autowidth': self._config.autowidth,
        'colNames': self._config._col_names,
        'colModel': self._config._col_model,
        'height': self._config.height,
        'rowList': self._config.row_list,
        'rowNum': max(1, self._config.row_num),
        'sortname': self._config._sortname,
        'sortorder': self._config._sortorder,
        'multiselect': False if self._config._row_operation else \
                       self._config.multiselect,
        'toolbar': self._config.toolbar,
    }

    operations = {
        'buttons': self._config._buttons,
        'row': self._config._row_operation,
    }

    listConfiguration = {
      'configuration': configuration,
      'operations': operations,
    }
    return listConfiguration

  def templatePath(self):
    """Returns the path to the template that should be used in render().
    """
    return 'v2/soc/list/list.html'

class ListContentResponse(object):
  """Class that builds the response for a list content request.
  """

  def __init__(self, request, config):
    """Initializes the list response.

    The request given can define the start parameter in the GET request
    otherwise an empty string will be used indicating a request for the first
    batch.

    Public fields:
      start: The start argument as parsed from the request.
      next: The value that should be used to query for the next set of
            rows. In other words what start will be on the next roundtrip.
      limit: The maximum number of rows to return as indicated by the request,
             defaults to 50.

    Args:
      request: The HTTPRequest containing the request for data.
      config: A ListConfiguration object
    """
    self._request = request
    self._config = config

    self.__rows = []

    get_args = request.GET
    self.next = ''
    self.start =  get_args.get('start', '')
    self.limit = int(get_args.get('limit', 50))

  def addRow(self, entity, *args, **kwargs):
    """Renders a row for a single entity.

    Args:
      entity: The entity to render.
      args: The args passed to the render functions defined in the config.
      kwargs: The kwargs passed to the render functions defined in the config.
    """
    columns = {}
    for id, func in self._config._col_functions.iteritems():
      columns[id] = func(entity, *args, **kwargs)

    row = {}
    buttons= {}

    if self._config._row_operation_func:
      # perform the row operation function to retrieve the link
      row['link'] = self._config._row_operation_func(entity, *args, **kwargs)

    for id, func in self._config._button_functions.iteritems():
      # The function called here should return a dictionary with 'link' and
      # an optional 'caption' as keys.
      buttons[id] = func(entity, *args, **kwargs)

    operations = {
        'row': row,
        'buttons': buttons,
    }

    data = {
      'columns': columns,
      'operations': operations,
    }
    self.__rows.append(data)

  def content(self):
    """Returns the object that should be parsed to JSON.
    """
    # The maximum number of rows to return is determined by the limit
    data = {self.start: self.__rows[0:self.limit]}
    return {'data': data,
            'next': self.next}

class RawQueryContentResponseBuilder(object):
  """Builds a ListContentResponse for lists that are based on a single query.
  """

  def __init__(self, request, config, query, starter, ender=None, prefetch=None):
    """Initializes the fields needed to built a response.

    Args:
      request: The HTTPRequest containing the request for data.
      config: The ListConfiguration object.
      fields: The fields to query on.
      query: The query object to use.
      starter: The function used to retrieve the start entity.
      ender: The function used to retrieve the value for the next start.
      prefetch: The fields that need to be prefetched for increased
                performance.
    """
    if not ender:
      ender = lambda entity: entity.key().id_or_name()

    self._request = request
    self._config = config
    self._query = query
    self._starter = starter
    self._ender = ender
    self._prefetch = prefetch

  def build(self, *args, **kwargs):
    """Returns a ListContentResponse containing the data as indicated by the
    query.

    The start variable will be used as the starting key for our query, the data
    returned does not contain the entity that is referred to by the start key.
    The next variable will be defined as the key of the last entity returned,
    empty if there are no entities to return.

    Args and Kwargs passed into this method will be passed along to
    ListContentResponse.addRow().
    """
    content_response = ListContentResponse(self._request, self._config)

    start = content_response.start
    if start:
      start_entity = self._starter(start)

      if not start_entity:
        logging.warning('Received data query for non-existing start entity')
        # return empty response
        return content_response

      self._query.filter('__key__ >', start_entity.key())

    entities = self._query.fetch(content_response.limit)

    # TODO(SRabbelier): prefetch

    for entity in entities:
      content_response.addRow(entity, *args, **kwargs)

    if entities:
      content_response.next = self._ender(entities[-1])

    return content_response

class QueryContentResponseBuilder(RawQueryContentResponseBuilder):
  """Builds a ListContentResponse for lists that are based on a single query.
  """

  def __init__(self, request, config, logic, fields, ancestors=None,
               prefetch=None):
    """Initializes the fields needed to built a response.

    Args:
      request: The HTTPRequest containing the request for data.
      config: The ListConfiguration object.
      logic: The Logic instance used for querying.
      fields: The fields to query on.
      ancestors: List of ancestor entities to add to the query
      prefetch: The fields that need to be prefetched for increased
                performance.
    """
    starter = lambda start: logic.getFromKeyNameOrID(start)

    query = logic.getQueryForFields(
        filter=fields, ancestors=ancestors)

    super(QueryContentResponseBuilder, self).__init__(
        request, config, query, starter, prefetch=prefetch)
<|endoftext|>


<commit_before>#!/usr/bin/python2.5
#
# Copyright 2008 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Helpers used with templates.
"""

__authors__ = [
  '"Todd Larsen" <tlarsen@google.com>',
  '"Pawel Solyga" <pawel.solyga@gmail.com',
  ]

def makeSiblingTemplatePath(template_path, view_template):
  """Replaces the template name at the end of template_path with
     view_template argument.
  """
  #TODO(solydzajs) : implements this funtion
  return<commit_msg>Pawel and I were working on the same thing, apparently.  My template_helpers.py is more complete, so I am removing this one (that Pawel appears to not have used yet).<commit_after><|endoftext|>


<commit_before># Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Lint as: python3
# pylint: disable=g-bad-import-order, g-import-not-at-top, reimported
"""Tests that protos are generated after using the generate_protos module."""
import glob
import os
import subprocess
import sys
import tempfile
from unittest import mock

from absl.testing import absltest
# Set the environment variable to false so the test can call the method to
# generate protos explicitly.
os.environ['FALKEN_AUTO_GENERATE_PROTOS'] = '0'
from common import generate_protos

class GenerateProtosTest(absltest.TestCase):
  """Test generate_protos module."""

  def setUp(self):
    super().setUp()
    os.environ['FALKEN_GENERATED_PROTOS_DIR'] = ''
    self._temp_dir = tempfile.TemporaryDirectory()

  def tearDown(self):
    """Tear down the testing environment."""
    super().tearDown()
    self._temp_dir.cleanup()

  def test_get_generated_protos_dir(self):
    """Get the generated protos directory."""
    self.assertEqual(
        generate_protos._PROTO_GEN_DIR,
        os.path.basename(generate_protos.get_generated_protos_dir()))
    os.environ['FALKEN_GENERATED_PROTOS_DIR'] = (
        os.path.join('custom', 'dir'))
    self.assertEqual(
        os.path.join('custom', 'dir', generate_protos._PROTO_GEN_DIR),
        generate_protos.get_generated_protos_dir())

  @mock.patch.object(generate_protos, 'get_generated_protos_dir')
  @mock.patch.object(generate_protos, 'clean_up')
  @mock.patch.object(subprocess, 'check_call')
  def test_generate_protos_failed(self, mock_check_call, mock_clean_up,
                                  mock_get_generated_protos_dir):
    """Call the generate method and make sure it cleans up on failure."""
    mock_get_generated_protos_dir.return_value = os.path.join(
        self._temp_dir.name, generate_protos._PROTO_GEN_DIR)
    mock_check_call.side_effect = subprocess.CalledProcessError(1, 'fake')
    with self.assertRaises(subprocess.CalledProcessError):
      generate_protos.generate()
    mock_clean_up.called_once()

  @mock.patch.object(generate_protos, 'get_generated_protos_dir')
  def test_generate_protos(self, mock_get_generated_protos_dir):
    """Call the generate method and verify generation and use."""
    generated_dir = os.path.join(self._temp_dir.name,
                                 generate_protos._PROTO_GEN_DIR)
    mock_get_generated_protos_dir.return_value = generated_dir
    generate_protos.generate()
    source_protos = []
    for d in generate_protos.get_source_proto_dirs():
      source_protos += glob.glob(f'{d}/*.proto')

    def extract_generated_proto_path(source_proto_path, generated_dir):
      """Create generated proto path with the proto name.

      This can then be concatenated with a suffix like .py, _pb2.py,
      or _pb2_grpc.py.

      Args:
        source_proto_path: Proto path of the source proto file.
        generated_dir: Directory containing the generated protos.

      Returns:
        String that follows
          /generated_dir/source_proto's proto name without extension.
          e.g. '/falken/service/proto_gen_module/brain'
      """
      return os.path.join(generated_dir,
                          os.path.basename(source_proto_path).split('.')[0])

    all_expected_protos = ([
        extract_generated_proto_path(proto_name, generated_dir) + '_pb2.py'
        for proto_name in source_protos
    ] + [
        extract_generated_proto_path(proto_name, generated_dir) + '_pb2_grpc.py'
        for proto_name in source_protos
    ])

    generated_protos = glob.glob(f'{generated_dir}/*.py')
    self.assertSameElements(generated_protos, all_expected_protos)
    import primitives_pb2
    self.assertIsNotNone(primitives_pb2.Rotation())

  @mock.patch.object(generate_protos, 'get_generated_protos_dir')
  def test_generate_protos_cache(self, mock_get_generated_protos_dir):
    """Verify calling generate_proto multiple times without clean_up works."""
    mock_get_generated_protos_dir.return_value = os.path.join(
        self._temp_dir.name, generate_protos._PROTO_GEN_DIR)
    # First, verify that importing brain_pb2 before generate_protos is called
    # raises a ModuleNotFoundError.
    with self.assertRaises(ModuleNotFoundError):
      import brain_pb2
    old_sys_path = sys.path[:]

    # Call generate for the first time.
    generate_protos.generate()
    import brain_pb2
    self.assertIsNotNone(brain_pb2.Brain())

    # Restore the sys.path to what it was before calling generate().
    sys.path = old_sys_path

    # Because the sys.path does not contain the path to the generated protos,
    # snapshot_pb2 cannot be found.
    with self.assertRaises(ModuleNotFoundError):
      import snapshot_pb2

    # Generate the protos again, which does not actually re-generate the protos,
    # but adds the path to the sys.path. Now snapshot_pb2 can be found.
    generate_protos.generate()
    import snapshot_pb2
    self.assertIsNotNone(snapshot_pb2.Snapshot())

if __name__ == '__main__':
  absltest.main()
<commit_msg>Fix generate_protos_test.<commit_after># Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Lint as: python3
# pylint: disable=g-bad-import-order, g-import-not-at-top, reimported
"""Tests that protos are generated after using the generate_protos module."""
import glob
import os
import subprocess
import sys
import tempfile
from unittest import mock

from absl.testing import absltest
# Set the environment variable to false so the test can call the method to
# generate protos explicitly.
os.environ['FALKEN_AUTO_GENERATE_PROTOS'] = '0'
from common import generate_protos
from common import pip_installer

class GenerateProtosTest(absltest.TestCase):
  """Test generate_protos module."""

  def setUp(self):
    super().setUp()
    os.environ['FALKEN_GENERATED_PROTOS_DIR'] = ''
    self._temp_dir = tempfile.TemporaryDirectory()

  def tearDown(self):
    """Tear down the testing environment."""
    super().tearDown()
    self._temp_dir.cleanup()

  def test_get_generated_protos_dir(self):
    """Get the generated protos directory."""
    self.assertEqual(
        generate_protos._PROTO_GEN_DIR,
        os.path.basename(generate_protos.get_generated_protos_dir()))
    os.environ['FALKEN_GENERATED_PROTOS_DIR'] = (
        os.path.join('custom', 'dir'))
    self.assertEqual(
        os.path.join('custom', 'dir', generate_protos._PROTO_GEN_DIR),
        generate_protos.get_generated_protos_dir())

  @mock.patch.object(generate_protos, 'get_generated_protos_dir')
  @mock.patch.object(generate_protos, 'clean_up')
  @mock.patch.object(subprocess, 'check_call')
  @mock.patch.object(pip_installer, 'find_module_by_name')
  def test_generate_protos_failed(self, mock_find_module_by_name,
                                  mock_check_call, mock_clean_up,
                                  mock_get_generated_protos_dir):
    """Call the generate method and make sure it cleans up on failure."""
    mock_get_generated_protos_dir.return_value = os.path.join(
        self._temp_dir.name, generate_protos._PROTO_GEN_DIR)
    mock_find_module_by_name.return_value = None
    mock_check_call.side_effect = subprocess.CalledProcessError(1, 'fake')
    with self.assertRaises(subprocess.CalledProcessError):
      generate_protos.generate()
    mock_clean_up.called_once()

  @mock.patch.object(generate_protos, 'get_generated_protos_dir')
  def test_generate_protos(self, mock_get_generated_protos_dir):
    """Call the generate method and verify generation and use."""
    generated_dir = os.path.join(self._temp_dir.name,
                                 generate_protos._PROTO_GEN_DIR)
    mock_get_generated_protos_dir.return_value = generated_dir
    generate_protos.generate()
    source_protos = []
    for d in generate_protos.get_source_proto_dirs():
      source_protos += glob.glob(f'{d}/*.proto')

    def extract_generated_proto_path(source_proto_path, generated_dir):
      """Create generated proto path with the proto name.

      This can then be concatenated with a suffix like .py, _pb2.py,
      or _pb2_grpc.py.

      Args:
        source_proto_path: Proto path of the source proto file.
        generated_dir: Directory containing the generated protos.

      Returns:
        String that follows
          /generated_dir/source_proto's proto name without extension.
          e.g. '/falken/service/proto_gen_module/brain'
      """
      return os.path.join(generated_dir,
                          os.path.basename(source_proto_path).split('.')[0])

    all_expected_protos = ([
        extract_generated_proto_path(proto_name, generated_dir) + '_pb2.py'
        for proto_name in source_protos
    ] + [
        extract_generated_proto_path(proto_name, generated_dir) + '_pb2_grpc.py'
        for proto_name in source_protos
    ])

    generated_protos = glob.glob(f'{generated_dir}/*.py')
    self.assertSameElements(generated_protos, all_expected_protos)
    import primitives_pb2
    self.assertIsNotNone(primitives_pb2.Rotation())

  @mock.patch.object(generate_protos, 'get_generated_protos_dir')
  def test_generate_protos_cache(self, mock_get_generated_protos_dir):
    """Verify calling generate_proto multiple times without clean_up works."""
    mock_get_generated_protos_dir.return_value = os.path.join(
        self._temp_dir.name, generate_protos._PROTO_GEN_DIR)
    # First, verify that importing brain_pb2 before generate_protos is called
    # raises a ModuleNotFoundError.
    with self.assertRaises(ModuleNotFoundError):
      import brain_pb2
    old_sys_path = sys.path[:]

    # Call generate for the first time.
    generate_protos.generate()
    import brain_pb2
    self.assertIsNotNone(brain_pb2.Brain())

    # Restore the sys.path to what it was before calling generate().
    sys.path = old_sys_path

    # Because the sys.path does not contain the path to the generated protos,
    # snapshot_pb2 cannot be found.
    with self.assertRaises(ModuleNotFoundError):
      import snapshot_pb2

    # Generate the protos again, which does not actually re-generate the protos,
    # but adds the path to the sys.path. Now snapshot_pb2 can be found.
    generate_protos.generate()
    import snapshot_pb2
    self.assertIsNotNone(snapshot_pb2.Snapshot())

if __name__ == '__main__':
  absltest.main()
<|endoftext|>


<commit_before>from hypothesis import given
from hypothesis.strategies import lists
import pytest
from rest_framework.test import APIClient
from rest_framework.reverse import reverse

# from services.management.commands.services_import.services import import_services
from services.management.commands.services_import.organizations import import_organizations
from services.management.commands.services_import.departments import import_departments
from services.management.commands.services_import.units import import_units

from services_import_hypothesis import closed_object_set

def get(api_client, url, data=None):
    response = api_client.get(url, data=data, format='json')
    assert response.status_code == 200, str(response.content)
    return response

@pytest.fixture
def api_client():
    return APIClient()

LANGUAGES = ['fi', 'sv', 'en']

FIELD_MAPPINGS = {
    'desc': 'description',
}

def api_field_value(dest, name):
    return dest[FIELD_MAPPINGS.get(name, name)]

def assert_field_exists(name, src, dest):
    if src.get(name) is None:
        assert api_field_value(dest, name) is None
    else:
        assert name in dest

def assert_field_match(name, src, dest):
    assert_field_exists(name, src, dest)
    assert src[name] == api_field_value(dest, name)

def assert_string_field_match(name, src, dest):
    assert_field_exists(name, src, dest)
    if src[name] is None:
        assert api_field_value(dest, name) is None
        return
    val = src[name].replace('\u0000', ' ')
    if len(val.split()) == 0:
        assert api_field_value(dest, name) is None
    else:
        assert val.split() == api_field_value(dest, name).split(), \
            "{}: '{}' does not match '{}'".format(name, src[name], api_field_value(dest, name))

def assert_translated_field_match(name, src, dest):
    assert_field_exists(name, src, dest)
    if src.get(name) is None:
        return
    for lang in LANGUAGES:
        s = src['name_{}'.format(lang)].replace('\u0000', ' ')
        d = api_field_value(dest, name)[lang]
        # compare ignoring whitespace and empty bytes
        assert s.split() == d.split()

def assert_unit_correctly_imported(unit, source_unit):
    d = unit
    s = source_unit

    assert_field_match('id', s, d)
    assert_string_field_match('accessibility_email', s, d)

    for field_name in [
            'address_city',
            'address_postal_full',
            'call_charge_info',
            'desc',
            'name',
            'picture_caption',
            'short_desc',
            'street_address',
            'www']:
        assert_translated_field_match(field_name, s, d)

    # TODO: look for extra_searchwords -> keywords

    # string
    # ======
    # 'accessibility_email'
    # 'accessibility_phone'
    # 'accessibility_viewpoints'
    # 'accessibility_www'
    # 'address_zip'
    # 'data_source_url'
    # 'dept_id'
    # 'email'
    # 'fax'
    # 'phone'
    # 'organizer_business_id'

    # ?
    # 'id'

    # 'modified_time'
    # 'created_time'

    # 'easting_etrs_gk25'
    # 'easting_etrs_tm35fin'
    # 'northing_etrs_gk25'
    # 'northing_etrs_tm35fin'
    # 'latitude'
    # 'longitude'

    # 'manual_coordinates'

    # 'ontologytree_ids'
    # 'ontologyword_ids'

    # 'organizer_type'

    # 'org_id'
    # 'picture_entrance_url'
    # 'picture_url'
    # 'provider_type'
    # 'source'
    # 'sources'
    # 'streetview_entrance_url'

@pytest.mark.django_db
@given(lists(closed_object_set()))
def test_import_units(api_client, all_resources):

    for resources in all_resources:
        def fetch_resource(name):
            return resources.get(name, set())

        def fetch_units():
            return fetch_resource('unit')

        org_syncher = import_organizations(fetch_resource=fetch_resource)
        dept_syncher = import_departments(fetch_resource=fetch_resource)

        import_units(
            fetch_units=fetch_units, fetch_resource=fetch_resource,
            org_syncher=org_syncher, dept_syncher=dept_syncher)

        response = get(api_client, reverse('unit-list'))

        # The API-exposed unit count must exactly equal the original
        # import source unit count.
        assert response.data['count'] == len(resources['unit'])

        def id_set(units):
            return set((u['id'] for u in units))

        result_units = response.data['results']

        # The ids in source and result must exactly match
        assert (id_set(result_units) ==
                id_set(resources['unit']))

        source_units_by_id = dict((u['id'], u) for u in resources['unit'])

        for unit in result_units:
            assert_unit_correctly_imported(unit, source_units_by_id.get(unit['id']))
<commit_msg>Fix translated field assertions<commit_after>from hypothesis import given
from hypothesis.strategies import lists
import pytest
from rest_framework.test import APIClient
from rest_framework.reverse import reverse

# from services.management.commands.services_import.services import import_services
from services.management.commands.services_import.organizations import import_organizations
from services.management.commands.services_import.departments import import_departments
from services.management.commands.services_import.units import import_units

from services_import_hypothesis import closed_object_set

def get(api_client, url, data=None):
    response = api_client.get(url, data=data, format='json')
    assert response.status_code == 200, str(response.content)
    return response

@pytest.fixture
def api_client():
    return APIClient()

LANGUAGES = ['fi', 'sv', 'en']

FIELD_MAPPINGS = {
    'desc': 'description',
    'short_desc': 'short_description',
}

def api_field_value(dest, name):
    return dest[FIELD_MAPPINGS.get(name, name)]

def assert_field_exists(name, src, dest):
    if src.get(name) is None:
        assert api_field_value(dest, name) is None
    else:
        assert name in dest

def assert_field_match(name, src, dest):
    assert_field_exists(name, src, dest)
    assert src[name] == api_field_value(dest, name)

def assert_string_field_match(name, src, dest):
    assert_field_exists(name, src, dest)
    if src[name] is None:
        assert api_field_value(dest, name) is None
        return
    val = src[name].replace('\u0000', ' ')
    if len(val.split()) == 0:
        assert api_field_value(dest, name) is None
    else:
        assert val.split() == api_field_value(dest, name).split(), \
            "{}: '{}' does not match '{}'".format(name, src[name], api_field_value(dest, name))

def assert_translated_field_match(name, src, dest):
    for lang in LANGUAGES:
        s = src['name_{}'.format(lang)].replace('\u0000', ' ')
        assert api_field_value(dest, name) is not None, '{} is none'.format(name)
        d = api_field_value(dest, name)[lang]
        if s is None or len(s) == 0:
            assert d is None
            return
        # compare ignoring whitespace and empty bytes
        assert s.split() == d.split()

def assert_unit_correctly_imported(unit, source_unit):
    d = unit
    s = source_unit

    assert_field_match('id', s, d)
    assert_string_field_match('accessibility_email', s, d)

    for field_name in [
            'address_city',
            'address_postal_full',
            'call_charge_info',
            'desc',
            'name',
            'picture_caption',
            'short_desc',
            'street_address',
            'www']:
        assert_translated_field_match(field_name, s, d)

    # TODO: look for extra_searchwords -> keywords

    # string
    # ======
    # 'accessibility_email'
    # 'accessibility_phone'
    # 'accessibility_viewpoints'
    # 'accessibility_www'
    # 'address_zip'
    # 'data_source_url'
    # 'dept_id'
    # 'email'
    # 'fax'
    # 'phone'
    # 'organizer_business_id'

    # ?
    # 'id'

    # 'modified_time'
    # 'created_time'

    # 'easting_etrs_gk25'
    # 'easting_etrs_tm35fin'
    # 'northing_etrs_gk25'
    # 'northing_etrs_tm35fin'
    # 'latitude'
    # 'longitude'

    # 'manual_coordinates'

    # 'ontologytree_ids'
    # 'ontologyword_ids'

    # 'organizer_type'

    # 'org_id'
    # 'picture_entrance_url'
    # 'picture_url'
    # 'provider_type'
    # 'source'
    # 'sources'
    # 'streetview_entrance_url'

@pytest.mark.django_db
@given(lists(closed_object_set()))
def test_import_units(api_client, all_resources):

    for resources in all_resources:
        def fetch_resource(name):
            return resources.get(name, set())

        def fetch_units():
            return fetch_resource('unit')

        org_syncher = import_organizations(fetch_resource=fetch_resource)
        dept_syncher = import_departments(fetch_resource=fetch_resource)

        import_units(
            fetch_units=fetch_units, fetch_resource=fetch_resource,
            org_syncher=org_syncher, dept_syncher=dept_syncher)

        response = get(api_client, reverse('unit-list'))

        # The API-exposed unit count must exactly equal the original
        # import source unit count.
        assert response.data['count'] == len(resources['unit'])

        def id_set(units):
            return set((u['id'] for u in units))

        result_units = response.data['results']

        # The ids in source and result must exactly match
        assert (id_set(result_units) ==
                id_set(resources['unit']))

        source_units_by_id = dict((u['id'], u) for u in resources['unit'])

        for unit in result_units:
            assert_unit_correctly_imported(unit, source_units_by_id.get(unit['id']))
<|endoftext|>


<commit_before>from setuptools import Command
from setuptools.archive_util import unpack_archive
from distutils import log, dir_util
import os, shutil, pkg_resources

class install_egg_info(Command):
    """Install an .egg-info directory for the package"""

    description = "Install an .egg-info directory for the package"

    user_options = [
        ('install-dir=', 'd', "directory to install to"),
    ]

    def initialize_options(self):
        self.install_dir = None

    def finalize_options(self):
        self.set_undefined_options('install_lib',('install_dir','install_dir'))
        ei_cmd = self.get_finalized_command("egg_info")
        basename = pkg_resources.Distribution(
            None, None, ei_cmd.egg_name, ei_cmd.egg_version
        ).egg_name()+'.egg-info'
        self.source = ei_cmd.egg_info
        self.target = os.path.join(self.install_dir, basename)
        self.outputs = [self.target]

    def run(self):
        self.run_command('egg_info')
        target = self.target
        if os.path.isdir(self.target) and not os.path.islink(self.target):
            dir_util.remove_tree(self.target, dry_run=self.dry_run)
        elif os.path.exists(self.target):
            self.execute(os.unlink,(self.target,),"Removing "+self.target)
        if not self.dry_run:
            pkg_resources.ensure_directory(self.target)
        self.execute(self.copytree, (),
            "Copying %s to %s" % (self.source, self.target)
        )
        self.install_namespaces()

    def get_outputs(self):
        return self.outputs

    def copytree(self):
        # Copy the .egg-info tree to site-packages
        def skimmer(src,dst):
            # filter out source-control directories; note that 'src' is always
            # a '/'-separated path, regardless of platform.  'dst' is a
            # platform-specific path.
            for skip in '.svn/','CVS/':
                if src.startswith(skip) or '/'+skip in src:
                    return None
            self.outputs.append(dst)
            log.debug("Copying %s to %s", src, dst)
            return dst
        unpack_archive(self.source, self.target, skimmer)

    def install_namespaces(self):
        nsp = self._get_all_ns_packages()
        if not nsp: return
        filename,ext = os.path.splitext(self.target)
        filename += '-nspkg.pth'; self.outputs.append(filename)
        log.info("Installing %s",filename)
        if not self.dry_run:
            f = open(filename,'wb')
            for pkg in nsp:
                pth = tuple(pkg.split('.'))
                trailer = '\n'
                if '.' in pkg:
                    trailer = (
                        "; m and setattr(sys.modules[%r], %r, m)\n"
                        % ('.'.join(pth[:-1]), pth[-1])
                    )
                f.write(
                    "import sys,types,os; "
                    "p = os.path.join(sys._getframe(1).f_locals['sitedir'], "
                        "*%(pth)r); "
                    "ie = os.path.exists(os.path.join(p,'__init__.py')); "
                    "m = not ie and "
                        "sys.modules.setdefault(%(pkg)r,types.ModuleType(%(pkg)r)); "
                    "mp = (m or []) and m.__dict__.setdefault('__path__',[]); "
                    "(p not in mp) and mp.append(p)%(trailer)s"
                    % locals()
                )
            f.close()

    def _get_all_ns_packages(self):
        nsp = {}
        for pkg in self.distribution.namespace_packages or []:
            pkg = pkg.split('.')
            while pkg:
                nsp['.'.join(pkg)] = 1
                pkg.pop()
        nsp=list(nsp)
        nsp.sort()  # set up shorter names first
        return nsp

<commit_msg>Wrong file mode when creating bdists under Python 3.1.<commit_after>from setuptools import Command
from setuptools.archive_util import unpack_archive
from distutils import log, dir_util
import os, shutil, pkg_resources

class install_egg_info(Command):
    """Install an .egg-info directory for the package"""

    description = "Install an .egg-info directory for the package"

    user_options = [
        ('install-dir=', 'd', "directory to install to"),
    ]

    def initialize_options(self):
        self.install_dir = None

    def finalize_options(self):
        self.set_undefined_options('install_lib',('install_dir','install_dir'))
        ei_cmd = self.get_finalized_command("egg_info")
        basename = pkg_resources.Distribution(
            None, None, ei_cmd.egg_name, ei_cmd.egg_version
        ).egg_name()+'.egg-info'
        self.source = ei_cmd.egg_info
        self.target = os.path.join(self.install_dir, basename)
        self.outputs = [self.target]

    def run(self):
        self.run_command('egg_info')
        target = self.target
        if os.path.isdir(self.target) and not os.path.islink(self.target):
            dir_util.remove_tree(self.target, dry_run=self.dry_run)
        elif os.path.exists(self.target):
            self.execute(os.unlink,(self.target,),"Removing "+self.target)
        if not self.dry_run:
            pkg_resources.ensure_directory(self.target)
        self.execute(self.copytree, (),
            "Copying %s to %s" % (self.source, self.target)
        )
        self.install_namespaces()

    def get_outputs(self):
        return self.outputs

    def copytree(self):
        # Copy the .egg-info tree to site-packages
        def skimmer(src,dst):
            # filter out source-control directories; note that 'src' is always
            # a '/'-separated path, regardless of platform.  'dst' is a
            # platform-specific path.
            for skip in '.svn/','CVS/':
                if src.startswith(skip) or '/'+skip in src:
                    return None
            self.outputs.append(dst)
            log.debug("Copying %s to %s", src, dst)
            return dst
        unpack_archive(self.source, self.target, skimmer)

    def install_namespaces(self):
        nsp = self._get_all_ns_packages()
        if not nsp: return
        filename,ext = os.path.splitext(self.target)
        filename += '-nspkg.pth'; self.outputs.append(filename)
        log.info("Installing %s",filename)
        if not self.dry_run:
            f = open(filename,'wt')
            for pkg in nsp:
                pth = tuple(pkg.split('.'))
                trailer = '\n'
                if '.' in pkg:
                    trailer = (
                        "; m and setattr(sys.modules[%r], %r, m)\n"
                        % ('.'.join(pth[:-1]), pth[-1])
                    )
                f.write(
                    "import sys,types,os; "
                    "p = os.path.join(sys._getframe(1).f_locals['sitedir'], "
                        "*%(pth)r); "
                    "ie = os.path.exists(os.path.join(p,'__init__.py')); "
                    "m = not ie and "
                        "sys.modules.setdefault(%(pkg)r,types.ModuleType(%(pkg)r)); "
                    "mp = (m or []) and m.__dict__.setdefault('__path__',[]); "
                    "(p not in mp) and mp.append(p)%(trailer)s"
                    % locals()
                )
            f.close()

    def _get_all_ns_packages(self):
        nsp = {}
        for pkg in self.distribution.namespace_packages or []:
            pkg = pkg.split('.')
            while pkg:
                nsp['.'.join(pkg)] = 1
                pkg.pop()
        nsp=list(nsp)
        nsp.sort()  # set up shorter names first
        return nsp

<|endoftext|>


<commit_before>"""
Tests for msvc9compiler.
"""

import os
import contextlib
import distutils.errors

import pytest
try:
    from unittest import mock
except ImportError:
    import mock

from . import contexts

# importing only setuptools should apply the patch
__import__('setuptools')

pytest.importorskip("distutils.msvc9compiler")

def mock_reg(hkcu=None, hklm=None):
    """
    Return a mock for distutils.msvc9compiler.Reg, patched
    to mock out the functions that access the registry.
    """

    _winreg = getattr(distutils.msvc9compiler, '_winreg', None)
    winreg = getattr(distutils.msvc9compiler, 'winreg', _winreg)

    hives = {
        winreg.HKEY_CURRENT_USER: hkcu or {},
        winreg.HKEY_LOCAL_MACHINE: hklm or {},
    }

    @classmethod
    def read_keys(cls, base, key):
        """Return list of registry keys."""
        hive = hives.get(base, {})
        return [
            k.rpartition('\\')[2]
            for k in hive if k.startswith(key.lower())
        ]

    @classmethod
    def read_values(cls, base, key):
        """Return dict of registry keys and values."""
        hive = hives.get(base, {})
        return dict(
            (k.rpartition('\\')[2], hive[k])
            for k in hive if k.startswith(key.lower())
        )

    return mock.patch.multiple(distutils.msvc9compiler.Reg,
        read_keys=read_keys, read_values=read_values)

class TestModulePatch:
    """
    Ensure that importing setuptools is sufficient to replace
    the standard find_vcvarsall function with a version that
    recognizes the "Visual C++ for Python" package.
    """

    key_32 = r'software\microsoft\devdiv\vcforpython\9.0\installdir'
    key_64 = r'software\wow6432node\microsoft\devdiv\vcforpython\9.0\installdir'

    def test_patched(self):
        "Test the module is actually patched"
        mod_name = distutils.msvc9compiler.find_vcvarsall.__module__
        assert mod_name == "setuptools.msvc9_support", "find_vcvarsall unpatched"

    def test_no_registry_entryies_means_nothing_found(self):
        """
        No registry entries or environment variable should lead to an error
        directing the user to download vcpython27.
        """
        find_vcvarsall = distutils.msvc9compiler.find_vcvarsall
        query_vcvarsall = distutils.msvc9compiler.query_vcvarsall

        with contexts.environment(VS90COMNTOOLS=None):
            with mock_reg():
                assert find_vcvarsall(9.0) is None

                expected = distutils.errors.DistutilsPlatformError
                with pytest.raises(expected) as exc:
                    query_vcvarsall(9.0)
                assert 'aka.ms/vcpython27' in str(exc)

    @pytest.yield_fixture
    def user_preferred_setting(self):
        """
        Set up environment with different install dirs for user vs. system
        and yield the user_install_dir for the expected result.
        """
        with self.mock_install_dir() as user_install_dir:
            with self.mock_install_dir() as system_install_dir:
                reg = mock_reg(
                    hkcu={
                        self.key_32: user_install_dir,
                    },
                    hklm={
                        self.key_32: system_install_dir,
                        self.key_64: system_install_dir,
                    },
                )
                with reg:
                    yield user_install_dir

    def test_prefer_current_user(self, user_preferred_setting):
        """
        Ensure user's settings are preferred.
        """
        result = distutils.msvc9compiler.find_vcvarsall(9.0)
        assert user_preferred_setting == result

    @pytest.yield_fixture
    def local_machine_setting(self):
        """
        Set up environment with only the system environment configured.
        """
        with self.mock_install_dir() as system_install_dir:
            reg = mock_reg(
                hklm={
                    self.key_32: system_install_dir,
                },
            )
            with reg:
                yield system_install_dir

    def test_local_machine_recognized(self, local_machine_setting):
        """
        Ensure machine setting is honored if user settings are not present.
        """
        result = distutils.msvc9compiler.find_vcvarsall(9.0)
        assert local_machine_setting == result

    @pytest.yield_fixture
    def x64_preferred_setting(self):
        """
        Set up environment with 64-bit and 32-bit system settings configured
        and yield the 64-bit location.
        """
        with self.mock_install_dir() as x32_dir:
            with self.mock_install_dir() as x64_dir:
                reg = mock_reg(
                    hklm={
                        # This *should* only exist on 32-bit machines
                        self.key_32: x32_dir,
                        # This *should* only exist on 64-bit machines
                        self.key_64: x64_dir,
                    },
                )
                with reg:
                    yield x64_dir

    def test_ensure_64_bit_preferred(self, x64_preferred_setting):
        """
        Ensure 64-bit system key is preferred.
        """
        result = distutils.msvc9compiler.find_vcvarsall(9.0)
        assert x64_preferred_setting == result

    @staticmethod
    @contextlib.contextmanager
    def mock_install_dir():
        """
        Make a mock install dir in a unique location so that tests can
        distinguish which dir was detected in a given scenario.
        """
        with contexts.tempdir() as result:
            vcvarsall = os.path.join(result, 'vcvarsall.bat')
            with open(vcvarsall, 'w'):
                pass
            yield vcvarsall
<commit_msg>Just the path is expected<commit_after>"""
Tests for msvc9compiler.
"""

import os
import contextlib
import distutils.errors

import pytest
try:
    from unittest import mock
except ImportError:
    import mock

from . import contexts

# importing only setuptools should apply the patch
__import__('setuptools')

pytest.importorskip("distutils.msvc9compiler")

def mock_reg(hkcu=None, hklm=None):
    """
    Return a mock for distutils.msvc9compiler.Reg, patched
    to mock out the functions that access the registry.
    """

    _winreg = getattr(distutils.msvc9compiler, '_winreg', None)
    winreg = getattr(distutils.msvc9compiler, 'winreg', _winreg)

    hives = {
        winreg.HKEY_CURRENT_USER: hkcu or {},
        winreg.HKEY_LOCAL_MACHINE: hklm or {},
    }

    @classmethod
    def read_keys(cls, base, key):
        """Return list of registry keys."""
        hive = hives.get(base, {})
        return [
            k.rpartition('\\')[2]
            for k in hive if k.startswith(key.lower())
        ]

    @classmethod
    def read_values(cls, base, key):
        """Return dict of registry keys and values."""
        hive = hives.get(base, {})
        return dict(
            (k.rpartition('\\')[2], hive[k])
            for k in hive if k.startswith(key.lower())
        )

    return mock.patch.multiple(distutils.msvc9compiler.Reg,
        read_keys=read_keys, read_values=read_values)

class TestModulePatch:
    """
    Ensure that importing setuptools is sufficient to replace
    the standard find_vcvarsall function with a version that
    recognizes the "Visual C++ for Python" package.
    """

    key_32 = r'software\microsoft\devdiv\vcforpython\9.0\installdir'
    key_64 = r'software\wow6432node\microsoft\devdiv\vcforpython\9.0\installdir'

    def test_patched(self):
        "Test the module is actually patched"
        mod_name = distutils.msvc9compiler.find_vcvarsall.__module__
        assert mod_name == "setuptools.msvc9_support", "find_vcvarsall unpatched"

    def test_no_registry_entryies_means_nothing_found(self):
        """
        No registry entries or environment variable should lead to an error
        directing the user to download vcpython27.
        """
        find_vcvarsall = distutils.msvc9compiler.find_vcvarsall
        query_vcvarsall = distutils.msvc9compiler.query_vcvarsall

        with contexts.environment(VS90COMNTOOLS=None):
            with mock_reg():
                assert find_vcvarsall(9.0) is None

                expected = distutils.errors.DistutilsPlatformError
                with pytest.raises(expected) as exc:
                    query_vcvarsall(9.0)
                assert 'aka.ms/vcpython27' in str(exc)

    @pytest.yield_fixture
    def user_preferred_setting(self):
        """
        Set up environment with different install dirs for user vs. system
        and yield the user_install_dir for the expected result.
        """
        with self.mock_install_dir() as user_install_dir:
            with self.mock_install_dir() as system_install_dir:
                reg = mock_reg(
                    hkcu={
                        self.key_32: user_install_dir,
                    },
                    hklm={
                        self.key_32: system_install_dir,
                        self.key_64: system_install_dir,
                    },
                )
                with reg:
                    yield user_install_dir

    def test_prefer_current_user(self, user_preferred_setting):
        """
        Ensure user's settings are preferred.
        """
        result = distutils.msvc9compiler.find_vcvarsall(9.0)
        expected = os.path.join(user_preferred_setting, 'vcvarsall.bat')
        assert expected == result

    @pytest.yield_fixture
    def local_machine_setting(self):
        """
        Set up environment with only the system environment configured.
        """
        with self.mock_install_dir() as system_install_dir:
            reg = mock_reg(
                hklm={
                    self.key_32: system_install_dir,
                },
            )
            with reg:
                yield system_install_dir

    def test_local_machine_recognized(self, local_machine_setting):
        """
        Ensure machine setting is honored if user settings are not present.
        """
        result = distutils.msvc9compiler.find_vcvarsall(9.0)
        expected = os.path.join(local_machine_setting, 'vcvarsall.bat')
        assert expected == result

    @pytest.yield_fixture
    def x64_preferred_setting(self):
        """
        Set up environment with 64-bit and 32-bit system settings configured
        and yield the 64-bit location.
        """
        with self.mock_install_dir() as x32_dir:
            with self.mock_install_dir() as x64_dir:
                reg = mock_reg(
                    hklm={
                        # This *should* only exist on 32-bit machines
                        self.key_32: x32_dir,
                        # This *should* only exist on 64-bit machines
                        self.key_64: x64_dir,
                    },
                )
                with reg:
                    yield x64_dir

    def test_ensure_64_bit_preferred(self, x64_preferred_setting):
        """
        Ensure 64-bit system key is preferred.
        """
        result = distutils.msvc9compiler.find_vcvarsall(9.0)
        expected = os.path.join(x64_preferred_setting, 'vcvarsall.bat')
        assert expected == result

    @staticmethod
    @contextlib.contextmanager
    def mock_install_dir():
        """
        Make a mock install dir in a unique location so that tests can
        distinguish which dir was detected in a given scenario.
        """
        with contexts.tempdir() as result:
            vcvarsall = os.path.join(result, 'vcvarsall.bat')
            with open(vcvarsall, 'w'):
                pass
            yield result
<|endoftext|>


<commit_before>import tensorflow as tf
from tensorflow.contrib.tensorboard.plugins import projector
import os
import numpy as np
from evaluation import get_cleaned_processed_df, drop_text_features

LOG_DIR='log'

def visualize_data(df):
    df = drop_text_features(df)
    df.fillna("", inplace=True)

    df.to_csv(os.path.join(LOG_DIR, 'metadata.tsv'), sep='\t', mode='w+')

    training_labels = df["label"].values
    df = df.drop("label",1)
    training_data = df.values

    input_var_data = tf.Variable(initial_value=training_data, dtype=np.dtype(float))
    input_var_labels = tf.Variable(initial_value=training_labels)

    init_op = tf.global_variables_initializer()

    saver = tf.train.Saver()

    if not os.path.exists(LOG_DIR):
        os.makedirs(LOG_DIR)

    summary_writer = tf.train.SummaryWriter(LOG_DIR)

    # Format: tensorflow/contrib/tensorboard/plugins/projector/projector_config.proto
    config = projector.ProjectorConfig()

    # You can add multiple embeddings. Here we add only one.
    embedding = config.embeddings.add()
    embedding.tensor_name = input_var_data.name
    # Link this tensor to its metadata file (e.g. labels).
    embedding.metadata_path = os.path.join(LOG_DIR, 'metadata.tsv')

    # Saves a configuration file that TensorBoard will read during startup.
    projector.visualize_embeddings(summary_writer, config)

    with tf.Session() as sess:

        sess.run(init_op)

        for n in range(1):
            saver.save(sess, os.path.join(LOG_DIR, "model.ckpt"), n)

if __name__ == '__main__':
   visualize_data(get_cleaned_processed_df())<commit_msg>log/ folder should exist for metadata.tsv<commit_after>import tensorflow as tf
from tensorflow.contrib.tensorboard.plugins import projector
import os
import numpy as np
from evaluation import get_cleaned_processed_df, drop_text_features

LOG_DIR='log'

def visualize_data(df):
    df = drop_text_features(df)
    df.fillna("", inplace=True)

    if not os.path.exists(LOG_DIR):
        os.makedirs(LOG_DIR)

    df.to_csv(os.path.join(LOG_DIR, 'metadata.tsv'), sep='\t', mode='w+')

    training_labels = df["label"].values
    df = df.drop("label",1)
    training_data = df.values

    input_var_data = tf.Variable(initial_value=training_data, dtype=np.dtype(float))
    input_var_labels = tf.Variable(initial_value=training_labels)

    init_op = tf.global_variables_initializer()

    saver = tf.train.Saver()

    summary_writer = tf.train.SummaryWriter(LOG_DIR)

    # Format: tensorflow/contrib/tensorboard/plugins/projector/projector_config.proto
    config = projector.ProjectorConfig()

    # You can add multiple embeddings. Here we add only one.
    embedding = config.embeddings.add()
    embedding.tensor_name = input_var_data.name
    # Link this tensor to its metadata file (e.g. labels).
    embedding.metadata_path = os.path.join(LOG_DIR, 'metadata.tsv')

    # Saves a configuration file that TensorBoard will read during startup.
    projector.visualize_embeddings(summary_writer, config)

    with tf.Session() as sess:

        sess.run(init_op)

        for n in range(1):
            saver.save(sess, os.path.join(LOG_DIR, "model.ckpt"), n)

if __name__ == '__main__':
   visualize_data(get_cleaned_processed_df())<|endoftext|>


<commit_before># Copyright 2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from app.curses_util import *
import app.buffer_manager
import app.controller
import os
import time

class DirectoryListController(app.controller.Controller):
  """

          Work in progress.

  """
  def __init__(self, host):
    app.controller.Controller.__init__(self, host, 'DirectoryListController')
    self.shownDirectory = None

  def focus(self):
    self.onChange()
    app.controller.Controller.focus(self)

  def onChange(self):
    input = self.host.getPath()
    app.log.debug(input, "\n", self.shownDirectory)
    if self.shownDirectory == input:
      return
    self.shownDirectory = input
    path = os.path.abspath(os.path.expanduser(os.path.expandvars(input)))
    if os.path.isfile(path):
      return
      if not os.access(path, os.R_OK):
        clip = [input + ":", 'Error opening file.']
      else:
        app.log.info('got a file', path)
        textBuffer = app.buffer_manager.buffers.loadTextBuffer(path,
            self.host.host.inputWindow)
        self.host.host.inputWindow.setTextBuffer(textBuffer)
        self.changeToInputWindow()
        return
    else:
      dirPath = path or '.'
      fileName = ''
      if len(input) > 0 and input[-1] != os.sep:
        dirPath, fileName = os.path.split(path)
      if os.path.isdir(dirPath):
        lines = []
        self.host.contents = []
        try:
          contents = os.listdir(dirPath)
          for i in contents:
            if not self.host.host.opt['dotFiles'] and i[0] == '.':
              continue
            fullPath = os.path.join(dirPath, i)
            if os.path.isdir(fullPath):
              i += os.path.sep
            self.host.contents.append(i)
            iSize = ''
            iModified = ''
            if self.host.host.opt['sizes'] and os.path.isfile(fullPath):
              iSize = '%d bytes' % os.path.getsize(fullPath)
            if self.host.host.opt['modified']:
              iModified = unicode(time.strftime('%c',
                  time.localtime(os.path.getmtime(fullPath))),)
            lines.append('%-40s  %16s  %24s' % (i, iSize, iModified))
        except OSError as e:
          lines = ['Error opening directory.']
          lines.append(unicode(e))
        if self.host.opt['Size'] is not None:
          # Sort by size.
          lines.sort(reverse=not self.host.opt['Size'],
              key=lambda x: x[40:])
        elif self.host.opt['Modified'] is not None:
          # Sort by size.
          lines.sort(reverse=not self.host.opt['Modified'],
              key=lambda x: x[40 + 17:])
        else:
          lines.sort(reverse=not self.host.opt['Name'], key=unicode.lower)
        clip = ['./', '../'] + lines
      else:
        clip = [dirPath + ": not found"]
    self.host.textBuffer.selectionAll()
    self.host.textBuffer.editPasteLines(tuple(clip))
    #self.host.textBuffer.findPlainText(fileName)
    self.host.textBuffer.penRow = 0
    self.host.textBuffer.penCol = 0
    self.host.scrollRow = 0
    self.host.scrollCol = 0

  def optionChanged(self, name, value):
    self.shownDirectory = None

  def setTextBuffer(self, textBuffer):
    app.controller.Controller.setTextBuffer(self, textBuffer)
    self.commandSet = {
      CTRL_Q: self.saveEventChangeToInputWindow,

      KEY_ESCAPE: self.changeToInputWindow,
    }
    self.commandDefault = self.textBuffer.noOpDefault

class FileManagerController(app.controller.Controller):
  """

          Work in progress.

  """
  def __init__(self, host):
    app.controller.Controller.__init__(self, host, 'FileManagerController')

  def createOrOpen(self):
    path = self.textBuffer.lines[0]
    if not os.path.isfile(path):
      return
    if not os.access(path, os.R_OK):
      clip = [input + ":", 'Error opening file.']
    else:
      app.log.info('got a file', path)
      textBuffer = app.buffer_manager.buffers.loadTextBuffer(path,
          self.host.host.inputWindow)
      self.host.host.inputWindow.setTextBuffer(textBuffer)
      self.changeToInputWindow()

  def focus(self):
    self.textBuffer.selectionAll()
    if len(self.host.textBuffer.fullPath) == 0:
      path = os.getcwd()
    else:
      path = os.path.dirname(self.host.textBuffer.fullPath)
    if len(path) != 0:
      path += os.path.sep
    self.textBuffer.editPasteLines((path,))
    self.host.directoryList.focus()
    app.controller.Controller.focus(self)

  def info(self):
    app.log.info('FileManagerController command set')

  def onChange(self):
    self.host.directoryList.controller.onChange()
    app.controller.Controller.onChange(self)

  def optionChanged(self, name, value):
    self.host.directoryList.controller.shownDirectory = None

  def maybeSlash(self, expandedPath):
    if (self.textBuffer.lines[0] and self.textBuffer.lines[0][-1] != '/' and
        os.path.isdir(expandedPath)):
      self.textBuffer.insert('/')

  def tabCompleteExtend(self):
    """Extend the selection to match characters in common."""
    dirPath, fileName = os.path.split(self.textBuffer.lines[0])
    expandedDir = os.path.expandvars(os.path.expanduser(dirPath)) or '.'
    matches = []
    if not os.path.isdir(expandedDir):
      return
    for i in os.listdir(expandedDir):
      if i.startswith(fileName):
        matches.append(i)
      else:
        pass
        #app.log.info('not', i)
    if len(matches) <= 0:
      self.maybeSlash(expandedDir)
      self.onChange()
      return
    if len(matches) == 1:
      self.textBuffer.insert(matches[0][len(fileName):])
      self.maybeSlash(os.path.join(expandedDir, matches[0]))
      self.onChange()
      return
    def findCommonPrefixLength(prefixLen):
      count = 0
      ch = None
      for match in matches:
        if len(match) <= prefixLen:
          return prefixLen
        if not ch:
          ch = match[prefixLen]
        if match[prefixLen] == ch:
          count += 1
      if count and count == len(matches):
        return findCommonPrefixLength(prefixLen + 1)
      return prefixLen
    prefixLen = findCommonPrefixLength(len(fileName))
    self.textBuffer.insert(matches[0][len(fileName):prefixLen])
    self.onChange()
<commit_msg>file open to use current file for current dir<commit_after># Copyright 2017 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from app.curses_util import *
import app.buffer_manager
import app.controller
import os
import time

class DirectoryListController(app.controller.Controller):
  """

          Work in progress.

  """
  def __init__(self, host):
    app.controller.Controller.__init__(self, host, 'DirectoryListController')
    self.shownDirectory = None

  def focus(self):
    self.onChange()
    app.controller.Controller.focus(self)

  def onChange(self):
    input = self.host.getPath()
    app.log.debug(input, "\n", self.shownDirectory)
    if self.shownDirectory == input:
      return
    self.shownDirectory = input
    path = os.path.abspath(os.path.expanduser(os.path.expandvars(input)))
    if os.path.isfile(path):
      return
      if not os.access(path, os.R_OK):
        clip = [input + ":", 'Error opening file.']
      else:
        app.log.info('got a file', path)
        textBuffer = app.buffer_manager.buffers.loadTextBuffer(path,
            self.host.host.inputWindow)
        self.host.host.inputWindow.setTextBuffer(textBuffer)
        self.changeToInputWindow()
        return
    else:
      dirPath = path or '.'
      fileName = ''
      if len(input) > 0 and input[-1] != os.sep:
        dirPath, fileName = os.path.split(path)
      if os.path.isdir(dirPath):
        lines = []
        self.host.contents = []
        try:
          contents = os.listdir(dirPath)
          for i in contents:
            if not self.host.host.opt['dotFiles'] and i[0] == '.':
              continue
            fullPath = os.path.join(dirPath, i)
            if os.path.isdir(fullPath):
              i += os.path.sep
            self.host.contents.append(i)
            iSize = ''
            iModified = ''
            if self.host.host.opt['sizes'] and os.path.isfile(fullPath):
              iSize = '%d bytes' % os.path.getsize(fullPath)
            if self.host.host.opt['modified']:
              iModified = unicode(time.strftime('%c',
                  time.localtime(os.path.getmtime(fullPath))),)
            lines.append('%-40s  %16s  %24s' % (i, iSize, iModified))
        except OSError as e:
          lines = ['Error opening directory.']
          lines.append(unicode(e))
        if self.host.opt['Size'] is not None:
          # Sort by size.
          lines.sort(reverse=not self.host.opt['Size'],
              key=lambda x: x[40:])
        elif self.host.opt['Modified'] is not None:
          # Sort by size.
          lines.sort(reverse=not self.host.opt['Modified'],
              key=lambda x: x[40 + 17:])
        else:
          lines.sort(reverse=not self.host.opt['Name'], key=unicode.lower)
        clip = ['./', '../'] + lines
      else:
        clip = [dirPath + ": not found"]
    self.host.textBuffer.selectionAll()
    self.host.textBuffer.editPasteLines(tuple(clip))
    #self.host.textBuffer.findPlainText(fileName)
    self.host.textBuffer.penRow = 0
    self.host.textBuffer.penCol = 0
    self.host.scrollRow = 0
    self.host.scrollCol = 0

  def optionChanged(self, name, value):
    self.shownDirectory = None

  def setTextBuffer(self, textBuffer):
    app.controller.Controller.setTextBuffer(self, textBuffer)
    self.commandSet = {
      CTRL_Q: self.saveEventChangeToInputWindow,

      KEY_ESCAPE: self.changeToInputWindow,
    }
    self.commandDefault = self.textBuffer.noOpDefault

class FileManagerController(app.controller.Controller):
  """

          Work in progress.

  """
  def __init__(self, host):
    app.controller.Controller.__init__(self, host, 'FileManagerController')

  def createOrOpen(self):
    path = self.textBuffer.lines[0]
    if not os.path.isfile(path):
      return
    if not os.access(path, os.R_OK):
      clip = [input + ":", 'Error opening file.']
    else:
      app.log.info('got a file', path)
      textBuffer = app.buffer_manager.buffers.loadTextBuffer(path,
          self.host.host.inputWindow)
      self.host.host.inputWindow.setTextBuffer(textBuffer)
      self.changeToInputWindow()

  def focus(self):
    self.textBuffer.selectionAll()
    if len(self.host.inputWindow.textBuffer.fullPath) == 0:
      path = os.getcwd()
    else:
      path = os.path.dirname(self.host.inputWindow.textBuffer.fullPath)
    if len(path) != 0:
      path += os.path.sep
    self.textBuffer.editPasteLines((path,))
    self.host.directoryList.focus()
    app.controller.Controller.focus(self)

  def info(self):
    app.log.info('FileManagerController command set')

  def onChange(self):
    self.host.directoryList.controller.onChange()
    app.controller.Controller.onChange(self)

  def optionChanged(self, name, value):
    self.host.directoryList.controller.shownDirectory = None

  def maybeSlash(self, expandedPath):
    if (self.textBuffer.lines[0] and self.textBuffer.lines[0][-1] != '/' and
        os.path.isdir(expandedPath)):
      self.textBuffer.insert('/')

  def tabCompleteExtend(self):
    """Extend the selection to match characters in common."""
    dirPath, fileName = os.path.split(self.textBuffer.lines[0])
    expandedDir = os.path.expandvars(os.path.expanduser(dirPath)) or '.'
    matches = []
    if not os.path.isdir(expandedDir):
      return
    for i in os.listdir(expandedDir):
      if i.startswith(fileName):
        matches.append(i)
      else:
        pass
        #app.log.info('not', i)
    if len(matches) <= 0:
      self.maybeSlash(expandedDir)
      self.onChange()
      return
    if len(matches) == 1:
      self.textBuffer.insert(matches[0][len(fileName):])
      self.maybeSlash(os.path.join(expandedDir, matches[0]))
      self.onChange()
      return
    def findCommonPrefixLength(prefixLen):
      count = 0
      ch = None
      for match in matches:
        if len(match) <= prefixLen:
          return prefixLen
        if not ch:
          ch = match[prefixLen]
        if match[prefixLen] == ch:
          count += 1
      if count and count == len(matches):
        return findCommonPrefixLength(prefixLen + 1)
      return prefixLen
    prefixLen = findCommonPrefixLength(len(fileName))
    self.textBuffer.insert(matches[0][len(fileName):prefixLen])
    self.onChange()
<|endoftext|>