<commit_before># ==================================================================================================
# Copyright 2012 Twitter, Inc.
# --------------------------------------------------------------------------------------------------
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this work except in compliance with the License.
# You may obtain a copy of the License in the LICENSE file, or 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 __future__ import print_function

import ast
from collections import defaultdict
import itertools
import os
import pprint

from twitter.common.collections import OrderedSet
from twitter.common.dirutil import safe_rmtree
from twitter.common.dirutil.chroot import Chroot
from twitter.common.python.installer import InstallerBase, Packager
from twitter.pants.base import (
    Address,
    Config,
    Target,
    TargetDefinitionException,
)
from twitter.pants.python.antlr_builder import PythonAntlrBuilder
from twitter.pants.python.thrift_builder import PythonThriftBuilder
from twitter.pants.targets import (
    PythonAntlrLibrary,
    PythonBinary,
    PythonRequirement,
    PythonTarget,
    PythonThriftLibrary,
)

from . import Command

SETUP_BOILERPLATE = """
# DO NOT EDIT THIS FILE -- AUTOGENERATED BY PANTS
# Target: %(setup_target)s

from setuptools import setup

setup(**
%(setup_dict)s
)
"""

class SetupPyRunner(InstallerBase):
  def __init__(self, source_dir, setup_command, **kw):
    self.__setup_command = setup_command.split()
    super(SetupPyRunner, self).__init__(source_dir, **kw)

  def _setup_command(self):
    return self.__setup_command

class SetupPy(Command):
  """Generate setup.py-based Python projects from python_library targets."""

  DECLARE_NAMESPACE = '__import__("pkg_resources").declare_namespace(__name__)'
  GENERATED_TARGETS = {
      PythonAntlrLibrary: PythonAntlrBuilder,
      PythonThriftLibrary: PythonThriftBuilder,
  }
  SOURCE_ROOT = 'src'
  __command__ = 'setup_py'

  @classmethod
  def _combined_dependencies(cls, target):
    dependencies = getattr(target, 'dependencies', OrderedSet())
    if isinstance(target, PythonTarget) and target.provides:
      return dependencies | OrderedSet(target.provides.binaries.values())
    else:
      return dependencies

  @classmethod
  def _construct_provider_map(cls, root_target, descendant, parents, providers, depmap):
    if isinstance(descendant, PythonTarget) and descendant.provides:
      providers.append(descendant)
    for dependency in cls._combined_dependencies(descendant):
      for prv in providers:
        for dep in dependency.resolve():
          depmap[prv].add(dep)
          if dep in parents:
            raise TargetDefinitionException(root_target,
               '%s and %s combined have a cycle!' % (root_target, dep))
          parents.add(dep)
          cls._construct_provider_map(root_target, dep, parents, providers, depmap)
          parents.remove(dep)
    if isinstance(descendant, PythonTarget) and descendant.provides:
      assert providers[-1] == descendant
      providers.pop()

  @classmethod
  def construct_provider_map(cls, root_target):
    """Construct a mapping of provider => minimal target set within :root_target.

       The algorithm works in the following fashion:

         1. Recursively resolve every dependency starting at root_target (the thing
            that setup_py is being called against).  This includes the dependencies
            of any binaries attached to the PythonArtifact using with_binaries
         2. For every PythonTarget that provides a PythonArtifact, add an
            entry for it to depmap[], keyed on the artifact name, containing
            an OrderedSet of all transitively resolved children
            dependencies.
         3. Any concrete target with sources that is provided by another PythonArtifact
            other than the one being built with setup_py will be elided.

       Downsides:
         - Explicitly requested dependencies may be elided if transitively included by others,
           e.g.
             python_library(
               ...,
               dependencies = [
                  pants('src/python/twitter/common/dirutil'),
                  pants('src/python/twitter/common/python'),
               ]
            )
          will result in only twitter.common.python being exported even if top-level sources
          directly reference twitter.common.dirutil, which could be considered a leak.
    """
    depmap = defaultdict(OrderedSet)
    cls._construct_provider_map(root_target, root_target, parents=set(), providers=[],
                                depmap=depmap)
    return depmap

  @classmethod
  def minified_dependencies(cls, root_target):
    """Minify the dependencies of a PythonTarget."""
    depmap = cls.construct_provider_map(root_target)
    root_deps = depmap.pop(root_target, OrderedSet())

    def elide(target):
      if any(target in depset for depset in depmap.values()):
        root_deps.discard(target)

    root_target.walk(elide)
    return root_deps

  @classmethod
  def iter_entry_points(cls, target):
    """Yields the name, entry_point pairs of binary targets in this PythonArtifact."""
    for name, binary_target in target.provides.binaries.items():
      concrete_target = binary_target.get()
      if not isinstance(concrete_target, PythonBinary) or concrete_target.entry_point is None:
        raise TargetDefinitionException(target,
            'Cannot add a binary to a PythonArtifact if it does not contain an entry_point.')
      yield name, concrete_target.entry_point

  @classmethod
  def declares_namespace_package(cls, filename):
    """Given a filename, walk its ast and determine if it is declaring a namespace package.
       Intended only for __init__.py files though it will work for any .py."""
    with open(filename) as fp:
      init_py = ast.parse(fp.read(), filename)
    calls = [node for node in ast.walk(init_py) if isinstance(node, ast.Call)]
    for call in calls:
      if len(call.args) != 1:
        continue
      if isinstance(call.func, ast.Attribute) and call.func.attr != 'declare_namespace':
        continue
      if isinstance(call.func, ast.Name) and call.func.id != 'declare_namespace':
        continue
      if isinstance(call.args[0], ast.Name) and call.args[0].id == '__name__':
        return True
    return False

  @classmethod
  def iter_generated_sources(cls, target, root, config=None):
    config = config or Config.load()
    # This is sort of facepalmy -- python.new will make this much better.
    for target_type, target_builder in cls.GENERATED_TARGETS.items():
      if isinstance(target, target_type):
        builder_cls = target_builder
        break
    else:
      raise TypeError(
          'write_generated_sources could not find suitable code generator for %s' % type(target))

    builder = builder_cls(target, root, config)
    builder.generate()
    for root, _, files in os.walk(builder.package_root):
      for fn in files:
        target_file = os.path.join(root, fn)
        yield os.path.relpath(target_file, builder.package_root), target_file

  @classmethod
  def nearest_subpackage(cls, package, all_packages):
    """Given a package, find its nearest parent in all_packages."""
    def shared_prefix(candidate):
      zipped = itertools.izip(package.split('.'), candidate.split('.'))
      matching = itertools.takewhile(lambda pair: pair[0] == pair[1], zipped)
      return [pair[0] for pair in matching]
    shared_packages = list(filter(None, map(shared_prefix, all_packages)))
    return '.'.join(max(shared_packages, key=len)) if shared_packages else package

  @classmethod
  def find_packages(cls, chroot):
    """Detect packages, namespace packages and resources from an existing chroot.

       Returns a tuple of:
         set(packages)
         set(namespace_packages)
         map(package => set(files))
    """
    base = os.path.join(chroot.path(), cls.SOURCE_ROOT)
    packages, namespace_packages = set(), set()
    resources = defaultdict(set)

    def iter_files():
      for root, _, files in os.walk(base):
        module = os.path.relpath(root, base).replace(os.path.sep, '.')
        for filename in files:
          yield module, filename, os.path.join(root, filename)

    # establish packages, namespace packages in first pass
    for module, filename, real_filename in iter_files():
      if filename != '__init__.py':
        continue
      packages.add(module)
      if cls.declares_namespace_package(real_filename):
        namespace_packages.add(module)

    # second pass establishes non-source content (resources)
    for module, filename, real_filename in iter_files():
      if filename.endswith('.py'):
        if module not in packages:
          # TODO(wickman) Consider changing this to a full-on error as it
          # could indicate bad BUILD hygiene.
          # raise cls.UndefinedSource('%s is source but does not belong to a package!' % filename)
          print('WARNING!  %s is source but does not belong to a package!' % real_filename)
        else:
          continue
      submodule = cls.nearest_subpackage(module, packages)
      if submodule == module:
        resources[submodule].add(filename)
      else:
        assert module.startswith(submodule + '.')
        relative_module = module[len(submodule) + 1:]
        relative_filename = os.path.join(relative_module.replace('.', os.path.sep), filename)
        resources[submodule].add(relative_filename)

    return packages, namespace_packages, resources

  def setup_parser(self, parser, args):
    parser.set_usage("\n"
                     "  %prog setup_py (options) [spec]\n")
    parser.add_option("--run", dest="run", default=None,
                      help="The command to run against setup.py.  Don't forget to quote "
                           "any additional parameters.  If no run command is specified, "
                           "pants will by default generate and dump the source distribution.")
    parser.add_option("--recursive", dest="recursive", default=False, action="store_true",
                      help="Transitively run setup_py on all provided downstream targets.")

  def __init__(self, run_tracker, root_dir, parser, argv):
    Command.__init__(self, run_tracker, root_dir, parser, argv)

    if not self.args:
      self.error("A spec argument is required")

    self._config = Config.load()
    self._root = root_dir

    address = Address.parse(root_dir, self.args[0])
    self.target = Target.get(address)
    if self.target is None:
      self.error('%s is not a valid target!' % self.args[0])

    if not self.target.provides:
      self.error('Target must provide an artifact.')

  def write_contents(self, root_target, chroot):
    """Write contents of the target."""
    def write_target_source(target, src):
      chroot.link(os.path.join(target.target_base, src), os.path.join(self.SOURCE_ROOT, src))
      # check parent __init__.pys to see if they also need to be linked.  this is to allow
      # us to determine if they belong to regular packages or namespace packages.
      while True:
        src = os.path.dirname(src)
        if not src:
          # Do not allow the repository root to leak (i.e. '.' should not be a package in setup.py)
          break
        if os.path.exists(os.path.join(target.target_base, src, '__init__.py')):
          chroot.link(os.path.join(target.target_base, src, '__init__.py'),
                      os.path.join(self.SOURCE_ROOT, src, '__init__.py'))

    def write_codegen_source(relpath, abspath):
      chroot.link(abspath, os.path.join(self.SOURCE_ROOT, relpath))

    def write_target(target):
      if isinstance(target, tuple(self.GENERATED_TARGETS.keys())):
        for relpath, abspath in self.iter_generated_sources(target, self._root, self._config):
          write_codegen_source(relpath, abspath)
      else:
        for source in list(target.sources) + list(target.resources):
          write_target_source(target, source)

    write_target(root_target)
    for dependency in self.minified_dependencies(root_target):
      if isinstance(dependency, PythonTarget) and not dependency.provides:
        write_target(dependency)

  def write_setup(self, root_target, chroot):
    """Write the setup.py of a target.  Must be run after writing the contents to the chroot."""
    setup_keywords = root_target.provides.setup_py_keywords

    package_dir = {'': self.SOURCE_ROOT}
    packages, namespace_packages, resources = self.find_packages(chroot)

    if namespace_packages:
      setup_keywords['namespace_packages'] = list(sorted(namespace_packages))

    if packages:
      setup_keywords.update(
          package_dir=package_dir,
          packages=list(sorted(packages)),
          package_data=dict((package, list(rs)) for (package, rs) in resources.items()))

    install_requires = set()
    for dep in self.minified_dependencies(root_target):
      if isinstance(dep, PythonRequirement):
        install_requires.add(str(dep.requirement))
      elif isinstance(dep, PythonTarget) and dep.provides:
        install_requires.add(dep.provides.key)
    setup_keywords['install_requires'] = list(install_requires)

    for binary_name, entry_point in self.iter_entry_points(root_target):
      if 'entry_points' not in setup_keywords:
        setup_keywords['entry_points'] = {}
      if 'console_scripts' not in setup_keywords['entry_points']:
        setup_keywords['entry_points']['console_scripts'] = []
      setup_keywords['entry_points']['console_scripts'].append(
          '%s = %s' % (binary_name, entry_point))

    chroot.write(SETUP_BOILERPLATE % {
      'setup_dict': pprint.pformat(setup_keywords, indent=4),
      'setup_target': repr(root_target)
    }, 'setup.py')

    # make sure that setup.py is included
    chroot.write('include *.py'.encode('utf8'), 'MANIFEST.in')

  def run_one(self, target):
    dist_dir = self._config.getdefault('pants_distdir')
    chroot = Chroot(dist_dir, name=target.provides.name)
    self.write_contents(target, chroot)
    self.write_setup(target, chroot)
    target_base = '%s-%s' % (target.provides.name, target.provides.version)
    setup_dir = os.path.join(dist_dir, target_base)
    safe_rmtree(setup_dir)
    os.rename(chroot.path(), setup_dir)

    if not self.options.run:
      print('Running packager against %s' % setup_dir)
      setup_runner = Packager(setup_dir)
      tgz_name = os.path.basename(setup_runner.sdist())
      print('Writing %s' % os.path.join(dist_dir, tgz_name))
      os.rename(setup_runner.sdist(), os.path.join(dist_dir, tgz_name))
      safe_rmtree(setup_dir)
    else:
      print('Running %s against %s' % (self.options.run, setup_dir))
      setup_runner = SetupPyRunner(setup_dir, self.options.run)
      setup_runner.run()

  def execute(self):
    if self.options.recursive:
      setup_targets = OrderedSet()
      def add_providing_target(target):
        if isinstance(target, PythonTarget) and getattr(target, 'provides', None):
          setup_targets.add(target)
          return OrderedSet(target.provides.binaries.values())
      self.target.walk(add_providing_target)
    else:
      setup_targets = [self.target]

    for target in setup_targets:
      if isinstance(target, PythonTarget) and target.provides:
        self.run_one(target)
<commit_msg>Kill unused constant.<commit_after># ==================================================================================================
# Copyright 2012 Twitter, Inc.
# --------------------------------------------------------------------------------------------------
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this work except in compliance with the License.
# You may obtain a copy of the License in the LICENSE file, or 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 __future__ import print_function

import ast
from collections import defaultdict
import itertools
import os
import pprint

from twitter.common.collections import OrderedSet
from twitter.common.dirutil import safe_rmtree
from twitter.common.dirutil.chroot import Chroot
from twitter.common.python.installer import InstallerBase, Packager
from twitter.pants.base import (
    Address,
    Config,
    Target,
    TargetDefinitionException,
)
from twitter.pants.python.antlr_builder import PythonAntlrBuilder
from twitter.pants.python.thrift_builder import PythonThriftBuilder
from twitter.pants.targets import (
    PythonAntlrLibrary,
    PythonBinary,
    PythonRequirement,
    PythonTarget,
    PythonThriftLibrary,
)

from . import Command

SETUP_BOILERPLATE = """
# DO NOT EDIT THIS FILE -- AUTOGENERATED BY PANTS
# Target: %(setup_target)s

from setuptools import setup

setup(**
%(setup_dict)s
)
"""

class SetupPyRunner(InstallerBase):
  def __init__(self, source_dir, setup_command, **kw):
    self.__setup_command = setup_command.split()
    super(SetupPyRunner, self).__init__(source_dir, **kw)

  def _setup_command(self):
    return self.__setup_command

class SetupPy(Command):
  """Generate setup.py-based Python projects from python_library targets."""

  GENERATED_TARGETS = {
      PythonAntlrLibrary: PythonAntlrBuilder,
      PythonThriftLibrary: PythonThriftBuilder,
  }
  SOURCE_ROOT = 'src'
  __command__ = 'setup_py'

  @classmethod
  def _combined_dependencies(cls, target):
    dependencies = getattr(target, 'dependencies', OrderedSet())
    if isinstance(target, PythonTarget) and target.provides:
      return dependencies | OrderedSet(target.provides.binaries.values())
    else:
      return dependencies

  @classmethod
  def _construct_provider_map(cls, root_target, descendant, parents, providers, depmap):
    if isinstance(descendant, PythonTarget) and descendant.provides:
      providers.append(descendant)
    for dependency in cls._combined_dependencies(descendant):
      for prv in providers:
        for dep in dependency.resolve():
          depmap[prv].add(dep)
          if dep in parents:
            raise TargetDefinitionException(root_target,
               '%s and %s combined have a cycle!' % (root_target, dep))
          parents.add(dep)
          cls._construct_provider_map(root_target, dep, parents, providers, depmap)
          parents.remove(dep)
    if isinstance(descendant, PythonTarget) and descendant.provides:
      assert providers[-1] == descendant
      providers.pop()

  @classmethod
  def construct_provider_map(cls, root_target):
    """Construct a mapping of provider => minimal target set within :root_target.

       The algorithm works in the following fashion:

         1. Recursively resolve every dependency starting at root_target (the thing
            that setup_py is being called against).  This includes the dependencies
            of any binaries attached to the PythonArtifact using with_binaries
         2. For every PythonTarget that provides a PythonArtifact, add an
            entry for it to depmap[], keyed on the artifact name, containing
            an OrderedSet of all transitively resolved children
            dependencies.
         3. Any concrete target with sources that is provided by another PythonArtifact
            other than the one being built with setup_py will be elided.

       Downsides:
         - Explicitly requested dependencies may be elided if transitively included by others,
           e.g.
             python_library(
               ...,
               dependencies = [
                  pants('src/python/twitter/common/dirutil'),
                  pants('src/python/twitter/common/python'),
               ]
            )
          will result in only twitter.common.python being exported even if top-level sources
          directly reference twitter.common.dirutil, which could be considered a leak.
    """
    depmap = defaultdict(OrderedSet)
    cls._construct_provider_map(root_target, root_target, parents=set(), providers=[],
                                depmap=depmap)
    return depmap

  @classmethod
  def minified_dependencies(cls, root_target):
    """Minify the dependencies of a PythonTarget."""
    depmap = cls.construct_provider_map(root_target)
    root_deps = depmap.pop(root_target, OrderedSet())

    def elide(target):
      if any(target in depset for depset in depmap.values()):
        root_deps.discard(target)

    root_target.walk(elide)
    return root_deps

  @classmethod
  def iter_entry_points(cls, target):
    """Yields the name, entry_point pairs of binary targets in this PythonArtifact."""
    for name, binary_target in target.provides.binaries.items():
      concrete_target = binary_target.get()
      if not isinstance(concrete_target, PythonBinary) or concrete_target.entry_point is None:
        raise TargetDefinitionException(target,
            'Cannot add a binary to a PythonArtifact if it does not contain an entry_point.')
      yield name, concrete_target.entry_point

  @classmethod
  def declares_namespace_package(cls, filename):
    """Given a filename, walk its ast and determine if it is declaring a namespace package.
       Intended only for __init__.py files though it will work for any .py."""
    with open(filename) as fp:
      init_py = ast.parse(fp.read(), filename)
    calls = [node for node in ast.walk(init_py) if isinstance(node, ast.Call)]
    for call in calls:
      if len(call.args) != 1:
        continue
      if isinstance(call.func, ast.Attribute) and call.func.attr != 'declare_namespace':
        continue
      if isinstance(call.func, ast.Name) and call.func.id != 'declare_namespace':
        continue
      if isinstance(call.args[0], ast.Name) and call.args[0].id == '__name__':
        return True
    return False

  @classmethod
  def iter_generated_sources(cls, target, root, config=None):
    config = config or Config.load()
    # This is sort of facepalmy -- python.new will make this much better.
    for target_type, target_builder in cls.GENERATED_TARGETS.items():
      if isinstance(target, target_type):
        builder_cls = target_builder
        break
    else:
      raise TypeError(
          'write_generated_sources could not find suitable code generator for %s' % type(target))

    builder = builder_cls(target, root, config)
    builder.generate()
    for root, _, files in os.walk(builder.package_root):
      for fn in files:
        target_file = os.path.join(root, fn)
        yield os.path.relpath(target_file, builder.package_root), target_file

  @classmethod
  def nearest_subpackage(cls, package, all_packages):
    """Given a package, find its nearest parent in all_packages."""
    def shared_prefix(candidate):
      zipped = itertools.izip(package.split('.'), candidate.split('.'))
      matching = itertools.takewhile(lambda pair: pair[0] == pair[1], zipped)
      return [pair[0] for pair in matching]
    shared_packages = list(filter(None, map(shared_prefix, all_packages)))
    return '.'.join(max(shared_packages, key=len)) if shared_packages else package

  @classmethod
  def find_packages(cls, chroot):
    """Detect packages, namespace packages and resources from an existing chroot.

       Returns a tuple of:
         set(packages)
         set(namespace_packages)
         map(package => set(files))
    """
    base = os.path.join(chroot.path(), cls.SOURCE_ROOT)
    packages, namespace_packages = set(), set()
    resources = defaultdict(set)

    def iter_files():
      for root, _, files in os.walk(base):
        module = os.path.relpath(root, base).replace(os.path.sep, '.')
        for filename in files:
          yield module, filename, os.path.join(root, filename)

    # establish packages, namespace packages in first pass
    for module, filename, real_filename in iter_files():
      if filename != '__init__.py':
        continue
      packages.add(module)
      if cls.declares_namespace_package(real_filename):
        namespace_packages.add(module)

    # second pass establishes non-source content (resources)
    for module, filename, real_filename in iter_files():
      if filename.endswith('.py'):
        if module not in packages:
          # TODO(wickman) Consider changing this to a full-on error as it
          # could indicate bad BUILD hygiene.
          # raise cls.UndefinedSource('%s is source but does not belong to a package!' % filename)
          print('WARNING!  %s is source but does not belong to a package!' % real_filename)
        else:
          continue
      submodule = cls.nearest_subpackage(module, packages)
      if submodule == module:
        resources[submodule].add(filename)
      else:
        assert module.startswith(submodule + '.')
        relative_module = module[len(submodule) + 1:]
        relative_filename = os.path.join(relative_module.replace('.', os.path.sep), filename)
        resources[submodule].add(relative_filename)

    return packages, namespace_packages, resources

  def setup_parser(self, parser, args):
    parser.set_usage("\n"
                     "  %prog setup_py (options) [spec]\n")
    parser.add_option("--run", dest="run", default=None,
                      help="The command to run against setup.py.  Don't forget to quote "
                           "any additional parameters.  If no run command is specified, "
                           "pants will by default generate and dump the source distribution.")
    parser.add_option("--recursive", dest="recursive", default=False, action="store_true",
                      help="Transitively run setup_py on all provided downstream targets.")

  def __init__(self, run_tracker, root_dir, parser, argv):
    Command.__init__(self, run_tracker, root_dir, parser, argv)

    if not self.args:
      self.error("A spec argument is required")

    self._config = Config.load()
    self._root = root_dir

    address = Address.parse(root_dir, self.args[0])
    self.target = Target.get(address)
    if self.target is None:
      self.error('%s is not a valid target!' % self.args[0])

    if not self.target.provides:
      self.error('Target must provide an artifact.')

  def write_contents(self, root_target, chroot):
    """Write contents of the target."""
    def write_target_source(target, src):
      chroot.link(os.path.join(target.target_base, src), os.path.join(self.SOURCE_ROOT, src))
      # check parent __init__.pys to see if they also need to be linked.  this is to allow
      # us to determine if they belong to regular packages or namespace packages.
      while True:
        src = os.path.dirname(src)
        if not src:
          # Do not allow the repository root to leak (i.e. '.' should not be a package in setup.py)
          break
        if os.path.exists(os.path.join(target.target_base, src, '__init__.py')):
          chroot.link(os.path.join(target.target_base, src, '__init__.py'),
                      os.path.join(self.SOURCE_ROOT, src, '__init__.py'))

    def write_codegen_source(relpath, abspath):
      chroot.link(abspath, os.path.join(self.SOURCE_ROOT, relpath))

    def write_target(target):
      if isinstance(target, tuple(self.GENERATED_TARGETS.keys())):
        for relpath, abspath in self.iter_generated_sources(target, self._root, self._config):
          write_codegen_source(relpath, abspath)
      else:
        for source in list(target.sources) + list(target.resources):
          write_target_source(target, source)

    write_target(root_target)
    for dependency in self.minified_dependencies(root_target):
      if isinstance(dependency, PythonTarget) and not dependency.provides:
        write_target(dependency)

  def write_setup(self, root_target, chroot):
    """Write the setup.py of a target.  Must be run after writing the contents to the chroot."""
    setup_keywords = root_target.provides.setup_py_keywords

    package_dir = {'': self.SOURCE_ROOT}
    packages, namespace_packages, resources = self.find_packages(chroot)

    if namespace_packages:
      setup_keywords['namespace_packages'] = list(sorted(namespace_packages))

    if packages:
      setup_keywords.update(
          package_dir=package_dir,
          packages=list(sorted(packages)),
          package_data=dict((package, list(rs)) for (package, rs) in resources.items()))

    install_requires = set()
    for dep in self.minified_dependencies(root_target):
      if isinstance(dep, PythonRequirement):
        install_requires.add(str(dep.requirement))
      elif isinstance(dep, PythonTarget) and dep.provides:
        install_requires.add(dep.provides.key)
    setup_keywords['install_requires'] = list(install_requires)

    for binary_name, entry_point in self.iter_entry_points(root_target):
      if 'entry_points' not in setup_keywords:
        setup_keywords['entry_points'] = {}
      if 'console_scripts' not in setup_keywords['entry_points']:
        setup_keywords['entry_points']['console_scripts'] = []
      setup_keywords['entry_points']['console_scripts'].append(
          '%s = %s' % (binary_name, entry_point))

    chroot.write(SETUP_BOILERPLATE % {
      'setup_dict': pprint.pformat(setup_keywords, indent=4),
      'setup_target': repr(root_target)
    }, 'setup.py')

    # make sure that setup.py is included
    chroot.write('include *.py'.encode('utf8'), 'MANIFEST.in')

  def run_one(self, target):
    dist_dir = self._config.getdefault('pants_distdir')
    chroot = Chroot(dist_dir, name=target.provides.name)
    self.write_contents(target, chroot)
    self.write_setup(target, chroot)
    target_base = '%s-%s' % (target.provides.name, target.provides.version)
    setup_dir = os.path.join(dist_dir, target_base)
    safe_rmtree(setup_dir)
    os.rename(chroot.path(), setup_dir)

    if not self.options.run:
      print('Running packager against %s' % setup_dir)
      setup_runner = Packager(setup_dir)
      tgz_name = os.path.basename(setup_runner.sdist())
      print('Writing %s' % os.path.join(dist_dir, tgz_name))
      os.rename(setup_runner.sdist(), os.path.join(dist_dir, tgz_name))
      safe_rmtree(setup_dir)
    else:
      print('Running %s against %s' % (self.options.run, setup_dir))
      setup_runner = SetupPyRunner(setup_dir, self.options.run)
      setup_runner.run()

  def execute(self):
    if self.options.recursive:
      setup_targets = OrderedSet()
      def add_providing_target(target):
        if isinstance(target, PythonTarget) and getattr(target, 'provides', None):
          setup_targets.add(target)
          return OrderedSet(target.provides.binaries.values())
      self.target.walk(add_providing_target)
    else:
      setup_targets = [self.target]

    for target in setup_targets:
      if isinstance(target, PythonTarget) and target.provides:
        self.run_one(target)
<|endoftext|>


<commit_before>from application import app
from collections import Subscriptions, User, Posts
from flask import render_template, session, redirect, url_for

@app.route('/dashboard')
def dashboard():
    if session.get("logged_in"):
        user = User.objects.get(alias = session.get("alias"))
        subscriptions=Subscriptions.objects.get_or_create(user=user)
        #posts = Posts.objects(author__in=subscriptions).order_by('-sticky', '-score', '-created_at')
        return str(subscriptions)
        #return render_template("dashboard.html", posts=posts)
    else:
        return redirect(url_for("index"))
<commit_msg>changes to dashboard?<commit_after>from application import app
from collections import Subscriptions, User, Posts
from flask import render_template, session, redirect, url_for

@app.route('/dashboard') #Defines URL for the dashboard
def dashboard():
    if session.get("logged_in"):
        user = User.objects.get(alias = session.get("alias"))
        subscriptions=Subscriptions.objects.get_or_create(user=user)
        #posts = Posts.objects(author__in=subscriptions).order_by('-sticky', '-score', '-created_at')
        return str(subscriptions)
        #return render_template("dashboard.html", posts=posts)
    else:
        return redirect(url_for("index"))
<|endoftext|>


<commit_before>version_number = "v6.0.0"
<commit_msg>v6.1.0<commit_after>version_number = "v6.1.0"
<|endoftext|>


<commit_before>import collections
from datetime import datetime
import hashlib
import time
import uuid

from django.conf import settings
from django.core.cache import cache
from django.db import models, connection

from MySQLdb import IntegrityError

import amo
import amo.models
from amo.utils import sorted_groupby
from amo.urlresolvers import reverse
from addons.models import Addon, AddonCategory, AddonRecommendation
from applications.models import Application
from users.models import UserProfile
from translations.fields import TranslatedField, LinkifiedField

class TopTags(object):
    """Descriptor to manage a collection's top tags in cache."""

    def key(self, obj):
        return '%s:top-tags:%s' % (settings.CACHE_PREFIX, obj.id)

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        return cache.get(self.key(obj), [])

    def __set__(self, obj, value):
        two_days = 60 * 60 * 24 * 2
        cache.set(self.key(obj), value, two_days)

class CollectionManager(amo.models.ManagerBase):

    def manual(self):
        """Only hand-crafted, favorites, and featured collections should appear
        in this filter."""
        types = (amo.COLLECTION_NORMAL, amo.COLLECTION_FAVORITE,
                 amo.COLLECTION_FEATURED, )

        return self.filter(type__in=types)

    def listed(self):
        """Return public collections only."""
        return self.filter(listed=True)

class Collection(amo.models.ModelBase):

    TYPE_CHOICES = amo.COLLECTION_CHOICES.items()
    RECOMMENDATION_LIMIT = 15  # Maximum number of add-ons to recommend.

    uuid = models.CharField(max_length=36, blank=True, unique=True)
    name = TranslatedField()
    nickname = models.CharField(max_length=30, blank=True, unique=True,
                                null=True)
    slug = models.CharField(max_length=30, blank=True, null=True)

    description = LinkifiedField()
    default_locale = models.CharField(max_length=10, default='en-US',
                                      db_column='defaultlocale')
    type = models.PositiveIntegerField(db_column='collection_type',
            choices=TYPE_CHOICES, default=0)
    icontype = models.CharField(max_length=25, blank=True)

    listed = models.BooleanField(
        default=True, help_text='Collections are either listed or private.')

    subscribers = models.PositiveIntegerField(default=0)
    downloads = models.PositiveIntegerField(default=0)
    weekly_subscribers = models.PositiveIntegerField(default=0)
    monthly_subscribers = models.PositiveIntegerField(default=0)
    application = models.ForeignKey(Application, null=True)
    addon_count = models.PositiveIntegerField(default=0,
                                              db_column='addonCount')

    upvotes = models.PositiveIntegerField(default=0)
    downvotes = models.PositiveIntegerField(default=0)
    rating = models.FloatField(default=0)
    all_personas = models.BooleanField(default=False,
        help_text='Does this collection only contain personas?')

    addons = models.ManyToManyField(Addon, through='CollectionAddon',
                                    related_name='collections')
    author = models.ForeignKey(UserProfile, null=True,
                               related_name='collections')
    users = models.ManyToManyField(UserProfile, through='CollectionUser',
                                  related_name='collections_publishable')

    addon_index = models.CharField(max_length=40, null=True, db_index=True,
        help_text='Custom index for the add-ons in this collection')
    recommended_collection = models.ForeignKey('self', null=True)

    objects = CollectionManager()

    top_tags = TopTags()

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collections'

    def __unicode__(self):
        return u'%s (%s)' % (self.name, self.addon_count)

    def save(self, **kw):
        if not self.uuid:
            self.uuid = unicode(uuid.uuid4())
        if not self.slug:
            self.slug = self.uuid[:30]

        # Maintain our index of add-on ids.
        if self.id:
            ids = self.addons.values_list('id', flat=True)
            self.addon_index = self.make_index(ids)

        super(Collection, self).save(**kw)

    def get_url_path(self):
        if settings.NEW_COLLECTIONS:
            return reverse('collections.detail',
                            args=[self.author_nickname, self.slug])
        else:
            return '/collection/%s' % self.url_slug

    def upvote_url(self):
        return reverse('collections.vote',
                       args=[self.author_nickname, self.slug, 'up'])

    def downvote_url(self):
        return reverse('collections.vote',
                       args=[self.author_nickname, self.slug, 'down'])

    @property
    def author_nickname(self):
        return self.author.nickname if self.author else 'anonymous'

    @classmethod
    def get_fallback(cls):
        return cls._meta.get_field('default_locale')

    @classmethod
    def make_index(cls, addon_ids):
        ids = ':'.join(map(str, sorted(addon_ids)))
        return hashlib.md5(ids).hexdigest()

    @property
    def url_slug(self):
        """uuid or nickname if chosen"""
        return self.nickname or self.uuid

    @property
    def icon_url(self):
        modified = int(time.mktime(self.modified.timetuple()))
        if self.icontype:
            return settings.COLLECTION_ICON_URL % (self.id, modified)
        else:
            return settings.MEDIA_URL + 'img/amo2009/icons/collection.png'

    def get_recommendations(self):
        """Get a collection of recommended add-ons for this collection."""
        if self.recommended_collection:
            return self.recommended_collection
        else:
            r = RecommendedCollection.objects.create(listed=False)
            addons = list(self.addons.values_list('id', flat=True))
            recs = RecommendedCollection.build_recs(addons)
            r.set_addons(recs[:Collection.RECOMMENDATION_LIMIT])
            self.recommended_collection = r
            self.save()
            return r

    def set_addons(self, addon_ids):
        """Replace the current add-ons with a new list of add-on ids."""
        order = dict((a, idx) for idx, a in enumerate(addon_ids))

        # Partition addon_ids into add/update/remove buckets.
        existing = set(self.addons.values_list('id', flat=True))
        add, update = [], []
        for addon in addon_ids:
            bucket = update if addon in existing else add
            bucket.append((addon, order[addon]))
        remove = existing.difference(addon_ids)

        cursor = connection.cursor()
        now = datetime.now()
        if remove:
            cursor.execute("DELETE FROM addons_collections "
                           "WHERE collection_id=%s AND addon_id IN (%s)" %
                           (self.id, ','.join(map(str, remove))))
        if add:
            insert = '(%s, %s, %s, NOW(), NOW(), NOW(), 0)'
            values = [insert % (a, self.id, idx) for a, idx in add]
            cursor.execute("""
                INSERT INTO addons_collections
                    (addon_id, collection_id, ordering, added, created,
                     modified, downloads)
                VALUES %s""" % ','.join(values))
        for addon, ordering in update:
            (CollectionAddon.objects.filter(collection=self.id, addon=addon)
             .update(ordering=ordering, modified=now))
        self.save()

    def is_subscribed(self, user):
        """Determines if the user is subscribed to this collection."""
        return self.subscriptions.filter(user=user).exists()

    # TODO(davedash): use this when we're on 1.3:
    # http://code.djangoproject.com/ticket/13240
    def add_addon(self, addon):
        "Adds an addon to the collection."
        ca = CollectionAddon()
        ca.addon = addon
        ca.collection = self
        try:
            ca.save()
        except IntegrityError:
            pass
        self.save()  # To invalidate Collection.

    def remove_addon(self, addon):
        CollectionAddon.objects.filter(addon=addon, collection=self).delete()
        self.save()  # To invalidate Collection.

    def is_owner(self, user):
        return (user.id == self.author_id)

class CollectionAddon(amo.models.ModelBase):
    addon = models.ForeignKey(Addon)
    collection = models.ForeignKey(Collection)
    added = models.DateTimeField(auto_now_add=True)
    # category (deprecated: for "Fashion Your Firefox")
    comments = LinkifiedField(null=True)
    downloads = models.PositiveIntegerField(default=0)
    user = models.ForeignKey(UserProfile, null=True)

    ordering = models.PositiveIntegerField(default=0,
        help_text='Add-ons are displayed in ascending order '
                  'based on this field.')

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'addons_collections'
        unique_together = (('addon', 'collection'),)

class CollectionAddonRecommendation(models.Model):
    collection = models.ForeignKey(Collection, null=True)
    addon = models.ForeignKey(Addon, null=True)
    score = models.FloatField(blank=True)

    class Meta:
        db_table = 'collection_addon_recommendations'

class CollectionCategory(amo.models.ModelBase):
    collection = models.ForeignKey(Collection)
    category = models.ForeignKey(AddonCategory)

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collection_categories'

class CollectionFeature(amo.models.ModelBase):
    title = TranslatedField()
    tagline = TranslatedField()

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collection_features'

class CollectionPromo(amo.models.ModelBase):
    collection = models.ForeignKey(Collection, null=True)
    locale = models.CharField(max_length=10, null=True)
    collection_feature = models.ForeignKey(CollectionFeature)

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collection_promos'
        unique_together = ('collection', 'locale', 'collection_feature')

    @staticmethod
    def transformer(promos):
        if not promos:
            return

        promo_dict = dict((p.id, p) for p in promos)
        q = (Collection.objects.no_cache()
             .filter(collectionpromo__in=promos)
             .extra(select={'promo_id': 'collection_promos.id'}))

        for promo_id, collection in (sorted_groupby(q, 'promo_id')):
            promo_dict[promo_id].collection = collection.next()

class CollectionRecommendation(amo.models.ModelBase):
    collection = models.ForeignKey(Collection, null=True,
            related_name="collection_one")
    other_collection = models.ForeignKey(Collection, null=True,
            related_name="collection_two")
    score = models.FloatField(blank=True)

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collection_recommendations'

class CollectionSummary(models.Model):
    """This materialized view maintains a indexed summary of the text data
    in a collection to make search faster.

    `id` commented out due to django complaining because id is not actually a
    primary key here.  This is a candidate for deletion once remora is gone;
    bug 540638.  As soon as this info is in sphinx, this is method is
    deprecated.
    """
    #id = models.PositiveIntegerField()
    locale = models.CharField(max_length=10, blank=True)
    name = models.TextField()
    description = models.TextField()

    class Meta:
        db_table = 'collection_search_summary'

class CollectionSubscription(amo.models.ModelBase):
    collection = models.ForeignKey(Collection, related_name='subscriptions')
    user = models.ForeignKey(UserProfile)

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collection_subscriptions'

class CollectionUser(models.Model):
    collection = models.ForeignKey(Collection)
    user = models.ForeignKey(UserProfile)
    role = models.SmallIntegerField(default=1,
            choices=amo.COLLECTION_AUTHOR_CHOICES.items())

    class Meta:
        db_table = 'collections_users'

class CollectionVote(models.Model):
    collection = models.ForeignKey(Collection, related_name='votes')
    user = models.ForeignKey(UserProfile, related_name='votes')
    vote = models.SmallIntegerField(default=0)
    created = models.DateTimeField(null=True, auto_now_add=True)

    class Meta:
        db_table = 'collections_votes'

    @staticmethod
    def post_save_or_delete(sender, instance, **kwargs):
        from . import tasks
        tasks.collection_votes(instance.collection_id, using='default')

models.signals.post_save.connect(CollectionVote.post_save_or_delete,
                                 sender=CollectionVote)
models.signals.post_delete.connect(CollectionVote.post_save_or_delete,
                                 sender=CollectionVote)

class SyncedCollection(Collection):
    """Collection that automatically sets type=SYNC."""

    class Meta:
        proxy = True

    def save(self, **kw):
        self.type = amo.COLLECTION_SYNCHRONIZED
        return super(SyncedCollection, self).save(**kw)

class RecommendedCollection(Collection):

    class Meta:
        proxy = True

    def save(self, **kw):
        self.type = amo.COLLECTION_RECOMMENDED
        return super(RecommendedCollection, self).save(**kw)

    @classmethod
    def build_recs(cls, addon_ids):
        """Get the top ranking add-ons according to recommendation scores."""
        scores = AddonRecommendation.scores(addon_ids)
        d = collections.defaultdict(int)
        for others in scores.values():
            for addon, score in others.items():
                d[addon] += score
        addons = [(score, addon) for addon, score in d.items()]
        return [addon for _, addon in sorted(addons)]

class CollectionToken(amo.models.ModelBase):
    """Links a Collection to an anonymous token."""
    token = models.CharField(max_length=255, unique=True)
    collection = models.ForeignKey(Collection, related_name='token_set')

    objects = models.Manager()

    class Meta:
        db_table = 'collections_tokens'
<commit_msg>transformer for Collection.author<commit_after>import collections
from datetime import datetime
import hashlib
import time
import uuid

from django.conf import settings
from django.core.cache import cache
from django.db import models, connection

from MySQLdb import IntegrityError

import amo
import amo.models
from amo.utils import sorted_groupby
from amo.urlresolvers import reverse
from addons.models import Addon, AddonCategory, AddonRecommendation
from applications.models import Application
from users.models import UserProfile
from translations.fields import TranslatedField, LinkifiedField

class TopTags(object):
    """Descriptor to manage a collection's top tags in cache."""

    def key(self, obj):
        return '%s:top-tags:%s' % (settings.CACHE_PREFIX, obj.id)

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        return cache.get(self.key(obj), [])

    def __set__(self, obj, value):
        two_days = 60 * 60 * 24 * 2
        cache.set(self.key(obj), value, two_days)

class CollectionManager(amo.models.ManagerBase):

    def get_query_set(self):
        qs = super(CollectionManager, self).get_query_set()
        return qs.transform(Collection.transformer)

    def manual(self):
        """Only hand-crafted, favorites, and featured collections should appear
        in this filter."""
        types = (amo.COLLECTION_NORMAL, amo.COLLECTION_FAVORITE,
                 amo.COLLECTION_FEATURED, )

        return self.filter(type__in=types)

    def listed(self):
        """Return public collections only."""
        return self.filter(listed=True)

class Collection(amo.models.ModelBase):

    TYPE_CHOICES = amo.COLLECTION_CHOICES.items()
    RECOMMENDATION_LIMIT = 15  # Maximum number of add-ons to recommend.

    uuid = models.CharField(max_length=36, blank=True, unique=True)
    name = TranslatedField()
    nickname = models.CharField(max_length=30, blank=True, unique=True,
                                null=True)
    slug = models.CharField(max_length=30, blank=True, null=True)

    description = LinkifiedField()
    default_locale = models.CharField(max_length=10, default='en-US',
                                      db_column='defaultlocale')
    type = models.PositiveIntegerField(db_column='collection_type',
            choices=TYPE_CHOICES, default=0)
    icontype = models.CharField(max_length=25, blank=True)

    listed = models.BooleanField(
        default=True, help_text='Collections are either listed or private.')

    subscribers = models.PositiveIntegerField(default=0)
    downloads = models.PositiveIntegerField(default=0)
    weekly_subscribers = models.PositiveIntegerField(default=0)
    monthly_subscribers = models.PositiveIntegerField(default=0)
    application = models.ForeignKey(Application, null=True)
    addon_count = models.PositiveIntegerField(default=0,
                                              db_column='addonCount')

    upvotes = models.PositiveIntegerField(default=0)
    downvotes = models.PositiveIntegerField(default=0)
    rating = models.FloatField(default=0)
    all_personas = models.BooleanField(default=False,
        help_text='Does this collection only contain personas?')

    addons = models.ManyToManyField(Addon, through='CollectionAddon',
                                    related_name='collections')
    author = models.ForeignKey(UserProfile, null=True,
                               related_name='collections')
    users = models.ManyToManyField(UserProfile, through='CollectionUser',
                                  related_name='collections_publishable')

    addon_index = models.CharField(max_length=40, null=True, db_index=True,
        help_text='Custom index for the add-ons in this collection')
    recommended_collection = models.ForeignKey('self', null=True)

    objects = CollectionManager()

    top_tags = TopTags()

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collections'

    def __unicode__(self):
        return u'%s (%s)' % (self.name, self.addon_count)

    def save(self, **kw):
        if not self.uuid:
            self.uuid = unicode(uuid.uuid4())
        if not self.slug:
            self.slug = self.uuid[:30]

        # Maintain our index of add-on ids.
        if self.id:
            ids = self.addons.values_list('id', flat=True)
            self.addon_index = self.make_index(ids)

        super(Collection, self).save(**kw)

    def get_url_path(self):
        if settings.NEW_COLLECTIONS:
            return reverse('collections.detail',
                            args=[self.author_nickname, self.slug])
        else:
            return '/collection/%s' % self.url_slug

    def upvote_url(self):
        return reverse('collections.vote',
                       args=[self.author_nickname, self.slug, 'up'])

    def downvote_url(self):
        return reverse('collections.vote',
                       args=[self.author_nickname, self.slug, 'down'])

    @property
    def author_nickname(self):
        return self.author.nickname if self.author else 'anonymous'

    @classmethod
    def get_fallback(cls):
        return cls._meta.get_field('default_locale')

    @classmethod
    def make_index(cls, addon_ids):
        ids = ':'.join(map(str, sorted(addon_ids)))
        return hashlib.md5(ids).hexdigest()

    @property
    def url_slug(self):
        """uuid or nickname if chosen"""
        return self.nickname or self.uuid

    @property
    def icon_url(self):
        modified = int(time.mktime(self.modified.timetuple()))
        if self.icontype:
            return settings.COLLECTION_ICON_URL % (self.id, modified)
        else:
            return settings.MEDIA_URL + 'img/amo2009/icons/collection.png'

    def get_recommendations(self):
        """Get a collection of recommended add-ons for this collection."""
        if self.recommended_collection:
            return self.recommended_collection
        else:
            r = RecommendedCollection.objects.create(listed=False)
            addons = list(self.addons.values_list('id', flat=True))
            recs = RecommendedCollection.build_recs(addons)
            r.set_addons(recs[:Collection.RECOMMENDATION_LIMIT])
            self.recommended_collection = r
            self.save()
            return r

    def set_addons(self, addon_ids):
        """Replace the current add-ons with a new list of add-on ids."""
        order = dict((a, idx) for idx, a in enumerate(addon_ids))

        # Partition addon_ids into add/update/remove buckets.
        existing = set(self.addons.values_list('id', flat=True))
        add, update = [], []
        for addon in addon_ids:
            bucket = update if addon in existing else add
            bucket.append((addon, order[addon]))
        remove = existing.difference(addon_ids)

        cursor = connection.cursor()
        now = datetime.now()
        if remove:
            cursor.execute("DELETE FROM addons_collections "
                           "WHERE collection_id=%s AND addon_id IN (%s)" %
                           (self.id, ','.join(map(str, remove))))
        if add:
            insert = '(%s, %s, %s, NOW(), NOW(), NOW(), 0)'
            values = [insert % (a, self.id, idx) for a, idx in add]
            cursor.execute("""
                INSERT INTO addons_collections
                    (addon_id, collection_id, ordering, added, created,
                     modified, downloads)
                VALUES %s""" % ','.join(values))
        for addon, ordering in update:
            (CollectionAddon.objects.filter(collection=self.id, addon=addon)
             .update(ordering=ordering, modified=now))
        self.save()

    def is_subscribed(self, user):
        """Determines if the user is subscribed to this collection."""
        return self.subscriptions.filter(user=user).exists()

    # TODO(davedash): use this when we're on 1.3:
    # http://code.djangoproject.com/ticket/13240
    def add_addon(self, addon):
        "Adds an addon to the collection."
        ca = CollectionAddon()
        ca.addon = addon
        ca.collection = self
        try:
            ca.save()
        except IntegrityError:
            pass
        self.save()  # To invalidate Collection.

    def remove_addon(self, addon):
        CollectionAddon.objects.filter(addon=addon, collection=self).delete()
        self.save()  # To invalidate Collection.

    def is_owner(self, user):
        return (user.id == self.author_id)

    @staticmethod
    def transformer(collections):
        if not collections:
            return
        author_ids = set(c.author_id for c in collections)
        authors = dict((u.id, u) for u in
                       UserProfile.objects.filter(id__in=author_ids))
        for c in collections:
            c.author = authors.get(c.author_id)

class CollectionAddon(amo.models.ModelBase):
    addon = models.ForeignKey(Addon)
    collection = models.ForeignKey(Collection)
    added = models.DateTimeField(auto_now_add=True)
    # category (deprecated: for "Fashion Your Firefox")
    comments = LinkifiedField(null=True)
    downloads = models.PositiveIntegerField(default=0)
    user = models.ForeignKey(UserProfile, null=True)

    ordering = models.PositiveIntegerField(default=0,
        help_text='Add-ons are displayed in ascending order '
                  'based on this field.')

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'addons_collections'
        unique_together = (('addon', 'collection'),)

class CollectionAddonRecommendation(models.Model):
    collection = models.ForeignKey(Collection, null=True)
    addon = models.ForeignKey(Addon, null=True)
    score = models.FloatField(blank=True)

    class Meta:
        db_table = 'collection_addon_recommendations'

class CollectionCategory(amo.models.ModelBase):
    collection = models.ForeignKey(Collection)
    category = models.ForeignKey(AddonCategory)

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collection_categories'

class CollectionFeature(amo.models.ModelBase):
    title = TranslatedField()
    tagline = TranslatedField()

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collection_features'

class CollectionPromo(amo.models.ModelBase):
    collection = models.ForeignKey(Collection, null=True)
    locale = models.CharField(max_length=10, null=True)
    collection_feature = models.ForeignKey(CollectionFeature)

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collection_promos'
        unique_together = ('collection', 'locale', 'collection_feature')

    @staticmethod
    def transformer(promos):
        if not promos:
            return

        promo_dict = dict((p.id, p) for p in promos)
        q = (Collection.objects.no_cache()
             .filter(collectionpromo__in=promos)
             .extra(select={'promo_id': 'collection_promos.id'}))

        for promo_id, collection in (sorted_groupby(q, 'promo_id')):
            promo_dict[promo_id].collection = collection.next()

class CollectionRecommendation(amo.models.ModelBase):
    collection = models.ForeignKey(Collection, null=True,
            related_name="collection_one")
    other_collection = models.ForeignKey(Collection, null=True,
            related_name="collection_two")
    score = models.FloatField(blank=True)

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collection_recommendations'

class CollectionSummary(models.Model):
    """This materialized view maintains a indexed summary of the text data
    in a collection to make search faster.

    `id` commented out due to django complaining because id is not actually a
    primary key here.  This is a candidate for deletion once remora is gone;
    bug 540638.  As soon as this info is in sphinx, this is method is
    deprecated.
    """
    #id = models.PositiveIntegerField()
    locale = models.CharField(max_length=10, blank=True)
    name = models.TextField()
    description = models.TextField()

    class Meta:
        db_table = 'collection_search_summary'

class CollectionSubscription(amo.models.ModelBase):
    collection = models.ForeignKey(Collection, related_name='subscriptions')
    user = models.ForeignKey(UserProfile)

    class Meta(amo.models.ModelBase.Meta):
        db_table = 'collection_subscriptions'

class CollectionUser(models.Model):
    collection = models.ForeignKey(Collection)
    user = models.ForeignKey(UserProfile)
    role = models.SmallIntegerField(default=1,
            choices=amo.COLLECTION_AUTHOR_CHOICES.items())

    class Meta:
        db_table = 'collections_users'

class CollectionVote(models.Model):
    collection = models.ForeignKey(Collection, related_name='votes')
    user = models.ForeignKey(UserProfile, related_name='votes')
    vote = models.SmallIntegerField(default=0)
    created = models.DateTimeField(null=True, auto_now_add=True)

    class Meta:
        db_table = 'collections_votes'

    @staticmethod
    def post_save_or_delete(sender, instance, **kwargs):
        from . import tasks
        tasks.collection_votes(instance.collection_id, using='default')

models.signals.post_save.connect(CollectionVote.post_save_or_delete,
                                 sender=CollectionVote)
models.signals.post_delete.connect(CollectionVote.post_save_or_delete,
                                 sender=CollectionVote)

class SyncedCollection(Collection):
    """Collection that automatically sets type=SYNC."""

    class Meta:
        proxy = True

    def save(self, **kw):
        self.type = amo.COLLECTION_SYNCHRONIZED
        return super(SyncedCollection, self).save(**kw)

class RecommendedCollection(Collection):

    class Meta:
        proxy = True

    def save(self, **kw):
        self.type = amo.COLLECTION_RECOMMENDED
        return super(RecommendedCollection, self).save(**kw)

    @classmethod
    def build_recs(cls, addon_ids):
        """Get the top ranking add-ons according to recommendation scores."""
        scores = AddonRecommendation.scores(addon_ids)
        d = collections.defaultdict(int)
        for others in scores.values():
            for addon, score in others.items():
                d[addon] += score
        addons = [(score, addon) for addon, score in d.items()]
        return [addon for _, addon in sorted(addons)]

class CollectionToken(amo.models.ModelBase):
    """Links a Collection to an anonymous token."""
    token = models.CharField(max_length=255, unique=True)
    collection = models.ForeignKey(Collection, related_name='token_set')

    objects = models.Manager()

    class Meta:
        db_table = 'collections_tokens'
<|endoftext|>


<commit_before>import os

import numpy as np
from numpy.testing import assert_array_almost_equal
from nose.tools import assert_equal, assert_list_equal, assert_raises

from cmt.component.grid import GridMixIn

class Port(object):
    def get_component_name(self):
        return 'test-component'

    def get_input_item_count(self):
        return 1

    def get_input_item_list(self):
        return ['invar']

    def get_output_item_count(self):
        return 1

    def get_output_item_list(self):
        return ['outvar']

def test_raster_1d():
    class RasterPort(Port):
        def get_grid_shape(self, name):
            return (3, )

        def get_grid_spacing(self, name):
            return (2., )

        def get_grid_origin(self, name):
            return (3., )

    class Component(GridMixIn):
        def __init__(self):
            self._port = RasterPort()
            super(Component, self).__init__()

    c = Component()
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([ 3.,  5.,  7.]))

def test_raster_2d():
    class RasterPort(Port):
        def get_grid_shape(self, name):
            return (2, 3)

        def get_grid_spacing(self, name):
            return (2., 1.)

        def get_grid_origin(self, name):
            return (0., 0.)

    class Component(GridMixIn):
        def __init__(self):
            self._port = RasterPort()
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.name, 'test-component')
    assert_list_equal(c.input_items, ['invar'])
    assert_list_equal(c.output_items, ['outvar'])
    assert_equal(c.get_grid_type('invar'), 'RASTER')
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([[ 0.,  1.,  2.], [ 0.,  1.,  2.]]))
    assert_array_almost_equal(c.get_y('invar'),
                              np.array([[ 0.,  0.,  0.], [ 2.,  2.,  2.]]))
    assert_array_almost_equal(c.get_connectivity('invar'),
                              np.array([0, 1, 4, 3, 1, 2, 5, 4]))
    assert_array_almost_equal(c.get_offset('invar'),
                              np.array([4, 8]))

def test_raster_3d():
    class RasterPort(Port):
        def get_grid_shape(self, name):
            return (2, 2, 3)

        def get_grid_spacing(self, name):
            return (1., 2., 1.)

        def get_grid_origin(self, name):
            return (0., 0., 0.)

    class Component(GridMixIn):
        def __init__(self):
            self._port = RasterPort()
            super(Component, self).__init__()

    c = Component()
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([[[ 0.,  1.,  2.], [ 0.,  1.,  2.]],
                                        [[ 0.,  1.,  2.], [ 0.,  1.,  2.]]]))
    assert_array_almost_equal(c.get_y('invar'),
                              np.array([[[ 0.,  0.,  0.], [ 2.,  2.,  2.]],
                                        [[ 0.,  0.,  0.], [ 2.,  2.,  2.]]]))
    assert_array_almost_equal(c.get_z('invar'),
                              np.array([[[ 0.,  0.,  0.], [ 0.,  0.,  0.]],
                                        [[ 1.,  1.,  1.], [ 1.,  1.,  1.]]]))

def test_rectilinear():
    class RectilinearPort(Port):
        def get_grid_shape(self, name):
            return (2, 3)

        def get_grid_x(self, name):
            return (0., 3., 4)

        def get_grid_y(self, name):
            return (2., 7.)

    class Component(GridMixIn):
        def __init__(self):
            self._port = RectilinearPort()
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.name, 'test-component')
    assert_list_equal(c.input_items, ['invar'])
    assert_list_equal(c.output_items, ['outvar'])
    assert_equal(c.get_grid_type('invar'), 'RECTILINEAR')
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([[ 0.,  3.,  4.], [ 0.,  3.,  4.]]))
    assert_array_almost_equal(c.get_y('invar'),
                              np.array([[ 2.,  2.,  2.], [ 7.,  7.,  7.]]))

def test_structured():
    class StructuredPort(Port):
        def get_grid_shape(self, name):
            return (2, 3)

        def get_grid_x(self, name):
            return np.array([0., 1., 2., 0., 1., 2.])

        def get_grid_y(self, name):
            return np.array([0., 1., 2., 1., 2., 3.])

    class Component(GridMixIn):
        def __init__(self):
            self._port = StructuredPort()
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.name, 'test-component')
    assert_list_equal(c.input_items, ['invar'])
    assert_list_equal(c.output_items, ['outvar'])
    assert_equal(c.get_grid_type('invar'), 'STRUCTURED')
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([0., 1., 2., 0., 1., 2.]))
    assert_array_almost_equal(c.get_y('invar'),
                              np.array([0., 1., 2., 1., 2., 3.]))

def test_unstructured():
    class UnstructuredPort(Port):
        def get_grid_x(self, name):
            return np.array([0., 1., 0., 1., 2.])

        def get_grid_y(self, name):
            return np.array([0., 0., 1., 1., 0.])

        def get_grid_connectivity(self, name):
            return np.array([0, 1, 3, 2, 4, 3, 1])

        def get_grid_offset(self, name):
            return np.array([4, 7])

    class Component(GridMixIn):
        def __init__(self):
            self._port = UnstructuredPort()
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.name, 'test-component')
    assert_list_equal(c.input_items, ['invar'])
    assert_list_equal(c.output_items, ['outvar'])
    assert_equal(c.get_grid_type('invar'), 'UNSTRUCTURED')
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([0., 1., 0., 1., 2.]))
    assert_array_almost_equal(c.get_y('invar'),
                              np.array([0., 0., 1., 1., 0.]))

def test_get_grid_shape_is_none():
    class UnstructuredPort(Port):
        def get_grid_shape(self, name):
            return None

        def get_grid_x(self, name):
            return np.array([0., 1., 2.])

    class Component(GridMixIn):
        def __init__(self):
            self._port = UnstructuredPort()
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.get_grid_type('invar'), 'UNSTRUCTURED')

def test_get_grid_shape_raises():
    class UnstructuredPort(Port):
        def get_grid_shape(self, name):
            raise NotImplementedError('get_grid_shape')

        def get_grid_x(self, name):
            return np.array([0., 1., 2.])

    class Component(GridMixIn):
        def __init__(self):
            self._port = UnstructuredPort()
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.get_grid_type('invar'), 'UNSTRUCTURED')

def test_structured_1d():
    class RectilinearPort(Port):
        def get_grid_shape(self, name):
            return (2, 3)

        def get_grid_x(self, name):
            return np.array([0., 1., 2.])

        def get_grid_y(self, name):
            return np.array([0., 1.])

        def get_grid_z(self, name):
            raise NotImplementedError('get_grid_z')

    class Component(GridMixIn):
        def __init__(self):
            self._port = RectilinearPort()
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.get_grid_type('invar'), 'RECTILINEAR')
    assert_equal(c.get_grid_type('outvar'), 'RECTILINEAR')
    with assert_raises(IndexError):
        c.get_z('invar')
<commit_msg>Test exchange item getters.<commit_after>import os

import numpy as np
from numpy.testing import assert_array_almost_equal
from nose.tools import assert_equal, assert_list_equal, assert_raises

from cmt.component.grid import GridMixIn

class Port(object):
    def __init__(self, name, uses=None, provides=None):
        self._name = name
        self._uses = uses or []
        self._provides = provides or []

    def get_component_name(self):
        return self._name

    def get_input_item_count(self):
        return len(self._uses)

    def get_input_item_list(self):
        return self._uses

    def get_output_item_count(self):
        return len(self._provides)

    def get_output_item_list(self):
        return self._provides

def test_exchange_items():
    class Component(GridMixIn):
        def __init__(self):
            self._port = Port('test', uses=['invar'], provides=['outvar'])
            super(Component, self).__init__()

    c = Component()
    assert_list_equal(c.input_items, ['invar'])
    assert_list_equal(c.output_items, ['outvar'])

def test_no_exchange_items():
    class Component(GridMixIn):
        def __init__(self):
            self._port = Port('test')
            super(Component, self).__init__()

    c = Component()
    assert_list_equal(c.input_items, [])
    assert_list_equal(c.output_items, [])

def test_raster_1d():
    class RasterPort(Port):
        def get_grid_shape(self, name):
            return (3, )

        def get_grid_spacing(self, name):
            return (2., )

        def get_grid_origin(self, name):
            return (3., )

    class Component(GridMixIn):
        def __init__(self):
            self._port = RasterPort('test', uses=['invar'])
            super(Component, self).__init__()

    c = Component()
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([ 3.,  5.,  7.]))

def test_raster_2d():
    class RasterPort(Port):
        def get_grid_shape(self, name):
            return (2, 3)

        def get_grid_spacing(self, name):
            return (2., 1.)

        def get_grid_origin(self, name):
            return (0., 0.)

    class Component(GridMixIn):
        def __init__(self):
            self._port = RasterPort('test-2d', uses=['invar'],
                                    provides=['outvar'])
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.name, 'test-2d')
    assert_equal(c.get_grid_type('invar'), 'RASTER')
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([[ 0.,  1.,  2.], [ 0.,  1.,  2.]]))
    assert_array_almost_equal(c.get_y('invar'),
                              np.array([[ 0.,  0.,  0.], [ 2.,  2.,  2.]]))
    assert_array_almost_equal(c.get_connectivity('invar'),
                              np.array([0, 1, 4, 3, 1, 2, 5, 4]))
    assert_array_almost_equal(c.get_offset('invar'),
                              np.array([4, 8]))

def test_raster_3d():
    class RasterPort(Port):
        def get_grid_shape(self, name):
            return (2, 2, 3)

        def get_grid_spacing(self, name):
            return (1., 2., 1.)

        def get_grid_origin(self, name):
            return (0., 0., 0.)

    class Component(GridMixIn):
        def __init__(self):
            self._port = RasterPort('test-3d', uses=['invar'])
            super(Component, self).__init__()

    c = Component()
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([[[ 0.,  1.,  2.], [ 0.,  1.,  2.]],
                                        [[ 0.,  1.,  2.], [ 0.,  1.,  2.]]]))
    assert_array_almost_equal(c.get_y('invar'),
                              np.array([[[ 0.,  0.,  0.], [ 2.,  2.,  2.]],
                                        [[ 0.,  0.,  0.], [ 2.,  2.,  2.]]]))
    assert_array_almost_equal(c.get_z('invar'),
                              np.array([[[ 0.,  0.,  0.], [ 0.,  0.,  0.]],
                                        [[ 1.,  1.,  1.], [ 1.,  1.,  1.]]]))

def test_rectilinear():
    class RectilinearPort(Port):
        def get_grid_shape(self, name):
            return (2, 3)

        def get_grid_x(self, name):
            return (0., 3., 4)

        def get_grid_y(self, name):
            return (2., 7.)

    class Component(GridMixIn):
        def __init__(self):
            self._port = RectilinearPort('test', uses=['invar'])
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.get_grid_type('invar'), 'RECTILINEAR')
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([[ 0.,  3.,  4.], [ 0.,  3.,  4.]]))
    assert_array_almost_equal(c.get_y('invar'),
                              np.array([[ 2.,  2.,  2.], [ 7.,  7.,  7.]]))

def test_structured():
    class StructuredPort(Port):
        def get_grid_shape(self, name):
            return (2, 3)

        def get_grid_x(self, name):
            return np.array([0., 1., 2., 0., 1., 2.])

        def get_grid_y(self, name):
            return np.array([0., 1., 2., 1., 2., 3.])

    class Component(GridMixIn):
        def __init__(self):
            self._port = StructuredPort('test', uses=['invar'])
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.get_grid_type('invar'), 'STRUCTURED')
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([0., 1., 2., 0., 1., 2.]))
    assert_array_almost_equal(c.get_y('invar'),
                              np.array([0., 1., 2., 1., 2., 3.]))

def test_unstructured():
    class UnstructuredPort(Port):
        def get_grid_x(self, name):
            return np.array([0., 1., 0., 1., 2.])

        def get_grid_y(self, name):
            return np.array([0., 0., 1., 1., 0.])

        def get_grid_connectivity(self, name):
            return np.array([0, 1, 3, 2, 4, 3, 1])

        def get_grid_offset(self, name):
            return np.array([4, 7])

    class Component(GridMixIn):
        def __init__(self):
            self._port = UnstructuredPort('test', uses=['invar'])
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.get_grid_type('invar'), 'UNSTRUCTURED')
    assert_array_almost_equal(c.get_x('invar'),
                              np.array([0., 1., 0., 1., 2.]))
    assert_array_almost_equal(c.get_y('invar'),
                              np.array([0., 0., 1., 1., 0.]))

def test_get_grid_shape_is_none():
    class UnstructuredPort(Port):
        def get_grid_shape(self, name):
            return None

        def get_grid_x(self, name):
            return np.array([0., 1., 2.])

    class Component(GridMixIn):
        def __init__(self):
            self._port = UnstructuredPort('test', uses=['invar'])
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.get_grid_type('invar'), 'UNSTRUCTURED')

def test_get_grid_shape_raises():
    class UnstructuredPort(Port):
        def get_grid_shape(self, name):
            raise NotImplementedError('get_grid_shape')

        def get_grid_x(self, name):
            return np.array([0., 1., 2.])

    class Component(GridMixIn):
        def __init__(self):
            self._port = UnstructuredPort('test', uses=['invar'])
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.get_grid_type('invar'), 'UNSTRUCTURED')

def test_structured_1d():
    class RectilinearPort(Port):
        def get_grid_shape(self, name):
            return (2, 3)

        def get_grid_x(self, name):
            return np.array([0., 1., 2.])

        def get_grid_y(self, name):
            #return np.array([0., 1.])
            raise NotImplementedError('get_grid_y')

        def get_grid_z(self, name):
            raise NotImplementedError('get_grid_z')

    class Component(GridMixIn):
        def __init__(self):
            self._port = RectilinearPort('test', uses=['invar'])
            super(Component, self).__init__()

    c = Component()
    assert_equal(c.get_grid_type('invar'), 'RECTILINEAR')
    with assert_raises(IndexError):
        c.get_z('invar')
<|endoftext|>