<commit_before>"""
Useful form fields for use with SQLAlchemy ORM.
"""
import operator
import warnings

from wtforms import widgets
from wtforms.fields import Field
from wtforms.validators import ValidationError

try:
    from sqlalchemy.orm.util import identity_key
    has_identity_key = True
except ImportError:
    has_identity_key = False

__all__ = (
    'QuerySelectField', 'ModelSelectField',
)

class QuerySelectField(Field):
    """
    Will display a select drop-down field to choose between ORM results in a
    sqlalchemy `Query`.  The `data` property actually will store/keep an ORM
    model instance, not the ID. Submitting a choice which is not in the query
    will result in a validation error.

    This field only works for queries on models whose primary key column(s)
    have a consistent string representation. This means it mostly only works
    for those composed of string, unicode, and integer types. For the most
    part, the primary keys will be auto-detected from the model, alternately
    pass a one-argument callable to `get_pk` which can return a unique
    comparable key.

    The `query` property on the field can be set from within a view to assign
    a query per-instance to the field. If the property is not set, the
    `query_factory` callable passed to the field constructor will be called to
    obtain a query.

    Specifying `label_attr` in the constructor will use that property of the
    model instance for display in the list, else the model object's `__str__`
    or `__unicode__` will be used.

    If `allow_blank` is set to `True`, then a blank choice will be added to the
    top of the list. Selecting this choice will result in the `data` property
    being `None`. The label for this blank choice can be set by specifying the
    `blank_text` parameter.

    The `pk_attr` parameter is deprecated and will likely be removed in a
    future release.
    """
    widget = widgets.Select()

    def __init__(self, label=u'', validators=None, query_factory=None,
                 get_pk=None, label_attr='', allow_blank=False, blank_text=u'',
                 pk_attr=None, **kwargs):
        super(QuerySelectField, self).__init__(label, validators, **kwargs)
        self.query_factory = query_factory
        if pk_attr is not None:
            warnings.warn('pk_attr= will be removed in WTForms 1.0, use get_pk= instead.', DeprecationWarning)
            self.get_pk = operator.attrgetter(pk_attr)
        elif get_pk is None:
            if not has_identity_key:
                raise Exception('The sqlalchemy identity_key function could not be imported.')
            self.get_pk = get_pk_from_identity
        else:
            self.get_pk = get_pk

        self.label_attr = label_attr
        self.allow_blank = allow_blank
        self.blank_text = blank_text
        self.query = None
        self._object_list = None

    def _get_data(self):
        if self._formdata is not None:
            for pk, obj in self._get_object_list():
                if pk == self._formdata:
                    self._set_data(obj)
                    break
        return self._data

    def _set_data(self, data):
        self._data = data
        self._formdata = None

    data = property(_get_data, _set_data)

    def _get_object_list(self):
        if self._object_list is None:
            query = self.query or self.query_factory()
            get_pk = self.get_pk
            self._object_list = list((unicode(get_pk(obj)), obj) for obj in query)
        return self._object_list

    def iter_choices(self):
        if self.allow_blank:
            yield (u'__None', self.blank_text, self.data is None)

        for pk, obj in self._get_object_list():
            label = self.label_attr and getattr(obj, self.label_attr) or obj
            yield (pk, label, obj == self.data)

    def process_formdata(self, valuelist):
        if valuelist:
            if self.allow_blank and valuelist[0] == u'__None':
                self.data = None
            else:
                self._data = None
                self._formdata = valuelist[0]

    def pre_validate(self, form):
        if not self.allow_blank or self.data is not None:
            for pk, obj in self._get_object_list():
                if self.data == obj:
                    break
            else:
                raise ValidationError('Not a valid choice')

class QueryMultipleSelectField(QuerySelectField):
    """
    Very similar to QuerySelectField with the difference that this will
    display a multiple select. The data property will hold a list with ORM
    model instances and will be an empty list when no value is selected.

    If any of the items in the data list or submitted form data cannot be
    found in the query, this will result in a validation error.
    """
    widget = widgets.Select(multiple=True)

    def __init__(self, label=u'', validators=None, query_factory=None, get_pk=None,
                 label_attr='', default=None, **kwargs):
        if default is None:
            default = []
        super(QueryMultipleSelectField, self).__init__(label, validators, query_factory=query_factory, get_pk=get_pk, label_attr=label_attr, default=default, **kwargs)
        self._invalid_formdata = False

    def _get_data(self):
        formdata = self._formdata
        if formdata is not None:
            data = []
            for pk, obj in self._get_object_list():
                if not formdata:
                    break
                elif pk in formdata:
                    formdata.remove(pk)
                    data.append(obj)
            if formdata:
                self._invalid_formdata = True
            self._set_data(data)
        return self._data

    def _set_data(self, data):
        self._data = data
        self._formdata = None

    data = property(_get_data, _set_data)

    def iter_choices(self):
        for pk, obj in self._get_object_list():
            label = self.label_attr and getattr(obj, self.label_attr) or obj
            yield (pk, label, obj in self.data)

    def process_formdata(self, valuelist):
        self._formdata = set(valuelist)

    def pre_validate(self, form):
        if self._invalid_formdata:
            raise ValidationError('Not a valid choice')
        elif self.data:
            obj_list = list(x[1] for x in self._get_object_list())
            for v in self.data:
                if v not in obj_list:
                    raise ValidationError('Not a valid choice')

class ModelSelectField(QuerySelectField):
    """
    Similar to QuerySelectField, only for model classes.

    Using this field is only meaningful when using scoped sessions in
    SQLAlchemy, because otherwise model instances do not know how to make
    queries of themselves. This field is simply a convenience for using
    `Model.query` as the factory for QuerySelectField.
    """
    def __init__(self, label=u'', validators=None, model=None, get_pk=None,
                 label_attr='', allow_blank=False, blank_text=u'', **kwargs):
        assert model is not None, "Must specify a model."
        query_factory = lambda: model.query
        super(ModelSelectField, self).__init__(label, validators, query_factory=query_factory, get_pk=get_pk, label_attr=label_attr, allow_blank=allow_blank, blank_text=blank_text, **kwargs)

def get_pk_from_identity(obj):
    cls, key = identity_key(instance=obj)
    return u':'.join(unicode(x) for x in key)
<commit_msg>Add the __iter__ method from SelectField so that it is possible to modify the QuerySelectField in a list of checkbox fields.<commit_after>"""
Useful form fields for use with SQLAlchemy ORM.
"""
import operator
import warnings

from wtforms import widgets
from wtforms.fields import Field
from wtforms.validators import ValidationError

try:
    from sqlalchemy.orm.util import identity_key
    has_identity_key = True
except ImportError:
    has_identity_key = False

__all__ = (
    'QuerySelectField', 'ModelSelectField',
)

class QuerySelectField(Field):
    """
    Will display a select drop-down field to choose between ORM results in a
    sqlalchemy `Query`.  The `data` property actually will store/keep an ORM
    model instance, not the ID. Submitting a choice which is not in the query
    will result in a validation error.

    This field only works for queries on models whose primary key column(s)
    have a consistent string representation. This means it mostly only works
    for those composed of string, unicode, and integer types. For the most
    part, the primary keys will be auto-detected from the model, alternately
    pass a one-argument callable to `get_pk` which can return a unique
    comparable key.

    The `query` property on the field can be set from within a view to assign
    a query per-instance to the field. If the property is not set, the
    `query_factory` callable passed to the field constructor will be called to
    obtain a query.

    Specifying `label_attr` in the constructor will use that property of the
    model instance for display in the list, else the model object's `__str__`
    or `__unicode__` will be used.

    If `allow_blank` is set to `True`, then a blank choice will be added to the
    top of the list. Selecting this choice will result in the `data` property
    being `None`. The label for this blank choice can be set by specifying the
    `blank_text` parameter.

    The `pk_attr` parameter is deprecated and will likely be removed in a
    future release.
    """
    widget = widgets.Select()

    def __init__(self, label=u'', validators=None, query_factory=None,
                 get_pk=None, label_attr='', allow_blank=False, blank_text=u'',
                 pk_attr=None, **kwargs):
        super(QuerySelectField, self).__init__(label, validators, **kwargs)
        self.query_factory = query_factory
        if pk_attr is not None:
            warnings.warn('pk_attr= will be removed in WTForms 1.0, use get_pk= instead.', DeprecationWarning)
            self.get_pk = operator.attrgetter(pk_attr)
        elif get_pk is None:
            if not has_identity_key:
                raise Exception('The sqlalchemy identity_key function could not be imported.')
            self.get_pk = get_pk_from_identity
        else:
            self.get_pk = get_pk

        self.label_attr = label_attr
        self.allow_blank = allow_blank
        self.blank_text = blank_text
        self.query = None
        self._object_list = None

    def _get_data(self):
        if self._formdata is not None:
            for pk, obj in self._get_object_list():
                if pk == self._formdata:
                    self._set_data(obj)
                    break
        return self._data

    def _set_data(self, data):
        self._data = data
        self._formdata = None

    data = property(_get_data, _set_data)

    def _get_object_list(self):
        if self._object_list is None:
            query = self.query or self.query_factory()
            get_pk = self.get_pk
            self._object_list = list((unicode(get_pk(obj)), obj) for obj in query)
        return self._object_list

    def iter_choices(self):
        if self.allow_blank:
            yield (u'__None', self.blank_text, self.data is None)

        for pk, obj in self._get_object_list():
            label = self.label_attr and getattr(obj, self.label_attr) or obj
            yield (pk, label, obj == self.data)

    def process_formdata(self, valuelist):
        if valuelist:
            if self.allow_blank and valuelist[0] == u'__None':
                self.data = None
            else:
                self._data = None
                self._formdata = valuelist[0]

    def pre_validate(self, form):
        if not self.allow_blank or self.data is not None:
            for pk, obj in self._get_object_list():
                if self.data == obj:
                    break
            else:
                raise ValidationError('Not a valid choice')

    def __iter__(self):
        opts = dict(widget=self.option_widget, _name=self.name, _form=None)
        for i, (value, label, checked) in enumerate(self.iter_choices()):
            opt = self._Option(label=label, id=u'%s-%d' % (self.id, i), **opts)
            opt.process(None, value)
            opt.checked = checked
            yield opt

    class _Option(Field):
        checked = False

        def _value(self):
            return self.data

class QueryMultipleSelectField(QuerySelectField):
    """
    Very similar to QuerySelectField with the difference that this will
    display a multiple select. The data property will hold a list with ORM
    model instances and will be an empty list when no value is selected.

    If any of the items in the data list or submitted form data cannot be
    found in the query, this will result in a validation error.
    """
    widget = widgets.Select(multiple=True)

    def __init__(self, label=u'', validators=None, query_factory=None, get_pk=None,
                 label_attr='', default=None, **kwargs):
        if default is None:
            default = []
        super(QueryMultipleSelectField, self).__init__(label, validators, query_factory=query_factory, get_pk=get_pk, label_attr=label_attr, default=default, **kwargs)
        self._invalid_formdata = False

    def _get_data(self):
        formdata = self._formdata
        if formdata is not None:
            data = []
            for pk, obj in self._get_object_list():
                if not formdata:
                    break
                elif pk in formdata:
                    formdata.remove(pk)
                    data.append(obj)
            if formdata:
                self._invalid_formdata = True
            self._set_data(data)
        return self._data

    def _set_data(self, data):
        self._data = data
        self._formdata = None

    data = property(_get_data, _set_data)

    def iter_choices(self):
        for pk, obj in self._get_object_list():
            label = self.label_attr and getattr(obj, self.label_attr) or obj
            yield (pk, label, obj in self.data)

    def process_formdata(self, valuelist):
        self._formdata = set(valuelist)

    def pre_validate(self, form):
        if self._invalid_formdata:
            raise ValidationError('Not a valid choice')
        elif self.data:
            obj_list = list(x[1] for x in self._get_object_list())
            for v in self.data:
                if v not in obj_list:
                    raise ValidationError('Not a valid choice')

class ModelSelectField(QuerySelectField):
    """
    Similar to QuerySelectField, only for model classes.

    Using this field is only meaningful when using scoped sessions in
    SQLAlchemy, because otherwise model instances do not know how to make
    queries of themselves. This field is simply a convenience for using
    `Model.query` as the factory for QuerySelectField.
    """
    def __init__(self, label=u'', validators=None, model=None, get_pk=None,
                 label_attr='', allow_blank=False, blank_text=u'', **kwargs):
        assert model is not None, "Must specify a model."
        query_factory = lambda: model.query
        super(ModelSelectField, self).__init__(label, validators, query_factory=query_factory, get_pk=get_pk, label_attr=label_attr, allow_blank=allow_blank, blank_text=blank_text, **kwargs)

def get_pk_from_identity(obj):
    cls, key = identity_key(instance=obj)
    return u':'.join(unicode(x) for x in key)
<|endoftext|>


<commit_before>"""views for the :mod:`apps.grid` app"""

from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.models import User
from django.contrib import messages
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.db.models import Count
from django.http import HttpResponseRedirect, Http404, HttpResponseForbidden
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext

from core.utils import cache_fetcher
from grid import cachekeys
from grid.forms import ElementForm, FeatureForm, GridForm, GridPackageForm
from grid.models import Element, Feature, Grid, GridPackage
from package.models import Package
from package.forms import PackageForm
from package.views import repo_data_for_js

def build_element_map(elements):
    # Horrifying two-level dict due to needing to use hash() function later
    element_map = {}
    for element in elements:
        element_map.setdefault(element.feature_id, {})
        element_map[element.feature_id][element.grid_package_id] = element
    return element_map

def grids(request, template_name="grid/grids.html"):
    """lists grids

    Template context:

    * ``grids`` - all grid objects
    """
    # annotations providing bad counts
    #grids = Grid.objects.annotate(gridpackage_count=Count('gridpackage'), feature_count=Count('feature'))
    return render_to_response(
        template_name, {
            'grids': Grid.objects.all(),
        }, context_instance = RequestContext(request)
    )

def grid_detail(request, slug, template_name="grid/grid_detail2.html"):
    """displays a grid in detail

    Template context:

    * ``grid`` - the grid object
    * ``elements`` - elements of the grid
    * ``features`` - feature set used in the grid
    * ``grid_packages`` - packages involved in the current grid
    """
    grid = get_object_or_404(Grid, slug=slug)
    features = grid.feature_set.all()

    grid_packages = grid.grid_packages

    elements = Element.objects.all() \
                .filter(feature__in=features,
                        grid_package__in=grid_packages)

    element_map = build_element_map(elements)

    # These attributes are how we determine what is displayed in the grid
    default_attributes = [('repo_description', 'Description'),
                ('category','Category'), ('pypi_downloads', 'Downloads'), ('last_updated', 'Last Updated'), ('pypi_version', 'Version'),
                ('repo', 'Repo'), ('commits_over_52', 'Commits'), ('repo_watchers', 'Repo watchers'), ('repo_forks', 'Forks'),
                ('participant_list', 'Participants'), ('license_latest', 'License')
            ]

    return render_to_response(
        template_name, {
            'grid': grid,
            'features': features,
            'grid_packages': grid_packages,
            'attributes': default_attributes,
            'elements': element_map,
        }, context_instance = RequestContext(request)
    )

def grid_detail_feature(request, slug, feature_id, bogus_slug, template_name="grid/grid_detail_feature.html"):
    """a slightly more focused view than :func:`grid.views.grid_detail`
    shows comparison for only one feature, and does not show the basic
    grid parameters

    Template context is the same as in :func:`grid.views.grid_detail`
    """
    grid = get_object_or_404(Grid, slug=slug)
    features = grid.feature_set.filter(id=feature_id)
    if not features.count():
        raise Http404
    grid_packages = grid.gridpackage_set.select_related('gridpackage')

    elements = Element.objects.all() \
                .filter(feature__in=features,
                        grid_package__in=grid_packages)

    element_map = build_element_map(elements)

    return render_to_response(
        template_name, {
            'grid': grid,
            'feature': features[0],
            'grid_packages': grid_packages,
            'elements': element_map,
        }, context_instance = RequestContext(request)
    )

@login_required
def add_grid(request, template_name="grid/add_grid.html"):
    """Creates a new grid, requires user to be logged in.
    Works for both GET and POST request methods

    Template context:

    * ``form`` - an instance of :class:`~app.grid.forms.GridForm`
    """

    if not request.user.get_profile().can_add_grid:
        return HttpResponseForbidden("permission denied")

    new_grid = Grid()
    form = GridForm(request.POST or None, instance=new_grid)

    if form.is_valid():
        new_grid = form.save()
        return HttpResponseRedirect(reverse('grid', kwargs={'slug':new_grid.slug}))

    return render_to_response(template_name, {
        'form': form
        },
        context_instance=RequestContext(request))

@login_required
def edit_grid(request, slug, template_name="grid/edit_grid.html"):
    """View to modify the grid, handles GET and POST requests.
    This view requires user to be logged in.

    Template context:

    * ``form`` - instance of :class:`grid.forms.GridForm`
    """

    if not request.user.get_profile().can_edit_grid:
        return HttpResponseForbidden("permission denied")

    grid = get_object_or_404(Grid, slug=slug)
    form = GridForm(request.POST or None, instance=grid)

    if form.is_valid():
        grid = form.save()
        return HttpResponseRedirect(reverse('grid', kwargs={'slug': grid.slug}))

    return render_to_response(template_name, {
        'form': form,
        'grid': grid
        },
        context_instance=RequestContext(request))

@login_required
def add_feature(request, grid_slug, template_name="grid/add_feature.html"):
    """Adds a feature to the grid, accepts GET and POST requests.

    Requires user to be logged in

    Template context:

    * ``form`` - instance of :class:`grid.forms.FeatureForm` form
    * ``grid`` - instance of :class:`grid.models.Grid` model
    """

    if not request.user.get_profile().can_add_grid_feature:
        return HttpResponseForbidden("permission denied")

    grid = get_object_or_404(Grid, slug=grid_slug)
    feature = Feature()
    form = FeatureForm(request.POST or None, instance=feature)

    if form.is_valid():
        feature = Feature(
                    grid=grid,
                    title=request.POST['title'],
                    description = request.POST['description']
                )
        feature.save()
        return HttpResponseRedirect(reverse('grid', kwargs={'slug':feature.grid.slug}))

    return render_to_response(template_name, {
        'form': form,
        'grid':grid
        },
        context_instance=RequestContext(request))

@login_required
def edit_feature(request, id, template_name="grid/edit_feature.html"):
    """edits feature on a grid - this view has the same
    semantics as :func:`grid.views.add_feature`.

    Requires the user to be logged in.
    """

    if not request.user.get_profile().can_edit_grid_feature:
        return HttpResponseForbidden("permission denied")

    feature = get_object_or_404(Feature, id=id)
    form = FeatureForm(request.POST or None, instance=feature)

    if form.is_valid():
        feature = form.save()
        return HttpResponseRedirect(reverse('grid', kwargs={'slug': feature.grid.slug}))

    return render_to_response(template_name, {
        'form': form,
        'grid': feature.grid
        },
        context_instance=RequestContext(request))

@permission_required('grid.delete_feature')
def delete_feature(request, id, template_name="grid/edit_feature.html"):
    # do not need to check permission via profile because
    # we default to being strict about deleting
    """deletes a feature from the grid, ``id`` is id of the
    :class:`grid.models.Feature` model that is to be deleted

    Requires permission `grid.delete_feature`.

    Redirects to the parent :func:`grid.views.grid_detail`
    """

    feature = get_object_or_404(Feature, id=id)
    Element.objects.filter(feature=feature).delete()
    feature.delete()

    return HttpResponseRedirect(reverse('grid', kwargs={'slug': feature.grid.slug}))

@permission_required('grid.delete_gridpackage')
def delete_grid_package(request, id, template_name="grid/edit_feature.html"):
    """Deletes package from the grid, ``id`` is the id of the
    :class:`grid.models.GridPackage` instance

    Requires permission ``grid.delete_gridpackage``.

    Redirects to :func:`grid.views.grid_detail`.
    """

    # do not need to check permission via profile because
    # we default to being strict about deleting
    package = get_object_or_404(GridPackage, id=id)
    Element.objects.filter(grid_package=package).delete()
    package.delete()

    return HttpResponseRedirect(reverse('grid', kwargs={'slug': package.grid.slug}))

@login_required
def edit_element(request, feature_id, package_id, template_name="grid/edit_element.html"):

    if not request.user.get_profile().can_edit_grid_element:
        return HttpResponseForbidden("permission denied")

    feature = get_object_or_404(Feature, pk=feature_id)
    grid_package = get_object_or_404(GridPackage, pk=package_id)

    # Sanity check to make sure both the feature and grid_package are related to
    # the same grid!
    if feature.grid_id != grid_package.grid_id:
        raise Http404

    element, created = Element.objects.get_or_create(
                                    grid_package=grid_package,
                                    feature=feature
                                    )

    form = ElementForm(request.POST or None, instance=element)

    if form.is_valid():
        element = form.save()
        return HttpResponseRedirect(reverse('grid', kwargs={'slug': feature.grid.slug}))

    return render_to_response(template_name, {
        'form': form,
        'feature':feature,
        'package':grid_package.package,
        'grid':feature.grid
        },
        context_instance=RequestContext(request))

@login_required
def add_grid_package(request, grid_slug, template_name="grid/add_grid_package.html"):
    """Add an existing package to this grid."""

    if not request.user.get_profile().can_add_grid_package:
        return HttpResponseForbidden("permission denied")

    grid = get_object_or_404(Grid, slug=grid_slug)
    grid_package = GridPackage()
    form = GridPackageForm(request.POST or None, instance=grid_package)

    if form.is_valid():
        package = get_object_or_404(Package, id=request.POST['package'])
        try:
            GridPackage.objects.get(grid=grid, package=package)
            message = "Sorry, but '%s' is already in this grid." % package.title
            messages.add_message(request, messages.ERROR, message)
        except GridPackage.DoesNotExist:
            grid_package = GridPackage(
                        grid=grid,
                        package=package
                    )
            grid_package.save()
            redirect = request.POST.get('redirect','')
            if redirect:
                return HttpResponseRedirect(redirect)

            return HttpResponseRedirect(reverse('grid', kwargs={'slug':grid.slug}))

    return render_to_response(template_name, {
        'form': form,
        'grid': grid
        },
        context_instance=RequestContext(request))

@login_required
def add_new_grid_package(request, grid_slug, template_name="package/package_form.html"):
    """Add a package to a grid that isn't yet represented on the site."""

    if not request.user.get_profile().can_add_grid_package:
        return HttpResponseForbidden("permission denied")

    grid = get_object_or_404(Grid, slug=grid_slug)

    new_package = Package()
    form = PackageForm(request.POST or None, instance=new_package)

    if form.is_valid():
        new_package = form.save()
        GridPackage.objects.create(
            grid=grid,
            package=new_package
        )
        return HttpResponseRedirect(reverse("grid", kwargs={"slug":grid_slug}))

    return render_to_response(template_name, {
        "form": form,
        "repo_data": repo_data_for_js(),
        "action": "add",
        },
        context_instance=RequestContext(request))

def ajax_grid_list(request, template_name="grid/ajax_grid_list.html"):
    q = request.GET.get('q','')
    grids = []
    if q:
        grids = Grid.objects.filter(title__istartswith=q)
    package_id = request.GET.get('package_id','')
    if package_id:
        grids = grids.exclude(gridpackage__package__id=package_id)
    return render_to_response(template_name, {
        'grids': grids
        },
        context_instance=RequestContext(request)
    )
<commit_msg>changing render_to_response to render<commit_after>"""views for the :mod:`apps.grid` app"""

from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.models import User
from django.contrib import messages
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.db.models import Count
from django.http import HttpResponseRedirect, Http404, HttpResponseForbidden
from django.shortcuts import render_to_response, get_object_or_404, render
from django.template import RequestContext

from core.utils import cache_fetcher
from grid import cachekeys
from grid.forms import ElementForm, FeatureForm, GridForm, GridPackageForm
from grid.models import Element, Feature, Grid, GridPackage
from package.models import Package
from package.forms import PackageForm
from package.views import repo_data_for_js

def build_element_map(elements):
    # Horrifying two-level dict due to needing to use hash() function later
    element_map = {}
    for element in elements:
        element_map.setdefault(element.feature_id, {})
        element_map[element.feature_id][element.grid_package_id] = element
    return element_map

def grids(request, template_name="grid/grids.html"):
    """lists grids

    Template context:

    * ``grids`` - all grid objects
    """
    # annotations providing bad counts
    #grids = Grid.objects.annotate(gridpackage_count=Count('gridpackage'), feature_count=Count('feature'))
    return render(request, template_name, {'grids': Grid.objects.all(),})

def grid_detail(request, slug, template_name="grid/grid_detail2.html"):
    """displays a grid in detail

    Template context:

    * ``grid`` - the grid object
    * ``elements`` - elements of the grid
    * ``features`` - feature set used in the grid
    * ``grid_packages`` - packages involved in the current grid
    """
    grid = get_object_or_404(Grid, slug=slug)
    features = grid.feature_set.all()

    grid_packages = grid.grid_packages

    elements = Element.objects.all() \
                .filter(feature__in=features,
                        grid_package__in=grid_packages)

    element_map = build_element_map(elements)

    # These attributes are how we determine what is displayed in the grid
    default_attributes = [('repo_description', 'Description'),
                ('category','Category'), ('pypi_downloads', 'Downloads'), ('last_updated', 'Last Updated'), ('pypi_version', 'Version'),
                ('repo', 'Repo'), ('commits_over_52', 'Commits'), ('repo_watchers', 'Repo watchers'), ('repo_forks', 'Forks'),
                ('participant_list', 'Participants'), ('license_latest', 'License')
            ]

    return render_to_response(
        template_name, {
            'grid': grid,
            'features': features,
            'grid_packages': grid_packages,
            'attributes': default_attributes,
            'elements': element_map,
        }, context_instance = RequestContext(request)
    )

def grid_detail_feature(request, slug, feature_id, bogus_slug, template_name="grid/grid_detail_feature.html"):
    """a slightly more focused view than :func:`grid.views.grid_detail`
    shows comparison for only one feature, and does not show the basic
    grid parameters

    Template context is the same as in :func:`grid.views.grid_detail`
    """
    grid = get_object_or_404(Grid, slug=slug)
    features = grid.feature_set.filter(id=feature_id)
    if not features.count():
        raise Http404
    grid_packages = grid.gridpackage_set.select_related('gridpackage')

    elements = Element.objects.all() \
                .filter(feature__in=features,
                        grid_package__in=grid_packages)

    element_map = build_element_map(elements)

    return render_to_response(
        template_name, {
            'grid': grid,
            'feature': features[0],
            'grid_packages': grid_packages,
            'elements': element_map,
        }, context_instance = RequestContext(request)
    )

@login_required
def add_grid(request, template_name="grid/add_grid.html"):
    """Creates a new grid, requires user to be logged in.
    Works for both GET and POST request methods

    Template context:

    * ``form`` - an instance of :class:`~app.grid.forms.GridForm`
    """

    if not request.user.get_profile().can_add_grid:
        return HttpResponseForbidden("permission denied")

    new_grid = Grid()
    form = GridForm(request.POST or None, instance=new_grid)

    if form.is_valid():
        new_grid = form.save()
        return HttpResponseRedirect(reverse('grid', kwargs={'slug':new_grid.slug}))

    return render_to_response(template_name, {
        'form': form
        },
        context_instance=RequestContext(request))

@login_required
def edit_grid(request, slug, template_name="grid/edit_grid.html"):
    """View to modify the grid, handles GET and POST requests.
    This view requires user to be logged in.

    Template context:

    * ``form`` - instance of :class:`grid.forms.GridForm`
    """

    if not request.user.get_profile().can_edit_grid:
        return HttpResponseForbidden("permission denied")

    grid = get_object_or_404(Grid, slug=slug)
    form = GridForm(request.POST or None, instance=grid)

    if form.is_valid():
        grid = form.save()
        return HttpResponseRedirect(reverse('grid', kwargs={'slug': grid.slug}))

    return render_to_response(template_name, {
        'form': form,
        'grid': grid
        },
        context_instance=RequestContext(request))

@login_required
def add_feature(request, grid_slug, template_name="grid/add_feature.html"):
    """Adds a feature to the grid, accepts GET and POST requests.

    Requires user to be logged in

    Template context:

    * ``form`` - instance of :class:`grid.forms.FeatureForm` form
    * ``grid`` - instance of :class:`grid.models.Grid` model
    """

    if not request.user.get_profile().can_add_grid_feature:
        return HttpResponseForbidden("permission denied")

    grid = get_object_or_404(Grid, slug=grid_slug)
    feature = Feature()
    form = FeatureForm(request.POST or None, instance=feature)

    if form.is_valid():
        feature = Feature(
                    grid=grid,
                    title=request.POST['title'],
                    description = request.POST['description']
                )
        feature.save()
        return HttpResponseRedirect(reverse('grid', kwargs={'slug':feature.grid.slug}))

    return render_to_response(template_name, {
        'form': form,
        'grid':grid
        },
        context_instance=RequestContext(request))

@login_required
def edit_feature(request, id, template_name="grid/edit_feature.html"):
    """edits feature on a grid - this view has the same
    semantics as :func:`grid.views.add_feature`.

    Requires the user to be logged in.
    """

    if not request.user.get_profile().can_edit_grid_feature:
        return HttpResponseForbidden("permission denied")

    feature = get_object_or_404(Feature, id=id)
    form = FeatureForm(request.POST or None, instance=feature)

    if form.is_valid():
        feature = form.save()
        return HttpResponseRedirect(reverse('grid', kwargs={'slug': feature.grid.slug}))

    return render_to_response(template_name, {
        'form': form,
        'grid': feature.grid
        },
        context_instance=RequestContext(request))

@permission_required('grid.delete_feature')
def delete_feature(request, id, template_name="grid/edit_feature.html"):
    # do not need to check permission via profile because
    # we default to being strict about deleting
    """deletes a feature from the grid, ``id`` is id of the
    :class:`grid.models.Feature` model that is to be deleted

    Requires permission `grid.delete_feature`.

    Redirects to the parent :func:`grid.views.grid_detail`
    """

    feature = get_object_or_404(Feature, id=id)
    Element.objects.filter(feature=feature).delete()
    feature.delete()

    return HttpResponseRedirect(reverse('grid', kwargs={'slug': feature.grid.slug}))

@permission_required('grid.delete_gridpackage')
def delete_grid_package(request, id, template_name="grid/edit_feature.html"):
    """Deletes package from the grid, ``id`` is the id of the
    :class:`grid.models.GridPackage` instance

    Requires permission ``grid.delete_gridpackage``.

    Redirects to :func:`grid.views.grid_detail`.
    """

    # do not need to check permission via profile because
    # we default to being strict about deleting
    package = get_object_or_404(GridPackage, id=id)
    Element.objects.filter(grid_package=package).delete()
    package.delete()

    return HttpResponseRedirect(reverse('grid', kwargs={'slug': package.grid.slug}))

@login_required
def edit_element(request, feature_id, package_id, template_name="grid/edit_element.html"):

    if not request.user.get_profile().can_edit_grid_element:
        return HttpResponseForbidden("permission denied")

    feature = get_object_or_404(Feature, pk=feature_id)
    grid_package = get_object_or_404(GridPackage, pk=package_id)

    # Sanity check to make sure both the feature and grid_package are related to
    # the same grid!
    if feature.grid_id != grid_package.grid_id:
        raise Http404

    element, created = Element.objects.get_or_create(
                                    grid_package=grid_package,
                                    feature=feature
                                    )

    form = ElementForm(request.POST or None, instance=element)

    if form.is_valid():
        element = form.save()
        return HttpResponseRedirect(reverse('grid', kwargs={'slug': feature.grid.slug}))

    return render_to_response(template_name, {
        'form': form,
        'feature':feature,
        'package':grid_package.package,
        'grid':feature.grid
        },
        context_instance=RequestContext(request))

@login_required
def add_grid_package(request, grid_slug, template_name="grid/add_grid_package.html"):
    """Add an existing package to this grid."""

    if not request.user.get_profile().can_add_grid_package:
        return HttpResponseForbidden("permission denied")

    grid = get_object_or_404(Grid, slug=grid_slug)
    grid_package = GridPackage()
    form = GridPackageForm(request.POST or None, instance=grid_package)

    if form.is_valid():
        package = get_object_or_404(Package, id=request.POST['package'])
        try:
            GridPackage.objects.get(grid=grid, package=package)
            message = "Sorry, but '%s' is already in this grid." % package.title
            messages.add_message(request, messages.ERROR, message)
        except GridPackage.DoesNotExist:
            grid_package = GridPackage(
                        grid=grid,
                        package=package
                    )
            grid_package.save()
            redirect = request.POST.get('redirect','')
            if redirect:
                return HttpResponseRedirect(redirect)

            return HttpResponseRedirect(reverse('grid', kwargs={'slug':grid.slug}))

    return render_to_response(template_name, {
        'form': form,
        'grid': grid
        },
        context_instance=RequestContext(request))

@login_required
def add_new_grid_package(request, grid_slug, template_name="package/package_form.html"):
    """Add a package to a grid that isn't yet represented on the site."""

    if not request.user.get_profile().can_add_grid_package:
        return HttpResponseForbidden("permission denied")

    grid = get_object_or_404(Grid, slug=grid_slug)

    new_package = Package()
    form = PackageForm(request.POST or None, instance=new_package)

    if form.is_valid():
        new_package = form.save()
        GridPackage.objects.create(
            grid=grid,
            package=new_package
        )
        return HttpResponseRedirect(reverse("grid", kwargs={"slug":grid_slug}))

    return render_to_response(template_name, {
        "form": form,
        "repo_data": repo_data_for_js(),
        "action": "add",
        },
        context_instance=RequestContext(request))

def ajax_grid_list(request, template_name="grid/ajax_grid_list.html"):
    q = request.GET.get('q','')
    grids = []
    if q:
        grids = Grid.objects.filter(title__istartswith=q)
    package_id = request.GET.get('package_id','')
    if package_id:
        grids = grids.exclude(gridpackage__package__id=package_id)
    return render_to_response(template_name, {
        'grids': grids
        },
        context_instance=RequestContext(request)
    )
<|endoftext|>


<commit_before>#!/bin/env python

"""Defines a source of words to be used in testing."""
import glob
import itertools
import random

class WordSource(object):
    """Models a source of words."""

    def __init__(self):
        lines = [l.rstrip('\n') for l
                 in open('latin_words.txt', 'rt').readlines()]
        latin_terms = itertools.islice(lines, 0, len(lines), 2)
        english_definitions = itertools.islice(lines,
                                               1, len(lines), 2)
        self.latin_english_dict = \
            dict(list(itertools.izip(latin_terms, english_definitions)))
        self.latin_words = self.latin_english_dict.keys()

    def __iter__(self):
        """This class is an iterator."""
        return self

    def __next__(self):
        """Return the next item in the sequence."""
        return self.next()

    def next(self):
        """Return the next item in the sequence."""
        word_index = random.randrange(len(self.latin_english_dict))
        next_latin_word = self.latin_words[word_index]
        return next_latin_word, self.latin_english_dict[next_latin_word]

def verb():
    """Calculate a random verb declension."""
    return [random.randrange(2), random.randrange(6)]

def noun():
    """Calculate a random (first or second) noun declension."""
    return [random.randrange(6), random.randrange(2)]

def noun3():
    """Calculate a random third noun declension."""
    return [random.randrange(3)] + noun()

def adj():
    """Calculate a random 1st and 2nd adjective declension."""
    return [random.randrange(3), random.randrange(6), random.randrange(2)]

def adj31():
    """Calculate a random 3rd adjective declension (one or two endings)."""
    return [random.randrange(2), random.randrange(6), random.randrange(2)]

def adj33():
    """Calculate a random 3rd adjective declension (two endings)."""
    return [random.randrange(3), random.randrange(6), random.randrange(2)]

if __name__ == '__main__':
    ws = WordSource()
    i = 0
    for latin, english in ws:
        i += 1
        print('%s\n%s' % (latin, english))
        if i >= 9:
            break
<commit_msg>Changed utility to handle multi-line definitions.<commit_after>#!/bin/env python

"""Defines a source of words to be used in testing."""
import glob
import itertools
import random

class WordSource(object):
    """Models a source of words."""

    def __init__(self):
        lines = [l.rstrip('\n') for l
                 in open('latin_words.txt', 'rt').readlines()]
        latin_terms = []
        english_definitions = []
        line_no = 0
        while line_no < len(lines):
            if ':' in lines[line_no]:
                latin_terms.append(lines[line_no])
                line_no += 1
            else:
                english_definition = lines[line_no]
                line_no += 1
                while line_no < len(lines) and ':' not in lines[line_no]:
                    english_definition += lines[line_no]
                    line_no += 1
                english_definitions.append(' ' + english_definition)

        self.latin_english_dict = \
            dict(list(itertools.izip(latin_terms, english_definitions)))
        self.latin_words = self.latin_english_dict.keys()

    def __iter__(self):
        """This class is an iterator."""
        return self

    def __next__(self):
        """Return the next item in the sequence."""
        return self.next()

    def next(self):
        """Return the next item in the sequence."""
        word_index = random.randrange(len(self.latin_english_dict))
        next_latin_word = self.latin_words[word_index]
        return next_latin_word, self.latin_english_dict[next_latin_word]

def verb():
    """Calculate a random verb declension."""
    return [random.randrange(2), random.randrange(6)]

def noun():
    """Calculate a random (first or second) noun declension."""
    return [random.randrange(6), random.randrange(2)]

def noun3():
    """Calculate a random third noun declension."""
    return [random.randrange(3)] + noun()

def adj():
    """Calculate a random 1st and 2nd adjective declension."""
    return [random.randrange(3), random.randrange(6), random.randrange(2)]

def adj31():
    """Calculate a random 3rd adjective declension (one or two endings)."""
    return [random.randrange(2), random.randrange(6), random.randrange(2)]

def adj33():
    """Calculate a random 3rd adjective declension (two endings)."""
    return [random.randrange(3), random.randrange(6), random.randrange(2)]

if __name__ == '__main__':
    ws = WordSource()
    i = 0
    for latin, english in ws:
        i += 1
        print('%s\n%s' % (latin, english))
        if i >= 9:
            break
<|endoftext|>


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

import urllib.request
import re
import smtplib
from email.mime.text import MIMEText

mailto_list=["***@xxx.com"] #收件人邮箱
mail_host="smtp.xxx.com"  #设置服务器
mail_user="***"    #用户名
mail_pass="**** "   #密码或者口令
mail_postfix="xxx.com"  #发件箱的后缀

#爬取页面的html代码

def get_page(url):
    req = urllib.request.Request(url)
    req.add_header('User-Agent','***')      #缺省部分填上浏览器字符串
    response = urllib.request.urlopen(url)
    html = response.read().decode('utf-8')
    return html

#取对应的字符串，可以使用正则表达式或者字符串函数

def get_string(html):
    result = ''
    list = re.findall(r'***',html)
    #目的不同，处理方法不同且不唯一，缺省部分为正则，写得太渣就不放了
    for string in list:
        match = re.search(r'***', string)
        result = result + match.group(1) + '\n'
    return result

#发送邮件

def send_mail(to_list,sub,content):
    me = "hello"+"<"+mail_user+"@"+mail_postfix+">"           #hello可以替换成其他字符
    msg = MIMEText(content,_subtype='plain',_charset='utf-8') #转换成相同的代码
    msg['Subject'] = sub
    msg['From'] = me
    msg['To'] = ";".join(to_list)
    try:
        server = smtplib.SMTP()
        server.connect(mail_host)
        server.login(mail_user,mail_pass)
        server.sendmail(me, to_list, msg.as_string())
        server.close()
        return True
    except Exception as e:
        print(str(e))
        return False

#主函数

if __name__ == '__main__':
    url = "****"
    html = get_page(url)
    string = get_string(html)
    if send_mail(mailto_list,"脚本推送",string):
        print("发送成功")
    else:
        print("发送失败")
<commit_msg>fix a mistake<commit_after># -*- coding: UTF-8 -*-

import urllib.request
import re
import smtplib
from email.mime.text import MIMEText

mailto_list=["***@xxx.com"] #收件人邮箱
mail_host="smtp.xxx.com"  #设置服务器
mail_user="***"    #用户名
mail_pass="**** "   #密码或者口令
mail_postfix="xxx.com"  #发件箱的后缀

#爬取页面的html代码

def get_page(url):
    req = urllib.request.Request(url)
    req.add_header('User-Agent','***')      #缺省部分填上浏览器字符串
    response = urllib.request.urlopen(req)
    html = response.read().decode('utf-8')
    return html

#取对应的字符串，可以使用正则表达式或者字符串函数

def get_string(html):
    result = ''
    list = re.findall(r'***',html)
    #目的不同，处理方法不同且不唯一，缺省部分为正则，写得太渣就不放了
    for string in list:
        match = re.search(r'***', string)
        result = result + match.group(1) + '\n'
    return result

#发送邮件

def send_mail(to_list,sub,content):
    me = "hello"+"<"+mail_user+"@"+mail_postfix+">"           #hello可以替换成其他字符
    msg = MIMEText(content,_subtype='plain',_charset='utf-8') #转换成相同的代码
    msg['Subject'] = sub
    msg['From'] = me
    msg['To'] = ";".join(to_list)
    try:
        server = smtplib.SMTP()
        server.connect(mail_host)
        server.login(mail_user,mail_pass)
        server.sendmail(me, to_list, msg.as_string())
        server.close()
        return True
    except Exception as e:
        print(str(e))
        return False

#主函数

if __name__ == '__main__':
    url = "****"
    html = get_page(url)
    string = get_string(html)
    if send_mail(mailto_list,"脚本推送",string):
        print("发送成功")
    else:
        print("发送失败")
<|endoftext|>


<commit_before>'''
1. Write a script that takes a file containing a number of words (one per line) and sorts them
by the number of times they occur in the file (descending order), but exclude words that only
occur once. Ignore the case of the words and filter out any punctuation characters. The output
should contain lines each with word and the number of times it occurs in the input separated
by space.
Example input:
microsoft
apple
microsoft.
Apple
security
microsoft
internet
Example output:
microsoft 3
apple 2 '''

import argparse

def count(file, out):
    print((file, out))

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-s', '--source', help='File containing the words', required=True)
    parser.add_argument('-o', '--out', help='File to write result into.', default='out.txt', required=False)
    args = parser.parse_args()
    count(args.source, args.out)
<commit_msg>Reading original file and parsing lines into array<commit_after>'''
1. Write a script that takes a file containing a number of words (one per line) and sorts them
by the number of times they occur in the file (descending order), but exclude words that only
occur once. Ignore the case of the words and filter out any punctuation characters. The output
should contain lines each with word and the number of times it occurs in the input separated
by space.
Example input:
microsoft
apple
microsoft.
Apple
security
microsoft
internet
Example output:
microsoft 3
apple 2 '''

import argparse

def count(file, out):
#    print((file, out))
    words = [word.strip() for word in open(file)]
    print(words)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-s', '--source', help='File containing the words', required=True)
    parser.add_argument('-o', '--out', help='File to write result into.', default='out.txt', required=False)
    args = parser.parse_args()
    count(args.source, args.out)
<|endoftext|>


<commit_before>from functools import wraps
from datetime import date
import urlparse
import json
import uuid

from django.http import (HttpResponse, HttpResponseRedirect,
                         HttpResponseBadRequest, HttpResponseForbidden)
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings

from models import Subscriber
from responsys import Responsys, NewsletterException, UnauthorizedException

NEWSLETTERS = {
    'mozilla-and-you': 'MOZILLA_AND_YOU',
    'mobile': 'ABOUT_MOBILE',
    'beta': 'FIREFOX_BETA_NEWS',
    'aurora': 'AURORA',
    'about-mozilla': 'ABOUT_MOZILLA',
    'drumbeat': 'DRUMBEAT_NEWS_GROUP',
    'addons': 'ABOUT_ADDONS',
    'hacks': 'ABOUT_HACKS',
    'labs': 'ABOUT_LABS',
    'qa-news': 'QA_NEWS',
    'student-reps': 'STUDENT_REPS',
    'about-standards': 'ABOUT_STANDARDS',
    'mobile-addon-dev': 'MOBILE_ADDON_DEV',
    'addon-dev': 'ADD_ONS',
    'join-mozilla': 'JOIN_MOZILLA',
    'mozilla-phone': 'MOZILLA_PHONE',
    'app-dev': 'APP_DEV'
}

NEWSLETTER_NAMES = NEWSLETTERS.keys()
NEWSLETTER_FIELDS = NEWSLETTERS.values()

# Utility functions

def newsletter_field(name):
    return NEWSLETTERS.get(name, False)

def newsletter_name(field):
    i = NEWSLETTER_FIELDS.index(field)
    return NEWSLETTER_NAMES[i]

def logged_in(f):
    """ Decorator to check if the user has permission to view these
    pages """

    @wraps(f)
    def wrapper(request, token, *args, **kwargs):
        subscriber = Subscriber.objects.filter(token=token)
        if not subscriber.exists():
            return json_response({'desc': 'Must have valid token for this request'},
                                 status=403)

        request.subscriber = subscriber[0]
        return f(request, token, *args, **kwargs)
    return wrapper

def json_response(data, status=200):
    res = HttpResponse(json.dumps(data),
                       mimetype='application/json')
    res.status_code = status

    # Allow all cross-domain requests, this service will restrict
    # access on the server level
    res['Access-Control-Allow-Origin'] = '*'
    return res

class Update(object):
    SUBSCRIBE=1
    UNSUBSCRIBE=2
    SET=3

@csrf_exempt
def subscribe(request):
    if 'newsletters' not in request.POST:
        return json_response({'desc': 'newsletters is missing'},
                             status=500)

    return update_user(request, Update.SUBSCRIBE)

@logged_in
@csrf_exempt
def unsubscribe(request, token):
    data = request.POST.copy()

    if data.get('optout', 'N') == 'Y':
        data['optin'] = 'N'

        for field in NEWSLETTER_FIELDS:
            data['newsletters'] = ','.join(NEWSLETTER_NAMES)

    return update_user(request, Update.UNSUBSCRIBE, data)

@logged_in
@csrf_exempt
def user(request, token):
    if request.method == 'POST':
        return update_user(request, Update.SET)

    newsletters = NEWSLETTERS.values()

    fields = [
        'EMAIL_ADDRESS_',
        'EMAIL_FORMAT_',
        'COUNTRY_',
        'LANGUAGE_ISO2'
    ]

    for nl in newsletters:
        fields.append('%s_FLG' % nl)

    try:
        rs = Responsys()
        rs.login(settings.RESPONSYS_USER, settings.RESPONSYS_PASS)
        user = rs.retrieve_list_members(request.subscriber.email,
                                        settings.RESPONSYS_FOLDER,
                                        settings.RESPONSYS_LIST,
                                        fields)
    except NewsletterException, e:
        return json_response({'desc': e.message},
                             status=500)
    except UnauthorizedException, e:
        return json_response({'desc': 'Responsys auth failure'},
                             status=500)

    user_data = {
        'email': request.subscriber.email,
        'format': user['EMAIL_FORMAT_'],
        'country': user['COUNTRY_'],
        'lang': user['LANGUAGE_ISO2'],
        'newsletters': [newsletter_name(nl) for nl in newsletters
                        if user.get('%s_FLG' % nl, False) == 'Y']
    }

    rs.logout()

    return json_response(user_data)

def parse_newsletters(record, type, newsletters, optout):
    """ Parse the newsletter data from a comma-delimited string and
    set the appropriate fields in the record """

    newsletters = [x.strip() for x in newsletters.split(',')]

    if type == Update.SUBSCRIBE or type == Update.SET:
        # Subscribe the user to these newsletters
        for nl in newsletters:
            name = newsletter_field(nl)
            if name:
                record['%s_FLG' % name] = 'N' if optout else 'Y'
                record['%s_DATE' % name] = date.today().strftime('%Y-%m-%d')

    if type == Update.UNSUBSCRIBE or type == Update.SET:
        # Unsubscribe the user to these newsletters
        unsubs = newsletters

        if type == Update.SET:
            # Unsubscribe to the inversion of these newsletters
            subs = set(newsletters)
            all = set(NEWSLETTER_NAMES)
            unsubs = all.difference(subs)

        for nl in unsubs:
            name = newsletter_field(nl)
            if name:
                record['%s_FLG' % name] = 'N'

def update_user(request, type, data=None):
    """ General method for updating user's preferences and subscribed
    newsletters. Assumes data to be in POST """

    if request.method != 'POST':
        return HttpResponseBadRequest("Only POST supported")

    data = data or request.POST
    has_auth = hasattr(request, 'subscriber')

    # validate parameters
    if not has_auth and 'email' not in data:
        return json_response({'desc': 'email is required when not using tokens'},
                             status=500)

    # parse the parameters
    record = {'EMAIL_ADDRESS_': data['email'],
              'EMAIL_PERMISSION_STATUS_': 'I'}

    extra_fields = {
        'format': 'EMAIL_FORMAT_',
        'country': 'COUNTRY_',
        'lang': 'LANGUAGE_ISO2',
        'locale': 'LANG_LOCALE',
        'source_url': 'SOURCE_URL'
    }

    # optionally add more fields
    for field in extra_fields.keys():
        if field in data:
            record[extra_fields[field]] = data[field]

    # setup the newsletter fields
    parse_newsletters(record,
                      type,
                      data.get('newsletters', ''),
                      data.get('optin', 'Y') != 'Y')

    # make a new token
    token = str(uuid.uuid4())

    if type == Update.SUBSCRIBE:
        # if we are subscribing and the user already exists, don't
        # update the token. otherwise create a new user with the token.
        try:
            sub = Subscriber.objects.get(email=record['EMAIL_ADDRESS_'])
            token = sub.token
        except Subscriber.DoesNotExist:
            sub = Subscriber(email=record['EMAIL_ADDRESS_'], token=token)
            sub.save()
    else:
        # if we are updating an existing user, set a new token
        sub = Subscriber.objects.get(email=request.subscriber.email)
        sub.token = token
        sub.save()

    record['TOKEN'] = token

    # save the user's fields
    try:
        rs = Responsys()
        rs.login(settings.RESPONSYS_USER, settings.RESPONSYS_PASS)

        if has_auth and record['EMAIL_ADDRESS_'] != request.subscriber.email:
            # email has changed, we need to delete the previous user
            rs.delete_list_members(request.subscriber.email,
                                   settings.RESPONSYS_FOLDER,
                                   settings.RESPONSYS_LIST)

        rs.merge_list_members(settings.RESPONSYS_FOLDER,
                              settings.RESPONSYS_LIST,
                              record.keys(),
                              record.values())

        if data.get('trigger_welcome', False) == 'Y':
            rs.trigger_custom_event(record['EMAIL_ADDRESS_'],
                                    settings.RESPONSYS_FOLDER,
                                    settings.RESPONSYS_LIST,
                                    'New_Signup_Welcome')

        rs.logout()
    except NewsletterException, e:
        return json_response({'desc': e.message},
                             status=500)
    except UnauthorizedException, e:
        return json_response({'desc': 'Responsys auth failure'},
                             status=500)

    return json_response({'token': token})

@logged_in
@csrf_exempt
def delete_user(request, token):
    try:
        rs = Responsys()
        rs.login(settings.RESPONSYS_USER, settings.RESPONSYS_PASS)
        rs.delete_list_members(request.subscriber.email,
                               settings.RESPONSYS_FOLDER,
                               settings.RESPONSYS_LIST)
        rs.logout()
    except NewsletterException, e:
        return json_response({'desc': e.message},
                             status=500)
    except UnauthorizedException, e:
        return json_response({'desc': 'Responsys auth failure'},
                             status=500)

    request.subscriber.delete()
    return json_response({})
<<<<<<< HEAD
=======

def debug_user(request):
    if not 'email' in request.GET or not 'supertoken' in request.GET:
        return json_response(
            {'status': 'error',
             'desc': 'Using debug_user, you need to pass the '
                     '`email` and `supertoken` GET parameters'},
            status=500
        )

    if request.GET['supertoken'] != settings.SUPERTOKEN:
        return json_response({'status': 'error',
                              'desc': 'Bad supertoken'},
                             status=401)

    return get_user(request.GET['email'])

# Custom update methods

@csrf_exempt
def custom_unsub_reason(request):
    """Update the reason field for the user, which logs why the user
    unsubscribed from all newsletters."""

    if not 'email' in request.POST or not 'reason' in request.POST:
        return json_response(
            {'status': 'error',
             'desc': 'custom_unsub_reason requires the `email` '
                     'and `reason` POST parameters'},
            status=401
        )

    email = request.POST['email']
    reason = request.POST['reason']

    rs = Responsys()
    rs.login(settings.RESPONSYS_USER, settings.RESPONSYS_PASS)
    rs.merge_list_members(settings.RESPONSYS_FOLDER,
                          settings.RESPONSYS_LIST,
                          ['EMAIL_ADDRESS_', 'UNSUBSCRIBE_REASON'],
                          [email, reason])
    rs.logout()

    return json_response({'status': 'ok'})
>>>>>>> 63deaf4... add custom method unsub_reason
<commit_msg>add moz-spaces newsletter<commit_after>from functools import wraps
from datetime import date
import urlparse
import json
import uuid

from django.http import (HttpResponse, HttpResponseRedirect,
                         HttpResponseBadRequest, HttpResponseForbidden)
from django.views.decorators.csrf import csrf_exempt
from django.conf import settings

from models import Subscriber
from responsys import Responsys, NewsletterException, UnauthorizedException

NEWSLETTERS = {
    'mozilla-and-you': 'MOZILLA_AND_YOU',
    'mobile': 'ABOUT_MOBILE',
    'beta': 'FIREFOX_BETA_NEWS',
    'aurora': 'AURORA',
    'about-mozilla': 'ABOUT_MOZILLA',
    'drumbeat': 'DRUMBEAT_NEWS_GROUP',
    'addons': 'ABOUT_ADDONS',
    'hacks': 'ABOUT_HACKS',
    'labs': 'ABOUT_LABS',
    'qa-news': 'QA_NEWS',
    'student-reps': 'STUDENT_REPS',
    'about-standards': 'ABOUT_STANDARDS',
    'mobile-addon-dev': 'MOBILE_ADDON_DEV',
    'addon-dev': 'ADD_ONS',
    'join-mozilla': 'JOIN_MOZILLA',
    'mozilla-phone': 'MOZILLA_PHONE',
    'app-dev': 'APP_DEV',
    'moz-spaces', 'MOZ_SPACE'
}

NEWSLETTER_NAMES = NEWSLETTERS.keys()
NEWSLETTER_FIELDS = NEWSLETTERS.values()

# Utility functions

def newsletter_field(name):
    return NEWSLETTERS.get(name, False)

def newsletter_name(field):
    i = NEWSLETTER_FIELDS.index(field)
    return NEWSLETTER_NAMES[i]

def logged_in(f):
    """ Decorator to check if the user has permission to view these
    pages """

    @wraps(f)
    def wrapper(request, token, *args, **kwargs):
        subscriber = Subscriber.objects.filter(token=token)
        if not subscriber.exists():
            return json_response({'desc': 'Must have valid token for this request'},
                                 status=403)

        request.subscriber = subscriber[0]
        return f(request, token, *args, **kwargs)
    return wrapper

def json_response(data, status=200):
    res = HttpResponse(json.dumps(data),
                       mimetype='application/json')
    res.status_code = status

    # Allow all cross-domain requests, this service will restrict
    # access on the server level
    res['Access-Control-Allow-Origin'] = '*'
    return res

class Update(object):
    SUBSCRIBE=1
    UNSUBSCRIBE=2
    SET=3

@csrf_exempt
def subscribe(request):
    if 'newsletters' not in request.POST:
        return json_response({'desc': 'newsletters is missing'},
                             status=500)

    return update_user(request, Update.SUBSCRIBE)

@logged_in
@csrf_exempt
def unsubscribe(request, token):
    data = request.POST.copy()

    if data.get('optout', 'N') == 'Y':
        data['optin'] = 'N'

        for field in NEWSLETTER_FIELDS:
            data['newsletters'] = ','.join(NEWSLETTER_NAMES)

    return update_user(request, Update.UNSUBSCRIBE, data)

@logged_in
@csrf_exempt
def user(request, token):
    if request.method == 'POST':
        return update_user(request, Update.SET)

    newsletters = NEWSLETTERS.values()

    fields = [
        'EMAIL_ADDRESS_',
        'EMAIL_FORMAT_',
        'COUNTRY_',
        'LANGUAGE_ISO2'
    ]

    for nl in newsletters:
        fields.append('%s_FLG' % nl)

    try:
        rs = Responsys()
        rs.login(settings.RESPONSYS_USER, settings.RESPONSYS_PASS)
        user = rs.retrieve_list_members(request.subscriber.email,
                                        settings.RESPONSYS_FOLDER,
                                        settings.RESPONSYS_LIST,
                                        fields)
    except NewsletterException, e:
        return json_response({'desc': e.message},
                             status=500)
    except UnauthorizedException, e:
        return json_response({'desc': 'Responsys auth failure'},
                             status=500)

    user_data = {
        'email': request.subscriber.email,
        'format': user['EMAIL_FORMAT_'],
        'country': user['COUNTRY_'],
        'lang': user['LANGUAGE_ISO2'],
        'newsletters': [newsletter_name(nl) for nl in newsletters
                        if user.get('%s_FLG' % nl, False) == 'Y']
    }

    rs.logout()

    return json_response(user_data)

def parse_newsletters(record, type, newsletters, optout):
    """ Parse the newsletter data from a comma-delimited string and
    set the appropriate fields in the record """

    newsletters = [x.strip() for x in newsletters.split(',')]

    if type == Update.SUBSCRIBE or type == Update.SET:
        # Subscribe the user to these newsletters
        for nl in newsletters:
            name = newsletter_field(nl)
            if name:
                record['%s_FLG' % name] = 'N' if optout else 'Y'
                record['%s_DATE' % name] = date.today().strftime('%Y-%m-%d')

    if type == Update.UNSUBSCRIBE or type == Update.SET:
        # Unsubscribe the user to these newsletters
        unsubs = newsletters

        if type == Update.SET:
            # Unsubscribe to the inversion of these newsletters
            subs = set(newsletters)
            all = set(NEWSLETTER_NAMES)
            unsubs = all.difference(subs)

        for nl in unsubs:
            name = newsletter_field(nl)
            if name:
                record['%s_FLG' % name] = 'N'

def update_user(request, type, data=None):
    """ General method for updating user's preferences and subscribed
    newsletters. Assumes data to be in POST """

    if request.method != 'POST':
        return HttpResponseBadRequest("Only POST supported")

    data = data or request.POST
    has_auth = hasattr(request, 'subscriber')

    # validate parameters
    if not has_auth and 'email' not in data:
        return json_response({'desc': 'email is required when not using tokens'},
                             status=500)

    # parse the parameters
    record = {'EMAIL_ADDRESS_': data['email'],
              'EMAIL_PERMISSION_STATUS_': 'I'}

    extra_fields = {
        'format': 'EMAIL_FORMAT_',
        'country': 'COUNTRY_',
        'lang': 'LANGUAGE_ISO2',
        'locale': 'LANG_LOCALE',
        'source_url': 'SOURCE_URL'
    }

    # optionally add more fields
    for field in extra_fields.keys():
        if field in data:
            record[extra_fields[field]] = data[field]

    # setup the newsletter fields
    parse_newsletters(record,
                      type,
                      data.get('newsletters', ''),
                      data.get('optin', 'Y') != 'Y')

    # make a new token
    token = str(uuid.uuid4())

    if type == Update.SUBSCRIBE:
        # if we are subscribing and the user already exists, don't
        # update the token. otherwise create a new user with the token.
        try:
            sub = Subscriber.objects.get(email=record['EMAIL_ADDRESS_'])
            token = sub.token
        except Subscriber.DoesNotExist:
            sub = Subscriber(email=record['EMAIL_ADDRESS_'], token=token)
            sub.save()
    else:
        # if we are updating an existing user, set a new token
        sub = Subscriber.objects.get(email=request.subscriber.email)
        sub.token = token
        sub.save()

    record['TOKEN'] = token

    # save the user's fields
    try:
        rs = Responsys()
        rs.login(settings.RESPONSYS_USER, settings.RESPONSYS_PASS)

        if has_auth and record['EMAIL_ADDRESS_'] != request.subscriber.email:
            # email has changed, we need to delete the previous user
            rs.delete_list_members(request.subscriber.email,
                                   settings.RESPONSYS_FOLDER,
                                   settings.RESPONSYS_LIST)

        rs.merge_list_members(settings.RESPONSYS_FOLDER,
                              settings.RESPONSYS_LIST,
                              record.keys(),
                              record.values())

        if data.get('trigger_welcome', False) == 'Y':
            rs.trigger_custom_event(record['EMAIL_ADDRESS_'],
                                    settings.RESPONSYS_FOLDER,
                                    settings.RESPONSYS_LIST,
                                    'New_Signup_Welcome')

        rs.logout()
    except NewsletterException, e:
        return json_response({'desc': e.message},
                             status=500)
    except UnauthorizedException, e:
        return json_response({'desc': 'Responsys auth failure'},
                             status=500)

    return json_response({'token': token})

@logged_in
@csrf_exempt
def delete_user(request, token):
    try:
        rs = Responsys()
        rs.login(settings.RESPONSYS_USER, settings.RESPONSYS_PASS)
        rs.delete_list_members(request.subscriber.email,
                               settings.RESPONSYS_FOLDER,
                               settings.RESPONSYS_LIST)
        rs.logout()
    except NewsletterException, e:
        return json_response({'desc': e.message},
                             status=500)
    except UnauthorizedException, e:
        return json_response({'desc': 'Responsys auth failure'},
                             status=500)

    request.subscriber.delete()
    return json_response({})
<<<<<<< HEAD
=======

def debug_user(request):
    if not 'email' in request.GET or not 'supertoken' in request.GET:
        return json_response(
            {'status': 'error',
             'desc': 'Using debug_user, you need to pass the '
                     '`email` and `supertoken` GET parameters'},
            status=500
        )

    if request.GET['supertoken'] != settings.SUPERTOKEN:
        return json_response({'status': 'error',
                              'desc': 'Bad supertoken'},
                             status=401)

    return get_user(request.GET['email'])

# Custom update methods

@csrf_exempt
def custom_unsub_reason(request):
    """Update the reason field for the user, which logs why the user
    unsubscribed from all newsletters."""

    if not 'email' in request.POST or not 'reason' in request.POST:
        return json_response(
            {'status': 'error',
             'desc': 'custom_unsub_reason requires the `email` '
                     'and `reason` POST parameters'},
            status=401
        )

    email = request.POST['email']
    reason = request.POST['reason']

    rs = Responsys()
    rs.login(settings.RESPONSYS_USER, settings.RESPONSYS_PASS)
    rs.merge_list_members(settings.RESPONSYS_FOLDER,
                          settings.RESPONSYS_LIST,
                          ['EMAIL_ADDRESS_', 'UNSUBSCRIBE_REASON'],
                          [email, reason])
    rs.logout()

    return json_response({'status': 'ok'})
>>>>>>> 63deaf4... add custom method unsub_reason
<|endoftext|>


<commit_before># -*- coding: UTF-8 -*-
"""
Usage:
  plumbum <template> <namespace> [region] [options]...

Options:
  template   path to the jinja2 template
  namespace  AWS namespace. Currently supports: elasticache, elb, ec2, rds, asg, sqs
  region     AWS region [default: us-east-1]
  options    key value combinations, they can be tags or any other property

Examples:

  plumbum elb.yaml.j2 elb
  plumbum elb.yaml.j2 elb us-west-2
  plumbum ec2.yaml.j2 ec2 environment=production
  plumbum ec2.yaml.j2 ec2 us-west-2 environment=production

Outputs to stdout.

About Templates:

Templates are used to generate config.yml files based on running resources.
They're written in jinja2, and have these variables available:

  filters    A dictionary of the filters that were passed in
  region     The region the resource is located in
  resources  A list of the resources as boto objects
"""
from __future__ import unicode_literals

import argparse
import re
import sys

import boto
import boto.dynamodb
import boto.ec2
import boto.ec2.elb
import boto.rds
import boto.elasticache
import boto.ec2.autoscale
import boto.kinesis
import boto.sqs
import jinja2
import os.path

__version__ = '0.9.0'

# DEFAULT_NAMESPACE = 'ec2'  # TODO
DEFAULT_REGION = 'us-east-1'

def get_property_func(key):
    """
    Get the accessor function for an instance to look for `key`.

    Look for it as an attribute, and if that does not work, look to see if it
    is a tag.
    """
    def get_it(obj):
        try:
            return getattr(obj, key)
        except AttributeError:
            return obj.tags.get(key)
    return get_it

def filter_key(filter_args):
    def filter_instance(instance):
        return all([value == get_property_func(key)(instance)
            for key, value in filter_args.items()])
    return filter_instance

def lookup(instances, filter_by=None):
    if filter_by is not None:
        return list(filter(filter_key(filter_by), instances))
    return instances

def interpret_options(args=sys.argv[1:]):

    parser = argparse.ArgumentParser()
    parser.add_argument('--version', action='version', version=__version__)
    parser.add_argument("-r", "--region", help="AWS region", default=DEFAULT_REGION)
    parser.add_argument("-f", "--filter", help="filter to apply to AWS objects")
    parser.add_argument('--token', action='append', help='a key=value pair to use when populating templates')
    parser.add_argument("namespace", type=str, help="AWS namespace")
    parser.add_argument("template", type=str, help="the template to interpret")

    args = parser.parse_args(args=args)

    # Support 'ec2' (human friendly) and 'AWS/EC2' (how CloudWatch natively calls these things)
    namespace = args.namespace.rsplit('/', 2)[-1].lower()
    return args.template, namespace, args.region, args.filter, args.token

def list_ec2(region, filter_by_kwargs):
    """List running ec2 instances."""
    conn = boto.ec2.connect_to_region(region)
    instances = conn.get_only_instances()
    return lookup(instances, filter_by=filter_by_kwargs)

def list_elb(region, filter_by_kwargs):
    """List all load balancers."""
    conn = boto.ec2.elb.connect_to_region(region)
    instances = conn.get_all_load_balancers()
    return lookup(instances, filter_by=filter_by_kwargs)

def list_rds(region, filter_by_kwargs):
    """List all RDS thingys."""
    conn = boto.rds.connect_to_region(region)
    instances = conn.get_all_dbinstances()
    return lookup(instances, filter_by=filter_by_kwargs)

def list_elasticache(region, filter_by_kwargs):
    """List all ElastiCache Clusters."""
    conn = boto.elasticache.connect_to_region(region)
    req = conn.describe_cache_clusters()
    data = req["DescribeCacheClustersResponse"]["DescribeCacheClustersResult"]["CacheClusters"]
    clusters = [x['CacheClusterId'] for x in data]
    return clusters

def list_autoscaling_group(region, filter_by_kwargs):
    """List all Auto Scaling Groups."""
    conn = boto.ec2.autoscale.connect_to_region(region)
    groups = conn.get_all_groups()
    return lookup(groups, filter_by=filter_by_kwargs)

def list_sqs(region, filter_by_kwargs):
    """List all SQS Queues."""
    conn = boto.sqs.connect_to_region(region)
    queues = conn.get_all_queues()
    return lookup(queues, filter_by=filter_by_kwargs)

def list_kinesis_applications(region, filter_by_kwargs):
    """List all the kinesis applications along with the shards for each stream"""
    conn = boto.kinesis.connect_to_region(region)
    streams = conn.list_streams()['StreamNames']
    kinesis_streams = {}
    for stream_name in streams:
        shard_ids = []
        shards = conn.describe_stream(stream_name)['StreamDescription']['Shards']
        for shard in shards:
            shard_ids.append(shard['ShardId'])
        kinesis_streams[stream_name] = shard_ids
    return kinesis_streams

def list_dynamodb(region, filter_by_kwargs):
    """List all DynamoDB tables."""
    conn = boto.dynamodb.connect_to_region(region)
    tables = conn.list_tables()
    return lookup(tables, filter_by=filter_by_kwargs)

list_resources = {
    'ec2': list_ec2,
    'elb': list_elb,
    'rds': list_rds,
    'elasticache': list_elasticache,
    'asg': list_autoscaling_group,
    'sqs': list_sqs,
    'kinesisapp': list_kinesis_applications,
    'dynamodb': list_dynamodb
}

def main():

    template, namespace, region, filters, tokens = interpret_options()

    # get the template first so this can fail before making a network request
    fs_path = os.path.abspath(os.path.dirname(template))
    loader = jinja2.FileSystemLoader(fs_path)
    jinja2_env = jinja2.Environment(loader=loader)
    template = jinja2_env.get_template(os.path.basename(template))

    # insure a valid region is set
    if region not in [r.name for r in boto.ec2.regions()]:
        raise ValueError("Invalid region:{0}".format(region))

    # should I be using ARNs?
    try:
        resources = list_resources[namespace](region, filters)
    except KeyError:
        print('ERROR: AWS namespace "{}" not supported or does not exist'
              .format(namespace))
        sys.exit(1)

    # base tokens
    template_tokens = {
        'filters': filters,
        'region': region,       # Use for Auth config section if needed
        'resources': resources,
    }
    # add tokens passed as cli args:
    if tokens is not None:
        for token_pair in tokens:
            (key, value) = token_pair.split('=')
            template_tokens[key] = value

    print(template.render( template_tokens))

if __name__ == '__main__':
    main()
<commit_msg>whitespace<commit_after># -*- coding: UTF-8 -*-
"""
Usage:
  plumbum <template> <namespace> [region] [options]...

Options:
  template   path to the jinja2 template
  namespace  AWS namespace. Currently supports: elasticache, elb, ec2, rds, asg, sqs
  region     AWS region [default: us-east-1]
  options    key value combinations, they can be tags or any other property

Examples:

  plumbum elb.yaml.j2 elb
  plumbum elb.yaml.j2 elb us-west-2
  plumbum ec2.yaml.j2 ec2 environment=production
  plumbum ec2.yaml.j2 ec2 us-west-2 environment=production

Outputs to stdout.

About Templates:

Templates are used to generate config.yml files based on running resources.
They're written in jinja2, and have these variables available:

  filters    A dictionary of the filters that were passed in
  region     The region the resource is located in
  resources  A list of the resources as boto objects
"""
from __future__ import unicode_literals

import argparse
import re
import sys

import boto
import boto.dynamodb
import boto.ec2
import boto.ec2.elb
import boto.rds
import boto.elasticache
import boto.ec2.autoscale
import boto.kinesis
import boto.sqs
import jinja2
import os.path

__version__ = '0.9.0'

# DEFAULT_NAMESPACE = 'ec2'  # TODO
DEFAULT_REGION = 'us-east-1'

def get_property_func(key):
    """
    Get the accessor function for an instance to look for `key`.

    Look for it as an attribute, and if that does not work, look to see if it
    is a tag.
    """
    def get_it(obj):
        try:
            return getattr(obj, key)
        except AttributeError:
            return obj.tags.get(key)
    return get_it

def filter_key(filter_args):
    def filter_instance(instance):
        return all([value == get_property_func(key)(instance)
                    for key, value in filter_args.items()])
    return filter_instance

def lookup(instances, filter_by=None):
    if filter_by is not None:
        return list(filter(filter_key(filter_by), instances))
    return instances

def interpret_options(args=sys.argv[1:]):

    parser = argparse.ArgumentParser()
    parser.add_argument('--version', action='version', version=__version__)
    parser.add_argument("-r", "--region", help="AWS region", default=DEFAULT_REGION)
    parser.add_argument("-f", "--filter", help="filter to apply to AWS objects")
    parser.add_argument('--token', action='append', help='a key=value pair to use when populating templates')
    parser.add_argument("namespace", type=str, help="AWS namespace")
    parser.add_argument("template", type=str, help="the template to interpret")

    args = parser.parse_args(args=args)

    # Support 'ec2' (human friendly) and 'AWS/EC2' (how CloudWatch natively calls these things)
    namespace = args.namespace.rsplit('/', 2)[-1].lower()
    return args.template, namespace, args.region, args.filter, args.token

def list_ec2(region, filter_by_kwargs):
    """List running ec2 instances."""
    conn = boto.ec2.connect_to_region(region)
    instances = conn.get_only_instances()
    return lookup(instances, filter_by=filter_by_kwargs)

def list_elb(region, filter_by_kwargs):
    """List all load balancers."""
    conn = boto.ec2.elb.connect_to_region(region)
    instances = conn.get_all_load_balancers()
    return lookup(instances, filter_by=filter_by_kwargs)

def list_rds(region, filter_by_kwargs):
    """List all RDS thingys."""
    conn = boto.rds.connect_to_region(region)
    instances = conn.get_all_dbinstances()
    return lookup(instances, filter_by=filter_by_kwargs)

def list_elasticache(region, filter_by_kwargs):
    """List all ElastiCache Clusters."""
    conn = boto.elasticache.connect_to_region(region)
    req = conn.describe_cache_clusters()
    data = req["DescribeCacheClustersResponse"]["DescribeCacheClustersResult"]["CacheClusters"]
    clusters = [x['CacheClusterId'] for x in data]
    return clusters

def list_autoscaling_group(region, filter_by_kwargs):
    """List all Auto Scaling Groups."""
    conn = boto.ec2.autoscale.connect_to_region(region)
    groups = conn.get_all_groups()
    return lookup(groups, filter_by=filter_by_kwargs)

def list_sqs(region, filter_by_kwargs):
    """List all SQS Queues."""
    conn = boto.sqs.connect_to_region(region)
    queues = conn.get_all_queues()
    return lookup(queues, filter_by=filter_by_kwargs)

def list_kinesis_applications(region, filter_by_kwargs):
    """List all the kinesis applications along with the shards for each stream"""
    conn = boto.kinesis.connect_to_region(region)
    streams = conn.list_streams()['StreamNames']
    kinesis_streams = {}
    for stream_name in streams:
        shard_ids = []
        shards = conn.describe_stream(stream_name)['StreamDescription']['Shards']
        for shard in shards:
            shard_ids.append(shard['ShardId'])
        kinesis_streams[stream_name] = shard_ids
    return kinesis_streams

def list_dynamodb(region, filter_by_kwargs):
    """List all DynamoDB tables."""
    conn = boto.dynamodb.connect_to_region(region)
    tables = conn.list_tables()
    return lookup(tables, filter_by=filter_by_kwargs)

list_resources = {
    'ec2': list_ec2,
    'elb': list_elb,
    'rds': list_rds,
    'elasticache': list_elasticache,
    'asg': list_autoscaling_group,
    'sqs': list_sqs,
    'kinesisapp': list_kinesis_applications,
    'dynamodb': list_dynamodb
}

def main():

    template, namespace, region, filters, tokens = interpret_options()

    # get the template first so this can fail before making a network request
    fs_path = os.path.abspath(os.path.dirname(template))
    loader = jinja2.FileSystemLoader(fs_path)
    jinja2_env = jinja2.Environment(loader=loader)
    template = jinja2_env.get_template(os.path.basename(template))

    # insure a valid region is set
    if region not in [r.name for r in boto.ec2.regions()]:
        raise ValueError("Invalid region:{0}".format(region))

    # should I be using ARNs?
    try:
        resources = list_resources[namespace](region, filters)
    except KeyError:
        print('ERROR: AWS namespace "{}" not supported or does not exist'
              .format(namespace))
        sys.exit(1)

    # base tokens
    template_tokens = {
        'filters': filters,
        'region': region,  # Use for Auth config section if needed
        'resources': resources,
    }
    # add tokens passed as cli args:
    if tokens is not None:
        for token_pair in tokens:
            (key, value) = token_pair.split('=')
            template_tokens[key] = value

    print(template.render(template_tokens))

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


<commit_before>import re

from .generic import Caption
from .exceptions import MalformedFileError, MalformedCaptionError

TIMEFRAME_LINE_PATTERN = re.compile('\s*(\d+:\d{2}:\d{2}.\d{3})\s*-->\s*(\d+:\d{2}:\d{2}.\d{3})')

class WebVTTParser:

    def _parse_timeframe_line(self, line, line_number):
        """Parse timeframe line and return start and end timestamps"""
        res = re.match(TIMEFRAME_LINE_PATTERN, line)
        if not res:
            raise MalformedCaptionError('Invalid time format in line {}'.format(line_number))

        return res.group(1), res.group(2)

    def _parse(self, lines):
        c = None
        for index, line in enumerate(lines):
            if '-->' in line:
                start, end = self._parse_timeframe_line(line, index)
                c = Caption(start, end)
            elif len(line) > 0:
                if c is None:
                    raise MalformedCaptionError('Caption missing timeframe in line {}'.format(index + 1))
                else:
                    c.add_line(line)
            else:
                if c is None:
                    continue
                if not c.lines:
                    raise MalformedCaptionError('Caption missing text in line {}'.format(index + 1))

                self.captions.append(c)
                c = None

        if c is not None and c.lines:
            self.captions.append(c)

    def read(self, file):
        self.captions = []

        with open(file, encoding='utf-8') as f:
            lines = [line.strip() for line in f.readlines()]

        if len(lines) == 0:
            raise MalformedFileError('The file is empty')
        if 'WEBVTT' not in lines[0]:
            raise MalformedFileError('The file does not have a valid format')

        self._parse(lines[1:])

        return self<commit_msg>Change stripping of blank characters in caption text only from the right<commit_after>import re

from .generic import Caption
from .exceptions import MalformedFileError, MalformedCaptionError

TIMEFRAME_LINE_PATTERN = re.compile('\s*(\d+:\d{2}:\d{2}.\d{3})\s*-->\s*(\d+:\d{2}:\d{2}.\d{3})')

class WebVTTParser:

    def _parse_timeframe_line(self, line, line_number):
        """Parse timeframe line and return start and end timestamps"""
        res = re.match(TIMEFRAME_LINE_PATTERN, line)
        if not res:
            raise MalformedCaptionError('Invalid time format in line {}'.format(line_number))

        return res.group(1), res.group(2)

    def _parse(self, lines):
        c = None
        for index, line in enumerate(lines):
            if '-->' in line:
                start, end = self._parse_timeframe_line(line, index)
                c = Caption(start, end)
            elif len(line) > 0:
                if c is None:
                    raise MalformedCaptionError('Caption missing timeframe in line {}'.format(index + 1))
                else:
                    c.add_line(line)
            else:
                if c is None:
                    continue
                if not c.lines:
                    raise MalformedCaptionError('Caption missing text in line {}'.format(index + 1))

                self.captions.append(c)
                c = None

        if c is not None and c.lines:
            self.captions.append(c)

    def read(self, file):
        self.captions = []

        with open(file, encoding='utf-8') as f:
            lines = [line.rstrip() for line in f.readlines()]

        if len(lines) == 0:
            raise MalformedFileError('The file is empty')
        if 'WEBVTT' not in lines[0]:
            raise MalformedFileError('The file does not have a valid format')

        self._parse(lines[1:])

        return self<|endoftext|>