<commit_before>from pprint import pformat
import sys
from threading import Lock
try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO
import socket

from django import http
from django.core import signals
from django.core.handlers import base
from django.core.urlresolvers import set_script_prefix
from django.utils import datastructures
from django.utils.encoding import force_unicode, iri_to_uri
from django.utils.log import getLogger

logger = getLogger('django.request')

# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
STATUS_CODE_TEXT = {
    100: 'CONTINUE',
    101: 'SWITCHING PROTOCOLS',
    200: 'OK',
    201: 'CREATED',
    202: 'ACCEPTED',
    203: 'NON-AUTHORITATIVE INFORMATION',
    204: 'NO CONTENT',
    205: 'RESET CONTENT',
    206: 'PARTIAL CONTENT',
    300: 'MULTIPLE CHOICES',
    301: 'MOVED PERMANENTLY',
    302: 'FOUND',
    303: 'SEE OTHER',
    304: 'NOT MODIFIED',
    305: 'USE PROXY',
    306: 'RESERVED',
    307: 'TEMPORARY REDIRECT',
    400: 'BAD REQUEST',
    401: 'UNAUTHORIZED',
    402: 'PAYMENT REQUIRED',
    403: 'FORBIDDEN',
    404: 'NOT FOUND',
    405: 'METHOD NOT ALLOWED',
    406: 'NOT ACCEPTABLE',
    407: 'PROXY AUTHENTICATION REQUIRED',
    408: 'REQUEST TIMEOUT',
    409: 'CONFLICT',
    410: 'GONE',
    411: 'LENGTH REQUIRED',
    412: 'PRECONDITION FAILED',
    413: 'REQUEST ENTITY TOO LARGE',
    414: 'REQUEST-URI TOO LONG',
    415: 'UNSUPPORTED MEDIA TYPE',
    416: 'REQUESTED RANGE NOT SATISFIABLE',
    417: 'EXPECTATION FAILED',
    500: 'INTERNAL SERVER ERROR',
    501: 'NOT IMPLEMENTED',
    502: 'BAD GATEWAY',
    503: 'SERVICE UNAVAILABLE',
    504: 'GATEWAY TIMEOUT',
    505: 'HTTP VERSION NOT SUPPORTED',
}

class LimitedStream(object):
    '''
    LimitedStream wraps another stream in order to not allow reading from it
    past specified amount of bytes.
    '''
    def __init__(self, stream, limit, buf_size=64 * 1024 * 1024):
        self.stream = stream
        self.remaining = limit
        self.buffer = ''
        self.buf_size = buf_size

    def _read_limited(self, size=None):
        if size is None or size > self.remaining:
            size = self.remaining
        if size == 0:
            return ''
        result = self.stream.read(size)
        self.remaining -= len(result)
        return result

    def read(self, size=None):
        if size is None:
            result = self.buffer + self._read_limited()
            self.buffer = ''
        elif size < len(self.buffer):
            result = self.buffer[:size]
            self.buffer = self.buffer[size:]
        else: # size >= len(self.buffer)
            result = self.buffer + self._read_limited(size - len(self.buffer))
            self.buffer = ''
        return result

    def readline(self, size=None):
        while '\n' not in self.buffer or \
              (size is not None and len(self.buffer) < size):
            if size:
                chunk = self._read_limited(size - len(self.buffer))
            else:
                chunk = self._read_limited()
            if not chunk:
                break
            self.buffer += chunk
        sio = StringIO(self.buffer)
        if size:
            line = sio.readline(size)
        else:
            line = sio.readline()
        self.buffer = sio.read()
        return line

class WSGIRequest(http.HttpRequest):
    def __init__(self, environ):
        script_name = base.get_script_name(environ)
        path_info = force_unicode(environ.get('PATH_INFO', u'/'))
        if not path_info or path_info == script_name:
            # Sometimes PATH_INFO exists, but is empty (e.g. accessing
            # the SCRIPT_NAME URL without a trailing slash). We really need to
            # operate as if they'd requested '/'. Not amazingly nice to force
            # the path like this, but should be harmless.
            #
            # (The comparison of path_info to script_name is to work around an
            # apparent bug in flup 1.0.1. Se Django ticket #8490).
            path_info = u'/'
        self.environ = environ
        self.path_info = path_info
        self.path = '%s%s' % (script_name, path_info)
        self.META = environ
        self.META['PATH_INFO'] = path_info
        self.META['SCRIPT_NAME'] = script_name
        self.method = environ['REQUEST_METHOD'].upper()
        self._post_parse_error = False
        if isinstance(self.environ['wsgi.input'], socket._fileobject):
            # Under development server 'wsgi.input' is an instance of
            # socket._fileobject which hangs indefinitely on reading bytes past
            # available count. To prevent this it's wrapped in LimitedStream
            # that doesn't read past Content-Length bytes.
            #
            # This is not done for other kinds of inputs (like flup's FastCGI
            # streams) beacuse they don't suffer from this problem and we can
            # avoid using another wrapper with its own .read and .readline
            # implementation.
            try:
                content_length = int(self.environ.get('CONTENT_LENGTH', 0))
            except (ValueError, TypeError):
                content_length = 0
            self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
        else:
            self._stream = self.environ['wsgi.input']
        self._read_started = False

    def __repr__(self):
        # Since this is called as part of error handling, we need to be very
        # robust against potentially malformed input.
        try:
            get = pformat(self.GET)
        except:
            get = '<could not parse>'
        if self._post_parse_error:
            post = '<could not parse>'
        else:
            try:
                post = pformat(self.POST)
            except:
                post = '<could not parse>'
        try:
            cookies = pformat(self.COOKIES)
        except:
            cookies = '<could not parse>'
        try:
            meta = pformat(self.META)
        except:
            meta = '<could not parse>'
        return '<WSGIRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
            (get, post, cookies, meta)

    def get_full_path(self):
        # RFC 3986 requires query string arguments to be in the ASCII range.
        # Rather than crash if this doesn't happen, we encode defensively.
        return '%s%s' % (self.path, self.environ.get('QUERY_STRING', '') and ('?' + iri_to_uri(self.environ.get('QUERY_STRING', ''))) or '')

    def is_secure(self):
        return 'wsgi.url_scheme' in self.environ \
            and self.environ['wsgi.url_scheme'] == 'https'

    def _get_request(self):
        if not hasattr(self, '_request'):
            self._request = datastructures.MergeDict(self.POST, self.GET)
        return self._request

    def _get_get(self):
        if not hasattr(self, '_get'):
            # The WSGI spec says 'QUERY_STRING' may be absent.
            self._get = http.QueryDict(self.environ.get('QUERY_STRING', ''), encoding=self._encoding)
        return self._get

    def _set_get(self, get):
        self._get = get

    def _get_post(self):
        if not hasattr(self, '_post'):
            self._load_post_and_files()
        return self._post

    def _set_post(self, post):
        self._post = post

    def _get_cookies(self):
        if not hasattr(self, '_cookies'):
            self._cookies = http.parse_cookie(self.environ.get('HTTP_COOKIE', ''))
        return self._cookies

    def _set_cookies(self, cookies):
        self._cookies = cookies

    def _get_files(self):
        if not hasattr(self, '_files'):
            self._load_post_and_files()
        return self._files

    GET = property(_get_get, _set_get)
    POST = property(_get_post, _set_post)
    COOKIES = property(_get_cookies, _set_cookies)
    FILES = property(_get_files)
    REQUEST = property(_get_request)

class WSGIHandler(base.BaseHandler):
    initLock = Lock()
    request_class = WSGIRequest

    def __call__(self, environ, start_response):
        from django.conf import settings

        # Set up middleware if needed. We couldn't do this earlier, because
        # settings weren't available.
        if self._request_middleware is None:
            self.initLock.acquire()
            # Check that middleware is still uninitialised.
            if self._request_middleware is None:
                self.load_middleware()
            self.initLock.release()

        set_script_prefix(base.get_script_name(environ))
        signals.request_started.send(sender=self.__class__)
        try:
            try:
                request = self.request_class(environ)
            except UnicodeDecodeError:
                logger.warning('Bad Request (UnicodeDecodeError): %s' % request.path,
                    exc_info=sys.exc_info(),
                    extra={
                        'status_code': 400,
                        'request': request
                    }
                )
                response = http.HttpResponseBadRequest()
            else:
                response = self.get_response(request)
        finally:
            signals.request_finished.send(sender=self.__class__)

        try:
            status_text = STATUS_CODE_TEXT[response.status_code]
        except KeyError:
            status_text = 'UNKNOWN STATUS CODE'
        status = '%s %s' % (response.status_code, status_text)
        response_headers = [(str(k), str(v)) for k, v in response.items()]
        for c in response.cookies.values():
            response_headers.append(('Set-Cookie', str(c.output(header=''))))
        start_response(status, response_headers)
        return response

<commit_msg>Fixed #14602 -- Added an extra check to wsgi.input handling to prevent AppEngine from choking. Thanks to Waldemar Kornewald for the report.<commit_after>from pprint import pformat
import sys
from threading import Lock
try:
    from cStringIO import StringIO
except ImportError:
    from StringIO import StringIO
import socket

from django import http
from django.core import signals
from django.core.handlers import base
from django.core.urlresolvers import set_script_prefix
from django.utils import datastructures
from django.utils.encoding import force_unicode, iri_to_uri
from django.utils.log import getLogger

logger = getLogger('django.request')

# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
STATUS_CODE_TEXT = {
    100: 'CONTINUE',
    101: 'SWITCHING PROTOCOLS',
    200: 'OK',
    201: 'CREATED',
    202: 'ACCEPTED',
    203: 'NON-AUTHORITATIVE INFORMATION',
    204: 'NO CONTENT',
    205: 'RESET CONTENT',
    206: 'PARTIAL CONTENT',
    300: 'MULTIPLE CHOICES',
    301: 'MOVED PERMANENTLY',
    302: 'FOUND',
    303: 'SEE OTHER',
    304: 'NOT MODIFIED',
    305: 'USE PROXY',
    306: 'RESERVED',
    307: 'TEMPORARY REDIRECT',
    400: 'BAD REQUEST',
    401: 'UNAUTHORIZED',
    402: 'PAYMENT REQUIRED',
    403: 'FORBIDDEN',
    404: 'NOT FOUND',
    405: 'METHOD NOT ALLOWED',
    406: 'NOT ACCEPTABLE',
    407: 'PROXY AUTHENTICATION REQUIRED',
    408: 'REQUEST TIMEOUT',
    409: 'CONFLICT',
    410: 'GONE',
    411: 'LENGTH REQUIRED',
    412: 'PRECONDITION FAILED',
    413: 'REQUEST ENTITY TOO LARGE',
    414: 'REQUEST-URI TOO LONG',
    415: 'UNSUPPORTED MEDIA TYPE',
    416: 'REQUESTED RANGE NOT SATISFIABLE',
    417: 'EXPECTATION FAILED',
    500: 'INTERNAL SERVER ERROR',
    501: 'NOT IMPLEMENTED',
    502: 'BAD GATEWAY',
    503: 'SERVICE UNAVAILABLE',
    504: 'GATEWAY TIMEOUT',
    505: 'HTTP VERSION NOT SUPPORTED',
}

class LimitedStream(object):
    '''
    LimitedStream wraps another stream in order to not allow reading from it
    past specified amount of bytes.
    '''
    def __init__(self, stream, limit, buf_size=64 * 1024 * 1024):
        self.stream = stream
        self.remaining = limit
        self.buffer = ''
        self.buf_size = buf_size

    def _read_limited(self, size=None):
        if size is None or size > self.remaining:
            size = self.remaining
        if size == 0:
            return ''
        result = self.stream.read(size)
        self.remaining -= len(result)
        return result

    def read(self, size=None):
        if size is None:
            result = self.buffer + self._read_limited()
            self.buffer = ''
        elif size < len(self.buffer):
            result = self.buffer[:size]
            self.buffer = self.buffer[size:]
        else: # size >= len(self.buffer)
            result = self.buffer + self._read_limited(size - len(self.buffer))
            self.buffer = ''
        return result

    def readline(self, size=None):
        while '\n' not in self.buffer or \
              (size is not None and len(self.buffer) < size):
            if size:
                chunk = self._read_limited(size - len(self.buffer))
            else:
                chunk = self._read_limited()
            if not chunk:
                break
            self.buffer += chunk
        sio = StringIO(self.buffer)
        if size:
            line = sio.readline(size)
        else:
            line = sio.readline()
        self.buffer = sio.read()
        return line

class WSGIRequest(http.HttpRequest):
    def __init__(self, environ):
        script_name = base.get_script_name(environ)
        path_info = force_unicode(environ.get('PATH_INFO', u'/'))
        if not path_info or path_info == script_name:
            # Sometimes PATH_INFO exists, but is empty (e.g. accessing
            # the SCRIPT_NAME URL without a trailing slash). We really need to
            # operate as if they'd requested '/'. Not amazingly nice to force
            # the path like this, but should be harmless.
            #
            # (The comparison of path_info to script_name is to work around an
            # apparent bug in flup 1.0.1. Se Django ticket #8490).
            path_info = u'/'
        self.environ = environ
        self.path_info = path_info
        self.path = '%s%s' % (script_name, path_info)
        self.META = environ
        self.META['PATH_INFO'] = path_info
        self.META['SCRIPT_NAME'] = script_name
        self.method = environ['REQUEST_METHOD'].upper()
        self._post_parse_error = False
        if type(socket._fileobject) is type and isinstance(self.environ['wsgi.input'], socket._fileobject):
            # Under development server 'wsgi.input' is an instance of
            # socket._fileobject which hangs indefinitely on reading bytes past
            # available count. To prevent this it's wrapped in LimitedStream
            # that doesn't read past Content-Length bytes.
            #
            # This is not done for other kinds of inputs (like flup's FastCGI
            # streams) beacuse they don't suffer from this problem and we can
            # avoid using another wrapper with its own .read and .readline
            # implementation.
            #
            # The type check is done because for some reason, AppEngine
            # implements _fileobject as a function, not a class.
            try:
                content_length = int(self.environ.get('CONTENT_LENGTH', 0))
            except (ValueError, TypeError):
                content_length = 0
            self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
        else:
            self._stream = self.environ['wsgi.input']
        self._read_started = False

    def __repr__(self):
        # Since this is called as part of error handling, we need to be very
        # robust against potentially malformed input.
        try:
            get = pformat(self.GET)
        except:
            get = '<could not parse>'
        if self._post_parse_error:
            post = '<could not parse>'
        else:
            try:
                post = pformat(self.POST)
            except:
                post = '<could not parse>'
        try:
            cookies = pformat(self.COOKIES)
        except:
            cookies = '<could not parse>'
        try:
            meta = pformat(self.META)
        except:
            meta = '<could not parse>'
        return '<WSGIRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
            (get, post, cookies, meta)

    def get_full_path(self):
        # RFC 3986 requires query string arguments to be in the ASCII range.
        # Rather than crash if this doesn't happen, we encode defensively.
        return '%s%s' % (self.path, self.environ.get('QUERY_STRING', '') and ('?' + iri_to_uri(self.environ.get('QUERY_STRING', ''))) or '')

    def is_secure(self):
        return 'wsgi.url_scheme' in self.environ \
            and self.environ['wsgi.url_scheme'] == 'https'

    def _get_request(self):
        if not hasattr(self, '_request'):
            self._request = datastructures.MergeDict(self.POST, self.GET)
        return self._request

    def _get_get(self):
        if not hasattr(self, '_get'):
            # The WSGI spec says 'QUERY_STRING' may be absent.
            self._get = http.QueryDict(self.environ.get('QUERY_STRING', ''), encoding=self._encoding)
        return self._get

    def _set_get(self, get):
        self._get = get

    def _get_post(self):
        if not hasattr(self, '_post'):
            self._load_post_and_files()
        return self._post

    def _set_post(self, post):
        self._post = post

    def _get_cookies(self):
        if not hasattr(self, '_cookies'):
            self._cookies = http.parse_cookie(self.environ.get('HTTP_COOKIE', ''))
        return self._cookies

    def _set_cookies(self, cookies):
        self._cookies = cookies

    def _get_files(self):
        if not hasattr(self, '_files'):
            self._load_post_and_files()
        return self._files

    GET = property(_get_get, _set_get)
    POST = property(_get_post, _set_post)
    COOKIES = property(_get_cookies, _set_cookies)
    FILES = property(_get_files)
    REQUEST = property(_get_request)

class WSGIHandler(base.BaseHandler):
    initLock = Lock()
    request_class = WSGIRequest

    def __call__(self, environ, start_response):
        from django.conf import settings

        # Set up middleware if needed. We couldn't do this earlier, because
        # settings weren't available.
        if self._request_middleware is None:
            self.initLock.acquire()
            # Check that middleware is still uninitialised.
            if self._request_middleware is None:
                self.load_middleware()
            self.initLock.release()

        set_script_prefix(base.get_script_name(environ))
        signals.request_started.send(sender=self.__class__)
        try:
            try:
                request = self.request_class(environ)
            except UnicodeDecodeError:
                logger.warning('Bad Request (UnicodeDecodeError): %s' % request.path,
                    exc_info=sys.exc_info(),
                    extra={
                        'status_code': 400,
                        'request': request
                    }
                )
                response = http.HttpResponseBadRequest()
            else:
                response = self.get_response(request)
        finally:
            signals.request_finished.send(sender=self.__class__)

        try:
            status_text = STATUS_CODE_TEXT[response.status_code]
        except KeyError:
            status_text = 'UNKNOWN STATUS CODE'
        status = '%s %s' % (response.status_code, status_text)
        response_headers = [(str(k), str(v)) for k, v in response.items()]
        for c in response.cookies.values():
            response_headers.append(('Set-Cookie', str(c.output(header=''))))
        start_response(status, response_headers)
        return response

<|endoftext|>


<commit_before>"""
Tests for openedx.core.djangoapps.appsembler.api.views.EnrollmentViewSet

These tests adapted from Appsembler enterprise `appsembler_api` tests

"""
# from django.contrib.sites.models import Site
from django.core.urlresolvers import resolve, reverse
from rest_framework import status
from rest_framework.test import APIRequestFactory, force_authenticate

import ddt
import mock

from openedx.core.djangoapps.site_configuration.tests.factories import (
    SiteFactory,
)

from student.models import CourseEnrollment, CourseEnrollmentAllowed
from student.tests.factories import CourseEnrollmentFactory, UserFactory

from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory

from organizations.models import UserOrganizationMapping
from openedx.core.djangoapps.appsembler.api.sites import (
    get_enrollments_for_site,
)

from openedx.core.djangoapps.appsembler.api.tests.factories import (
    CourseOverviewFactory,
    OrganizationFactory,
    OrganizationCourseFactory,
    UserOrganizationMappingFactory,
)

APPSEMBLER_API_VIEWS_MODULE = 'openedx.core.djangoapps.appsembler.api.v1.views'

class BaseEnrollmentApiTestCase(ModuleStoreTestCase):
    def setUp(self):
        super(BaseEnrollmentApiTestCase, self).setUp()
        self.my_site = SiteFactory(domain='my-site.test')
        self.other_site = SiteFactory(domain='other-site.test')
        self.other_site_org = OrganizationFactory(sites=[self.other_site])
        self.my_site_org = OrganizationFactory(sites=[self.my_site])

        self.my_courses = [CourseFactory.create() for i in range(0, 2)]
        self.my_course_overviews = [
            CourseOverviewFactory(id=course.id) for course in self.my_courses
        ]

        for co in self.my_course_overviews:
            OrganizationCourseFactory(organization=self.my_site_org,
                                      course_id=str(co.id))

        self.my_enrollments = [
            CourseEnrollmentFactory(course=self.my_course_overviews[0]),
            CourseEnrollmentFactory(course=self.my_course_overviews[1]),
        ]

        for enrollment in self.my_enrollments:
            UserOrganizationMappingFactory(user=enrollment.user,
                                           organization=self.my_site_org)

        self.other_enrollments = [CourseEnrollmentFactory()]
        OrganizationCourseFactory(organization=self.other_site_org,
                                  course_id=str(
                                      self.other_enrollments[0].course_overview.id))

        self.caller = UserFactory()
        UserOrganizationMappingFactory(user=self.caller,
                                       organization=self.my_site_org,
                                       is_amc_admin=True)

    def call_enrollment_api(self, method, site, caller, req_extra=None):
        req_extra = req_extra or {}
        url = reverse('tahoe-api:v1:enrollments-list')
        method = getattr(APIRequestFactory(), method)
        request = method(url, **req_extra)
        request.META['HTTP_HOST'] = site.domain
        force_authenticate(request, user=caller)

        view = resolve(url).func
        response = view(request)
        response.render()
        return response

@ddt.ddt
@mock.patch(APPSEMBLER_API_VIEWS_MODULE + '.EnrollmentViewSet.throttle_classes', [])
class EnrollmentApiGetTest(BaseEnrollmentApiTestCase):
    def test_get_all(self):
        response = self.call_enrollment_api('get', self.my_site, self.caller)
        results = response.data['results']
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(results), len(self.my_enrollments))
        # TODO: Validate each record

    def test_get_enrollments_for_course(self):
        selected_course = self.my_course_overviews[0]
        expected_enrollments = [
            CourseEnrollmentFactory(course=selected_course),
            CourseEnrollmentFactory(course=selected_course),
        ]

        for enrollment in expected_enrollments:
            UserOrganizationMappingFactory(user=enrollment.user,
                                           organization=self.my_site_org)
        expected_enrollments.append(self.my_enrollments[0])
        response = self.call_enrollment_api('get', self.my_site, self.caller, {
            'data': {
                'course_id': str(selected_course.id),
            }
        })
        results = response.data['results']

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(results), len(expected_enrollments))
        # TODO: Validate each record

    def test_get_single_enrollment(self):
        pass

    @ddt.data(('user_id', 'id'), ('username', 'username'))
    @ddt.unpack
    def test_get_enrollments_for_user(self, query_param, attr_name):
        # Set up additional test data
        user = UserFactory()
        UserOrganizationMappingFactory(user=self.caller,
                                       organization=self.my_site_org,
                                       is_amc_admin=True)
        courses = [CourseFactory.create() for i in range(0, 3)]
        course_overviews = []
        course_enrollments = []
        for course in courses:
            OrganizationCourseFactory(organization=self.my_site_org,
                                      course_id=str(course.id))
            course_overview = CourseOverviewFactory(id=course.id)
            course_enrollment = CourseEnrollmentFactory(course=course_overview,
                                                        user=user)
            course_overviews.append(course_overview)
            course_enrollments.append(course_enrollment)

        response = self.call_enrollment_api('get', self.my_site, self.caller, {
            'data': {
                query_param: str(getattr(user, attr_name)),
            }
        })
        results = response.data['results']

        expected_course_ids = [str(co.id) for co in course_overviews]
        found_course_ids = [obj['course_details']['course_id'] for obj in results]
        assert set(found_course_ids) == set(expected_course_ids)
        for result in results:
            assert result['user'] == user.username

    def test_invalid_enroll_data_no_learners(self):
        """
        This does a partial test
        """
        co = self.my_course_overviews[0]
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': {
                'action': 'enroll',
                'auto_enroll': True,
                'identifiers': [],
                'email_learners': True,
                'courses': [
                    str(co.id)
                ],
            }
        })
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_invalid_enroll_data_no_courses(self):
        """
        This does a partial test
        """
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': {
                'action': 'enroll',
                'auto_enroll': True,
                'identifiers': ['alpha@example.com', 'bravo@example.com'],
                'email_learners': True,
                'courses': [],
            }
        })
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

@ddt.ddt
@mock.patch(APPSEMBLER_API_VIEWS_MODULE + '.EnrollmentViewSet.throttle_classes', [])
class EnrollmentApiPostTest(BaseEnrollmentApiTestCase):
    def test_enroll_learners_single_course(self):
        """
        The payload structure is subject to change

        TODO: Refactor: Add a test to ensure that the other site does not have
        any new 'CourseEnrollmentAllowed' records
        """
        co = self.my_course_overviews[0]
        reg_users = [UserFactory.create(), UserFactory.create()]

        for reg_user in reg_users:
            # make sure that the registered users are not in the enrollments
            mode, is_active = CourseEnrollment.enrollment_mode_for_user(reg_user, co.id)
            assert mode is None and is_active is None, "email: {}".format(reg_user.email)

        new_users_emails = ['alpha@example.com', 'bravo@example.com']
        # TODO: make sure these emails don't exist
        for new_user_email in new_users_emails:
            assert not CourseEnrollmentAllowed.objects.filter(email=new_user_email).exists()

        before_my_site_ce_count = get_enrollments_for_site(self.my_site).count()
        before_my_site_user_count = UserOrganizationMapping.objects.filter(
            organization=self.my_site_org).count()

        before_other_site_ce_count = get_enrollments_for_site(self.other_site).count()
        before_other_site_user_count = UserOrganizationMapping.objects.filter(
            organization=self.other_site_org).count()

        payload = {
            'action': 'enroll',
            'auto_enroll': True,
            # Enroll both of the registered users and new ones
            'identifiers': [obj.email for obj in reg_users] + new_users_emails,
            'email_learners': True,
            'courses': [
                str(co.id)
            ],
        }
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': payload,
        })
        results = response.data['results']
        after_my_site_ce_count = get_enrollments_for_site(self.my_site).count()
        after_my_site_user_count = UserOrganizationMapping.objects.filter(
            organization=self.my_site_org).count()

        after_other_site_ce_count = get_enrollments_for_site(self.other_site).count()
        after_other_site_user_count = UserOrganizationMapping.objects.filter(
            organization=self.other_site_org).count()

        assert after_other_site_ce_count == before_other_site_ce_count
        assert after_other_site_user_count == before_other_site_user_count

        assert after_my_site_ce_count == before_my_site_ce_count + len(reg_users)
        assert after_my_site_user_count == before_my_site_user_count

        # By comparing the total count of CourseEnrollmentAllowed records to the
        # number of new users, we verify that CourseEnrollmentAllowed records
        # are not created for the other site. However, this is a hack and brittle.
        # Therefore we want to test this in a more robust way
        assert CourseEnrollmentAllowed.objects.count() == len(new_users_emails)

        for rec in results:
            assert 'error' not in rec
            if rec['identifier'] in new_users_emails:
                assert CourseEnrollmentAllowed.objects.filter(
                    email=rec['identifier']).exists()

                assert rec['before'] == dict(enrollment=False,
                                             auto_enroll=False,
                                             user=False,
                                             allowed=False)
                assert rec['after'] == dict(enrollment=False,
                                            auto_enroll=payload['auto_enroll'],
                                            user=False,
                                            allowed=True)
            else:
                assert rec['before'] == dict(enrollment=False,
                                             auto_enroll=False,
                                             user=True,
                                             allowed=False)
                assert rec['after'] == dict(enrollment=True,
                                            auto_enroll=False,
                                            user=True,
                                            allowed=False)
                assert not CourseEnrollmentAllowed.objects.filter(
                    email=rec['identifier']).exists()

    def test_enroll_with_other_site_course(self):

        reg_users = [UserFactory(), UserFactory()]
        # TODO: Improvement - make sure these emails don't exist
        learner_emails = [obj.email for obj in reg_users]
        course_ids = [str(co.id) for co in self.my_course_overviews]
        invalid_course_ids = [str(ce.course.id) for ce in self.other_enrollments]
        course_ids.extend(invalid_course_ids)
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': {
                'action': 'enroll',
                'auto_enroll': True,
                'identifiers': learner_emails,
                'email_learners': True,
                'courses': course_ids,
            }
        })
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.data['error'] == 'invalid-course-ids'
        assert set(response.data['invalid_course_ids']) == set(invalid_course_ids)

    def test_enroll_with_unsupported_unenroll(self):
        reg_users = [UserFactory(), UserFactory()]
        # TODO: Improvement - make sure these emails don't exist
        learner_emails = [obj.email for obj in reg_users]
        course_ids = [str(co.id) for co in self.my_course_overviews]
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': {
                'action': 'unenroll',
                'auto_enroll': True,
                'identifiers': learner_emails,
                'email_learners': True,
                'courses': course_ids,
            },
        })
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.data['error'] == 'action-not-supported'
        assert response.data['action_not_supported'] == 'unenroll'

    @ddt.data(None, 'spam', 'delete')
    def test_enroll_with_invalid_action(self, action):
        reg_users = [UserFactory(), UserFactory()]
        # TODO: Improvement - make sure these emails don't exist
        learner_emails = [obj.email for obj in reg_users]
        course_ids = [str(co.id) for co in self.my_course_overviews]
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': {
                'action': action,
                'auto_enroll': True,
                'identifiers': learner_emails,
                'email_learners': True,
                'courses': course_ids,
            },
        })
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.data['action'] == [u'"{}" is not a valid choice.'.format(
            'None' if not action else action)]
<commit_msg>Fix the broken test_enroll_learners_single_course because of bulk enrollment changes<commit_after>"""
Tests for openedx.core.djangoapps.appsembler.api.views.EnrollmentViewSet

These tests adapted from Appsembler enterprise `appsembler_api` tests

"""
# from django.contrib.sites.models import Site
from django.core.urlresolvers import resolve, reverse
from rest_framework import status
from rest_framework.test import APIRequestFactory, force_authenticate

import ddt
import mock

from openedx.core.djangoapps.site_configuration.tests.factories import (
    SiteFactory,
)

from student.models import CourseEnrollment, CourseEnrollmentAllowed
from student.tests.factories import CourseEnrollmentFactory, UserFactory

from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory

from organizations.models import UserOrganizationMapping
from openedx.core.djangoapps.appsembler.api.sites import (
    get_enrollments_for_site,
)

from openedx.core.djangoapps.appsembler.api.tests.factories import (
    CourseOverviewFactory,
    OrganizationFactory,
    OrganizationCourseFactory,
    UserOrganizationMappingFactory,
)

APPSEMBLER_API_VIEWS_MODULE = 'openedx.core.djangoapps.appsembler.api.v1.views'

class BaseEnrollmentApiTestCase(ModuleStoreTestCase):
    def setUp(self):
        super(BaseEnrollmentApiTestCase, self).setUp()
        self.my_site = SiteFactory(domain='my-site.test')
        self.other_site = SiteFactory(domain='other-site.test')
        self.other_site_org = OrganizationFactory(sites=[self.other_site])
        self.my_site_org = OrganizationFactory(sites=[self.my_site])

        self.my_courses = [CourseFactory.create() for i in range(0, 2)]
        self.my_course_overviews = [
            CourseOverviewFactory(id=course.id) for course in self.my_courses
        ]

        for co in self.my_course_overviews:
            OrganizationCourseFactory(organization=self.my_site_org,
                                      course_id=str(co.id))

        self.my_enrollments = [
            CourseEnrollmentFactory(course=self.my_course_overviews[0]),
            CourseEnrollmentFactory(course=self.my_course_overviews[1]),
        ]

        for enrollment in self.my_enrollments:
            UserOrganizationMappingFactory(user=enrollment.user,
                                           organization=self.my_site_org)

        self.other_enrollments = [CourseEnrollmentFactory()]
        OrganizationCourseFactory(organization=self.other_site_org,
                                  course_id=str(
                                      self.other_enrollments[0].course_overview.id))

        self.caller = UserFactory()
        UserOrganizationMappingFactory(user=self.caller,
                                       organization=self.my_site_org,
                                       is_amc_admin=True)

    def call_enrollment_api(self, method, site, caller, req_extra=None):
        req_extra = req_extra or {}
        url = reverse('tahoe-api:v1:enrollments-list')
        method = getattr(APIRequestFactory(), method)
        request = method(url, **req_extra)
        request.META['HTTP_HOST'] = site.domain
        force_authenticate(request, user=caller)

        with mock.patch('lms.djangoapps.instructor.sites.get_current_site', return_value=site):
            with mock.patch('student.models.get_current_site', return_value=site):
                view = resolve(url).func
                response = view(request)
                response.render()
                return response

@ddt.ddt
@mock.patch(APPSEMBLER_API_VIEWS_MODULE + '.EnrollmentViewSet.throttle_classes', [])
class EnrollmentApiGetTest(BaseEnrollmentApiTestCase):
    def test_get_all(self):
        response = self.call_enrollment_api('get', self.my_site, self.caller)
        results = response.data['results']
        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(results), len(self.my_enrollments))
        # TODO: Validate each record

    def test_get_enrollments_for_course(self):
        selected_course = self.my_course_overviews[0]
        expected_enrollments = [
            CourseEnrollmentFactory(course=selected_course),
            CourseEnrollmentFactory(course=selected_course),
        ]

        for enrollment in expected_enrollments:
            UserOrganizationMappingFactory(user=enrollment.user,
                                           organization=self.my_site_org)
        expected_enrollments.append(self.my_enrollments[0])
        response = self.call_enrollment_api('get', self.my_site, self.caller, {
            'data': {
                'course_id': str(selected_course.id),
            }
        })
        results = response.data['results']

        self.assertEqual(response.status_code, 200)
        self.assertEqual(len(results), len(expected_enrollments))
        # TODO: Validate each record

    def test_get_single_enrollment(self):
        pass

    @ddt.data(('user_id', 'id'), ('username', 'username'))
    @ddt.unpack
    def test_get_enrollments_for_user(self, query_param, attr_name):
        # Set up additional test data
        user = UserFactory()
        UserOrganizationMappingFactory(user=self.caller,
                                       organization=self.my_site_org,
                                       is_amc_admin=True)
        courses = [CourseFactory.create() for i in range(0, 3)]
        course_overviews = []
        course_enrollments = []
        for course in courses:
            OrganizationCourseFactory(organization=self.my_site_org,
                                      course_id=str(course.id))
            course_overview = CourseOverviewFactory(id=course.id)
            course_enrollment = CourseEnrollmentFactory(course=course_overview,
                                                        user=user)
            course_overviews.append(course_overview)
            course_enrollments.append(course_enrollment)

        response = self.call_enrollment_api('get', self.my_site, self.caller, {
            'data': {
                query_param: str(getattr(user, attr_name)),
            }
        })
        results = response.data['results']

        expected_course_ids = [str(co.id) for co in course_overviews]
        found_course_ids = [obj['course_details']['course_id'] for obj in results]
        assert set(found_course_ids) == set(expected_course_ids)
        for result in results:
            assert result['user'] == user.username

    def test_invalid_enroll_data_no_learners(self):
        """
        This does a partial test
        """
        co = self.my_course_overviews[0]
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': {
                'action': 'enroll',
                'auto_enroll': True,
                'identifiers': [],
                'email_learners': True,
                'courses': [
                    str(co.id)
                ],
            }
        })
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_invalid_enroll_data_no_courses(self):
        """
        This does a partial test
        """
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': {
                'action': 'enroll',
                'auto_enroll': True,
                'identifiers': ['alpha@example.com', 'bravo@example.com'],
                'email_learners': True,
                'courses': [],
            }
        })
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

@ddt.ddt
@mock.patch(APPSEMBLER_API_VIEWS_MODULE + '.EnrollmentViewSet.throttle_classes', [])
class EnrollmentApiPostTest(BaseEnrollmentApiTestCase):
    def test_enroll_learners_single_course(self):
        """
        The payload structure is subject to change

        TODO: Refactor: Add a test to ensure that the other site does not have
        any new 'CourseEnrollmentAllowed' records
        """
        co = self.my_course_overviews[0]
        reg_users = [UserFactory.create(), UserFactory.create()]

        for reg_user in reg_users:
            # add the users to the site, otherwise they won't have new enrollments
            UserOrganizationMappingFactory(user=reg_user, organization=self.my_site_org)
            # make sure that the registered users are not in the enrollments
            mode, is_active = CourseEnrollment.enrollment_mode_for_user(reg_user, co.id)
            assert mode is None and is_active is None, "email: {}".format(reg_user.email)

        new_users_emails = ['alpha@example.com', 'bravo@example.com']
        # TODO: make sure these emails don't exist
        for new_user_email in new_users_emails:
            assert not CourseEnrollmentAllowed.objects.filter(email=new_user_email).exists()

        before_my_site_ce_count = get_enrollments_for_site(self.my_site).count()
        before_my_site_user_count = UserOrganizationMapping.objects.filter(
            organization=self.my_site_org).count()

        before_other_site_ce_count = get_enrollments_for_site(self.other_site).count()
        before_other_site_user_count = UserOrganizationMapping.objects.filter(
            organization=self.other_site_org).count()

        payload = {
            'action': 'enroll',
            'auto_enroll': True,
            # Enroll both of the registered users and new ones
            'identifiers': [obj.email for obj in reg_users] + new_users_emails,
            'email_learners': True,
            'courses': [
                str(co.id)
            ],
        }
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': payload,
        })
        results = response.data['results']
        after_my_site_ce_count = get_enrollments_for_site(self.my_site).count()
        after_my_site_user_count = UserOrganizationMapping.objects.filter(
            organization=self.my_site_org).count()

        after_other_site_ce_count = get_enrollments_for_site(self.other_site).count()
        after_other_site_user_count = UserOrganizationMapping.objects.filter(
            organization=self.other_site_org).count()

        assert after_other_site_ce_count == before_other_site_ce_count
        assert after_other_site_user_count == before_other_site_user_count

        assert after_my_site_ce_count == before_my_site_ce_count + len(reg_users)
        assert after_my_site_user_count == before_my_site_user_count

        # By comparing the total count of CourseEnrollmentAllowed records to the
        # number of new users, we verify that CourseEnrollmentAllowed records
        # are not created for the other site. However, this is a hack and brittle.
        # Therefore we want to test this in a more robust way
        assert CourseEnrollmentAllowed.objects.count() == len(new_users_emails)

        for rec in results:
            assert 'error' not in rec
            if rec['identifier'] in new_users_emails:
                assert CourseEnrollmentAllowed.objects.filter(
                    email=rec['identifier']).exists()

                assert rec['before'] == dict(enrollment=False,
                                             auto_enroll=False,
                                             user=False,
                                             allowed=False)
                assert rec['after'] == dict(enrollment=False,
                                            auto_enroll=payload['auto_enroll'],
                                            user=False,
                                            allowed=True)
            else:
                assert rec['before'] == dict(enrollment=False,
                                             auto_enroll=False,
                                             user=True,
                                             allowed=False)
                assert rec['after'] == dict(enrollment=True,
                                            auto_enroll=False,
                                            user=True,
                                            allowed=False)
                assert not CourseEnrollmentAllowed.objects.filter(
                    email=rec['identifier']).exists()

    def test_enroll_with_other_site_course(self):

        reg_users = [UserFactory(), UserFactory()]
        # TODO: Improvement - make sure these emails don't exist
        learner_emails = [obj.email for obj in reg_users]
        course_ids = [str(co.id) for co in self.my_course_overviews]
        invalid_course_ids = [str(ce.course.id) for ce in self.other_enrollments]
        course_ids.extend(invalid_course_ids)
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': {
                'action': 'enroll',
                'auto_enroll': True,
                'identifiers': learner_emails,
                'email_learners': True,
                'courses': course_ids,
            }
        })
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.data['error'] == 'invalid-course-ids'
        assert set(response.data['invalid_course_ids']) == set(invalid_course_ids)

    def test_enroll_with_unsupported_unenroll(self):
        reg_users = [UserFactory(), UserFactory()]
        # TODO: Improvement - make sure these emails don't exist
        learner_emails = [obj.email for obj in reg_users]
        course_ids = [str(co.id) for co in self.my_course_overviews]
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': {
                'action': 'unenroll',
                'auto_enroll': True,
                'identifiers': learner_emails,
                'email_learners': True,
                'courses': course_ids,
            },
        })
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.data['error'] == 'action-not-supported'
        assert response.data['action_not_supported'] == 'unenroll'

    @ddt.data(None, 'spam', 'delete')
    def test_enroll_with_invalid_action(self, action):
        reg_users = [UserFactory(), UserFactory()]
        # TODO: Improvement - make sure these emails don't exist
        learner_emails = [obj.email for obj in reg_users]
        course_ids = [str(co.id) for co in self.my_course_overviews]
        response = self.call_enrollment_api('post', self.my_site, self.caller, {
            'data': {
                'action': action,
                'auto_enroll': True,
                'identifiers': learner_emails,
                'email_learners': True,
                'courses': course_ids,
            },
        })
        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert response.data['action'] == [u'"{}" is not a valid choice.'.format(
            'None' if not action else action)]
<|endoftext|>


<commit_before>"""Display details for a specified volume."""
# :license: MIT, see LICENSE for more details.

import click
import SoftLayer
from SoftLayer.CLI import environment
from SoftLayer.CLI import formatting
from SoftLayer import utils

@click.command()
@click.argument('volume_id')
@environment.pass_env
def cli(env, volume_id):
    """Display details for a specified volume."""
    file_manager = SoftLayer.FileStorageManager(env.client)
    file_volume = file_manager.get_file_volume_details(volume_id)
    file_volume = utils.NestedDict(file_volume)

    table = formatting.KeyValueTable(['Name', 'Value'])
    table.align['Name'] = 'r'
    table.align['Value'] = 'l'

    storage_type = file_volume['storageType']['keyName'].split('_').pop(0)
    table.add_row(['ID', file_volume['id']])
    table.add_row(['Username', file_volume['username']])
    table.add_row(['Type', storage_type])
    table.add_row(['Capacity (GB)', "%iGB" % file_volume['capacityGb']])

    used_space = int(file_volume['bytesUsed'])\
        if file_volume['bytesUsed'] else 0
    if used_space < (1 << 10):
        table.add_row(['Used Space', "%dB" % used_space])
    elif used_space < (1 << 20):
        table.add_row(['Used Space', "%dKB" % (used_space / (1 << 10))])
    elif used_space < (1 << 30):
        table.add_row(['Used Space', "%dMB" % (used_space / (1 << 20))])
    else:
        table.add_row(['Used Space', "%dGB" % (used_space / (1 << 30))])

    if file_volume.get('provisionedIops'):
        table.add_row(['IOPs', int(file_volume['provisionedIops'])])

    if file_volume.get('storageTierLevel'):
        table.add_row([
            'Endurance Tier',
            file_volume['storageTierLevel'],
        ])

    table.add_row([
        'Data Center',
        file_volume['serviceResource']['datacenter']['name'],
    ])
    table.add_row([
        'Target IP',
        file_volume['serviceResourceBackendIpAddress'],
    ])

    if file_volume['fileNetworkMountAddress']:
        table.add_row([
            'Mount Address',
            file_volume['fileNetworkMountAddress'],
        ])

    if file_volume['snapshotCapacityGb']:
        table.add_row([
            'Snapshot Capacity (GB)',
            file_volume['snapshotCapacityGb'],
        ])
        if 'snapshotSizeBytes' in file_volume['parentVolume']:
            table.add_row([
                'Snapshot Used (Bytes)',
                file_volume['parentVolume']['snapshotSizeBytes'],
            ])

    table.add_row(['# of Active Transactions', "%i"
                   % file_volume['activeTransactionCount']])

    if file_volume['activeTransactions']:
        for trans in file_volume['activeTransactions']:
            if 'transactionStatus' in trans and 'friendlyName' in trans['transactionStatus']:
                table.add_row(['Ongoing Transaction', trans['transactionStatus']['friendlyName']])

    table.add_row(['Replicant Count', "%u" % file_volume.get('replicationPartnerCount', 0)])

    if file_volume['replicationPartnerCount'] > 0:
        # This if/else temporarily handles a bug in which the SL API
        # returns a string or object for 'replicationStatus'; it seems that
        # the type is string for File volumes and object for Block volumes
        if 'message' in file_volume['replicationStatus']:
            table.add_row(['Replication Status', "%s"
                           % file_volume['replicationStatus']['message']])
        else:
            table.add_row(['Replication Status', "%s"
                           % file_volume['replicationStatus']])

        replicant_list = []
        for replicant in file_volume['replicationPartners']:
            replicant_table = formatting.Table(['Replicant ID',
                                                replicant['id']])
            replicant_table.add_row([
                'Volume Name',
                utils.lookup(replicant, 'username')])
            replicant_table.add_row([
                'Target IP',
                utils.lookup(replicant, 'serviceResourceBackendIpAddress')])
            replicant_table.add_row([
                'Data Center',
                utils.lookup(replicant,
                             'serviceResource', 'datacenter', 'name')])
            replicant_table.add_row([
                'Schedule',
                utils.lookup(replicant,
                             'replicationSchedule', 'type', 'keyname')])
            replicant_list.append(replicant_table)
        table.add_row(['Replicant Volumes', replicant_list])

    if file_volume.get('originalVolumeSize'):
        original_volume_info = formatting.Table(['Property', 'Value'])
        original_volume_info.add_row(['Original Volume Size', file_volume['originalVolumeSize']])
        if file_volume.get('originalVolumeName'):
            original_volume_info.add_row(['Original Volume Name', file_volume['originalVolumeName']])
        if file_volume.get('originalSnapshotName'):
            original_volume_info.add_row(['Original Snapshot Name', file_volume['originalSnapshotName']])
        table.add_row(['Original Volume Properties', original_volume_info])

    env.fout(table)
<commit_msg>Update provisionedIops reading to handle float-y values<commit_after>"""Display details for a specified volume."""
# :license: MIT, see LICENSE for more details.

import click
import SoftLayer
from SoftLayer.CLI import environment
from SoftLayer.CLI import formatting
from SoftLayer import utils

@click.command()
@click.argument('volume_id')
@environment.pass_env
def cli(env, volume_id):
    """Display details for a specified volume."""
    file_manager = SoftLayer.FileStorageManager(env.client)
    file_volume = file_manager.get_file_volume_details(volume_id)
    file_volume = utils.NestedDict(file_volume)

    table = formatting.KeyValueTable(['Name', 'Value'])
    table.align['Name'] = 'r'
    table.align['Value'] = 'l'

    storage_type = file_volume['storageType']['keyName'].split('_').pop(0)
    table.add_row(['ID', file_volume['id']])
    table.add_row(['Username', file_volume['username']])
    table.add_row(['Type', storage_type])
    table.add_row(['Capacity (GB)', "%iGB" % file_volume['capacityGb']])

    used_space = int(file_volume['bytesUsed'])\
        if file_volume['bytesUsed'] else 0
    if used_space < (1 << 10):
        table.add_row(['Used Space', "%dB" % used_space])
    elif used_space < (1 << 20):
        table.add_row(['Used Space', "%dKB" % (used_space / (1 << 10))])
    elif used_space < (1 << 30):
        table.add_row(['Used Space', "%dMB" % (used_space / (1 << 20))])
    else:
        table.add_row(['Used Space', "%dGB" % (used_space / (1 << 30))])

    if file_volume.get('provisionedIops'):
        table.add_row(['IOPs', float(file_volume['provisionedIops'])])

    if file_volume.get('storageTierLevel'):
        table.add_row([
            'Endurance Tier',
            file_volume['storageTierLevel'],
        ])

    table.add_row([
        'Data Center',
        file_volume['serviceResource']['datacenter']['name'],
    ])
    table.add_row([
        'Target IP',
        file_volume['serviceResourceBackendIpAddress'],
    ])

    if file_volume['fileNetworkMountAddress']:
        table.add_row([
            'Mount Address',
            file_volume['fileNetworkMountAddress'],
        ])

    if file_volume['snapshotCapacityGb']:
        table.add_row([
            'Snapshot Capacity (GB)',
            file_volume['snapshotCapacityGb'],
        ])
        if 'snapshotSizeBytes' in file_volume['parentVolume']:
            table.add_row([
                'Snapshot Used (Bytes)',
                file_volume['parentVolume']['snapshotSizeBytes'],
            ])

    table.add_row(['# of Active Transactions', "%i"
                   % file_volume['activeTransactionCount']])

    if file_volume['activeTransactions']:
        for trans in file_volume['activeTransactions']:
            if 'transactionStatus' in trans and 'friendlyName' in trans['transactionStatus']:
                table.add_row(['Ongoing Transaction', trans['transactionStatus']['friendlyName']])

    table.add_row(['Replicant Count', "%u" % file_volume.get('replicationPartnerCount', 0)])

    if file_volume['replicationPartnerCount'] > 0:
        # This if/else temporarily handles a bug in which the SL API
        # returns a string or object for 'replicationStatus'; it seems that
        # the type is string for File volumes and object for Block volumes
        if 'message' in file_volume['replicationStatus']:
            table.add_row(['Replication Status', "%s"
                           % file_volume['replicationStatus']['message']])
        else:
            table.add_row(['Replication Status', "%s"
                           % file_volume['replicationStatus']])

        replicant_list = []
        for replicant in file_volume['replicationPartners']:
            replicant_table = formatting.Table(['Replicant ID',
                                                replicant['id']])
            replicant_table.add_row([
                'Volume Name',
                utils.lookup(replicant, 'username')])
            replicant_table.add_row([
                'Target IP',
                utils.lookup(replicant, 'serviceResourceBackendIpAddress')])
            replicant_table.add_row([
                'Data Center',
                utils.lookup(replicant,
                             'serviceResource', 'datacenter', 'name')])
            replicant_table.add_row([
                'Schedule',
                utils.lookup(replicant,
                             'replicationSchedule', 'type', 'keyname')])
            replicant_list.append(replicant_table)
        table.add_row(['Replicant Volumes', replicant_list])

    if file_volume.get('originalVolumeSize'):
        original_volume_info = formatting.Table(['Property', 'Value'])
        original_volume_info.add_row(['Original Volume Size', file_volume['originalVolumeSize']])
        if file_volume.get('originalVolumeName'):
            original_volume_info.add_row(['Original Volume Name', file_volume['originalVolumeName']])
        if file_volume.get('originalSnapshotName'):
            original_volume_info.add_row(['Original Snapshot Name', file_volume['originalSnapshotName']])
        table.add_row(['Original Volume Properties', original_volume_info])

    env.fout(table)
<|endoftext|>


<commit_before>#
# Django Development
# ..........................

DEBUG = True

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

#
# Developper additions
# ..........................

INSTALLED_APPS = (
    'django_extensions',
    'debug_toolbar',
) + INSTALLED_APPS

INTERNAL_IPS = type(str('c'), (), {'__contains__': lambda *a: True})()

ALLOWED_HOSTS = ['*']

MIDDLEWARE += (
    'debug_toolbar.middleware.DebugToolbarMiddleware',
)
#
# Use some default tiles
# ..........................

LOGGING['loggers']['geotrek']['level'] = 'DEBUG'
LOGGING['loggers']['']['level'] = 'DEBUG'
<commit_msg>Add drf_yasg module for dev env<commit_after>#
# Django Development
# ..........................

DEBUG = True

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

#
# Developper additions
# ..........................

INSTALLED_APPS = (
    'django_extensions',
    'debug_toolbar',
    'drf_yasg',
) + INSTALLED_APPS

INTERNAL_IPS = type(str('c'), (), {'__contains__': lambda *a: True})()

ALLOWED_HOSTS = ['*']

MIDDLEWARE += (
    'debug_toolbar.middleware.DebugToolbarMiddleware',
)
#
# Use some default tiles
# ..........................

LOGGING['loggers']['geotrek']['level'] = 'DEBUG'
LOGGING['loggers']['']['level'] = 'DEBUG'
<|endoftext|>


<commit_before>#!/usr/bin/env python2
#import all the libraries and elements that we need
import time
import sys
import RPi.GPIO as GPIO
#import CloudFunctions

#define global variables defaults
LED_FREQ = 1000	#frequency of LED PWM switching
LOCATION = "CAXX0518"	#location of the weather we want
MODE = "mirror"	#mode we want to be in (default is mirror weather at location)

#open the settings file and grab all our globals from there
config = open("settings.conf", "r")
line = config.readline()
print "Line Read: %s" % (line)
line = config.readline()
print "Line Read: %s" % (line)
line = config.readline()
print "Line Read: %s" % (line)

#define all the functions we need in the program
#function to start all the pins with current duty cycle
def startLeds( leds, duty_cycles ):
	"Function turns on all LEDs in array of LED objects provided"
	print "\nStarting all LEDs!"
	for index in range(len(leds)):
		leds[index].start(duty_cycles[index])
	return

#function to stop all pins
def stopLeds( leds ):
	"Turns off all LEDs in array of LED objects provided"
	print "\nSwitching off all LEDs!"
	for index in range(len(leds)):
		leds[index].stop()
	return

#function to update the pin duty cylces
def updateLeds( leds, duty_cycles ):
	"Updates all leds with latest duty cycle provided"
	print "\nUpdating LED duty cycles!"
	for index in range(len(leds)):
		leds[index].ChangeDutyCycle(duty_cycles[index])
	return

#welcome statements
print "\nCloud Clouds is starting..."

#setup GPIO output pins
print "\nSetting up GPIO pins and libraries..."
GPIO.setmode(GPIO.BCM)
#LED Strip 1
GPIO.setup(0, GPIO.OUT)	#LED Red 1
GPIO.setup(1, GPIO.OUT)	#LED Green 1
GPIO.setup(2, GPIO.OUT)	#LED Blue 1
#LED Strip 2w
GPIO.setup(4, GPIO.OUT)	#LED Red 2
GPIO.setup(5, GPIO.OUT)	#LED Green 2
GPIO.setup(6, GPIO.OUT)	#LED Blue 2
#LED Strip 3
GPIO.setup(12, GPIO.OUT)	#LED Red 3
GPIO.setup(13, GPIO.OUT)	#LED Green 3
GPIO.setup(14, GPIO.OUT)	#LED Blue 3

#create an array to store all the led objects
leds = []
#LED Strip 1
led = GPIO.PWM(0, LED_FREQ)
leds.append(led)
led = GPIO.PWM(1, LED_FREQ)
leds.append(led)
led = GPIO.PWM(2, LED_FREQ)
leds.append(led)
#LED strip 2
led = GPIO.PWM(4, LED_FREQ)
leds.append(led)
led = GPIO.PWM(5, LED_FREQ)
leds.append(led)
led = GPIO.PWM(6, LED_FREQ)
leds.append(led)
#LED strip 3
led = GPIO.PWM(12, LED_FREQ)
leds.append(led)
led = GPIO.PWM(13, LED_FREQ)
leds.append(led)
led = GPIO.PWM(14, LED_FREQ)
leds.append(led)

#create an array to store all the led pin duty cycles
duty_cycles = [1, 1, 1, 1, 1, 1, 1, 1, 1]

#start all the leds
startLeds(leds, duty_cycles)

#main program loop
#inifite, just updates the LED duty cycles from the duty_cycles array and drives them up and down
while (1):
	print "\nMain program loop started."
	#update the duty cycles from 0% to 100%
	for i in range(0, 100, 1):
		for j in range(len(duty_cycles)):
			duty_cycles[j] = i

		#send new duty cyles to LEDs
		updateLeds( leds, duty_cycles)

		#wait for some time for the changes to take effect
		time.sleep(0.2)

	#update the duty cycles from 100% to 0%
	for i in range(100, 0, -1):
		for j in range(len(duty_cycles)):
			duty_cycles[j] = i

		#send new duty cyles to LEDs
		updateLeds( leds, duty_cycles)

		#wait for some time for the changes to take effect
		time.sleep(0.2)<commit_msg>warnings update<commit_after>#!/usr/bin/env python2
#import all the libraries and elements that we need
import time
import sys
import RPi.GPIO as GPIO
#import CloudFunctions

#turn off meaningless warnings
GPIO.setWarnings(False)

#define global variables defaults
LED_FREQ = 1000	#frequency of LED PWM switching
LOCATION = "CAXX0518"	#location of the weather we want
MODE = "mirror"	#mode we want to be in (default is mirror weather at location)

#open the settings file and grab all our globals from there
config = open("settings.conf", "r")
line = config.readline()
print "Line Read: %s" % (line)
line = config.readline()
print "Line Read: %s" % (line)
line = config.readline()
print "Line Read: %s" % (line)

#define all the functions we need in the program
#function to start all the pins with current duty cycle
def startLeds( leds, duty_cycles ):
	"Function turns on all LEDs in array of LED objects provided"
	print "\nStarting all LEDs!"
	for index in range(len(leds)):
		leds[index].start(duty_cycles[index])
	return

#function to stop all pins
def stopLeds( leds ):
	"Turns off all LEDs in array of LED objects provided"
	print "\nSwitching off all LEDs!"
	for index in range(len(leds)):
		leds[index].stop()
	return

#function to update the pin duty cylces
def updateLeds( leds, duty_cycles ):
	"Updates all leds with latest duty cycle provided"
	print "\nUpdating LED duty cycles!"
	for index in range(len(leds)):
		leds[index].ChangeDutyCycle(duty_cycles[index])
	return

#welcome statements
print "\nCloud Clouds is starting..."

#setup GPIO output pins
print "\nSetting up GPIO pins and libraries..."
GPIO.setmode(GPIO.BCM)
#LED Strip 1
GPIO.setup(0, GPIO.OUT)	#LED Red 1
GPIO.setup(1, GPIO.OUT)	#LED Green 1
GPIO.setup(2, GPIO.OUT)	#LED Blue 1
#LED Strip 2w
GPIO.setup(4, GPIO.OUT)	#LED Red 2
GPIO.setup(5, GPIO.OUT)	#LED Green 2
GPIO.setup(6, GPIO.OUT)	#LED Blue 2
#LED Strip 3
GPIO.setup(12, GPIO.OUT)	#LED Red 3
GPIO.setup(13, GPIO.OUT)	#LED Green 3
GPIO.setup(14, GPIO.OUT)	#LED Blue 3

#create an array to store all the led objects
leds = []
#LED Strip 1
led = GPIO.PWM(0, LED_FREQ)
leds.append(led)
led = GPIO.PWM(1, LED_FREQ)
leds.append(led)
led = GPIO.PWM(2, LED_FREQ)
leds.append(led)
#LED strip 2
led = GPIO.PWM(4, LED_FREQ)
leds.append(led)
led = GPIO.PWM(5, LED_FREQ)
leds.append(led)
led = GPIO.PWM(6, LED_FREQ)
leds.append(led)
#LED strip 3
led = GPIO.PWM(12, LED_FREQ)
leds.append(led)
led = GPIO.PWM(13, LED_FREQ)
leds.append(led)
led = GPIO.PWM(14, LED_FREQ)
leds.append(led)

#create an array to store all the led pin duty cycles
duty_cycles = [1, 1, 1, 1, 1, 1, 1, 1, 1]

#start all the leds
startLeds(leds, duty_cycles)

#main program loop
#inifite, just updates the LED duty cycles from the duty_cycles array and drives them up and down
while (1):
	print "\nMain program loop started."
	#update the duty cycles from 0% to 100%
	for i in range(0, 100, 1):
		for j in range(len(duty_cycles)):
			duty_cycles[j] = i

		#send new duty cyles to LEDs
		updateLeds( leds, duty_cycles)

		#wait for some time for the changes to take effect
		time.sleep(0.2)

	#update the duty cycles from 100% to 0%
	for i in range(100, 0, -1):
		for j in range(len(duty_cycles)):
			duty_cycles[j] = i

		#send new duty cyles to LEDs
		updateLeds( leds, duty_cycles)

		#wait for some time for the changes to take effect
		time.sleep(0.2)<|endoftext|>


<commit_before>import os
import asyncio
import threading
import time
import signal
try:
    from asyncio import ensure_future
except ImportError:
    from asyncio import async as ensure_future

import pytest
from shelver.util import AsyncBase, AsyncLoopSupervisor

def test_async_base_init_def():
    async_obj = AsyncBase()
    assert isinstance(async_obj._loop, asyncio.AbstractEventLoop)
    assert async_obj._executor is None

def test_async_base_init_explicit(event_loop):
    executor = object()
    async_obj = AsyncBase(loop=event_loop, executor=executor)

    assert async_obj._loop is event_loop
    assert async_obj._executor is executor

@pytest.mark.asyncio
def test_async_base_delay(event_loop):
    async_obj = AsyncBase(loop=event_loop)

    # Run a sleep in the background with delay, get the time in a second
    # coroutine and compare with the time after the sleep to check for blocking
    def get_time():
        time.sleep(0.2)
        return event_loop.time()

    f = ensure_future(async_obj.delay(get_time))
    t1 = event_loop.time()
    t2 = yield from f

    # If running get_time() blocked, it would have finished before running the
    # subsequent lines, making t2 >= t1.
    assert t1 < t2

@pytest.fixture
def supervisor(event_loop):
    return AsyncLoopSupervisor(event_loop)

def test_async_loop_supervisor_init(event_loop):
    supervisor = AsyncLoopSupervisor(event_loop, timeout=10)
    assert supervisor.loop is event_loop
    assert supervisor.timeout == 10

def _count_cancellations(timeout, times=1, loop=None):
    cancel_count = 0

    for _ in range(times):
        try:
            yield from asyncio.sleep(timeout, loop=loop)
        except asyncio.CancelledError:
            cancel_count += 1

    return cancel_count

def _interrupt(timeout, times=1):
    os.kill(os.getpid(), signal.SIGINT)

    if times > 1:
        t = threading.Timer(timeout, _interrupt, (timeout, times - 1))
        t.start()

@pytest.mark.boxed
def test_async_loop_supervisor_supervise(event_loop, supervisor):
    supervisor.timeout = 1
    cancellations = supervisor.supervise(
        _count_cancellations(0.2, times=1, loop=event_loop))

    assert not supervisor.loop.is_running()
    assert cancellations == 0
    assert not supervisor.timed_out

@pytest.mark.boxed
def test_async_loop_supervisor_interrupt(event_loop, supervisor):
    # Set up the supervisor with a short timeout. Also send a signal a short
    # time after starting. The supervisor should pick up the first signal, send
    # the first cancellation, then afterwards send the second cancellation when
    # the timeout expires.

    t = threading.Timer(0.2, _interrupt, (0.2, 1))
    t.start()
    supervisor.timeout = 0.2

    cancellations = supervisor.supervise(
        _count_cancellations(0.5, times=2, loop=event_loop))

    assert not supervisor.loop.is_running()
    assert cancellations == 2
    assert supervisor.timed_out

@pytest.mark.boxed
def test_async_loop_supervisor_double_interrupt(event_loop, supervisor):
    # Same as above, but send signals twice to check if the second one triggers
    # a second cancellation, just as a timeout would.

    t = threading.Timer(0.2, _interrupt, (0.2, 2))
    t.start()
    supervisor.timeout = 2

    cancellations = supervisor.supervise(
        _count_cancellations(0.5, times=2, loop=event_loop))

    assert not supervisor.loop.is_running()
    assert cancellations == 2
    assert not supervisor.timed_out
<commit_msg>Fix invalid async import in tests<commit_after>import os
import asyncio
import threading
import time
import signal
try:
    from asyncio import ensure_future
except ImportError:
    ensure_future = getattr(asyncio, 'async')

import pytest
from shelver.util import AsyncBase, AsyncLoopSupervisor

def test_async_base_init_def():
    async_obj = AsyncBase()
    assert isinstance(async_obj._loop, asyncio.AbstractEventLoop)
    assert async_obj._executor is None

def test_async_base_init_explicit(event_loop):
    executor = object()
    async_obj = AsyncBase(loop=event_loop, executor=executor)

    assert async_obj._loop is event_loop
    assert async_obj._executor is executor

@pytest.mark.asyncio
def test_async_base_delay(event_loop):
    async_obj = AsyncBase(loop=event_loop)

    # Run a sleep in the background with delay, get the time in a second
    # coroutine and compare with the time after the sleep to check for blocking
    def get_time():
        time.sleep(0.2)
        return event_loop.time()

    f = ensure_future(async_obj.delay(get_time))
    t1 = event_loop.time()
    t2 = yield from f

    # If running get_time() blocked, it would have finished before running the
    # subsequent lines, making t2 >= t1.
    assert t1 < t2

@pytest.fixture
def supervisor(event_loop):
    return AsyncLoopSupervisor(event_loop)

def test_async_loop_supervisor_init(event_loop):
    supervisor = AsyncLoopSupervisor(event_loop, timeout=10)
    assert supervisor.loop is event_loop
    assert supervisor.timeout == 10

def _count_cancellations(timeout, times=1, loop=None):
    cancel_count = 0

    for _ in range(times):
        try:
            yield from asyncio.sleep(timeout, loop=loop)
        except asyncio.CancelledError:
            cancel_count += 1

    return cancel_count

def _interrupt(timeout, times=1):
    os.kill(os.getpid(), signal.SIGINT)

    if times > 1:
        t = threading.Timer(timeout, _interrupt, (timeout, times - 1))
        t.start()

@pytest.mark.boxed
def test_async_loop_supervisor_supervise(event_loop, supervisor):
    supervisor.timeout = 1
    cancellations = supervisor.supervise(
        _count_cancellations(0.2, times=1, loop=event_loop))

    assert not supervisor.loop.is_running()
    assert cancellations == 0
    assert not supervisor.timed_out

@pytest.mark.boxed
def test_async_loop_supervisor_interrupt(event_loop, supervisor):
    # Set up the supervisor with a short timeout. Also send a signal a short
    # time after starting. The supervisor should pick up the first signal, send
    # the first cancellation, then afterwards send the second cancellation when
    # the timeout expires.

    t = threading.Timer(0.2, _interrupt, (0.2, 1))
    t.start()
    supervisor.timeout = 0.2

    cancellations = supervisor.supervise(
        _count_cancellations(0.5, times=2, loop=event_loop))

    assert not supervisor.loop.is_running()
    assert cancellations == 2
    assert supervisor.timed_out

@pytest.mark.boxed
def test_async_loop_supervisor_double_interrupt(event_loop, supervisor):
    # Same as above, but send signals twice to check if the second one triggers
    # a second cancellation, just as a timeout would.

    t = threading.Timer(0.2, _interrupt, (0.2, 2))
    t.start()
    supervisor.timeout = 2

    cancellations = supervisor.supervise(
        _count_cancellations(0.5, times=2, loop=event_loop))

    assert not supervisor.loop.is_running()
    assert cancellations == 2
    assert not supervisor.timed_out
<|endoftext|>


<commit_before>'''Class SigFig for repressenting digits of a measurement that
   convey meaningful inormation (i.e. are significant).

   Current implementation is rather messy and have found we can
   get away simpler data structure to represent the same information.

   None the less, this implementation has been tested and found correct
   for a large class of cases.  Additionally, this file includes some
   simple and dirty testing to be ran as the code is evolved. Constant
   testing is very important as the SigFig class is the core component
   for the chemistry and physics calculations provided in this library.
'''

from decimal import Decimal

from hlab.lexing import Lexer, LexicalError
from hlab.bases import AutoRepr

class SigFig(AutoRepr):

    def __init__(self, arg):
        if isinstance(arg, unicode):
            arg = str(arg)
        if isinstance(arg, str):
            arg = parse_string(arg)
        elif isinstance(arg, Decimal):
            arg = convert_decimal(arg)
        elif isinstance(arg, (int,long)):
            arg = convert_integer(arg)
        elif isinstance(arg, SigFig):
            arg = (arg.sign, arg.digs_pre_dot, arg.dot, arg.digs_post_dot, arg.exp, arg.exp_power)
        (self.sign,
         self.digs_pre_dot,
         self.dot,
         self.digs_post_dot,
         self.exp,
         self.exp_power
         ) = arg

    def repr_args(self):
        return [str(self)]

    def as_tuple(self):
        return (self.sign, self.digs_pre_dot, self.dot, self.digs_post_dot, self.exp, self.exp_power)

    def __str__(self):
        base, exp = self.get_format_args()
        if exp is None:
            return base
        return '%se%s' % (base, exp)

    def get_format_args(self, min_exp_power=3):
        return self.as_scientific()._get_format_args(min_exp_power)

    def get_exp_format_args(self):
        return self.as_scientific()._get_exp_format_args()

    def _get_format_args(self, min_exp_power):
        if min_exp_power!=None and abs(self.exp_power) > max(self.sigfigs, min_exp_power):
            return self._get_exp_format_args()
        digs = map(str, self.digs_pre_dot + self.digs_post_dot)
        if self.sigfigs <= self.exp_power:
            #check for trailing zeros
            if self.digs_post_dot[-1::] == (0,):
                return self._get_exp_format_args()
            digs += ['0'] * (1 + self.exp_power - self.sigfigs)
        #prevet trailing decimal, i.e. 10.
        elif self.exp_power > 0 and len(digs) == self.exp_power+1 and digs[-1] == '0':
            return self._get_exp_format_args()
        else:
            if digs == ['0']:
                return '0', None
            if self.exp_power >= 0:
                if not (1+self.exp_power==len(digs) and digs[-1] != '0'):
                    digs.insert(1+self.exp_power, '.')
            else:
                digs = ['0.'] + ['0'] * (-1 - self.exp_power) + digs
        return ['%s%s' % ('-' if self.sign else '', ''.join(digs)),
                None]

    def _get_exp_format_args(self):
        return ['%s%d%s' % ('-' if self.sign else '',
                            (self.digs_pre_dot or [0])[0],
                            '.' + ''.join(map(str, self.digs_post_dot)) if
                            self.digs_post_dot else ''),
                self.exp_power]

    def as_decimal(self):
        x = self.as_scientific()
        return Decimal((x.sign,
                        x.digs_pre_dot + x.digs_post_dot,
                        x.exp_power - len(x.digs_post_dot)))

    def shift_sigfigs(self, sigfigs=1):
        s = self.as_scientific()
        if s is self:
            s = self.__class__(self)
        assert s is not self
        s.dot = s.exp = True
        if sigfigs > 0:
            s.digs_post_dot += (0,) * sigfigs
        elif -sigfigs > len(s.digs_post_dot):
            raise ValueError("bad shift")
        else:
            s.digs_post_dot = s.digs_post_dot[:sigfigs]
        return s

    @staticmethod
    def strip_leading_zeros(seq):
        i = iter(seq)
        for el in i:
            if el != 0:
                break
        else:
            return []
        return [el] + list(i)

    _sigfigs = None
    @property
    def sigfigs(self):
        '''cache this calculated property as accessed a lot!
        '''
        return self._calculate_sigfigs()
        #if self._sigfigs is None:
        #    self._sigfigs = self._calculate_sigfigs()
        #return self._sigfigs

    def _calculate_sigfigs(self):
        #handle tricky case of zero
        if list(set(self.digs_pre_dot) | set(self.digs_post_dot)) == [0]:
            if self.exp_power == 0:
                return max(1, len(self.digs_post_dot))
            return len(self.digs_pre_dot) + len(self.digs_post_dot)
        if self.dot:
            if set(self.digs_pre_dot) <= set([0]):
                return len(self.strip_leading_zeros(self.digs_post_dot))
            return (len(self.strip_leading_zeros(self.digs_pre_dot)) +
                    len(self.digs_post_dot))
        return len(self.strip_leading_zeros(self.digs_pre_dot[::-1]))

    @property
    def least_significant_place(self):
        if self.dot:
            return self.exp_power - len(self.digs_post_dot)
        n = len(self.digs_pre_dot)
        if tuple(set(self.digs_pre_dot)) == (0,):
            return 0
        while n and self.digs_pre_dot[n-1]==0:
            n -= 1
        return len(self.digs_pre_dot) - n + self.exp_power

    def as_scientific(self):
        if len(self.digs_pre_dot)==1 and self.digs_pre_dot != (0,):
            return self
        # strip trailing zeros
        trailing_zeros = 0
        digs_pre_dot = list(self.digs_pre_dot)
        digs_post_dot = list(self.digs_post_dot)
        if not self.dot:
            while digs_pre_dot and digs_pre_dot[-1] == 0:
                trailing_zeros += 1
                digs_pre_dot.pop(-1)
            while digs_post_dot and digs_post_dot[-1] == 0:
                digs_post_dot.pop(-1)
        #combine all digits
        exp_power = self.exp_power + trailing_zeros + len(digs_pre_dot) - 1
        digs = list(digs_pre_dot + digs_post_dot)
        #strip leading zeros
        while digs and digs[0] == 0:
            digs.pop(0)
            exp_power -= 1
        #handle zero
        if not digs:
            sigfigs = self.sigfigs
            return self.__class__((0, (0,), self.dot and sigfigs > 1,
                                   (0,) * sigfigs if self.dot else (),
                                   False, 0))
        return self.__class__((self.sign, tuple(digs[:1:]), len(digs) > 1,
                               tuple(digs[1:]), True, exp_power))

    def round_to_sigfigs(self, n):
        if n<1:
            raise ValueError("bad number of sigfigs to round to %r" % (n,))
        s = self.as_scientific()
        if list(s.digs_pre_dot) != [0]:
            n -= 1
        return self.round_scientific_to_index(n, s)

    def round_to_place(self, n):
        s = self.as_scientific()
        return self.round_scientific_to_index(max(-1, -(n - s.exp_power)), s)

    def round_scientific_to_index(self, index, s=None):
        if s is None:
            s = self.as_scientific()
        digs = list(s.digs_pre_dot + s.digs_post_dot)
        index += 1
        if len(digs) <= index:
            while len(digs) < index:
                digs.append(0)
        else:
            if index==0:
                digs.insert(0,0)
                s.exp_power += 1
                index += 1
            round_dig = digs[index]
            if (round_dig > 5) or (round_dig == 5 and digs[index-1]%2):
                digs[index-1] += 1
            del digs[index::]
            #carry
            for i in xrange(len(digs)-1, 0, -1):
                if digs[i]==10:
                    digs[i] = 0
                    digs[i-1] += 1
            if digs[0]==10:
                digs[0] = 0
                digs.insert(0, 1)
                s.exp_power += 1
        s.digs_pre_dot = (digs[0],)
        s.digs_post_dot = tuple(digs[1:])
        return s

    def __pos__(self):
        return self

    def __neg__(self):
        return self.__class__((int(not self.sign), self.digs_pre_dot, self.dot,
                               self.digs_post_dot, self.exp, self.exp_power))

    def __nonzero__(self):
        return self.as_decimal() != 0

    def __hash__(self):
        return hash(self.as_decimal())

    def perform_binary_operation(self, other, func, rule):
        selfd = self.as_decimal()
        if not isinstance(other, (SigFig, Decimal, int, long)):
            return NotImplemented
        otherd = other.as_decimal() if isinstance(other, SigFig) else other
        valued = func(selfd, otherd)
        value = self.__class__(valued)
        sigfigs = (min(self.sigfigs, other.sigfigs)
                   if isinstance(other, SigFig) else
                   self.sigfigs)
        if rule=='mul':
            value = value.round_to_sigfigs(min(self.sigfigs, other.sigfigs)
                                           if isinstance(other, SigFig) else
                                           self.sigfigs)
            if valued == 0:
                value.digs_pre_dot = ()
        elif rule=='add':
            lsp = (max(self.least_significant_place, other.least_significant_place)
                   if isinstance(other, SigFig) else
                   self.least_significant_place)
            value = value.round_to_place(lsp)
            if value.digs_pre_dot == (0,) and value.digs_post_dot == ():
                value.digs_post_dot = (0,) * max(0, -lsp)
                value.digs_pre_dot = (0,) * max(0, sigfigs-max(0, -lsp))
        else:
            raise ValueError("bad sigfig rule %r" % (rule,))
        return value

    def __mul__(self, other):
        return self.perform_binary_operation(other, lambda a,b: a*b, 'mul')
    def __rmul__(self, other):
        return self.perform_binary_operation(other, lambda a,b: b*a, 'mul')
    def __div__(self, other):
        return self.perform_binary_operation(other, lambda a,b: a/b, 'mul')
    def __rdiv__(self, other):
        return self.perform_binary_operation(other, lambda a,b: b/a, 'mul')
    def __mod__(self, other):
        return self.perform_binary_operation(other, lambda a,b: a%b, 'mul')
    def __rmod__(self, other):
        return self.perform_binary_operation(other, lambda a,b: b%a, 'mul')
    __truediv__ = __div__
    __rtruediv__ = __rdiv__

    def __add__(self, other):
        return self.perform_binary_operation(other, lambda a,b: a+b, 'add')
    def __radd__(self, other):
        return self.perform_binary_operation(other, lambda a,b: b+a, 'add')
    def __sub__(self, other):
        return self.perform_binary_operation(other, lambda a,b: a-b, 'add')
    def __rsub__(self, other):
        return self.perform_binary_operation(other, lambda a,b: b-a, 'add')

    def __gt__(self, other):
        return (self-other).as_decimal() > 0
    def __ge__(self, other):
        return (self-other).as_decimal() >= 0
    def __eq__(self, other):
        if not isinstance(other, (int,long,Decimal,SigFig)):
            return NotImplemented
        try:
            return not (abs((self-other).as_decimal()) > 0)
        except TypeError,e:
            return False
    def __ne__(self, other):
        return not (self == other)
    def __le__(self, other):
        return (self-other).as_decimal() <= 0
    def __lt__(self, other):
        return (self-other).as_decimal() < 0

    def __pow__(self, op):
        if not isinstance(op, (int,long)):
            raise TypeError("pow not implemented for non-integer type %r" % (op,))
        if op < 0:
            return 1 / self**-op
        if op == 0:
            return self / self
        acc = self
        for i in xrange(op-1):
            acc = acc * self
        return acc

    def sqrt(self):
        return self.__class__(self.as_decimal().sqrt()).round_to_sigfigs(self.sigfigs)

def parse_string(bytes):
    lex = Lexer(bytes.strip())
    [pm, digs_pre_dot, dot, digs_post_dot, exp, exp_power
     ] = lex.pulls(r'[+-]', r'\d+', r'\.', r'\d+', r'[eE]', r'[+-]?\d+')
    if not lex.eof or (exp and not exp_power):
        raise LexicalError("bad sigfig literal %r" % (bytes,))
    if not digs_pre_dot:
        digs_pre_dot = '0'
    sign = 1 if pm == '-' else 0
    digs_pre_dot = tuple(map(int, digs_pre_dot))
    digs_post_dot = tuple(map(int, digs_post_dot))
    dot = bool(dot)
    exp = bool(exp)
    exp_power = int(exp_power) if exp_power else 0
    #print 'PARSE', bytes, (sign, digs_pre_dot, dot, digs_post_dot, exp, exp_power)
    return (sign, digs_pre_dot, dot, digs_post_dot, exp, exp_power)

def convert_decimal(d):
    sign, digits, exp = d.as_tuple()
    return (sign, digits, True, (), True, exp)

def convert_integer(i):
    #cheap trick, but works
    return parse_string(str(i))

__name__ == '__main__' and main()
<commit_msg>Removed old_sigfig module<commit_after><|endoftext|>