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

from __future__ import division, print_function, unicode_literals

import os
import sys
from configparser import RawConfigParser

import sphinx_rtd_theme

sys.path.insert(0, os.path.abspath('..'))
sys.path.append(os.path.dirname(__file__))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "readthedocs.settings.dev")

from django.conf import settings
from django.utils import timezone

import django
django.setup()

def get_version():
    """Return package version from setup.cfg."""
    config = RawConfigParser()
    config.read(os.path.join('..', 'setup.cfg'))
    return config.get('metadata', 'version')

sys.path.append(os.path.abspath('_ext'))
extensions = [
    'sphinx.ext.autosectionlabel',
    'sphinx.ext.autodoc',
    'sphinx.ext.intersphinx',
    'sphinxcontrib.httpdomain',
    'djangodocs',
    'doc_extensions',
    'sphinx_tabs.tabs',
    'sphinx-prompt',
    'recommonmark',
    'notfound.extension',
    'sphinx_search.extension',
]
templates_path = ['_templates']

source_suffix = ['.rst', '.md']

master_doc = 'index'
project = u'Read the Docs'
copyright = '2010-{}, Read the Docs, Inc & contributors'.format(
    timezone.now().year
)
version = get_version()
release = version
exclude_patterns = ['_build']
default_role = 'obj'
intersphinx_mapping = {
    'python': ('https://docs.python.org/3.6/', None),
    'django': ('https://docs.djangoproject.com/en/1.11/', 'https://docs.djangoproject.com/en/1.11/_objects/'),
    'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
}
htmlhelp_basename = 'ReadTheDocsdoc'
latex_documents = [
    ('index', 'ReadTheDocs.tex', u'Read the Docs Documentation',
     u'Eric Holscher, Charlie Leifer, Bobby Grace', 'manual'),
]
man_pages = [
    ('index', 'read-the-docs', u'Read the Docs Documentation',
     [u'Eric Holscher, Charlie Leifer, Bobby Grace'], 1)
]

exclude_patterns = [
    # 'api' # needed for ``make gettext`` to not die.
]

language = 'en'

locale_dirs = [
    'locale/',
]
gettext_compact = False

html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_logo = 'img/logo.svg'
html_theme_options = {
    'logo_only': True,
    'display_version': False,
}

# Activate autosectionlabel plugin
autosectionlabel_prefix_document = True

# sphinx-notfound-page
# https://github.com/readthedocs/sphinx-notfound-page
notfound_context = {
    'title': 'Page Not Found',
    'body': '''

Page Not Found

Sorry, we couldn't find that page.

Try using the search box or go to the homepage.

''',
}
linkcheck_ignore = [
    r'http://127\.0\.0\.1',
    r'http://localhost',
    r'https://yourproject\.readthedocs\.io',
    r'https?://docs\.example\.com',
    r'https://foo\.readthedocs\.io/projects',
    r'https://github\.com.+?#L\d+',
]

def setup(app):
    app.add_stylesheet('css/sphinx_prompt_css.css')
<commit_msg>added more links to linkcheck_ignore for a speedup of the test<commit_after># -*- coding: utf-8 -*-

from __future__ import division, print_function, unicode_literals

import os
import sys
from configparser import RawConfigParser

import sphinx_rtd_theme

sys.path.insert(0, os.path.abspath('..'))
sys.path.append(os.path.dirname(__file__))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "readthedocs.settings.dev")

from django.conf import settings
from django.utils import timezone

import django
django.setup()

def get_version():
    """Return package version from setup.cfg."""
    config = RawConfigParser()
    config.read(os.path.join('..', 'setup.cfg'))
    return config.get('metadata', 'version')

sys.path.append(os.path.abspath('_ext'))
extensions = [
    'sphinx.ext.autosectionlabel',
    'sphinx.ext.autodoc',
    'sphinx.ext.intersphinx',
    'sphinxcontrib.httpdomain',
    'djangodocs',
    'doc_extensions',
    'sphinx_tabs.tabs',
    'sphinx-prompt',
    'recommonmark',
    'notfound.extension',
    'sphinx_search.extension',
]
templates_path = ['_templates']

source_suffix = ['.rst', '.md']

master_doc = 'index'
project = u'Read the Docs'
copyright = '2010-{}, Read the Docs, Inc & contributors'.format(
    timezone.now().year
)
version = get_version()
release = version
exclude_patterns = ['_build']
default_role = 'obj'
intersphinx_mapping = {
    'python': ('https://docs.python.org/3.6/', None),
    'django': ('https://docs.djangoproject.com/en/1.11/', 'https://docs.djangoproject.com/en/1.11/_objects/'),
    'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
}
htmlhelp_basename = 'ReadTheDocsdoc'
latex_documents = [
    ('index', 'ReadTheDocs.tex', u'Read the Docs Documentation',
     u'Eric Holscher, Charlie Leifer, Bobby Grace', 'manual'),
]
man_pages = [
    ('index', 'read-the-docs', u'Read the Docs Documentation',
     [u'Eric Holscher, Charlie Leifer, Bobby Grace'], 1)
]

exclude_patterns = [
    # 'api' # needed for ``make gettext`` to not die.
]

language = 'en'

locale_dirs = [
    'locale/',
]
gettext_compact = False

html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_logo = 'img/logo.svg'
html_theme_options = {
    'logo_only': True,
    'display_version': False,
}

# Activate autosectionlabel plugin
autosectionlabel_prefix_document = True

# sphinx-notfound-page
# https://github.com/readthedocs/sphinx-notfound-page
notfound_context = {
    'title': 'Page Not Found',
    'body': '''

Page Not Found

Sorry, we couldn't find that page.

Try using the search box or go to the homepage.

''',
}
linkcheck_ignore = [
    r'http://127\.0\.0\.1',
    r'http://localhost',
    r'https://yourproject\.readthedocs\.io',
    r'https?://docs\.example\.com',
    r'https://foo\.readthedocs\.io/projects',
    r'https://github\.com.+?#L\d+',
    r'https://github\.com/readthedocs/readthedocs\.org/issues',
    r'https://github\.com/readthedocs/readthedocs\.org/pull',
    r'https://docs\.readthedocs\.io/\?rtd_search',
    r'https://readthedocs\.org/search',
]

def setup(app):
    app.add_stylesheet('css/sphinx_prompt_css.css')
<|endoftext|>


<commit_before>import csv

from django import forms
from django.contrib.contenttypes.models import ContentType
from django.core.files.base import ContentFile
from django.conf import settings

from csvimporter.models import CSV

class CSVForm(forms.ModelForm):
    class Meta:
        model = CSV
    exclude_types = getattr(settings, 'CSVIMPORTER_EXCLUDE', [])
    # TODO: this could be so much nicer.
    content_types = ContentType.objects.all()
    for t in exclude_types:
        if '.' in t:
            content_types = content_types.exclude(app_label__iexact=t.split('.')[0], model__iexact=t.split('.')[1].lower())
        else:
            content_types = content_types.exclude(app_label__iexact=t)
    content_type = forms.ModelChoiceField(queryset=content_types)

key_to_field_map = getattr(settings, 'CSVIMPORTER_KEY_TO_FIELD_MAP', lambda k: k.replace(' ','_').lower())
class CSVAssociateForm(forms.Form):
    def __init__(self, instance, *args, **kwargs):
        self.instance = instance
        # BUGFIX: this removes trailing spaces in fieldnames.
        instance.csv_file.file[0] = re.sub('\s+,', ',', instance.csv_file.file[0])
        self.reader = csv.DictReader(instance.csv_file)
        self.reader.next()
        self.klass = self.instance.content_type.model_class()
        choices = [(None,'---- (None)')] + [(f.name, f.name) for f in self.klass._meta.fields]
        # self.base_fields gets copied over to create self.fields in __init__
        self.base_fields = {}
        for field_name in self.reader.fieldnames:
            self.base_fields[field_name] = forms.ChoiceField(choices=choices, required=False)
            if key_to_field_map(field_name) in [f.name for f in self.klass._meta.fields]:
                self.base_fields[field_name].initial = key_to_field_map(field_name)
        super(CSVAssociateForm, self).__init__(*args, **kwargs)

    def save(self, request):
        # these are out here because we only need to retreive them from settings the once.
        transforms = getattr(settings, 'CSVIMPORTER_DATA_TRANSFORMS', {})
        for row in self.reader:
            data = {}
            for field_name in self.reader.fieldnames:
                data[self.cleaned_data[field_name]] = row[field_name]
            transform_key = '%s.%s' % (self.instance.content_type.app_label, self.instance.content_type.model)
            data = transforms.get(transform_key, lambda r, d: d)(request, data)
            new_obj = self.klass()
            for key in data.keys():
                setattr(new_obj, key, data[key])
            new_obj.save()
        self.instance.delete()<commit_msg>whoops; added a missing import<commit_after>import csv, re

from django import forms
from django.contrib.contenttypes.models import ContentType
from django.core.files.base import ContentFile
from django.conf import settings

from csvimporter.models import CSV

class CSVForm(forms.ModelForm):
    class Meta:
        model = CSV
    exclude_types = getattr(settings, 'CSVIMPORTER_EXCLUDE', [])
    # TODO: this could be so much nicer.
    content_types = ContentType.objects.all()
    for t in exclude_types:
        if '.' in t:
            content_types = content_types.exclude(app_label__iexact=t.split('.')[0], model__iexact=t.split('.')[1].lower())
        else:
            content_types = content_types.exclude(app_label__iexact=t)
    content_type = forms.ModelChoiceField(queryset=content_types)

key_to_field_map = getattr(settings, 'CSVIMPORTER_KEY_TO_FIELD_MAP', lambda k: k.replace(' ','_').lower())
class CSVAssociateForm(forms.Form):
    def __init__(self, instance, *args, **kwargs):
        self.instance = instance
        # BUGFIX: this removes trailing spaces in fieldnames.
        instance.csv_file.file[0] = re.sub('\s+,', ',', instance.csv_file.file[0])
        self.reader = csv.DictReader(instance.csv_file)
        self.reader.next()
        self.klass = self.instance.content_type.model_class()
        choices = [(None,'---- (None)')] + [(f.name, f.name) for f in self.klass._meta.fields]
        # self.base_fields gets copied over to create self.fields in __init__
        self.base_fields = {}
        for field_name in self.reader.fieldnames:
            self.base_fields[field_name] = forms.ChoiceField(choices=choices, required=False)
            if key_to_field_map(field_name) in [f.name for f in self.klass._meta.fields]:
                self.base_fields[field_name].initial = key_to_field_map(field_name)
        super(CSVAssociateForm, self).__init__(*args, **kwargs)

    def save(self, request):
        # these are out here because we only need to retreive them from settings the once.
        transforms = getattr(settings, 'CSVIMPORTER_DATA_TRANSFORMS', {})
        for row in self.reader:
            data = {}
            for field_name in self.reader.fieldnames:
                data[self.cleaned_data[field_name]] = row[field_name]
            transform_key = '%s.%s' % (self.instance.content_type.app_label, self.instance.content_type.model)
            data = transforms.get(transform_key, lambda r, d: d)(request, data)
            new_obj = self.klass()
            for key in data.keys():
                setattr(new_obj, key, data[key])
            new_obj.save()
        self.instance.delete()<|endoftext|>


<commit_before>from __future__ import unicode_literals

from copy import deepcopy

from django.contrib import admin
from django.utils.translation import ugettext_lazy as _

from mezzanine_agenda.models import Event, EventLocation
from mezzanine_agenda.admin import *

from mezzanine.conf import settings
from mezzanine.core.admin import DisplayableAdmin, OwnableAdmin

from festival.models import *

class ArtistAdmin(admin.ModelAdmin):

    model = Artist

class VideoAdmin(admin.ModelAdmin):

    model = Video
    filter_horizontal = ['artists']

class VideoAdminDisplayable(DisplayableAdmin):

    fieldsets = deepcopy(VideoAdmin.fieldsets)

class AudioAdmin(admin.ModelAdmin):

    model = Audio
    filter_horizontal = ['artists']

class AudioAdminDisplayable(DisplayableAdmin):

    fieldsets = deepcopy(AudioAdmin.fieldsets)

class ArtistAdminDisplayable(DisplayableAdmin):

    fieldsets = deepcopy(ArtistAdmin.fieldsets)

class PlaylistAdmin(admin.ModelAdmin):

    model = Playlist
    list_display = ('__unicode__',)
    filter_horizontal = ['audios']

class FeaturedAdmin(admin.ModelAdmin):

    model = Featured
    list_display = ('__unicode__',)
    filter_horizontal = ['artists', 'events', 'videos', 'pages', 'blogposts', 'pages', 'playlists']

admin.site.register(Artist, ArtistAdminDisplayable)
admin.site.register(Video, VideoAdminDisplayable)
admin.site.register(Audio, AudioAdminDisplayable)
admin.site.register(Playlist, PlaylistAdmin)
admin.site.register(Featured, FeaturedAdmin)
admin.site.register(VideoCategory)
<commit_msg>fix admin filters<commit_after>from __future__ import unicode_literals

from copy import deepcopy

from django.contrib import admin
from django.utils.translation import ugettext_lazy as _

from mezzanine_agenda.models import Event, EventLocation
from mezzanine_agenda.admin import *

from mezzanine.conf import settings
from mezzanine.core.admin import DisplayableAdmin, OwnableAdmin

from festival.models import *

class ArtistAdmin(admin.ModelAdmin):

    model = Artist

class VideoAdmin(admin.ModelAdmin):

    model = Video

class VideoAdminDisplayable(DisplayableAdmin):

    fieldsets = deepcopy(VideoAdmin.fieldsets)
    filter_horizontal = ['artists']

class AudioAdmin(admin.ModelAdmin):

    model = Audio

class AudioAdminDisplayable(DisplayableAdmin):

    fieldsets = deepcopy(AudioAdmin.fieldsets)
    filter_horizontal = ['artists']

class ArtistAdminDisplayable(DisplayableAdmin):

    fieldsets = deepcopy(ArtistAdmin.fieldsets)

class PlaylistAdmin(admin.ModelAdmin):

    model = Playlist
    list_display = ('__unicode__',)
    filter_horizontal = ['audios']

class FeaturedAdmin(admin.ModelAdmin):

    model = Featured
    list_display = ('__unicode__',)
    filter_horizontal = ['artists', 'events', 'videos', 'pages', 'blogposts', 'pages', 'playlists']

admin.site.register(Artist, ArtistAdminDisplayable)
admin.site.register(Video, VideoAdminDisplayable)
admin.site.register(Audio, AudioAdminDisplayable)
admin.site.register(Playlist, PlaylistAdmin)
admin.site.register(Featured, FeaturedAdmin)
admin.site.register(VideoCategory)
<|endoftext|>


<commit_before>"""Iperf benchmark
    https://github.com/esnet/iperf
"""
from __future__ import division

import json

from cached_property import cached_property

from hpcbench.api import (
    Benchmark,
    Metrics,
    MetricsExtractor,
)
from hpcbench.toolbox.process import find_executable

class IPERFExtractor(MetricsExtractor):
    """Parse JSON file written by stream to extract interested metrics
    """
    @property
    def metrics(self):
        return dict(
            bandwidth_receiver=Metrics.MegaBytesPerSecond,
            bandwidth_sender=Metrics.MegaBytesPerSecond,
            max_bandwidth=Metrics.MegaBytesPerSecond,
            retransmits=Metrics.Cardinal,
        )

    def extract_metrics(self, outdir, metas):
        bits_in_mb = float(8 * 1024 * 1024)
        with open(self.stdout(outdir)) as istr:
            data = json.load(istr)
        if not data['intervals']:
            raise Exception('Missing "intervals" in JSON: ')
        max_bits_per_second = max(
            [
                interval['sum']['bits_per_second']
                for interval in data['intervals']
            ]
        )
        sent = data['end']['sum_sent']
        received = data['end']['sum_received']
        return dict(
            max_bandwidth=max_bits_per_second / bits_in_mb,
            bandwidth_receiver=received['bits_per_second'] / bits_in_mb,
            bandwidth_sender=sent['bits_per_second'] / bits_in_mb,
            retransmits=sent['retransmits']
        )

class Iperf(Benchmark):
    """Benchmark wrapper for the HPLbench utility
    """
    name = 'iperf'

    description = "Provides TCP benchmark."

    DEFAULT_DEVICE = 'network'
    DEFAULT_EXECUTABLE = 'iperf3'
    DEFAULT_SERVER = "localhost"

    def __init__(self):
        # locate `stream_c` executable
        super(Iperf, self).__init__(
            attributes=dict(
                executable=Iperf.DEFAULT_EXECUTABLE,
                server=Iperf.DEFAULT_SERVER,
                options=[],
            )
        )

    @cached_property
    def executable(self):
        """Get absolute path to iperf executable
        """
        return find_executable(self.attributes['executable'])

    def execution_matrix(self, context):
        del context  # unused
        yield dict(
            category=Iperf.DEFAULT_DEVICE,
            command=[
                self.executable,
                '-c',
                self.attributes['server'],
                '-J',
            ] + self.attributes['options'],
        )

    @cached_property
    def metrics_extractors(self):
        return IPERFExtractor()
<commit_msg>iperf client use all available CPU<commit_after>"""Iperf benchmark
    https://github.com/esnet/iperf
"""
from __future__ import division

import json

from cached_property import cached_property

from hpcbench.api import (
    Benchmark,
    Metrics,
    MetricsExtractor,
)
from hpcbench.toolbox.process import (
    find_executable,
    physical_cpus,
)

class IPERFExtractor(MetricsExtractor):
    """Parse JSON file written by stream to extract interested metrics
    """
    @property
    def metrics(self):
        return dict(
            bandwidth_receiver=Metrics.MegaBytesPerSecond,
            bandwidth_sender=Metrics.MegaBytesPerSecond,
            max_bandwidth=Metrics.MegaBytesPerSecond,
            retransmits=Metrics.Cardinal,
        )

    def extract_metrics(self, outdir, metas):
        bits_in_mb = float(8 * 1024 * 1024)
        with open(self.stdout(outdir)) as istr:
            data = json.load(istr)
        if not data['intervals']:
            raise Exception('Missing "intervals" in JSON: ')
        max_bits_per_second = max(
            [
                interval['sum']['bits_per_second']
                for interval in data['intervals']
            ]
        )
        sent = data['end']['sum_sent']
        received = data['end']['sum_received']
        return dict(
            max_bandwidth=max_bits_per_second / bits_in_mb,
            bandwidth_receiver=received['bits_per_second'] / bits_in_mb,
            bandwidth_sender=sent['bits_per_second'] / bits_in_mb,
            retransmits=sent['retransmits']
        )

class Iperf(Benchmark):
    """Benchmark wrapper for the HPLbench utility
    """
    name = 'iperf'

    description = "Provides TCP benchmark."

    DEFAULT_DEVICE = 'network'
    DEFAULT_EXECUTABLE = 'iperf3'
    DEFAULT_SERVER = "localhost"

    def __init__(self):
        # locate `stream_c` executable
        super(Iperf, self).__init__(
            attributes=dict(
                executable=Iperf.DEFAULT_EXECUTABLE,
                server=Iperf.DEFAULT_SERVER,
                options=["-P", str(len(physical_cpus()))],
            )
        )

    @cached_property
    def executable(self):
        """Get absolute path to iperf executable
        """
        return find_executable(self.attributes['executable'])

    def execution_matrix(self, context):
        del context  # unused
        yield dict(
            category=Iperf.DEFAULT_DEVICE,
            command=[
                self.executable,
                '-c',
                self.attributes['server'],
                '-J',
            ] + self.attributes['options'],
        )

    @cached_property
    def metrics_extractors(self):
        return IPERFExtractor()
<|endoftext|>


<commit_before># This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import time

from configman.config_manager import RequiredConfig
from configman import Namespace

#==============================================================================
class TransactionExecutor(RequiredConfig):
    required_config = Namespace()

    #--------------------------------------------------------------------------
    def __init__(self, config, db_conn_context_source,
                 quit_check_callback=lambda: False):
        self.config = config
        self.db_conn_context_source = db_conn_context_source
        self.quit_check = quit_check_callback
        self.do_quit_check = True

    #--------------------------------------------------------------------------
    def __call__(self, function, *args, **kwargs):
        """execute a function within the context of a transaction"""
        if self.do_quit_check:
            self.quit_check()
        with self.db_conn_context_source() as connection:
            try:
                #self.config.logger.debug('starting transaction')
                result = function(connection, *args, **kwargs)
                connection.commit()
                return result
            except:
                if self.db_conn_context_source.in_transaction(connection):
                    connection.rollback()
                self.config.logger.error(
                  'Exception raised during transaction',
                  exc_info=True)
                self.db_conn_context_source.force_reconnect()
                raise

#==============================================================================
class TransactionExecutorWithInfiniteBackoff(TransactionExecutor):
    # back off times
    required_config = Namespace()
    required_config.add_option('backoff_delays',
                               default=[10, 30, 60, 120, 300, 300, 300, 300,
                                        300, 300],
                               doc='delays in seconds between retries',
                               from_string_converter=eval)
    # wait_log_interval
    required_config.add_option('wait_log_interval',
                               default=1,
                               doc='seconds between log during retries')

    #--------------------------------------------------------------------------
    def backoff_generator(self):
        """Generate a series of integers used for the length of the sleep
        between retries.  It produces after exhausting the list, it repeats
        the last value from the list forever.  This generator will never raise
        the StopIteration exception."""
        for x in self.config.backoff_delays:
            yield x
        while True:
            yield self.config.backoff_delays[-1]

    #--------------------------------------------------------------------------
    def responsive_sleep(self, seconds, wait_reason=''):
        """Sleep for the specified number of seconds, logging every
        'wait_log_interval' seconds with progress info."""
        for x in xrange(int(seconds)):
            if (self.config.wait_log_interval and
                not x % self.config.wait_log_interval):
                self.config.logger.debug(
                  '%s: %dsec of %dsec' % (wait_reason, x, seconds))
            self.quit_check()
            time.sleep(1.0)

    #--------------------------------------------------------------------------
    def __call__(self, function, *args, **kwargs):
        """execute a function within the context of a transaction"""
        for wait_in_seconds in self.backoff_generator():
            try:
                if self.do_quit_check:
                    self.quit_check()
                # self.db_conn_context_source is an instance of a
                # wrapper class on the actual connection driver
                with self.db_conn_context_source() as connection:
                    try:
                        result = function(connection, *args, **kwargs)
                        connection.commit()
                        return result
                    except:
                        if self.db_conn_context_source.in_transaction(
                                                                   connection):
                            connection.rollback()
                        raise
            except self.db_conn_context_source.conditional_exceptions, x:
                # these exceptions may or may not be retriable
                # the test is for is a last ditch effort to see if
                # we can retry
                if not self.db_conn_context_source.is_operational_exception(x):
                    self.config.logger.critical(
                      'Unrecoverable transaction error',
                       exc_info=True
                    )
                    raise
                self.config.logger.critical(
                  'transaction error eligible for retry',
                  exc_info=True)

            except self.db_conn_context_source.operational_exceptions:
                self.config.logger.critical(
                  'transaction error eligible for retry',
                  exc_info=True)
            self.db_conn_context_source.force_reconnect()
            self.config.logger.debug(
              'retry in %s seconds' % wait_in_seconds
            )
            self.responsive_sleep(
              wait_in_seconds,
              'waiting for retry after failure in transaction'
            )
        raise

#==============================================================================
class TransactionExecutorWithLimitedBackoff(
                                       TransactionExecutorWithInfiniteBackoff):
    #--------------------------------------------------------------------------
    def backoff_generator(self):
        """Generate a series of integers used for the length of the sleep
        between retries."""
        for x in self.config.backoff_delays:
            yield x
<commit_msg>Fix quit_check defaulting I broke in e927e7e0b56c9503e93b217940a23a40665b48f1.<commit_after># This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import time

from configman.config_manager import RequiredConfig
from configman import Namespace

#==============================================================================
class TransactionExecutor(RequiredConfig):
    required_config = Namespace()

    #--------------------------------------------------------------------------
    def __init__(self, config, db_conn_context_source,
                 quit_check_callback=None):
        self.config = config
        self.db_conn_context_source = db_conn_context_source
        self.quit_check = quit_check_callback or (lambda: False)
        self.do_quit_check = True

    #--------------------------------------------------------------------------
    def __call__(self, function, *args, **kwargs):
        """execute a function within the context of a transaction"""
        if self.do_quit_check:
            self.quit_check()
        with self.db_conn_context_source() as connection:
            try:
                #self.config.logger.debug('starting transaction')
                result = function(connection, *args, **kwargs)
                connection.commit()
                return result
            except:
                if self.db_conn_context_source.in_transaction(connection):
                    connection.rollback()
                self.config.logger.error(
                  'Exception raised during transaction',
                  exc_info=True)
                self.db_conn_context_source.force_reconnect()
                raise

#==============================================================================
class TransactionExecutorWithInfiniteBackoff(TransactionExecutor):
    # back off times
    required_config = Namespace()
    required_config.add_option('backoff_delays',
                               default=[10, 30, 60, 120, 300, 300, 300, 300,
                                        300, 300],
                               doc='delays in seconds between retries',
                               from_string_converter=eval)
    # wait_log_interval
    required_config.add_option('wait_log_interval',
                               default=1,
                               doc='seconds between log during retries')

    #--------------------------------------------------------------------------
    def backoff_generator(self):
        """Generate a series of integers used for the length of the sleep
        between retries.  It produces after exhausting the list, it repeats
        the last value from the list forever.  This generator will never raise
        the StopIteration exception."""
        for x in self.config.backoff_delays:
            yield x
        while True:
            yield self.config.backoff_delays[-1]

    #--------------------------------------------------------------------------
    def responsive_sleep(self, seconds, wait_reason=''):
        """Sleep for the specified number of seconds, logging every
        'wait_log_interval' seconds with progress info."""
        for x in xrange(int(seconds)):
            if (self.config.wait_log_interval and
                not x % self.config.wait_log_interval):
                self.config.logger.debug(
                  '%s: %dsec of %dsec' % (wait_reason, x, seconds))
            self.quit_check()
            time.sleep(1.0)

    #--------------------------------------------------------------------------
    def __call__(self, function, *args, **kwargs):
        """execute a function within the context of a transaction"""
        for wait_in_seconds in self.backoff_generator():
            try:
                if self.do_quit_check:
                    self.quit_check()
                # self.db_conn_context_source is an instance of a
                # wrapper class on the actual connection driver
                with self.db_conn_context_source() as connection:
                    try:
                        result = function(connection, *args, **kwargs)
                        connection.commit()
                        return result
                    except:
                        if self.db_conn_context_source.in_transaction(
                                                                   connection):
                            connection.rollback()
                        raise
            except self.db_conn_context_source.conditional_exceptions, x:
                # these exceptions may or may not be retriable
                # the test is for is a last ditch effort to see if
                # we can retry
                if not self.db_conn_context_source.is_operational_exception(x):
                    self.config.logger.critical(
                      'Unrecoverable transaction error',
                       exc_info=True
                    )
                    raise
                self.config.logger.critical(
                  'transaction error eligible for retry',
                  exc_info=True)

            except self.db_conn_context_source.operational_exceptions:
                self.config.logger.critical(
                  'transaction error eligible for retry',
                  exc_info=True)
            self.db_conn_context_source.force_reconnect()
            self.config.logger.debug(
              'retry in %s seconds' % wait_in_seconds
            )
            self.responsive_sleep(
              wait_in_seconds,
              'waiting for retry after failure in transaction'
            )
        raise

#==============================================================================
class TransactionExecutorWithLimitedBackoff(
                                       TransactionExecutorWithInfiniteBackoff):
    #--------------------------------------------------------------------------
    def backoff_generator(self):
        """Generate a series of integers used for the length of the sleep
        between retries."""
        for x in self.config.backoff_delays:
            yield x
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# Requests documentation build configuration file, created by
# sphinx-quickstart on Sun Feb 13 23:54:25 2011.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
import requests
from requests import __version__

# -- General configuration -----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'Requests'
copyright = u'2011. A Kenneth Reitz Project'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = version

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'flask_theme_support.FlaskyStyle'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'default'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
    'index':    ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
    '**':       ['sidebarlogo.html', 'localtoc.html', 'relations.html',
                 'sourcelink.html', 'searchbox.html']
}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
html_show_sphinx = False

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Output file base name for HTML help builder.
htmlhelp_basename = 'Requestsdoc'

# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ('index', 'Requests.tex', u'Requests Documentation',
   u'Kenneth Reitz', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output --------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ('index', 'requests', u'Requests Documentation',
     [u'Kenneth Reitz'], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output ------------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  ('index', 'Requests', u'Requests Documentation', u'Kenneth Reitz',
   'Requests', 'One line description of project.', 'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
texinfo_appendices = []

sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'kr'<commit_msg>"a kennethreitz project"<commit_after># -*- coding: utf-8 -*-
#
# Requests documentation build configuration file, created by
# sphinx-quickstart on Sun Feb 13 23:54:25 2011.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys, os

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
import requests
from requests import __version__

# -- General configuration -----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix of source filenames.
source_suffix = '.rst'

# The encoding of source files.
#source_encoding = 'utf-8-sig'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = u'Requests'
copyright = u'2011. A Kenneth Reitz Project'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = version

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'flask_theme_support.FlaskyStyle'

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = 'default'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
    'index':    ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
    '**':       ['sidebarlogo.html', 'localtoc.html', 'relations.html',
                 'sourcelink.html', 'searchbox.html']
}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
html_show_sphinx = False

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None

# Output file base name for HTML help builder.
htmlhelp_basename = 'Requestsdoc'

# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ('index', 'Requests.tex', u'Requests Documentation',
   u'Kenneth Reitz', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output --------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ('index', 'requests', u'Requests Documentation',
     [u'Kenneth Reitz'], 1)
]

# If true, show URL addresses after external links.
#man_show_urls = False

# -- Options for Texinfo output ------------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
  ('index', 'Requests', u'Requests Documentation', u'Kenneth Reitz',
   'Requests', 'One line description of project.', 'Miscellaneous'),
]

# Documents to append as an appendix to all manuals.
texinfo_appendices = []

sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'kr'<|endoftext|>


<commit_before>import re
from collections import Counter, defaultdict
from datetime import datetime

from hamper.interfaces import ChatCommandPlugin, Command
from hamper.utils import ude, uen

from sqlalchemy import Column, DateTime, Integer, String, func
from sqlalchemy.ext.declarative import declarative_base

SQLAlchemyBase = declarative_base()

class Karma(ChatCommandPlugin):
    '''Give, take, and scoreboard Internet Points'''

    """
    Hamper will look for lines that end in ++ or -- and modify that user's
    karma value accordingly

    NOTE: The user is just a string, this really could be anything...like
    potatoes or the infamous cookie clicker....
    """

    name = 'karma'

    priority = -2

    short_desc = ("karma - Give positive or negative karma. Where you see"
                  " !karma, !score will work as well")
    long_desc = ("username++ - Give karma\n"
                 "username-- - Take karma\n"
                 "!karma --top - Show the top 5 karma earners\n"
                 "!karma --bottom - Show the bottom 5 karma earners\n"
                 "!karma --giver or --taker - Show who's given the most"
                 " positive or negative karma\n"
                 "!karma --when-positive or --when-negative "
                 " - Show when people are the most positive or negative\n"
                 "!karma username - Show the user's karma count\n")

    gotta_catch_em_all = r"""# 3 or statement
                             (

                             # Starting with a (, look for anything within
                             # parens that end with 2 or more + or -
                             (?=\()[^\)]+\)(\+\++|--+) |

                             # Looking from the start of the line until 2 or
                             # more - or + are found. No whitespace in this
                             # grouping
                             ^[^\s]+(\+\++|--+) |

                             # Finally group any non-whitespace groupings
                             # that end with 2 or more + or -
                             [^\s]+?(\+\++|--+)((?=\s)|(?=$))
                            )
                          """

    regstr = re.compile(gotta_catch_em_all, re.X)

    def setup(self, loader):
        super(Karma, self).setup(loader)
        self.db = loader.db
        SQLAlchemyBase.metadata.create_all(self.db.engine)

    def message(self, bot, comm):
        """
        Check for strings ending with 2 or more '-' or '+'
        """
        super(Karma, self).message(bot, comm)

        # No directed karma giving or taking
        if not comm['directed'] and not comm['pm']:
            msg = comm['message'].strip().lower()
            # use the magic above
            words = self.regstr.findall(msg)
            # Do things to people
            karmas = self.modify_karma(words)
            # Notify the users they can't modify their own karma
            if comm['user'] in karmas.keys():
                if karmas[comm['user']] <= 0:
                    bot.reply(comm, "Don't be so hard on yourself.")
                else:
                    bot.reply(comm, "Tisk, tisk, no up'ing your own karma.")
            # Commit karma changes to the db
            self.update_db(comm["user"], karmas)

    def modify_karma(self, words):
        """
        Given a regex object, look through the groups and modify karma
        as necessary
        """

        # 'user': karma
        k = defaultdict(int)

        if words:
            # For loop through all of the group members
            for word_tuple in words:
                word = word_tuple[0]
                ending = word[-1]
                # This will either end with a - or +, if it's a - subract 1
                # kara, if it ends with a +, add 1 karma
                change = -1 if ending == '-' else 1
                # Now strip the ++ or -- from the end
                if '-' in ending:
                    word = word.rstrip('-')
                elif '+' in ending:
                    word = word.rstrip('+')
                # Check if surrounded by parens, if so, remove them
                if word.startswith('(') and word.endswith(')'):
                    word = word[1:-1]
                # Finally strip whitespace
                word = word.strip()
                # Add the user to the dict
                if word:
                    k[word] += change
        return k

    def update_db(self, giver, receiverkarma):
        """
        Record a the giver of karma, the receiver of karma, and the karma
        amount. Typically the count will be 1, but it can be any positive or
        negative integer.
        """

        for receiver in receiverkarma:
            if receiver != giver:
                urow = KarmaStatsTable(ude(giver), ude(receiver),
                                       receiverkarma[receiver])
                self.db.session.add(urow)
        self.db.session.commit()

    class KarmaList(Command):
        """
        Return the highest or lowest 5 receivers of karma
        """

        regex = r'^(?:score|karma) --(top|bottom)$'

        LIST_MAX = 5

        def command(self, bot, comm, groups):
            # Let the database restrict the amount of rows we get back.
            # We can then just deal with a few rows later on
            kts = bot.factory.loader.db.session\
                                    .query(KarmaStatsTable.receiver,
                                           func.sum(KarmaStatsTable.kcount))\
                                    .group_by(KarmaStatsTable.receiver)

            # Forlegacy support
            classic = bot.factory.loader.db.session.query(KarmaTable)
            # Counter for sorting and updating data
            counter = Counter()

            if kts.count() or classic.count():
                # We should limit the list of users to at most self.LIST_MAX
                if groups[0] == 'top':
                    classic_q = classic.order_by(KarmaTable.kcount.desc())\
                                       .limit(self.LIST_MAX).all()
                    query = kts.order_by(KarmaStatsTable.kcount.desc())\
                               .limit(self.LIST_MAX).all()

                    counter.update(dict(classic_q))
                    counter.update(dict(query))
                    snippet = counter.most_common(self.LIST_MAX)
                elif groups[0] == 'bottom':
                    classic_q = classic.order_by(KarmaTable.kcount)\
                                       .limit(self.LIST_MAX).all()
                    query = kts.order_by(KarmaStatsTable.kcount)\
                               .limit(self.LIST_MAX).all()
                    counter.update(dict(classic_q))
                    counter.update(dict(query))
                    snippet = reversed(counter.most_common(self.LIST_MAX))
                else:
                    bot.reply(
                        comm, r'Something went wrong with karma\'s regex'
                    )
                    return

                for rec in snippet:
                    bot.reply(
                        comm, '%s\x0f: %d' % (uen(rec[0]), rec[1]),
                        encode=False
                    )
            else:
                bot.reply(comm, r'No one has any karma yet :-(')

    class UserKarma(Command):
        """
        Retrieve karma for a given user
        """

        # !karma <username>
        regex = r'^(?:score|karma)\s+([^-].*)$'

        def command(self, bot, comm, groups):
            # Play nice when the user isn't in the db
            kt = bot.factory.loader.db.session.query(KarmaStatsTable)
            thing = ude(groups[0].strip().lower())
            rec_list = kt.filter(KarmaStatsTable.receiver == thing).all()

            if rec_list:
                total = 0
                for r in rec_list:
                    total += r.kcount
                bot.reply(
                    comm, '%s has %d points' % (uen(thing), total),
                    encode=False
                )
            else:
                bot.reply(
                    comm, 'No karma for %s ' % uen(thing), encode=False
                )

    class KarmaGiver(Command):
        """
        Identifies the person who gives the most karma
        """

        regex = r'^(?:score|karma) --(giver|taker)$'

        def command(self, bot, comm, groups):
            kt = bot.factory.loader.db.session.query(KarmaStatsTable)
            counter = Counter()

            if groups[0] == 'giver':
                positive_karma = kt.filter(KarmaStatsTable.kcount > 0)
                for row in positive_karma:
                    counter[row.giver] += row.kcount

                m = counter.most_common(1)
                most = m[0] if m else None
                if most:
                    bot.reply(
                        comm,
                        '%s has given the most karma (%d)' %
                        (uen(most[0]), most[1])
                    )
                else:
                    bot.reply(
                        comm,
                        'No positive karma has been given yet :-('
                    )
            elif groups[0] == 'taker':
                negative_karma = kt.filter(KarmaStatsTable.kcount < 0)
                for row in negative_karma:
                    counter[row.giver] += row.kcount

                m = counter.most_common()
                most = m[-1] if m else None
                if most:
                    bot.reply(
                        comm,
                        '%s has given the most negative karma (%d)' %
                        (uen(most[0]), most[1])
                    )
                else:
                    bot.reply(
                        comm,
                        'No negative karma has been given yet'
                    )

    class MostActive(Command):
        """
        Least/Most active hours of karma giving/taking
        """

        regex = r'^(?:score|karma)\s+--when-(positive|negative)'

        def command(self, bot, comm, groups):
            kt = bot.factory.loader.db.session.query(KarmaStatsTable)
            counter = Counter()

            if groups[0] == "positive":
                karma = kt.filter(KarmaStatsTable.kcount > 0)
            elif groups[0] == "negative":
                karma = kt.filter(KarmaStatsTable.kcount < 0)

            for row in karma:
                hour = row.datetime.hour
                counter[hour] += row.kcount

            common_hour = (counter.most_common(1)[0][0]
                           if counter.most_common(1) else None)
            title_case = groups[0][0].upper() + groups[0][1:]

            if common_hour:
                bot.reply(
                    comm,
                    '%s karma is usually given during the %d:00 hour (UTC)' %
                    (title_case, common_hour)
                )
            else:
                bot.reply(
                    comm,
                    '%s karma has been given yet' % title_case
                )

class KarmaTable(SQLAlchemyBase):
    """
    Bringing back the classic table so data doesn't need to be dumped
    """

    __tablename__ = 'karma'

    # Karma Classic Table
    user = Column(String, primary_key=True)
    kcount = Column(Integer)

    def __init__(self, user, kcount):
        self.user = user
        self.kcount = kcount

class KarmaStatsTable(SQLAlchemyBase):
    """
    Keep track of users karma in a persistant manner
    """

    __tablename__ = 'karmastats'

    # Calling the primary key user, though, really, this can be any string
    id = Column(Integer, primary_key=True)
    giver = Column(String)
    receiver = Column(String)
    kcount = Column(Integer)
    datetime = Column(DateTime, default=datetime.utcnow())

    def __init__(self, giver, receiver, kcount):
        self.giver = giver
        self.receiver = receiver
        self.kcount = kcount

karma = Karma()
<commit_msg>karma: karma totals work with old data now<commit_after>import re
from collections import Counter, defaultdict
from datetime import datetime

from hamper.interfaces import ChatCommandPlugin, Command
from hamper.utils import ude, uen

from sqlalchemy import Column, DateTime, Integer, String, func
from sqlalchemy.ext.declarative import declarative_base

SQLAlchemyBase = declarative_base()

class Karma(ChatCommandPlugin):
    '''Give, take, and scoreboard Internet Points'''

    """
    Hamper will look for lines that end in ++ or -- and modify that user's
    karma value accordingly

    NOTE: The user is just a string, this really could be anything...like
    potatoes or the infamous cookie clicker....
    """

    name = 'karma'

    priority = -2

    short_desc = ("karma - Give positive or negative karma. Where you see"
                  " !karma, !score will work as well")
    long_desc = ("username++ - Give karma\n"
                 "username-- - Take karma\n"
                 "!karma --top - Show the top 5 karma earners\n"
                 "!karma --bottom - Show the bottom 5 karma earners\n"
                 "!karma --giver or --taker - Show who's given the most"
                 " positive or negative karma\n"
                 "!karma --when-positive or --when-negative "
                 " - Show when people are the most positive or negative\n"
                 "!karma username - Show the user's karma count\n")

    gotta_catch_em_all = r"""# 3 or statement
                             (

                             # Starting with a (, look for anything within
                             # parens that end with 2 or more + or -
                             (?=\()[^\)]+\)(\+\++|--+) |

                             # Looking from the start of the line until 2 or
                             # more - or + are found. No whitespace in this
                             # grouping
                             ^[^\s]+(\+\++|--+) |

                             # Finally group any non-whitespace groupings
                             # that end with 2 or more + or -
                             [^\s]+?(\+\++|--+)((?=\s)|(?=$))
                            )
                          """

    regstr = re.compile(gotta_catch_em_all, re.X)

    def setup(self, loader):
        super(Karma, self).setup(loader)
        self.db = loader.db
        SQLAlchemyBase.metadata.create_all(self.db.engine)

    def message(self, bot, comm):
        """
        Check for strings ending with 2 or more '-' or '+'
        """
        super(Karma, self).message(bot, comm)

        # No directed karma giving or taking
        if not comm['directed'] and not comm['pm']:
            msg = comm['message'].strip().lower()
            # use the magic above
            words = self.regstr.findall(msg)
            # Do things to people
            karmas = self.modify_karma(words)
            # Notify the users they can't modify their own karma
            if comm['user'] in karmas.keys():
                if karmas[comm['user']] <= 0:
                    bot.reply(comm, "Don't be so hard on yourself.")
                else:
                    bot.reply(comm, "Tisk, tisk, no up'ing your own karma.")
            # Commit karma changes to the db
            self.update_db(comm["user"], karmas)

    def modify_karma(self, words):
        """
        Given a regex object, look through the groups and modify karma
        as necessary
        """

        # 'user': karma
        k = defaultdict(int)

        if words:
            # For loop through all of the group members
            for word_tuple in words:
                word = word_tuple[0]
                ending = word[-1]
                # This will either end with a - or +, if it's a - subract 1
                # kara, if it ends with a +, add 1 karma
                change = -1 if ending == '-' else 1
                # Now strip the ++ or -- from the end
                if '-' in ending:
                    word = word.rstrip('-')
                elif '+' in ending:
                    word = word.rstrip('+')
                # Check if surrounded by parens, if so, remove them
                if word.startswith('(') and word.endswith(')'):
                    word = word[1:-1]
                # Finally strip whitespace
                word = word.strip()
                # Add the user to the dict
                if word:
                    k[word] += change
        return k

    def update_db(self, giver, receiverkarma):
        """
        Record a the giver of karma, the receiver of karma, and the karma
        amount. Typically the count will be 1, but it can be any positive or
        negative integer.
        """

        for receiver in receiverkarma:
            if receiver != giver:
                urow = KarmaStatsTable(ude(giver), ude(receiver),
                                       receiverkarma[receiver])
                self.db.session.add(urow)
        self.db.session.commit()

    class KarmaList(Command):
        """
        Return the highest or lowest 5 receivers of karma
        """

        regex = r'^(?:score|karma) --(top|bottom)$'

        LIST_MAX = 5

        def command(self, bot, comm, groups):
            # Let the database restrict the amount of rows we get back.
            # We can then just deal with a few rows later on
            kts = bot.factory.loader.db.session\
                                    .query(KarmaStatsTable.receiver,
                                           func.sum(KarmaStatsTable.kcount))\
                                    .group_by(KarmaStatsTable.receiver)

            # Forlegacy support
            classic = bot.factory.loader.db.session.query(KarmaTable)
            # Counter for sorting and updating data
            counter = Counter()

            if kts.count() or classic.count():
                # We should limit the list of users to at most self.LIST_MAX
                if groups[0] == 'top':
                    classic_q = classic.order_by(KarmaTable.kcount.desc())\
                                       .limit(self.LIST_MAX).all()
                    query = kts.order_by(KarmaStatsTable.kcount.desc())\
                               .limit(self.LIST_MAX).all()

                    counter.update(dict(classic_q))
                    counter.update(dict(query))
                    snippet = counter.most_common(self.LIST_MAX)
                elif groups[0] == 'bottom':
                    classic_q = classic.order_by(KarmaTable.kcount)\
                                       .limit(self.LIST_MAX).all()
                    query = kts.order_by(KarmaStatsTable.kcount)\
                               .limit(self.LIST_MAX).all()
                    counter.update(dict(classic_q))
                    counter.update(dict(query))
                    snippet = reversed(counter.most_common(self.LIST_MAX))
                else:
                    bot.reply(
                        comm, r'Something went wrong with karma\'s regex'
                    )
                    return

                for rec in snippet:
                    bot.reply(
                        comm, '%s\x0f: %d' % (uen(rec[0]), rec[1]),
                        encode=False
                    )
            else:
                bot.reply(comm, r'No one has any karma yet :-(')

    class UserKarma(Command):
        """
        Retrieve karma for a given user
        """

        # !karma <username>
        regex = r'^(?:score|karma)\s+([^-].*)$'

        def command(self, bot, comm, groups):
            # The receiver (or in old terms, user) of the karma being tallied
            receiver = ude(groups[0].strip().lower())

            # Manage both tables
            sesh = bot.factory.loader.db.session

            # Old Table
            kt = sesh.query(KarmaTable)
            user = kt.filter(KarmaTable.user == receiver).first()

            # New Table
            kst = sesh.query(KarmaStatsTable)
            kst_list = kst.filter(KarmaStatsTable.receiver == receiver).all()

            # The total amount of karma from both tables
            total = 0

            # Add karma from the old table
            if user:
                total += user.kcount

            # Add karma from the new table
            if kst_list:
                for row in kst_list:
                    total += row.kcount

            # Send the message
            bot.reply(
                comm, '%s has %d points' % (uen(receiver), total),
                encode=False
            )

    class KarmaGiver(Command):
        """
        Identifies the person who gives the most karma
        """

        regex = r'^(?:score|karma) --(giver|taker)$'

        def command(self, bot, comm, groups):
            kt = bot.factory.loader.db.session.query(KarmaStatsTable)
            counter = Counter()

            if groups[0] == 'giver':
                positive_karma = kt.filter(KarmaStatsTable.kcount > 0)
                for row in positive_karma:
                    counter[row.giver] += row.kcount

                m = counter.most_common(1)
                most = m[0] if m else None
                if most:
                    bot.reply(
                        comm,
                        '%s has given the most karma (%d)' %
                        (uen(most[0]), most[1])
                    )
                else:
                    bot.reply(
                        comm,
                        'No positive karma has been given yet :-('
                    )
            elif groups[0] == 'taker':
                negative_karma = kt.filter(KarmaStatsTable.kcount < 0)
                for row in negative_karma:
                    counter[row.giver] += row.kcount

                m = counter.most_common()
                most = m[-1] if m else None
                if most:
                    bot.reply(
                        comm,
                        '%s has given the most negative karma (%d)' %
                        (uen(most[0]), most[1])
                    )
                else:
                    bot.reply(
                        comm,
                        'No negative karma has been given yet'
                    )

    class MostActive(Command):
        """
        Least/Most active hours of karma giving/taking
        """

        regex = r'^(?:score|karma)\s+--when-(positive|negative)'

        def command(self, bot, comm, groups):
            kt = bot.factory.loader.db.session.query(KarmaStatsTable)
            counter = Counter()

            if groups[0] == "positive":
                karma = kt.filter(KarmaStatsTable.kcount > 0)
            elif groups[0] == "negative":
                karma = kt.filter(KarmaStatsTable.kcount < 0)

            for row in karma:
                hour = row.datetime.hour
                counter[hour] += row.kcount

            common_hour = (counter.most_common(1)[0][0]
                           if counter.most_common(1) else None)
            title_case = groups[0][0].upper() + groups[0][1:]

            if common_hour:
                bot.reply(
                    comm,
                    '%s karma is usually given during the %d:00 hour (UTC)' %
                    (title_case, common_hour)
                )
            else:
                bot.reply(
                    comm,
                    '%s karma has been given yet' % title_case
                )

class KarmaTable(SQLAlchemyBase):
    """
    Bringing back the classic table so data doesn't need to be dumped
    """

    __tablename__ = 'karma'

    # Karma Classic Table
    user = Column(String, primary_key=True)
    kcount = Column(Integer)

    def __init__(self, user, kcount):
        self.user = user
        self.kcount = kcount

class KarmaStatsTable(SQLAlchemyBase):
    """
    Keep track of users karma in a persistant manner
    """

    __tablename__ = 'karmastats'

    # Calling the primary key user, though, really, this can be any string
    id = Column(Integer, primary_key=True)
    giver = Column(String)
    receiver = Column(String)
    kcount = Column(Integer)
    datetime = Column(DateTime, default=datetime.utcnow())

    def __init__(self, giver, receiver, kcount):
        self.giver = giver
        self.receiver = receiver
        self.kcount = kcount

karma = Karma()
<|endoftext|>


<commit_before>def fibgolf(t, n):
    I, o, l  = [1]*3, [0, 1], [1, 2]
    s = {'ib': (o, l, I),'ri': (o + [1], l + [3], I),'uc': ([2, 1], l, I),'ac': (o, l, l + [1]),'el': (o, l, [2, 1, 1]),'er': ([3, 0, 2], [2, 3], I),'ad': (o + [1], [2, 3], I)}
    a, F, m = s[t[1:3]]
    while len(a)<n+2: a.append(sum([m[i]*a[-d] for i, d in enumerate(F)]))
    return a[n]

def test_function():
    assert fibgolf(u'fibonacci', 10) == 55
    assert fibgolf(u'tribonacci', 10) == 149
    assert fibgolf(u'lucas', 10) == 123
    assert fibgolf(u'jacobsthal', 10) == 341
    assert fibgolf(u'pell', 10) == 2378
    assert fibgolf(u'perrin', 10) == 17
    assert fibgolf(u'padovan', 10) == 9

if __name__ == '__main__':
    test_function()

"""
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55,
0, 0, 1, 1, 2, 4, 7, 13, 24, 44, 81, 149,
2, 1, 3, 4, 7, 11, 18, 29, 47, 76, 123,
0, 1, 1, 3, 5, 11, 21, 43, 85, 171, 341,
0, 1, 2, 5, 12, 29, 70, 169, 408, 985, 2378,
3, 0, 2, 3, 2, 5, 5, 7, 10, 12, 17, 22,
1, 0, 0, 1, 0, 1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21, 28
"""
<commit_msg>deleted comment<commit_after>def fibgolf(t, n):
    I, o, l  = [1]*3, [0, 1], [1, 2]
    s = {'ib': (o, l, I),'ri': (o + [1], l + [3], I),'uc': ([2, 1], l, I),'ac': (o, l, l + [1]),'el': (o, l, [2, 1, 1]),'er': ([3, 0, 2], [2, 3], I),'ad': (o + [1], [2, 3], I)}
    a, F, m = s[t[1:3]]
    while len(a)<n+2: a.append(sum([m[i]*a[-d] for i, d in enumerate(F)]))
    return a[n]

def test_function():
    assert fibgolf(u'fibonacci', 10) == 55
    assert fibgolf(u'tribonacci', 10) == 149
    assert fibgolf(u'lucas', 10) == 123
    assert fibgolf(u'jacobsthal', 10) == 341
    assert fibgolf(u'pell', 10) == 2378
    assert fibgolf(u'perrin', 10) == 17
    assert fibgolf(u'padovan', 10) == 9

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


<commit_before>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Titanic documentation build configuration file, created by
# sphinx-quickstart on Sat Feb 18 14:31:37 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))

# -- General configuration ------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc']

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'

# The master toctree document.
master_doc = 'index'

# General information about the project.
project = 'Titanic'
copyright = '2017, Brooke Mosby'
author = 'Brooke Mosby'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.3'
# The full version, including alpha/beta/rc tags.
release = '1.3.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']

# -- Options for HTMLHelp output ------------------------------------------

# Output file base name for HTML help builder.
htmlhelp_basename = 'Titanicdoc'

# -- Options for LaTeX output ---------------------------------------------

latex_elements = {
    # The paper size ('letterpaper' or 'a4paper').
    #
    # 'papersize': 'letterpaper',

    # The font size ('10pt', '11pt' or '12pt').
    #
    # 'pointsize': '10pt',

    # Additional stuff for the LaTeX preamble.
    #
    # 'preamble': '',

    # Latex figure (float) alignment
    #
    # 'figure_align': 'htbp',
}

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
#  author, documentclass [howto, manual, or own class]).
latex_documents = [
    (master_doc, 'Titanic.tex', 'Titanic Documentation',
     'Brooke Mosby', 'manual'),
]

# -- Options for manual page output ---------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    (master_doc, 'titanic', 'Titanic Documentation',
     [author], 1)
]

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
#  dir menu entry, description, category)
texinfo_documents = [
    (master_doc, 'Titanic', 'Titanic Documentation',
     author, 'Titanic', 'One line description of project.',
     'Miscellaneous'),
]

<commit_msg>Delete conf.py<commit_after><|endoftext|>


<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-

# (c) 2012-2013, Timothy Appnel <tim@appnel.com>
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import os.path

from ansible.plugins.action import ActionBase
from ansible.utils.boolean import boolean
from ansible import constants

class ActionModule(ActionBase):

    def _get_absolute_path(self, path):
        if self._task._role is not None:
            original_path = path
            path = self._loader.path_dwim_relative(self._task._role._role_path, 'files', path)
            if original_path and original_path[-1] == '/' and path[-1] != '/':
                # make sure the dwim'd path ends in a trailing "/"
                # if the original path did
                path += '/'

        return path

    def _process_origin(self, host, path, user):

        if not host in ['127.0.0.1', 'localhost']:
            if user:
                return '%s@%s:%s' % (user, host, path)
            else:
                return '%s:%s' % (host, path)
        else:
            if not ':' in path:
                if not path.startswith('/'):
                    path = self._get_absolute_path(path=path)
            return path

    def _process_remote(self, host, path, user):
        transport = self._connection_info.connection
        return_data = None
        if not host in ['127.0.0.1', 'localhost'] or transport != "local":
            if user:
                return_data = '%s@%s:%s' % (user, host, path)
            else:
                return_data = '%s:%s' % (host, path)
        else:
            return_data = path

        if not ':' in return_data:
            if not return_data.startswith('/'):
                return_data = self._get_absolute_path(path=return_data)

        return return_data

    def run(self, tmp=None, task_vars=dict()):
        ''' generates params and passes them on to the rsync module '''

        original_transport = task_vars.get('ansible_connection') or self._connection_info.connection
        transport_overridden = False
        if task_vars.get('delegate_to') is None:
            task_vars['delegate_to'] = '127.0.0.1'
            # IF original transport is not local, override transport and disable sudo.
            if original_transport != 'local':
                task_vars['ansible_connection'] = 'local'
                transport_overridden = True
                self._connection_info.become = False

        src  = self._task.args.get('src', None)
        dest = self._task.args.get('dest', None)
        use_ssh_args = self._task.args.pop('use_ssh_args', None)

        # FIXME: this doesn't appear to be used anywhere?
        local_rsync_path = task_vars.get('ansible_rsync_path')

        # from the perspective of the rsync call the delegate is the localhost
        src_host  = '127.0.0.1'
        dest_host = task_vars.get('ansible_ssh_host') or task_vars.get('inventory_hostname')

        # allow ansible_ssh_host to be templated
        dest_is_local = dest_host in ['127.0.0.1', 'localhost']

        # CHECK FOR NON-DEFAULT SSH PORT
        dest_port = self._task.args.get('dest_port')
        inv_port  = task_vars.get('ansible_ssh_port') or task_vars.get('inventory_hostname')
        if inv_port != dest_port and inv_port != task_vars.get('inventory_hostname'):
            dest_port = inv_port

        # edge case: explicit delegate and dest_host are the same
        if dest_host == task_vars.get('delegate_to'):
            dest_host = '127.0.0.1'

        # SWITCH SRC AND DEST PER MODE
        if self._task.args.get('mode', 'push') == 'pull':
            (dest_host, src_host) = (src_host, dest_host)

        # CHECK DELEGATE HOST INFO
        use_delegate = False
        # FIXME: not sure if this is in connection info yet or not...
        #if conn.delegate != conn.host:
        #    if 'hostvars' in task_vars:
        #        if conn.delegate in task_vars['hostvars'] and original_transport != 'local':
        #            # use a delegate host instead of localhost
        #            use_delegate = True

        # COMPARE DELEGATE, HOST AND TRANSPORT
        process_args = False
        if not dest_host is src_host and original_transport != 'local':
            # interpret and task_vars remote host info into src or dest
            process_args = True

        # MUNGE SRC AND DEST PER REMOTE_HOST INFO
        if process_args or use_delegate:

            user = None
            if boolean(task_vars.get('set_remote_user', 'yes')):
                if use_delegate:
                    user = task_vars['hostvars'][conn.delegate].get('ansible_ssh_user')

                if not use_delegate or not user:
                    user = task_vars.get('ansible_ssh_user') or self._connection_info.remote_user

            if use_delegate:
                # FIXME
                private_key = task_vars.get('ansible_ssh_private_key_file') or self._connection_info.private_key_file
            else:
                private_key = task_vars.get('ansible_ssh_private_key_file') or self._connection_info.private_key_file

            if private_key is not None:
                private_key = os.path.expanduser(private_key)
                self._task.args['private_key'] = private_key

            # use the mode to define src and dest's url
            if self._task.args.get('mode', 'push') == 'pull':
                # src is a remote path: <user>@<host>, dest is a local path
                src  = self._process_remote(src_host, src, user)
                dest = self._process_origin(dest_host, dest, user)
            else:
                # src is a local path, dest is a remote path: <user>@<host>
                src  = self._process_origin(src_host, src, user)
                dest = self._process_remote(dest_host, dest, user)

            self._task.args['src'] = src
            self._task.args['dest'] = dest

        # Allow custom rsync path argument.
        rsync_path = self._task.args.get('rsync_path', None)

        # If no rsync_path is set, sudo was originally set, and dest is remote then add 'sudo rsync' argument.
        if not rsync_path and transport_overridden and self._connection_info.become and self._connection_info.become_method == 'sudo' and not dest_is_local:
            rsync_path = 'sudo rsync'

        # make sure rsync path is quoted.
        if rsync_path:
            self._task.args['rsync_path'] = '"%s"' % rsync_path

        if use_ssh_args:
            self._task.args['ssh_args'] = constants.ANSIBLE_SSH_ARGS

        # run the module and store the result
        result = self._execute_module('synchronize')

        return result

<commit_msg>Cleaning up some portions of synchronize action plugin (v2)<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-

# (c) 2012-2013, Timothy Appnel <tim@appnel.com>
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import os.path

from ansible.plugins.action import ActionBase
from ansible.utils.boolean import boolean
from ansible import constants

class ActionModule(ActionBase):

    def _get_absolute_path(self, path):
        if self._task._role is not None:
            original_path = path
            path = self._loader.path_dwim_relative(self._task._role._role_path, 'files', path)
            if original_path and original_path[-1] == '/' and path[-1] != '/':
                # make sure the dwim'd path ends in a trailing "/"
                # if the original path did
                path += '/'

        return path

    def _process_origin(self, host, path, user):

        if not host in ['127.0.0.1', 'localhost']:
            if user:
                return '%s@%s:%s' % (user, host, path)
            else:
                return '%s:%s' % (host, path)
        else:
            if not ':' in path:
                if not path.startswith('/'):
                    path = self._get_absolute_path(path=path)
            return path

    def _process_remote(self, host, path, user):
        transport = self._connection_info.connection
        return_data = None
        if not host in ['127.0.0.1', 'localhost'] or transport != "local":
            if user:
                return_data = '%s@%s:%s' % (user, host, path)
            else:
                return_data = '%s:%s' % (host, path)
        else:
            return_data = path

        if not ':' in return_data:
            if not return_data.startswith('/'):
                return_data = self._get_absolute_path(path=return_data)

        return return_data

    def run(self, tmp=None, task_vars=dict()):
        ''' generates params and passes them on to the rsync module '''

        original_transport = task_vars.get('ansible_connection') or self._connection_info.connection
        transport_overridden = False
        if task_vars.get('delegate_to') is None:
            task_vars['delegate_to'] = '127.0.0.1'
            # IF original transport is not local, override transport and disable sudo.
            if original_transport != 'local':
                task_vars['ansible_connection'] = 'local'
                transport_overridden = True
                self._connection_info.become = False

        src  = self._task.args.get('src', None)
        dest = self._task.args.get('dest', None)
        use_ssh_args = self._task.args.pop('use_ssh_args', None)

        # FIXME: this doesn't appear to be used anywhere?
        local_rsync_path = task_vars.get('ansible_rsync_path')

        # from the perspective of the rsync call the delegate is the localhost
        src_host  = '127.0.0.1'
        dest_host = task_vars.get('ansible_ssh_host') or task_vars.get('inventory_hostname')

        # allow ansible_ssh_host to be templated
        dest_is_local = dest_host in ['127.0.0.1', 'localhost']

        # CHECK FOR NON-DEFAULT SSH PORT
        dest_port = task_vars.get('ansible_ssh_port') or self._task.args.get('dest_port') or 22

        # edge case: explicit delegate and dest_host are the same
        if dest_host == task_vars.get('delegate_to'):
            dest_host = '127.0.0.1'

        # SWITCH SRC AND DEST PER MODE
        if self._task.args.get('mode', 'push') == 'pull':
            (dest_host, src_host) = (src_host, dest_host)

        # CHECK DELEGATE HOST INFO
        use_delegate = False
        # FIXME: not sure if this is in connection info yet or not...
        #if conn.delegate != conn.host:
        #    if 'hostvars' in task_vars:
        #        if conn.delegate in task_vars['hostvars'] and original_transport != 'local':
        #            # use a delegate host instead of localhost
        #            use_delegate = True

        # COMPARE DELEGATE, HOST AND TRANSPORT
        process_args = False
        if not dest_host is src_host and original_transport != 'local':
            # interpret and task_vars remote host info into src or dest
            process_args = True

        # MUNGE SRC AND DEST PER REMOTE_HOST INFO
        if process_args or use_delegate:

            user = None
            if boolean(task_vars.get('set_remote_user', 'yes')):
                if use_delegate:
                    user = task_vars['hostvars'][conn.delegate].get('ansible_ssh_user')

                if not use_delegate or not user:
                    user = task_vars.get('ansible_ssh_user') or self._connection_info.remote_user

            if use_delegate:
                # FIXME
                private_key = task_vars.get('ansible_ssh_private_key_file') or self._connection_info.private_key_file
            else:
                private_key = task_vars.get('ansible_ssh_private_key_file') or self._connection_info.private_key_file

            if private_key is not None:
                private_key = os.path.expanduser(private_key)
                self._task.args['private_key'] = private_key

            # use the mode to define src and dest's url
            if self._task.args.get('mode', 'push') == 'pull':
                # src is a remote path: <user>@<host>, dest is a local path
                src  = self._process_remote(src_host, src, user)
                dest = self._process_origin(dest_host, dest, user)
            else:
                # src is a local path, dest is a remote path: <user>@<host>
                src  = self._process_origin(src_host, src, user)
                dest = self._process_remote(dest_host, dest, user)

            self._task.args['src'] = src
            self._task.args['dest'] = dest

        # Allow custom rsync path argument.
        rsync_path = self._task.args.get('rsync_path', None)

        # If no rsync_path is set, sudo was originally set, and dest is remote then add 'sudo rsync' argument.
        if not rsync_path and transport_overridden and self._connection_info.become and self._connection_info.become_method == 'sudo' and not dest_is_local:
            rsync_path = 'sudo rsync'

        # make sure rsync path is quoted.
        if rsync_path:
            self._task.args['rsync_path'] = '"%s"' % rsync_path

        if use_ssh_args:
            self._task.args['ssh_args'] = constants.ANSIBLE_SSH_ARGS

        # run the module and store the result
        result = self._execute_module('synchronize')

        return result

<|endoftext|>