<commit_before># -*- coding: utf-8 -*-
from __future__ import unicode_literals

import six
import re

from django.utils.encoding import python_2_unicode_compatible

from django.db import models
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _

from unidecode import unidecode

import autoslug

from .settings import INDEX_SEARCH_NAMES, CITIES_LIGHT_APP_NAME

__all__ = ['AbstractCountry', 'AbstractRegion', 'AbstractCity',
    'CONTINENT_CHOICES']

CONTINENT_CHOICES = (
    ('OC', _('Oceania')),
    ('EU', _('Europe')),
    ('AF', _('Africa')),
    ('NA', _('North America')),
    ('AN', _('Antarctica')),
    ('SA', _('South America')),
    ('AS', _('Asia')),
)

ALPHA_REGEXP = re.compile('[\W_]+', re.UNICODE)

def to_ascii(value):
    """
    Convert a unicode value to ASCII-only unicode string.

    For example, 'République Françaisen' would become 'Republique Francaisen'
    """
    return force_text(unidecode(value))

def to_search(value):
    """
    Convert a string value into a string that is usable against
    City.search_names.

    For example, 'Paris Texas' would become 'paristexas'.
    """

    return ALPHA_REGEXP.sub('', to_ascii(value)).lower()

@python_2_unicode_compatible
class Base(models.Model):
    """
    Base model with boilerplate for all models.
    """

    name = models.CharField(max_length=200, db_index=True)
    name_ascii = models.CharField(max_length=200, blank=True, db_index=True)
    slug = autoslug.AutoSlugField(populate_from='name_ascii')
    geoname_id = models.IntegerField(null=True, blank=True, unique=True)
    alternate_names = models.TextField(null=True, blank=True, default='')

    class Meta:
        abstract = True
        ordering = ['name']

    def __str__(self):
        display_name = getattr(self, 'display_name', None)
        if display_name:
            return display_name
        return self.name

class AbstractCountry(Base):
    """
    Base Country model.
    """

    code2 = models.CharField(max_length=2, null=True, blank=True, unique=True)
    code3 = models.CharField(max_length=3, null=True, blank=True, unique=True)
    continent = models.CharField(max_length=2, db_index=True,
        choices=CONTINENT_CHOICES)
    tld = models.CharField(max_length=5, blank=True, db_index=True)
    phone = models.CharField(max_length=20, null=True, blank=True)

    class Meta(Base.Meta):
        verbose_name_plural = _('countries')
        abstract = True

class AbstractRegion(Base):
    """
    Base Region/State model.
    """

    display_name = models.CharField(max_length=200)
    geoname_code = models.CharField(max_length=50, null=True, blank=True,
        db_index=True)

    country = models.ForeignKey(CITIES_LIGHT_APP_NAME + '.Country',
        on_delete=models.CASCADE)

    class Meta(Base.Meta):
        unique_together = (('country', 'name'), ('country', 'slug'))
        verbose_name = _('region/state')
        verbose_name_plural = _('regions/states')
        abstract = True

    def get_display_name(self):
        return '%s, %s' % (self.name, self.country.name)

class ToSearchTextField(models.TextField):
    """
    Trivial TextField subclass that passes values through to_search
    automatically.
    """
    def get_prep_lookup(self, lookup_type, value):
        """
        Return the value passed through to_search().
        """
        value = super(ToSearchTextField, self).get_prep_lookup(lookup_type,
            value)
        return to_search(value)

class AbstractCity(Base):
    """
    Base City model.
    """

    display_name = models.CharField(max_length=200)

    search_names = ToSearchTextField(max_length=4000,
        db_index=INDEX_SEARCH_NAMES, blank=True, default='')

    latitude = models.DecimalField(max_digits=8, decimal_places=5,
        null=True, blank=True)
    longitude = models.DecimalField(max_digits=8, decimal_places=5,
        null=True, blank=True)

    region = models.ForeignKey(CITIES_LIGHT_APP_NAME + '.Region', blank=True,
                               null=True, on_delete=models.CASCADE)
    country = models.ForeignKey(CITIES_LIGHT_APP_NAME + '.Country',
                                on_delete=models.CASCADE)
    population = models.BigIntegerField(null=True, blank=True, db_index=True)
    feature_code = models.CharField(max_length=10, null=True, blank=True,
                                    db_index=True)
    timezone = models.CharField(max_length=40, blank=True, null=True,
                                db_index=True)

    class Meta(Base.Meta):
        unique_together = (('region', 'name'), ('region', 'slug'))
        verbose_name_plural = _('cities')
        abstract = True

    def get_display_name(self):
        if self.region_id:
            return '%s, %s, %s' % (self.name, self.region.name,
                self.country.name)
        else:
            return '%s, %s' % (self.name, self.country.name)
<commit_msg>Some pep8 fixes.<commit_after># -*- coding: utf-8 -*-
from __future__ import unicode_literals

import re

from django.utils.encoding import python_2_unicode_compatible

from django.db import models
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _

from unidecode import unidecode

import autoslug

from .settings import INDEX_SEARCH_NAMES, CITIES_LIGHT_APP_NAME

__all__ = ['AbstractCountry', 'AbstractRegion', 'AbstractCity',
           'CONTINENT_CHOICES']

CONTINENT_CHOICES = (
    ('OC', _('Oceania')),
    ('EU', _('Europe')),
    ('AF', _('Africa')),
    ('NA', _('North America')),
    ('AN', _('Antarctica')),
    ('SA', _('South America')),
    ('AS', _('Asia')),
)

ALPHA_REGEXP = re.compile('[\W_]+', re.UNICODE)

def to_ascii(value):
    """
    Convert a unicode value to ASCII-only unicode string.

    For example, 'République Françaisen' would become 'Republique Francaisen'
    """
    return force_text(unidecode(value))

def to_search(value):
    """
    Convert a string value into a string that is usable against
    City.search_names.

    For example, 'Paris Texas' would become 'paristexas'.
    """

    return ALPHA_REGEXP.sub('', to_ascii(value)).lower()

@python_2_unicode_compatible
class Base(models.Model):
    """
    Base model with boilerplate for all models.
    """

    name = models.CharField(max_length=200, db_index=True)
    name_ascii = models.CharField(max_length=200, blank=True, db_index=True)
    slug = autoslug.AutoSlugField(populate_from='name_ascii')
    geoname_id = models.IntegerField(null=True, blank=True, unique=True)
    alternate_names = models.TextField(null=True, blank=True, default='')

    class Meta:
        abstract = True
        ordering = ['name']

    def __str__(self):
        display_name = getattr(self, 'display_name', None)
        if display_name:
            return display_name
        return self.name

class AbstractCountry(Base):
    """
    Base Country model.
    """

    code2 = models.CharField(max_length=2, null=True, blank=True, unique=True)
    code3 = models.CharField(max_length=3, null=True, blank=True, unique=True)
    continent = models.CharField(max_length=2, db_index=True,
                                 choices=CONTINENT_CHOICES)
    tld = models.CharField(max_length=5, blank=True, db_index=True)
    phone = models.CharField(max_length=20, null=True, blank=True)

    class Meta(Base.Meta):
        verbose_name_plural = _('countries')
        abstract = True

class AbstractRegion(Base):
    """
    Base Region/State model.
    """

    display_name = models.CharField(max_length=200)
    geoname_code = models.CharField(max_length=50, null=True, blank=True,
                                    db_index=True)

    country = models.ForeignKey(CITIES_LIGHT_APP_NAME + '.Country',
                                on_delete=models.CASCADE)

    class Meta(Base.Meta):
        unique_together = (('country', 'name'), ('country', 'slug'))
        verbose_name = _('region/state')
        verbose_name_plural = _('regions/states')
        abstract = True

    def get_display_name(self):
        return '%s, %s' % (self.name, self.country.name)

class ToSearchTextField(models.TextField):
    """
    Trivial TextField subclass that passes values through to_search
    automatically.
    """
    def get_prep_lookup(self, lookup_type, value):
        """
        Return the value passed through to_search().
        """
        value = super(ToSearchTextField, self).get_prep_lookup(
            lookup_type,
            value)
        return to_search(value)

class AbstractCity(Base):
    """
    Base City model.
    """

    display_name = models.CharField(max_length=200)

    search_names = ToSearchTextField(
        max_length=4000,
        db_index=INDEX_SEARCH_NAMES,
        blank=True,
        default='')

    latitude = models.DecimalField(
        max_digits=8,
        decimal_places=5,
        null=True,
        blank=True)

    longitude = models.DecimalField(
        max_digits=8,
        decimal_places=5,
        null=True,
        blank=True)

    region = models.ForeignKey(CITIES_LIGHT_APP_NAME + '.Region', blank=True,
                               null=True, on_delete=models.CASCADE)
    country = models.ForeignKey(CITIES_LIGHT_APP_NAME + '.Country',
                                on_delete=models.CASCADE)
    population = models.BigIntegerField(null=True, blank=True, db_index=True)
    feature_code = models.CharField(max_length=10, null=True, blank=True,
                                    db_index=True)
    timezone = models.CharField(max_length=40, blank=True, null=True,
                                db_index=True)

    class Meta(Base.Meta):
        unique_together = (('region', 'name'), ('region', 'slug'))
        verbose_name_plural = _('cities')
        abstract = True

    def get_display_name(self):
        if self.region_id:
            return '%s, %s, %s' % (self.name, self.region.name,
                                   self.country.name)
        else:
            return '%s, %s' % (self.name, self.country.name)
<|endoftext|>


<commit_before>#(C) Marek Chrusciel,
#    Jakub Kosinski,
#    Marcin Krupowicz,
#    Mateusz Strycharski
#
# $Id$

import urllib
import urllib2
import types

class HTTPServices(object):

	def get_server(self,uin):
		"""
		Metoda pobiera z serwerow Gadu-Gadu adres serwera oraz port, na ktorym nasluchuje
		"""
		assert type(uin) == types.IntType

		url = 'http://appmsg.gadu-gadu.pl/appsvc/appmsg4.asp?fmnumber=' + str(uin) + '&version=7,7,0,3315&lastmsg=0'
		user_agent = 'Mozilla/4.7 [en] (Win98; I)'

		request = urllib2.Request(url)
		request.add_header('User-Agent', user_agent)
		response = urllib2.urlopen(request)
		info = response.read().split(' ')
		server = info[2].split(':')[0]
		port = info[2].split(':')[1]
		return server, int(port)

	get_server = classmethod(get_server)
<commit_msg>Klasa Token i HTTPServices.get_token_data<commit_after>#(C) Marek Chrusciel,
#    Jakub Kosinski,
#    Marcin Krupowicz,
#    Mateusz Strycharski
#
# $Id$

import urllib
import urllib2
import types

class Token(object):
	"""
	Klasa reprezentujaca dane tokena potrzebnego do rejestracji konta Gadu-Gadu
	"""
	def __init__(self, width, height, length, id, url):
		self.__width = width
		self.__height = height
		self.__length = length
		self.__id = id
		self.__url = url

	def __get_width(self):
		return self.__width

	def __get_height(self):
		return self.__height

	def __get_length(self):
		return self.__lenght

	def __get_id(self):
		return self.__id

	def __get_url(self):
		return self.__url

	width = property(__get_width)
	height = property(__get_height)
	length = property(__get_length)
	id = property(__get_id)
	url = property(__get_url)

class HTTPServices(object):

	def get_server(self,uin):
		"""
		Metoda pobiera z serwerow Gadu-Gadu adres serwera oraz port, na ktorym nasluchuje
		"""
		assert type(uin) == types.IntType

		url = 'http://appmsg.gadu-gadu.pl/appsvc/appmsg4.asp?fmnumber=' + str(uin) + '&version=7,7,0,3315&lastmsg=0'
		user_agent = 'Mozilla/4.7 [en] (Win98; I)'

		request = urllib2.Request(url)
		request.add_header('User-Agent', user_agent)

		response = urllib2.urlopen(request)
		info = response.read().split(' ')
		server = info[2].split(':')[0]
		port = info[2].split(':')[1]
		return server, int(port)

	def get_token_data(self):
		"""
		Metoda pobiera z serwera Gadu-Gadu dane tokena potrzebnego do rejestracji konta. Zwraca obiekt typu Token
		"""
		url = 'http://register.gadu-gadu.pl/appsvc/regtoken.asp'
		user_agent = 'Mozilla/4.0 (compatible; MSIE 5.0; Windows 98)'

		request = urllib2.Request(url)
		request.add_header('User-Agent', user_agent)

		response = urllib2.urlopen(request)
		info = response.read().replace('\r\n',' ').split(' ')
		width, height, length, id, url = info
		return Token(width, height, length, id, url)

	get_server = classmethod(get_server)
	get_token_data = classmethod(get_token_data)
<|endoftext|>


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

# Copyright (C) 2016-2017 Stadt Karlsruhe (www.karlsruhe.de)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import, print_function, unicode_literals

from ckan import plugins

class IExtractorPostprocessor(plugins.Interface):
    '''
    Postprocess extracted metadata.

    This interfaces provides hooks for postprocess resource metadata
    after its extraction. They can be used to modify the metadata before
    it is stored or to automatically trigger actions that use the
    extraction results for other purposes.

    Note that none of the hooks are called if an exception ocurred
    during the extraction.
    '''
    def extractor_after_extract(self, resource_dict, extracted):
        '''
        Postprocess metadata after extraction.

        Called directly after metadata has been extracted and
        normalized. ``extracted`` is a dict containing the metadata.

        Implementations can modify ``extracted`` in place, the return
        value of the function is ignored.

        Note that filtering of metadata according to
        ``ckanext.extractor.indexed_fields`` is done after this function
        is called.
        '''

    def extractor_after_save(self, resource_dict, metadata_dict):
        '''
        Postprocess metadata after it has been saved.

        Called after metadata has been extracted and saved in the
        database.

        ``metadata_dict`` is a dict-representation of the resource's
        ``ckanext.extractor.model.ResourceMetadata`` instance. Changes
        to ``metadata_dict`` and the return value of the function are
        ignored.

        Note: When this function is called the fields have been filtered
        according to ``ckanext.extractor.indexed_fields``, but the
        extracted metadata hasn't been indexed, yet.
        '''

    def extractor_after_index(self, resource_dict, metadata_dict):
        '''
        Postprocess metadata after it has been indexed.

        Called after the package of the resource whose metadata has been
        extracted has been re-indexed after the extraction.

        ``resource_dict`` and ``metadata_dict`` are dict-representations
        of the resource and the metadata, respectively. Changes to them
        and the return value of the function are ignored.

        Note: When this function is called the fields have been filtered
        according to ``ckanext.extractor.indexed_fields``.
        '''

class IExtractorRequest(plugins.Interface):
    '''
    Plugin into the requests made to download content for extraction.

    The hooks provide a possibility to change the parameters of the request,
    e.g. if the remote server requires certain headers or authorization tokens
    or to completly alter the URL.
    '''
    def extractor_before_request(self, request):
        '''
        Change the PreparedRequest object according to your needs

        This function must return a PreparedRequest object, which could be
        the original one after modifying it.

        See the requests documentation for details:
        http://docs.python-requests.org/en/master/user/advanced/
        '''
        return request
<commit_msg>Minor documentation improvements<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright (C) 2016-2017 Stadt Karlsruhe (www.karlsruhe.de)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import, print_function, unicode_literals

from ckan import plugins

class IExtractorPostprocessor(plugins.Interface):
    '''
    Postprocess extracted metadata.

    This interfaces provides hooks for postprocess resource metadata
    after its extraction. They can be used to modify the metadata before
    it is stored or to automatically trigger actions that use the
    extraction results for other purposes.

    Note that none of the hooks are called if an exception ocurred
    during the extraction.
    '''
    def extractor_after_extract(self, resource_dict, extracted):
        '''
        Postprocess metadata after extraction.

        Called directly after metadata has been extracted and
        normalized. ``extracted`` is a dict containing the metadata.

        Implementations can modify ``extracted`` in place, the return
        value of the function is ignored.

        Note that filtering of metadata according to
        ``ckanext.extractor.indexed_fields`` is done after this function
        is called.
        '''

    def extractor_after_save(self, resource_dict, metadata_dict):
        '''
        Postprocess metadata after it has been saved.

        Called after metadata has been extracted and saved in the
        database.

        ``metadata_dict`` is a dict-representation of the resource's
        ``ckanext.extractor.model.ResourceMetadata`` instance. Changes
        to ``metadata_dict`` and the return value of the function are
        ignored.

        Note: When this function is called the fields have been filtered
        according to ``ckanext.extractor.indexed_fields``, but the
        extracted metadata hasn't been indexed, yet.
        '''

    def extractor_after_index(self, resource_dict, metadata_dict):
        '''
        Postprocess metadata after it has been indexed.

        Called after the package of the resource whose metadata has been
        extracted has been re-indexed after the extraction.

        ``resource_dict`` and ``metadata_dict`` are dict-representations
        of the resource and the metadata, respectively. Changes to them
        and the return value of the function are ignored.

        Note: When this function is called the fields have been filtered
        according to ``ckanext.extractor.indexed_fields``.
        '''

class IExtractorRequest(plugins.Interface):
    '''
    Alter HTTP download requests.

    This interface allows you to modify the HTTP request for downloading
    a resource file, e.g. if the remote server requires certain
    authentication headers.
    '''
    def extractor_before_request(self, request):
        '''
        Alter a HTTP download request.

        ``request`` is a ``PreparedRequest`` object.

        This function must return a ``PreparedRequest`` object, which
        can either be the original request after it has been modified or
        a completely new one.

        Details can be found in the `requests documentation
        <http://docs.python-requests.org/en/master/user/advanced/>`_.
        '''
        return request
<|endoftext|>


<commit_before>'''Views and logic for github OAuth.'''

from flask import url_for, request, session
from flask_oauthlib.client import OAuth

def install_github_oauth(app):
    oauth = OAuth(app)
    github_auth = oauth.remote_app(
        'github',
        consumer_key=app.config['GITHUB_CLIENT_ID'],
        consumer_secret=app.config['GITHUB_CLIENT_SECRET'],
        request_token_params={'scope': 'repo,user'},
        base_url='https://api.github.com/',
        request_token_url=None,
        access_token_method='POST',
        access_token_url='https://github.com/login/oauth/access_token',
        authorize_url='https://github.com/login/oauth/authorize'
    )

    @app.route('/login')
    def login():
        return github_auth.authorize(callback=url_for('authorized', _external=True, next=request.args.get('next') or request.referrer or None))

    @app.route('/logout')
    def logout():
        session.pop('token', None)
        return redirect(url_for('index'))

    @app.route('/oauth_callback')
    @github_auth.authorized_handler
    def authorized(resp):
        next_url = request.args.get('next') or url_for('index')
        if resp is None:
            return 'Access denied: reason=%s error=%s' % (
                request.args['error_reason'],
                request.args['error_description']
            )
        session['token'] = resp['access_token']
        user_info = github.get_current_user_info(session['token'])
        if not user_info:
            return "Unable to get user info."
        session['login'] = user_info['login']
        return redirect(next_url)

    @github_auth.tokengetter
    def get_github_oauth_token():
        token = session.get('token')
        if token:
            return (token, '')
        else:
            return token

<commit_msg>more imports<commit_after>'''Views and logic for github OAuth.'''

from flask import url_for, request, session, redirect
from flask_oauthlib.client import OAuth

import github

def install_github_oauth(app):
    oauth = OAuth(app)
    github_auth = oauth.remote_app(
        'github',
        consumer_key=app.config['GITHUB_CLIENT_ID'],
        consumer_secret=app.config['GITHUB_CLIENT_SECRET'],
        request_token_params={'scope': 'repo,user'},
        base_url='https://api.github.com/',
        request_token_url=None,
        access_token_method='POST',
        access_token_url='https://github.com/login/oauth/access_token',
        authorize_url='https://github.com/login/oauth/authorize'
    )

    @app.route('/login')
    def login():
        return github_auth.authorize(callback=url_for('authorized', _external=True, next=request.args.get('next') or request.referrer or None))

    @app.route('/logout')
    def logout():
        session.pop('token', None)
        return redirect(url_for('index'))

    @app.route('/oauth_callback')
    @github_auth.authorized_handler
    def authorized(resp):
        next_url = request.args.get('next') or url_for('index')
        if resp is None:
            return 'Access denied: reason=%s error=%s' % (
                request.args['error_reason'],
                request.args['error_description']
            )
        session['token'] = resp['access_token']
        user_info = github.get_current_user_info(session['token'])
        if not user_info:
            return "Unable to get user info."
        session['login'] = user_info['login']
        return redirect(next_url)

    @github_auth.tokengetter
    def get_github_oauth_token():
        token = session.get('token')
        if token:
            return (token, '')
        else:
            return token

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Copyright (c) 2013 The Pycroft Authors. See the AUTHORS file.
# This file is part of the Pycroft project and licensed under the terms of
# the Apache License, Version 2.0. See the LICENSE file for details.

__author__ = 'florian'

from tests import FixtureDataTestBase
from pycroft.lib import user as UserHelper, user_config
from tests.helpers.fixtures.user_fixtures import DormitoryData, FinanceAccountData, \
    RoomData, UserData, UserNetDeviceData, UserHostData, IpData, VLanData, SubnetData, \
    PatchPortData, SemesterData, TrafficGroupData, PropertyGroupData, \
    PropertyData
from pycroft.model import user, dormitory, port, session, logging, finance, \
    property, host_alias, host
from datetime import datetime

class Test_010_User_Move(FixtureDataTestBase):
    datasets = [DormitoryData, RoomData, UserData, UserNetDeviceData, UserHostData,
                IpData, VLanData, SubnetData, PatchPortData]

    def setUp(self):
        super(Test_010_User_Move, self).setUp()
        self.user = user.User.q.get(1)
        self.processing_user = user.User.q.get(2)
        self.old_room = dormitory.Room.q.get(1)
        self.new_room_other_dormitory = dormitory.Room.q.get(2)
        self.new_room_same_dormitory = dormitory.Room.q.get(3)
        self.new_patch_port = port.PatchPort.q.get(2)

    def tearDown(self):
        #TODO don't delete all log entries but the user log entries
        logging.LogEntry.q.delete()
        session.session.commit()
        super(Test_010_User_Move, self).tearDown()

    def test_010_moves_into_same_room(self):
        self.assertRaises(AssertionError, UserHelper.move,
            self.user, self.old_room.dormitory, self.old_room.level,
            self.old_room.number, self.processing_user)

    def test_020_moves_into_other_dormitory(self):
        UserHelper.move(self.user, self.new_room_other_dormitory.dormitory,
            self.new_room_other_dormitory.level,
            self.new_room_other_dormitory.number, self.processing_user)
        self.assertEqual(self.user.room, self.new_room_other_dormitory)
        self.assertEqual(self.user.user_host.room, self.new_room_other_dormitory)
        #TODO test for changing ip

class Test_020_User_Move_In(FixtureDataTestBase):
    datasets = [DormitoryData, FinanceAccountData, RoomData, UserData,
                UserNetDeviceData, UserHostData, IpData, VLanData, SubnetData,
                PatchPortData, SemesterData, TrafficGroupData,
                PropertyGroupData, PropertyData]

    def setUp(self):
        super(Test_020_User_Move_In, self).setUp()
        self.processing_user = user.User.q.get(1)

    def tearDown(self):
        #TODO don't delete all log entries but the user log entries
        logging.LogEntry.q.delete()
        finance.Transaction.q.delete()
        session.session.commit()
        super(Test_020_User_Move_In, self).tearDown()

    def test_010_move_in(self):
        def get_initial_groups():
            initial_groups = []
            for group in user_config.initial_groups:
                initial_groups.append(property.Group.q.filter(
                    property.Group.name == group["group_name"]
                ).one())
            return initial_groups

        test_name = u"Hans"
        test_login = u"hans66"
        test_email = u"hans@hans.de"
        test_dormitory = dormitory.Dormitory.q.first()
        test_hostname = "hans"
        test_mac = "12:11:11:11:11:11"

        new_user = UserHelper.moves_in(test_name,
            test_login, test_email, test_dormitory,
            1,
            "1",
            test_hostname,
            test_mac,
            finance.Semester.q.first(),
            self.processing_user)

        self.assertEqual(new_user.name, test_name)
        self.assertEqual(new_user.login, test_login)
        self.assertEqual(new_user.email, test_email)
        self.assertEqual(new_user.room.dormitory, test_dormitory)
        self.assertEqual(new_user.room.number, "1")
        self.assertEqual(new_user.room.level, 1)
        self.assertEqual(new_user.user_host.user_net_device.mac, test_mac)

        user_host = host.UserHost.q.filter_by(user=new_user).one()
        user_net_device = host.UserNetDevice.q.filter_by(host=user_host).one()
        self.assertEqual(user_net_device.mac, test_mac)
        user_cnamerecord = host_alias.CNameRecord.q.filter_by(host=user_host).one()
        self.assertEqual(user_cnamerecord.name, test_hostname)
        user_arecord = host_alias.ARecord.q.filter_by(host=user_host).one()
        self.assertEqual(user_cnamerecord.alias_for, user_arecord)

        #checks the initial group memberhsips
        user_groups = new_user.active_property_groups + new_user.active_traffic_groups
        for group in get_initial_groups():
            self.assertIn(group, user_groups)

        self.assertEqual(UserHelper.has_internet(new_user), True)
        user_account = finance.FinanceAccount.q.filter(
                finance.FinanceAccount.user==new_user
            ).filter(
                finance.FinanceAccount.name==u"Nutzerid: %d" % new_user.id
            ).one()
        splits = finance.Split.q.filter(
                finance.Split.account_id == user_account.id
            ).all()
        account_sum = sum([split.amount for split in splits])
        self.assertEqual(account_sum,4000)

class Test_030_User_Move_Out(FixtureDataTestBase):
    datasets = [IpData, PatchPortData, SemesterData, TrafficGroupData,
                PropertyGroupData, FinanceAccountData]

    def setUp(self):
        super(Test_030_User_Move_Out, self).setUp()
        self.processing_user = user.User.q.get(1)

    def tearDown(self):
        logging.LogEntry.q.delete()
        finance.Transaction.q.delete()
        session.session.commit()
        super(Test_030_User_Move_Out, self).tearDown()

    def test_030_move_out(self):
        def get_initial_groups():
            initial_groups = []
            for group in user_config.initial_groups:
                initial_groups.append(property.Group.q.filter(
                    property.Group.name == group["group_name"]
                ).one())
            return initial_groups

        test_name = u"Hans"
        test_login = u"hans66"
        test_email = u"hans@hans.de"
        test_dormitory = dormitory.Dormitory.q.first()
        test_mac = "12:11:11:11:11:11"

        new_user = UserHelper.moves_in(test_name,
            test_login, test_email, test_dormitory,
            1,
            "1",
            None,
            test_mac,
            finance.Semester.q.first(),
            self.processing_user)

        out_time = datetime.now()

        UserHelper.move_out(new_user, out_time, "", self.processing_user)

        # check end_date of moved out user
        for membership in new_user.memberships:
            if membership.end_date >= out_time:
                self.assertEquals(membership.end_date, out_time)

        # check if users finance account still exists
        user_account = finance.FinanceAccount.q.filter(
            finance.FinanceAccount.user==new_user
        ).filter(
            finance.FinanceAccount.name==u"Nutzerid: %d" % new_user.id
        ).one()

class Test_040_User_Edit_Name(FixtureDataTestBase):
    datasets = [RoomData, DormitoryData, UserData]

    def setUp(self):
        super(Test_040_User_Edit_Name, self).setUp()
        self.user = user.User.q.get(2)

    def tearDown(self):
        logging.LogEntry.q.delete()
        session.session.commit()
        super(Test_040_User_Edit_Name, self).tearDown()

    def test_010_correct_new_name(self):
        print self.user.name
        print self.user.id

        UserHelper.edit_name(self.user, "toller neuer Name", self.user)

        self.assertEqual(self.user.name, "toller neuer Name")

    def test_020_name_zero_length(self):
        old_name = self.user.name

        UserHelper.edit_name(self.user, "", self.user)

        self.assertEqual(self.user.name, old_name)

class Test_050_User_Edit_Email(FixtureDataTestBase):
    datasets = [RoomData, DormitoryData, UserData]

    def setUp(self):
        super(Test_050_User_Edit_Email, self).setUp()
        self.user = user.User.q.get(2)

    def tearDown(self):
        logging.LogEntry.q.delete()
        session.session.commit()
        super(Test_050_User_Edit_Email, self).tearDown()

    def test_010_correct_new_email(self):
        UserHelper.edit_email(self.user, "sebastian@schrader.de", self.user)

        self.assertEqual(self.user.email, "sebastian@schrader.de")

    def test_020_email_zero_length(self):
        old_email = self.user.email

        UserHelper.edit_email(self.user, "", self.user)

        self.assertEqual(self.user.email, old_email)
<commit_msg>Changed the test methods names<commit_after># -*- coding: utf-8 -*-
# Copyright (c) 2013 The Pycroft Authors. See the AUTHORS file.
# This file is part of the Pycroft project and licensed under the terms of
# the Apache License, Version 2.0. See the LICENSE file for details.

__author__ = 'florian'

from tests import FixtureDataTestBase
from pycroft.lib import user as UserHelper, user_config
from tests.helpers.fixtures.user_fixtures import DormitoryData, FinanceAccountData, \
    RoomData, UserData, UserNetDeviceData, UserHostData, IpData, VLanData, SubnetData, \
    PatchPortData, SemesterData, TrafficGroupData, PropertyGroupData, \
    PropertyData
from pycroft.model import user, dormitory, port, session, logging, finance, \
    property, host_alias, host
from datetime import datetime

class Test_010_User_Move(FixtureDataTestBase):
    datasets = [DormitoryData, RoomData, UserData, UserNetDeviceData, UserHostData,
                IpData, VLanData, SubnetData, PatchPortData]

    def setUp(self):
        super(Test_010_User_Move, self).setUp()
        self.user = user.User.q.get(1)
        self.processing_user = user.User.q.get(2)
        self.old_room = dormitory.Room.q.get(1)
        self.new_room_other_dormitory = dormitory.Room.q.get(2)
        self.new_room_same_dormitory = dormitory.Room.q.get(3)
        self.new_patch_port = port.PatchPort.q.get(2)

    def tearDown(self):
        #TODO don't delete all log entries but the user log entries
        logging.LogEntry.q.delete()
        session.session.commit()
        super(Test_010_User_Move, self).tearDown()

    def test_0010_moves_into_same_room(self):
        self.assertRaises(AssertionError, UserHelper.move,
            self.user, self.old_room.dormitory, self.old_room.level,
            self.old_room.number, self.processing_user)

    def test_0020_moves_into_other_dormitory(self):
        UserHelper.move(self.user, self.new_room_other_dormitory.dormitory,
            self.new_room_other_dormitory.level,
            self.new_room_other_dormitory.number, self.processing_user)
        self.assertEqual(self.user.room, self.new_room_other_dormitory)
        self.assertEqual(self.user.user_host.room, self.new_room_other_dormitory)
        #TODO test for changing ip

class Test_020_User_Move_In(FixtureDataTestBase):
    datasets = [DormitoryData, FinanceAccountData, RoomData, UserData,
                UserNetDeviceData, UserHostData, IpData, VLanData, SubnetData,
                PatchPortData, SemesterData, TrafficGroupData,
                PropertyGroupData, PropertyData]

    def setUp(self):
        super(Test_020_User_Move_In, self).setUp()
        self.processing_user = user.User.q.get(1)

    def tearDown(self):
        #TODO don't delete all log entries but the user log entries
        logging.LogEntry.q.delete()
        finance.Transaction.q.delete()
        session.session.commit()
        super(Test_020_User_Move_In, self).tearDown()

    def test_0010_move_in(self):
        def get_initial_groups():
            initial_groups = []
            for group in user_config.initial_groups:
                initial_groups.append(property.Group.q.filter(
                    property.Group.name == group["group_name"]
                ).one())
            return initial_groups

        test_name = u"Hans"
        test_login = u"hans66"
        test_email = u"hans@hans.de"
        test_dormitory = dormitory.Dormitory.q.first()
        test_hostname = "hans"
        test_mac = "12:11:11:11:11:11"

        new_user = UserHelper.moves_in(test_name,
            test_login, test_email, test_dormitory,
            1,
            "1",
            test_hostname,
            test_mac,
            finance.Semester.q.first(),
            self.processing_user)

        self.assertEqual(new_user.name, test_name)
        self.assertEqual(new_user.login, test_login)
        self.assertEqual(new_user.email, test_email)
        self.assertEqual(new_user.room.dormitory, test_dormitory)
        self.assertEqual(new_user.room.number, "1")
        self.assertEqual(new_user.room.level, 1)
        self.assertEqual(new_user.user_host.user_net_device.mac, test_mac)

        user_host = host.UserHost.q.filter_by(user=new_user).one()
        user_net_device = host.UserNetDevice.q.filter_by(host=user_host).one()
        self.assertEqual(user_net_device.mac, test_mac)
        user_cnamerecord = host_alias.CNameRecord.q.filter_by(host=user_host).one()
        self.assertEqual(user_cnamerecord.name, test_hostname)
        user_arecord = host_alias.ARecord.q.filter_by(host=user_host).one()
        self.assertEqual(user_cnamerecord.alias_for, user_arecord)

        #checks the initial group memberhsips
        user_groups = new_user.active_property_groups + new_user.active_traffic_groups
        for group in get_initial_groups():
            self.assertIn(group, user_groups)

        self.assertEqual(UserHelper.has_internet(new_user), True)
        user_account = finance.FinanceAccount.q.filter(
                finance.FinanceAccount.user==new_user
            ).filter(
                finance.FinanceAccount.name==u"Nutzerid: %d" % new_user.id
            ).one()
        splits = finance.Split.q.filter(
                finance.Split.account_id == user_account.id
            ).all()
        account_sum = sum([split.amount for split in splits])
        self.assertEqual(account_sum,4000)

class Test_0030_User_Move_Out(FixtureDataTestBase):
    datasets = [IpData, PatchPortData, SemesterData, TrafficGroupData,
                PropertyGroupData, FinanceAccountData]

    def setUp(self):
        super(Test_0030_User_Move_Out, self).setUp()
        self.processing_user = user.User.q.get(1)

    def tearDown(self):
        logging.LogEntry.q.delete()
        finance.Transaction.q.delete()
        session.session.commit()
        super(Test_0030_User_Move_Out, self).tearDown()

    def test_0030_move_out(self):
        def get_initial_groups():
            initial_groups = []
            for group in user_config.initial_groups:
                initial_groups.append(property.Group.q.filter(
                    property.Group.name == group["group_name"]
                ).one())
            return initial_groups

        test_name = u"Hans"
        test_login = u"hans66"
        test_email = u"hans@hans.de"
        test_dormitory = dormitory.Dormitory.q.first()
        test_mac = "12:11:11:11:11:11"

        new_user = UserHelper.moves_in(test_name,
            test_login, test_email, test_dormitory,
            1,
            "1",
            None,
            test_mac,
            finance.Semester.q.first(),
            self.processing_user)

        out_time = datetime.now()

        UserHelper.move_out(new_user, out_time, "", self.processing_user)

        # check end_date of moved out user
        for membership in new_user.memberships:
            if membership.end_date >= out_time:
                self.assertEquals(membership.end_date, out_time)

        # check if users finance account still exists
        user_account = finance.FinanceAccount.q.filter(
            finance.FinanceAccount.user==new_user
        ).filter(
            finance.FinanceAccount.name==u"Nutzerid: %d" % new_user.id
        ).one()

class Test_040_User_Edit_Name(FixtureDataTestBase):
    datasets = [RoomData, DormitoryData, UserData]

    def setUp(self):
        super(Test_040_User_Edit_Name, self).setUp()
        self.user = user.User.q.get(2)

    def tearDown(self):
        logging.LogEntry.q.delete()
        session.session.commit()
        super(Test_040_User_Edit_Name, self).tearDown()

    def test_0010_correct_new_name(self):
        print self.user.name
        print self.user.id

        UserHelper.edit_name(self.user, "toller neuer Name", self.user)

        self.assertEqual(self.user.name, "toller neuer Name")

    def test_0020_name_zero_length(self):
        old_name = self.user.name

        UserHelper.edit_name(self.user, "", self.user)

        self.assertEqual(self.user.name, old_name)

class Test_050_User_Edit_Email(FixtureDataTestBase):
    datasets = [RoomData, DormitoryData, UserData]

    def setUp(self):
        super(Test_050_User_Edit_Email, self).setUp()
        self.user = user.User.q.get(2)

    def tearDown(self):
        logging.LogEntry.q.delete()
        session.session.commit()
        super(Test_050_User_Edit_Email, self).tearDown()

    def test_0010_correct_new_email(self):
        UserHelper.edit_email(self.user, "sebastian@schrader.de", self.user)

        self.assertEqual(self.user.email, "sebastian@schrader.de")

    def test_0020_email_zero_length(self):
        old_email = self.user.email

        UserHelper.edit_email(self.user, "", self.user)

        self.assertEqual(self.user.email, old_email)
<|endoftext|>


<commit_before># run unit tests on image utilities.

import numpy as np
from skimage import img_as_float
import psyutils as pu
from nose.tools import *

############# Testing guess_type ##############

def test_guess_type_1():
    im = np.ndarray((1, 1, 3))
    correct = "RGB"
    assert pu.image.guess_type(im) == correct

def test_guess_type_2():
    im = np.ndarray((1, 1, 4))
    correct = "RGBA"
    assert pu.image.guess_type(im) == correct

def test_guess_type_3():
    im = np.ndarray((1, 1, 2))
    correct = "IA"
    assert pu.image.guess_type(im) == correct

def test_guess_type_4():
    im = np.ndarray((1, 1))
    correct = "I"
    assert pu.image.guess_type(im) == correct

@raises(Exception)
def test_guess_type_5():
    im = np.ndarray((1, 1, 1, 1))
    pu.image.guess_type(im)

@raises(Exception)
def test_guess_type_6():
    im = np.ndarray((1, 1, 5))
    pu.image.guess_type(im)

############# Testing contrast_image ##############

def test_contrast_image_1():
    im = img_as_float(pu.im_data.tiger())
    im = pu.image.contrast_image(im, factor=0.2)
    min_range = round(im.min(), ndigits=2)
    max_range = round(im.max(), ndigits=2)
    mean = round(im.mean(), ndigits=2)
    sd = round(im.std(), ndigits=2)
    assert min_range == 0.21 \
        and max_range == 0.59 \
        and mean == 0.39 \
        and sd == 0.1

def test_contrast_image_2():
    im = img_as_float(pu.im_data.tiger())
    im = pu.image.contrast_image(im, factor=0.2, returns="contrast")
    min_range = round(im.min(), ndigits=2)
    max_range = round(im.max(), ndigits=2)
    mean = round(im.mean(), ndigits=2)
    sd = round(im.std(), ndigits=2)
    assert min_range == -0.1 \
        and max_range == 0.15 \
        and mean == -0.0 \
        and sd == 0.04

def test_contrast_image_3():
    im = img_as_float(pu.im_data.tiger_grey())
    im = pu.image.contrast_image(im, factor=0.1)
    min_range = round(im.min(), ndigits=2)
    max_range = round(im.max(), ndigits=2)
    mean = round(im.mean(), ndigits=2)
    sd = round(im.std(), ndigits=2)
    assert min_range == 0.46 \
        and max_range == 0.56 \
        and mean == 0.51 \
        and sd == 0.02

def test_contrast_image_4():
    im = img_as_float(pu.im_data.tiger_grey())
    im = pu.image.contrast_image(im, factor=0.1, returns="contrast")
    min_range = round(im.min(), ndigits=2)
    max_range = round(im.max(), ndigits=2)
    mean = round(im.mean(), ndigits=2)
    sd = round(im.std(), ndigits=2)
    assert min_range == -0.05 \
        and max_range == 0.05 \
        and mean == 0.0 \
        and sd == 0.02

# See
# http://docs.scipy.org/doc/numpy-dev/reference/generated/numpy.testing.assert_allclose.html#numpy.testing.assert_allclose<commit_msg>added a test for SD.<commit_after># run unit tests on image utilities.

import numpy as np
from skimage import img_as_float
import psyutils as pu
from nose.tools import *

############# Testing guess_type ##############

def test_guess_type_1():
    im = np.ndarray((1, 1, 3))
    correct = "RGB"
    assert pu.image.guess_type(im) == correct

def test_guess_type_2():
    im = np.ndarray((1, 1, 4))
    correct = "RGBA"
    assert pu.image.guess_type(im) == correct

def test_guess_type_3():
    im = np.ndarray((1, 1, 2))
    correct = "IA"
    assert pu.image.guess_type(im) == correct

def test_guess_type_4():
    im = np.ndarray((1, 1))
    correct = "I"
    assert pu.image.guess_type(im) == correct

@raises(Exception)
def test_guess_type_5():
    im = np.ndarray((1, 1, 1, 1))
    pu.image.guess_type(im)

@raises(Exception)
def test_guess_type_6():
    im = np.ndarray((1, 1, 5))
    pu.image.guess_type(im)

############# Testing contrast_image ##############

def test_contrast_image_1():
    im = img_as_float(pu.im_data.tiger())
    im = pu.image.contrast_image(im, factor=0.2)
    min_range = round(im.min(), ndigits=2)
    max_range = round(im.max(), ndigits=2)
    mean = round(im.mean(), ndigits=2)
    sd = round(im.std(), ndigits=2)
    assert min_range == 0.21 \
        and max_range == 0.59 \
        and mean == 0.39 \
        and sd == 0.1

def test_contrast_image_2():
    im = img_as_float(pu.im_data.tiger())
    im = pu.image.contrast_image(im, factor=0.2, returns="contrast")
    min_range = round(im.min(), ndigits=2)
    max_range = round(im.max(), ndigits=2)
    mean = round(im.mean(), ndigits=2)
    sd = round(im.std(), ndigits=2)
    assert min_range == -0.1 \
        and max_range == 0.15 \
        and mean == -0.0 \
        and sd == 0.04

def test_contrast_image_3():
    im = img_as_float(pu.im_data.tiger_grey())
    im = pu.image.contrast_image(im, factor=0.1)
    min_range = round(im.min(), ndigits=2)
    max_range = round(im.max(), ndigits=2)
    mean = round(im.mean(), ndigits=2)
    sd = round(im.std(), ndigits=2)
    assert min_range == 0.46 \
        and max_range == 0.56 \
        and mean == 0.51 \
        and sd == 0.02

def test_contrast_image_4():
    im = img_as_float(pu.im_data.tiger_grey())
    im = pu.image.contrast_image(im, factor=0.1, returns="contrast")
    min_range = round(im.min(), ndigits=2)
    max_range = round(im.max(), ndigits=2)
    mean = round(im.mean(), ndigits=2)
    sd = round(im.std(), ndigits=2)
    assert min_range == -0.05 \
        and max_range == 0.05 \
        and mean == 0.0 \
        and sd == 0.02

def test_contrast_image_5():
    im = img_as_float(pu.im_data.tiger_grey())
    im = pu.image.contrast_image(im, sd=0.1)
    min_range = round(im.min(), ndigits=2)
    max_range = round(im.max(), ndigits=2)
    mean = round(im.mean(), ndigits=2)
    sd = round(im.std(), ndigits=2)
    assert min_range == 0.25 \
        and max_range == 0.76 \
        and mean == 0.51 \
        and sd == 0.1

# See
# http://docs.scipy.org/doc/numpy-dev/reference/generated
# /numpy.testing.assert_allclose.html#numpy.testing.assert_allclose
<|endoftext|>


<commit_before>import click
import os
import glob
import popper.utils as pu
from popper.exceptions import BadArgumentUsage
from popper.cli import pass_context
from os.path import isfile, isdir, basename

@click.command('init', short_help='Initialize a Popper repo or pipeline.')
@click.argument('name', required=False)
@click.option(
    '--stages',
    help='Comma-separated list of stage names',
    show_default=True,
    default='setup,run,post-run,validate,teardown'
)
@click.option(
    '--envs',
    help='Comma-separated list of environments to use to run a pipeline',
    show_default=True,
    default='host'
)
@click.option(
    '--existing',
    help=('Treat NAME as a path and define a pipeline by '
          'creating an entry in .popper.yml for this existing folder.'),
    is_flag=True
)
@click.option(
    '--infer-stages',
    help=('Infers the name of the stages from the existing bash scripts'
          'on the folder while creating a new pipeline from the existing'
          'folder.'),
    is_flag=True
)
@pass_context
def cli(ctx, name, stages, envs, existing, infer_stages):
    """Initializes a repository or a pipeline. Without an argument, this
    command initializes a popper repository. If an argument is given, a
    pipeline or paper folder is initialized. If the given name is 'paper',
    then a 'paper' folder is created. Otherwise, a pipeline named NAME is
    created and initialized inside the 'pipelines' folder.

    By default, the stages of a pipeline are: setup, run, post-run, validate
    and teardown. To override these, the `--stages` flag can be provided, which
    expects a comma-separated list of stage names.

    The teardown stage is to be provided at the end if the --stages flag is
    being used.

    If the --existing flag is given, the NAME argument is treated as a path to
    a folder, which is assumed to contain bash scripts. --stages must be given.
    """

    # check if the the teardown stage is the last stage of the pipeline
    if stages and 'teardown' in stages and stages.split(',')[-1] != 'teardown':
        raise BadArgumentUsage(
            '--stages = Teardown should be the last stage.' +
            ' Consider renaming it or putting it at the end.')

    project_root = pu.get_project_root()

    # init repo
    if name is None:
        initialize_repo(project_root)
        return

    if not pu.is_popperized():
        pu.fail("Repository has not been popperized yet. See 'init --help'")

    if isdir(os.path.join(project_root, name)) and existing:
        # existing pipeline
        abs_path = os.path.join(project_root, name)
        relative_path = name
        if infer_stages:
            stages = ",".join(map(lambda x: x[:-3],
                                  sorted(glob.glob1(abs_path, '*.sh'))))
        else:
            initialize_existing_pipeline(abs_path, stages, envs)
    elif name == 'paper':
        # create a paper pipeline
        abs_path = os.path.join(project_root, 'paper')
        relative_path = os.path.join('paper')
        initialize_paper(abs_path, envs)
    else:
        # new pipeline
        abs_path = os.path.join(project_root, 'pipelines', name)
        relative_path = os.path.join('pipelines', name)
        initialize_new_pipeline(abs_path, stages, envs)

    pu.update_config(name, stages, envs, relative_path)

    pu.info('Initialized pipeline ' + name, fg='blue', bold=True)

def initialize_repo(project_root):
    """This function is used for initializing a popper repository."""

    if pu.is_popperized():
        pu.fail('Repository has already been popperized')

    with open(os.path.join(project_root, '.popper.yml'), 'w') as f:
        f.write('{ metadata: { }, pipelines: { } }\n')

    pu.info('Popperized repository ' + project_root, fg='blue', bold=True)

def initialize_existing_pipeline(pipeline_path, stages, envs):
    """This function is used for initalizing an existing pipeline."""

    for s in stages.split(','):
        s_filename = os.path.join(pipeline_path, s)
        if not isfile(s_filename) and not isfile(s_filename + '.sh'):
            pu.fail(
                ("Unable to find script for stage '" + s + "'. You might need "
                 "to provide values for the --stages flag. See 'init --help'.")
            )

def initialize_paper(paper_path, envs):
    """This function is used for initializing the special paper pipeline."""

    # create the paper folder
    if isdir(paper_path):
        pu.fail('The paper pipeline already exists')
    os.makedirs(paper_path)

    # write the build.sh bash script
    with open(os.path.join(paper_path, 'build.sh'), 'w') as f:
        f.write('#!/usr/bin/env bash\n')
        f.write('# [wf] execute build stage.')
        f.write('\n')
    os.chmod(os.path.join(paper_path, 'build.sh'), 0o755)

    # write README
    with open(os.path.join(paper_path, 'README',), 'w') as f:
        f.write('# ' + basename(paper_path) + '\n')

def initialize_new_pipeline(pipeline_path, stages, envs):
    """This function is used for initalizing a new pipeline."""

    # create folders
    if isdir(pipeline_path) or isfile(pipeline_path):
        pu.fail('File {} already exits'.format(pipeline_path))
    os.makedirs(pipeline_path)

    # write stage bash scripts
    for s in stages.split(','):
        if not s.endswith('.sh'):
            s = s + '.sh'

        with open(os.path.join(pipeline_path, s), 'w') as f:
            f.write('#!/usr/bin/env bash\n')
            f.write('# [wf] execute {} stage\n'.format(s))
            f.write('\n')
        os.chmod(os.path.join(pipeline_path, s), 0o755)

    # write README
    with open(os.path.join(pipeline_path, 'README'), 'w') as f:
        f.write('# ' + basename(pipeline_path) + '\n')
<commit_msg>Adds README as markdown file for popper init<commit_after>import click
import os
import glob
import popper.utils as pu
from popper.exceptions import BadArgumentUsage
from popper.cli import pass_context
from os.path import isfile, isdir, basename

@click.command('init', short_help='Initialize a Popper repo or pipeline.')
@click.argument('name', required=False)
@click.option(
    '--stages',
    help='Comma-separated list of stage names',
    show_default=True,
    default='setup,run,post-run,validate,teardown'
)
@click.option(
    '--envs',
    help='Comma-separated list of environments to use to run a pipeline',
    show_default=True,
    default='host'
)
@click.option(
    '--existing',
    help=('Treat NAME as a path and define a pipeline by '
          'creating an entry in .popper.yml for this existing folder.'),
    is_flag=True
)
@click.option(
    '--infer-stages',
    help=('Infers the name of the stages from the existing bash scripts'
          'on the folder while creating a new pipeline from the existing'
          'folder.'),
    is_flag=True
)
@pass_context
def cli(ctx, name, stages, envs, existing, infer_stages):
    """Initializes a repository or a pipeline. Without an argument, this
    command initializes a popper repository. If an argument is given, a
    pipeline or paper folder is initialized. If the given name is 'paper',
    then a 'paper' folder is created. Otherwise, a pipeline named NAME is
    created and initialized inside the 'pipelines' folder.

    By default, the stages of a pipeline are: setup, run, post-run, validate
    and teardown. To override these, the `--stages` flag can be provided, which
    expects a comma-separated list of stage names.

    The teardown stage is to be provided at the end if the --stages flag is
    being used.

    If the --existing flag is given, the NAME argument is treated as a path to
    a folder, which is assumed to contain bash scripts. --stages must be given.
    """

    # check if the the teardown stage is the last stage of the pipeline
    if stages and 'teardown' in stages and stages.split(',')[-1] != 'teardown':
        raise BadArgumentUsage(
            '--stages = Teardown should be the last stage.' +
            ' Consider renaming it or putting it at the end.')

    project_root = pu.get_project_root()

    # init repo
    if name is None:
        initialize_repo(project_root)
        return

    if not pu.is_popperized():
        pu.fail("Repository has not been popperized yet. See 'init --help'")

    if isdir(os.path.join(project_root, name)) and existing:
        # existing pipeline
        abs_path = os.path.join(project_root, name)
        relative_path = name
        if infer_stages:
            stages = ",".join(map(lambda x: x[:-3],
                                  sorted(glob.glob1(abs_path, '*.sh'))))
        else:
            initialize_existing_pipeline(abs_path, stages, envs)
    elif name == 'paper':
        # create a paper pipeline
        abs_path = os.path.join(project_root, 'paper')
        relative_path = os.path.join('paper')
        initialize_paper(abs_path, envs)
    else:
        # new pipeline
        abs_path = os.path.join(project_root, 'pipelines', name)
        relative_path = os.path.join('pipelines', name)
        initialize_new_pipeline(abs_path, stages, envs)

    pu.update_config(name, stages, envs, relative_path)

    pu.info('Initialized pipeline ' + name, fg='blue', bold=True)

def initialize_repo(project_root):
    """This function is used for initializing a popper repository."""

    if pu.is_popperized():
        pu.fail('Repository has already been popperized')

    with open(os.path.join(project_root, '.popper.yml'), 'w') as f:
        f.write('{ metadata: { }, pipelines: { } }\n')

    pu.info('Popperized repository ' + project_root, fg='blue', bold=True)

def initialize_existing_pipeline(pipeline_path, stages, envs):
    """This function is used for initalizing an existing pipeline."""

    for s in stages.split(','):
        s_filename = os.path.join(pipeline_path, s)
        if not isfile(s_filename) and not isfile(s_filename + '.sh'):
            pu.fail(
                ("Unable to find script for stage '" + s + "'. You might need "
                 "to provide values for the --stages flag. See 'init --help'.")
            )

def initialize_paper(paper_path, envs):
    """This function is used for initializing the special paper pipeline."""

    # create the paper folder
    if isdir(paper_path):
        pu.fail('The paper pipeline already exists')
    os.makedirs(paper_path)

    # write the build.sh bash script
    with open(os.path.join(paper_path, 'build.sh'), 'w') as f:
        f.write('#!/usr/bin/env bash\n')
        f.write('# [wf] execute build stage.')
        f.write('\n')
    os.chmod(os.path.join(paper_path, 'build.sh'), 0o755)

    # write README
    with open(os.path.join(paper_path, 'README.md',), 'w') as f:
        f.write('# ' + basename(paper_path) + '\n')

def initialize_new_pipeline(pipeline_path, stages, envs):
    """This function is used for initalizing a new pipeline."""

    # create folders
    if isdir(pipeline_path) or isfile(pipeline_path):
        pu.fail('File {} already exits'.format(pipeline_path))
    os.makedirs(pipeline_path)

    # write stage bash scripts
    for s in stages.split(','):
        if not s.endswith('.sh'):
            s = s + '.sh'

        with open(os.path.join(pipeline_path, s), 'w') as f:
            f.write('#!/usr/bin/env bash\n')
            f.write('# [wf] execute {} stage\n'.format(s))
            f.write('\n')
        os.chmod(os.path.join(pipeline_path, s), 0o755)

    # write README
    with open(os.path.join(pipeline_path, 'README'), 'w') as f:
        f.write('# ' + basename(pipeline_path) + '\n')
<|endoftext|>


<commit_before>import os
import sys
import time
import logging
import logging.config
import traceback
import itertools
from logging import StreamHandler
from slacker import Slacker
from slacksocket import SlackSocket
from .slack_request import SlackRequest

logger = logging.getLogger(__name__)

class SimpleSlackBot:
    """Simplifies interacting with the Slack API. Allows users to register functions to specific events, get those
    functions called when those specific events are triggered and run their business code
    """

    KEYBOARD_INTERRUPT_EXCEPTION_LOG_MESSAGE = "KeyboardInterrupt exception caught."
    SYSTEM_INTERRUPT_EXCEPTION_LOG_MESSAGE = "SystemExit exception caught."

    @staticmethod
    def peek(iterable):
        """Allows us to look at the next yield in an Iterable.
        From: https://stackoverflow.com/a/664239/1983957

        :param iterable: some Iterable to peek at
        :return: the first and rest of the yielded items
        """

        try:
            first = next(iterable)
        except StopIteration:
            return None
        return first, itertools.chain([first], iterable)

    @staticmethod
    def log_gracefully_shutdown(prefix_str):
        """Just a convenient way to log multiple messages in a similar way

        :param prefix_str: String to log in the begging
        :return: None
        """
        logger.info(f"{prefix_str} Gracefully shutting down")

    def __init__(self, debug=False):
        """Initializes our Slack bot and slack bot token. Will exit if the required environment
        variable is not set.

        :param debug: Whether or not to use default a Logging config
        """

        self._SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN")
        if self._SLACK_BOT_TOKEN is None:
            sys.exit("ERROR: environment variable SLACK_BOT_TOKEN is not set")

        self._slacker = Slacker(self._SLACK_BOT_TOKEN)
        self._slackSocket = SlackSocket(self._SLACK_BOT_TOKEN, translate=False)
        self._BOT_ID = self._slacker.auth.test().body["user_id"]
        self._registrations = {}  # our dictionary of event_types to a list of callbacks

        if debug:
            # Enable logging for our users
            logger.addHandler(StreamHandler())
            logger.setLevel(logging.DEBUG)

        logger.info(f"set bot id to {self._BOT_ID} with name {self.helper_user_id_to_user_name(self._BOT_ID)}")
        logger.info("initialized")

    def register(self, event_type):
        """Registers a callback function to a a event type. All supported even types are defined here
        https://api.slack.com/events-api

        :param event_type: the type of the event to register
        :return: reference to wrapped function
        """

        def function_wrapper(callback):
            """Registers event before executing wrapped function, referred to as callback

            :param callback: function to execute after runnign wrapped code
            :return: None
            """

            logger.info(f"registering callback {callback.__name__} to event type {event_type}")

            if event_type not in self._registrations:
                self._registrations[event_type] = []  # create an empty list
            self._registrations[event_type].append(callback)

        return function_wrapper

    def route_request_to_callbacks(self, request):
        """Routes the request to the correct notify

        :param request: request to be routed
        :return: None
        """

        logger.info(f"received an event of type {request.type} and slack event {request._slack_event.event}")

        if request.type in self._registrations:
            for callback in self._registrations[request.type]:
                try:
                    callback(request)
                except Exception as ex:
                    logger.exception(f'exception processing event {request.type}')

    def listen(self):
        """Listens forever for Slack events, triggering appropriately callbacks when respective events are received.
        Catches and logs all Exceptions except for KeyboardInterrupt or SystemExit, which it re-raises.
        """

        READ_WEBSOCKET_DELAY = 1  # 1 second delay between reading from fire hose
        running = True

        logger.info("began listening!")

        while running:  # required to continue to run after experiencing an unexpected exception
            res = self.peek(self._slackSocket.events())
            if res is None:
                self.log_gracefully_shutdown(self.KEYBOARD_INTERRUPT_EXCEPTION_LOG_MESSAGE)
                running = False
                break
            else:
                slack_event, mysequence = res

                if slack_event.event and "bot_id" not in slack_event.event:  # We don't reply to bots
                    try:
                        request = SlackRequest(self._slacker, slack_event)
                        self.route_request_to_callbacks(request)

                        time.sleep(READ_WEBSOCKET_DELAY)
                    except KeyboardInterrupt:
                        self.log_gracefully_shutdown(self.KEYBOARD_INTERRUPT_EXCEPTION_LOG_MESSAGE)
                        running = False
                        break
                    except SystemExit:
                        self.log_gracefully_shutdown(self.SYSTEM_INTERRUPT_EXCEPTION_LOG_MESSAGE)
                        running = False
                        break
                    except Exception as e:
                        logging.warning(f"Unexpected exception caught, but we will keep listening. Exception: {e}")
                        logging.warning(traceback.format_stack())
                        continue  # ensuring the loop continues

        logger.info("stopped listening!")

    def start(self):
        """Connect the Slack bot to the chatroom and begin listening
        """

        ok = self._slacker.rtm.start().body["ok"]

        if ok:
            logger.info("started!")
            self.listen()
        else:
            logger.error("Connection failed. Are you connected to the internet? Potentially invalid Slack token? "
                         "Check environment variable and \"SLACK_BOT_TOKEN\"")

        logger.info("stopped!")

    def get_slacker(self):
        """Returns SimpleSlackBot's SlackClient.

        This is useful if you are writing a more advanced bot and want complete access to all SlackClient has to offer.
        """

        return self._slacker

    def get_slack_socket(self):
        """Returns SimpleSlackBot's SlackSocket.

        This is useful if you are writing a more advanced bot and want complete access to all SlackSocket has to offer.
        """

        return self._slackSocket

    def helper_get_public_channel_ids(self):
        """Helper function that gets all public channel ids
        """

        public_channel_ids = []

        public_channels = self._slacker.channels.list().body["channels"]
        for channel in public_channels:
            public_channel_ids.append(channel["id"])

        if len(public_channel_ids) == 0:
            logger.warning("got no public channel ids")
        else:
            logger.debug(f"got public channel ids {public_channel_ids}")

        return public_channel_ids

    def helper_get_private_channel_ids(self):
        """Helper function that gets all private channel ids
        """

        private_channel_ids = []

        private_channels = self._slacker.groups.list().body["groups"]

        for private_channel in private_channels:
            private_channels.append(private_channel["id"])

        if len(private_channel_ids) == 0:
            logger.warning("got no private channel ids")
        else:
            logger.debug(f"got private channel ids {private_channel_ids}")

        return private_channel_ids

    def helper_get_user_ids(self):
        """Helper function that gets all user ids
        """

        user_ids = []

        users = self._slacker.users.list().body["members"]
        for user in users:
            user_ids.append(user["id"])

        if len(user_ids) == 0:
            logger.warning("got no user ids")
        else:
            logger.debug(f"got user ids {user_ids}")

        return user_ids

    def helper_get_user_names(self):
        """Helper function that gets all user names
        """

        user_names = []

        users = self._slacker.users.list().body["members"]
        for user in users:
            user_names.append(user["name"])

        if len(user_names) == 0:
            logger.warning("got no user names")
        else:
            logger.debug(f"got user names {user_names}")

        return user_names

    def helper_get_users_in_channel(self, channel_id):
        """Helper function that gets all users in a given channel id

        :param channel_id: channel id to get all user ids in it
        :return: list of user ids
        """

        user_ids = []

        channels_list = self._slacker.channels.list().body["channels"]
        for channel in channels_list:
            if channel["id"] == channel_id:
                for user_id in channel["members"]:
                    user_ids.append(user_id)

        if len(user_ids) == 0:
            logger.warning(f"got no user ids for channel {channel_id}")
        else:
            logger.debug(f"got user ids {user_ids}")

        return user_ids

    def helper_channel_name_to_channel_id(self, name):
        """Helper function that converts a channel name to its respected channel id

        :param name: name of channel to convert to id
        :return: id representation of original channel name
        """

        channels_list = self._slacker.channels.list().body["channels"]

        for channel in channels_list:
            if channel["name"] == name:
                logger.debug(f"converted {channel['name']} to {channel['id']}")
                return channel["id"]

        logger.warning(f"could not convert channel name {name} to an id")

    def helper_user_name_to_user_id(self, name):
        """Helper function that converts a user name to its respected user id

        :param name: name of user to convert to id
        :return: id representation of original user name
        """

        users = self._slacker.users.list().body["members"]

        for user in users:
            if user["name"] == name:
                logger.debug(f"converted {name} to {user['id']}")
                return user["id"]

        logger.warning(f"could not convert user name {name} to a user id")

    def helper_channel_id_to_channel_name(self, channel_id):
        """Helper function that converts a channel id to its respected channel name

        :param channel_id: id of channel to convert to name
        :return: name representation of original channel id
        """

        channels_list = self._slacker.channels.list().body["channels"]

        for channel in channels_list:
            if channel["id"] == channel_id:
                logger.debug("converted {} to {}".format(channel_id, channel["name"]))
                return channel["name"]

        logger.warning(f"could not convert channel id {channel_id} to a name")

    def helper_user_id_to_user_name(self, user_id):
        """Helper function that converts a user id to its respected user name

        :param user_id: id of user to convert to name
        :return: name representation of original user id
        """

        users_list = self._slacker.users.list()

        for user in users_list.body["members"]:
            if user["id"] == user_id:
                logger.debug(f"converted {user_id} to {user['name']}")
                return user["name"]

        logger.warning(f"could not convert user id {user_id} to a name")
<commit_msg>Added function comment to explain core function.<commit_after>import os
import sys
import time
import logging
import logging.config
import traceback
import itertools
from logging import StreamHandler
from slacker import Slacker
from slacksocket import SlackSocket
from .slack_request import SlackRequest

logger = logging.getLogger(__name__)

class SimpleSlackBot:
    """Simplifies interacting with the Slack API. Allows users to register functions to specific events, get those
    functions called when those specific events are triggered and run their business code
    """

    KEYBOARD_INTERRUPT_EXCEPTION_LOG_MESSAGE = "KeyboardInterrupt exception caught."
    SYSTEM_INTERRUPT_EXCEPTION_LOG_MESSAGE = "SystemExit exception caught."

    @staticmethod
    def peek(iterable):
        """Allows us to look at the next yield in an Iterable.
        From: https://stackoverflow.com/a/664239/1983957

        :param iterable: some Iterable to peek at
        :return: the first and rest of the yielded items
        """

        try:
            first = next(iterable)
        except StopIteration:
            return None
        return first, itertools.chain([first], iterable)

    @staticmethod
    def log_gracefully_shutdown(prefix_str):
        """Just a convenient way to log multiple messages in a similar way

        :param prefix_str: String to log in the begging
        :return: None
        """
        logger.info(f"{prefix_str} Gracefully shutting down")

    def __init__(self, debug=False):
        """Initializes our Slack bot and slack bot token. Will exit if the required environment
        variable is not set.

        :param debug: Whether or not to use default a Logging config
        """

        self._SLACK_BOT_TOKEN = os.environ.get("SLACK_BOT_TOKEN")
        if self._SLACK_BOT_TOKEN is None:
            sys.exit("ERROR: environment variable SLACK_BOT_TOKEN is not set")

        self._slacker = Slacker(self._SLACK_BOT_TOKEN)
        self._slackSocket = SlackSocket(self._SLACK_BOT_TOKEN, translate=False)
        self._BOT_ID = self._slacker.auth.test().body["user_id"]
        self._registrations = {}  # our dictionary of event_types to a list of callbacks

        if debug:
            # Enable logging for our users
            logger.addHandler(StreamHandler())
            logger.setLevel(logging.DEBUG)

        logger.info(f"set bot id to {self._BOT_ID} with name {self.helper_user_id_to_user_name(self._BOT_ID)}")
        logger.info("initialized")

    def register(self, event_type):
        """Registers a callback function to a a event type. All supported even types are defined here
        https://api.slack.com/events-api

        :param event_type: the type of the event to register
        :return: reference to wrapped function
        """

        def function_wrapper(callback):
            """Registers event before executing wrapped function, referred to as callback

            :param callback: function to execute after runnign wrapped code
            :return: None
            """

            logger.info(f"registering callback {callback.__name__} to event type {event_type}")

            if event_type not in self._registrations:
                self._registrations[event_type] = []  # create an empty list
            self._registrations[event_type].append(callback)

        return function_wrapper

    def route_request_to_callbacks(self, request):
        """Routes the request to the correct notify

        :param request: request to be routed
        :return: None
        """

        logger.info(f"received an event of type {request.type} and slack event {request._slack_event.event}")

        if request.type in self._registrations:
            for callback in self._registrations[request.type]:
                try:
                    callback(request)
                except Exception as ex:
                    logger.exception(f'exception processing event {request.type}')

    def listen(self):
        """Listens forever for Slack events, triggering appropriately callbacks when respective events are received.
        Catches and logs all Exceptions except for KeyboardInterrupt or SystemExit, which gracefully shuts down program.

        The following function is crucial to Simple Slack Bot and looks a little messy. This is do partly to the way
        that our dependency SlackSocket is written. They do not re-raise any caught KeyboardInterrupt exceptions and
        instead we have to infer one was caught based on what their generator returns. This is incredibly unfortunate,
        but this currently works. Since most of Simple Slack Bot's time is spent blocked on waiting for events from
        SlackSocket, a solution was needed to deal with this. Otherwise our application would not respond to a request
        from the user to stop the program with a CTRL + C.
        """

        READ_WEBSOCKET_DELAY = 1  # 1 second delay between reading from fire hose
        running = True

        logger.info("began listening!")

        while running:  # required to continue to run after experiencing an unexpected exception
            res = self.peek(self._slackSocket.events())
            if res is None:
                self.log_gracefully_shutdown(self.KEYBOARD_INTERRUPT_EXCEPTION_LOG_MESSAGE)
                running = False
                break
            else:
                slack_event, mysequence = res

                if slack_event.event and "bot_id" not in slack_event.event:  # We don't reply to bots
                    try:
                        request = SlackRequest(self._slacker, slack_event)
                        self.route_request_to_callbacks(request)

                        time.sleep(READ_WEBSOCKET_DELAY)
                    except KeyboardInterrupt:
                        self.log_gracefully_shutdown(self.KEYBOARD_INTERRUPT_EXCEPTION_LOG_MESSAGE)
                        running = False
                        break
                    except SystemExit:
                        self.log_gracefully_shutdown(self.SYSTEM_INTERRUPT_EXCEPTION_LOG_MESSAGE)
                        running = False
                        break
                    except Exception as e:
                        logging.warning(f"Unexpected exception caught, but we will keep listening. Exception: {e}")
                        logging.warning(traceback.format_stack())
                        continue  # ensuring the loop continues

        logger.info("stopped listening!")

    def start(self):
        """Connect the Slack bot to the chatroom and begin listening
        """

        ok = self._slacker.rtm.start().body["ok"]

        if ok:
            logger.info("started!")
            self.listen()
        else:
            logger.error("Connection failed. Are you connected to the internet? Potentially invalid Slack token? "
                         "Check environment variable and \"SLACK_BOT_TOKEN\"")

        logger.info("stopped!")

    def get_slacker(self):
        """Returns SimpleSlackBot's SlackClient.

        This is useful if you are writing a more advanced bot and want complete access to all SlackClient has to offer.
        """

        return self._slacker

    def get_slack_socket(self):
        """Returns SimpleSlackBot's SlackSocket.

        This is useful if you are writing a more advanced bot and want complete access to all SlackSocket has to offer.
        """

        return self._slackSocket

    def helper_get_public_channel_ids(self):
        """Helper function that gets all public channel ids
        """

        public_channel_ids = []

        public_channels = self._slacker.channels.list().body["channels"]
        for channel in public_channels:
            public_channel_ids.append(channel["id"])

        if len(public_channel_ids) == 0:
            logger.warning("got no public channel ids")
        else:
            logger.debug(f"got public channel ids {public_channel_ids}")

        return public_channel_ids

    def helper_get_private_channel_ids(self):
        """Helper function that gets all private channel ids
        """

        private_channel_ids = []

        private_channels = self._slacker.groups.list().body["groups"]

        for private_channel in private_channels:
            private_channels.append(private_channel["id"])

        if len(private_channel_ids) == 0:
            logger.warning("got no private channel ids")
        else:
            logger.debug(f"got private channel ids {private_channel_ids}")

        return private_channel_ids

    def helper_get_user_ids(self):
        """Helper function that gets all user ids
        """

        user_ids = []

        users = self._slacker.users.list().body["members"]
        for user in users:
            user_ids.append(user["id"])

        if len(user_ids) == 0:
            logger.warning("got no user ids")
        else:
            logger.debug(f"got user ids {user_ids}")

        return user_ids

    def helper_get_user_names(self):
        """Helper function that gets all user names
        """

        user_names = []

        users = self._slacker.users.list().body["members"]
        for user in users:
            user_names.append(user["name"])

        if len(user_names) == 0:
            logger.warning("got no user names")
        else:
            logger.debug(f"got user names {user_names}")

        return user_names

    def helper_get_users_in_channel(self, channel_id):
        """Helper function that gets all users in a given channel id

        :param channel_id: channel id to get all user ids in it
        :return: list of user ids
        """

        user_ids = []

        channels_list = self._slacker.channels.list().body["channels"]
        for channel in channels_list:
            if channel["id"] == channel_id:
                for user_id in channel["members"]:
                    user_ids.append(user_id)

        if len(user_ids) == 0:
            logger.warning(f"got no user ids for channel {channel_id}")
        else:
            logger.debug(f"got user ids {user_ids}")

        return user_ids

    def helper_channel_name_to_channel_id(self, name):
        """Helper function that converts a channel name to its respected channel id

        :param name: name of channel to convert to id
        :return: id representation of original channel name
        """

        channels_list = self._slacker.channels.list().body["channels"]

        for channel in channels_list:
            if channel["name"] == name:
                logger.debug(f"converted {channel['name']} to {channel['id']}")
                return channel["id"]

        logger.warning(f"could not convert channel name {name} to an id")

    def helper_user_name_to_user_id(self, name):
        """Helper function that converts a user name to its respected user id

        :param name: name of user to convert to id
        :return: id representation of original user name
        """

        users = self._slacker.users.list().body["members"]

        for user in users:
            if user["name"] == name:
                logger.debug(f"converted {name} to {user['id']}")
                return user["id"]

        logger.warning(f"could not convert user name {name} to a user id")

    def helper_channel_id_to_channel_name(self, channel_id):
        """Helper function that converts a channel id to its respected channel name

        :param channel_id: id of channel to convert to name
        :return: name representation of original channel id
        """

        channels_list = self._slacker.channels.list().body["channels"]

        for channel in channels_list:
            if channel["id"] == channel_id:
                logger.debug("converted {} to {}".format(channel_id, channel["name"]))
                return channel["name"]

        logger.warning(f"could not convert channel id {channel_id} to a name")

    def helper_user_id_to_user_name(self, user_id):
        """Helper function that converts a user id to its respected user name

        :param user_id: id of user to convert to name
        :return: name representation of original user id
        """

        users_list = self._slacker.users.list()

        for user in users_list.body["members"]:
            if user["id"] == user_id:
                logger.debug(f"converted {user_id} to {user['name']}")
                return user["name"]

        logger.warning(f"could not convert user id {user_id} to a name")
<|endoftext|>